DiscIO/VolumeWii: Decouple "is encrypted" from "is hashed"

Needed for the next commit. NFS disc images are hashed but not encrypted.

While we're at it, also get rid of SupportsIntegrityCheck.
It does the same thing as old IsEncryptedAndHashed and new HasWiiHashes.
This commit is contained in:
JosJuice 2022-07-31 13:28:01 +02:00
parent b02653722d
commit bb27d4cc95
11 changed files with 101 additions and 59 deletions

View file

@ -1472,10 +1472,9 @@ static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& parti
u32 buffered_blocks = 0;
u32 unbuffered_blocks = 0;
const u32 bytes_per_chunk =
partition != DiscIO::PARTITION_NONE && DVDThread::IsEncryptedAndHashed() ?
DiscIO::VolumeWii::BLOCK_DATA_SIZE :
DVD_ECC_BLOCK_SIZE;
const u32 bytes_per_chunk = partition != DiscIO::PARTITION_NONE && DVDThread::HasWiiHashes() ?
DiscIO::VolumeWii::BLOCK_DATA_SIZE :
DVD_ECC_BLOCK_SIZE;
do
{

View file

@ -184,10 +184,10 @@ bool HasDisc()
return s_disc != nullptr;
}
bool IsEncryptedAndHashed()
bool HasWiiHashes()
{
// IsEncryptedAndHashed is thread-safe, so calling WaitUntilIdle isn't necessary.
return s_disc->IsEncryptedAndHashed();
// HasWiiHashes is thread-safe, so calling WaitUntilIdle isn't necessary.
return s_disc->HasWiiHashes();
}
DiscIO::Platform GetDiscType()

View file

@ -41,7 +41,7 @@ void DoState(PointerWrap& p);
void SetDisc(std::unique_ptr<DiscIO::Volume> disc);
bool HasDisc();
bool IsEncryptedAndHashed();
bool HasWiiHashes();
DiscIO::Platform GetDiscType();
u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition);
IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition);

View file

@ -668,7 +668,7 @@ void DirectoryBlobReader::SetPartitions(std::vector<PartitionWithType>&& partiti
m_partitions.emplace(partition_data_offset, std::move(partitions[i].partition));
m_nonpartition_contents.Add(partition_data_offset, data_size,
ContentPartition{this, 0, partition_data_offset});
const u64 unaligned_next_partition_address = VolumeWii::EncryptedPartitionOffsetToRawOffset(
const u64 unaligned_next_partition_address = VolumeWii::OffsetInHashedPartitionToRawOffset(
data_size, Partition(partition_address), PARTITION_DATA_OFFSET);
partition_address = Common::AlignUp(unaligned_next_partition_address, 0x10000ull);
}
@ -743,7 +743,7 @@ void DirectoryBlobReader::SetPartitionHeader(DirectoryBlobPartition* partition,
if (wrapped_partition)
{
if (m_wrapped_volume->IsEncryptedAndHashed())
if (m_wrapped_volume->HasWiiHashes())
{
const std::optional<u64> offset = m_wrapped_volume->ReadSwappedAndShifted(
wrapped_partition->offset + WII_PARTITION_H3_OFFSET_ADDRESS, PARTITION_NONE);

View file

@ -286,7 +286,7 @@ bool ExportSystemData(const Volume& volume, const Partition& partition,
success &= ExportTicket(volume, partition, export_folder + "/ticket.bin");
success &= ExportTMD(volume, partition, export_folder + "/tmd.bin");
success &= ExportCertificateChain(volume, partition, export_folder + "/cert.bin");
if (volume.IsEncryptedAndHashed())
if (volume.HasWiiHashes())
success &= ExportH3Hashes(volume, partition, export_folder + "/h3.bin");
}

View file

@ -92,7 +92,7 @@ void DiscScrubber::MarkAsUsedE(u64 partition_data_offset, u64 offset, u64 size)
// Compensate for 0x400 (SHA-1) per 0x8000 (cluster), and round to whole clusters
u64 DiscScrubber::ToClusterOffset(u64 offset) const
{
if (m_disc->IsEncryptedAndHashed())
if (m_disc->HasWiiHashes())
return offset / 0x7c00 * CLUSTER_SIZE;
else
return Common::AlignDown(offset, CLUSTER_SIZE);

View file

@ -63,7 +63,8 @@ public:
return static_cast<u64>(*temp) << GetOffsetShift();
}
virtual bool IsEncryptedAndHashed() const { return false; }
virtual bool HasWiiHashes() const { return false; }
virtual bool HasWiiEncryption() const { return false; }
virtual std::vector<Partition> GetPartitions() const { return {}; }
virtual Partition GetGamePartition() const { return PARTITION_NONE; }
virtual std::optional<u32> GetPartitionType(const Partition& partition) const
@ -122,7 +123,6 @@ public:
virtual Platform GetVolumeType() const = 0;
virtual bool IsDatelDisc() const = 0;
virtual bool IsNKit() const = 0;
virtual bool SupportsIntegrityCheck() const { return false; }
virtual bool CheckH3TableIntegrity(const Partition& partition) const { return false; }
virtual bool CheckBlockIntegrity(u64 block_index, const u8* encrypted_data,
const Partition& partition) const

View file

@ -403,9 +403,8 @@ void VolumeVerifier::Start()
m_is_tgc = m_volume.GetBlobType() == BlobType::TGC;
m_is_datel = m_volume.IsDatelDisc();
m_is_not_retail =
(m_volume.GetVolumeType() == Platform::WiiDisc && !m_volume.IsEncryptedAndHashed()) ||
IsDebugSigned();
m_is_not_retail = (m_volume.GetVolumeType() == Platform::WiiDisc && !m_volume.HasWiiHashes()) ||
IsDebugSigned();
const std::vector<Partition> partitions = CheckPartitions();
@ -492,7 +491,7 @@ std::vector<Partition> VolumeVerifier::CheckPartitions()
Common::GetStringT("The update partition is not at its normal position."));
}
const u64 normal_data_offset = m_volume.IsEncryptedAndHashed() ? 0xF800000 : 0x838000;
const u64 normal_data_offset = m_volume.HasWiiHashes() ? 0xF800000 : 0x838000;
if (m_volume.GetPartitionType(partition) == PARTITION_DATA &&
partition.offset != normal_data_offset && !has_channel_partition && !has_install_partition)
{
@ -593,14 +592,14 @@ bool VolumeVerifier::CheckPartition(const Partition& partition)
}
}
if (m_volume.SupportsIntegrityCheck() && !m_volume.CheckH3TableIntegrity(partition))
if (m_volume.HasWiiHashes() && !m_volume.CheckH3TableIntegrity(partition))
{
AddProblem(Severity::Low,
Common::FmtFormatT("The H3 hash table for the {0} partition is not correct.", name));
}
// Prepare for hash verification in the Process step
if (m_volume.SupportsIntegrityCheck())
if (m_volume.HasWiiHashes())
{
const u64 data_size =
m_volume.ReadSwappedAndShifted(partition.offset + 0x2bc, PARTITION_NONE).value_or(0);
@ -780,7 +779,7 @@ void VolumeVerifier::CheckVolumeSize()
Common::GetStringT("The format that the disc image is saved in does not "
"store the size of the disc image."));
if (m_volume.SupportsIntegrityCheck())
if (m_volume.HasWiiHashes())
{
volume_size = m_biggest_verified_offset;
volume_size_roughly_known = true;

View file

@ -41,7 +41,11 @@ VolumeWii::VolumeWii(std::unique_ptr<BlobReader> reader)
{
ASSERT(m_reader);
m_encrypted = m_reader->ReadSwapped<u32>(0x60) == u32(0);
m_has_hashes = m_reader->ReadSwapped<u8>(0x60) == u8(0);
m_has_encryption = m_reader->ReadSwapped<u8>(0x61) == u8(0);
if (m_has_encryption && !m_has_hashes)
ERROR_LOG_FMT(DISCIO, "Wii disc has encryption but no hashes! This probably won't work well");
for (u32 partition_group = 0; partition_group < 4; ++partition_group)
{
@ -114,7 +118,7 @@ VolumeWii::VolumeWii(std::unique_ptr<BlobReader> reader)
};
auto get_h3_table = [this, partition]() -> std::vector<u8> {
if (!m_encrypted)
if (!m_has_hashes)
return {};
const std::optional<u64> h3_table_offset = ReadSwappedAndShifted(
partition.offset + WII_PARTITION_H3_OFFSET_ADDRESS, PARTITION_NONE);
@ -170,35 +174,55 @@ bool VolumeWii::Read(u64 offset, u64 length, u8* buffer, const Partition& partit
const PartitionDetails& partition_details = it->second;
const u64 partition_data_offset = partition.offset + *partition_details.data_offset;
if (m_reader->SupportsReadWiiDecrypted(offset, length, partition_data_offset))
return m_reader->ReadWiiDecrypted(offset, length, buffer, partition_data_offset);
if (!m_encrypted)
if (m_has_hashes && m_has_encryption &&
m_reader->SupportsReadWiiDecrypted(offset, length, partition_data_offset))
{
return m_reader->Read(partition.offset + *partition_details.data_offset + offset, length,
buffer);
return m_reader->ReadWiiDecrypted(offset, length, buffer, partition_data_offset);
}
auto aes_context = partition_details.key->get();
if (!aes_context)
return false;
if (!m_has_hashes)
{
return m_reader->Read(partition_data_offset + offset, length, buffer);
}
Common::AES::Context* aes_context = nullptr;
std::unique_ptr<u8[]> read_buffer = nullptr;
if (m_has_encryption)
{
aes_context = partition_details.key->get();
if (!aes_context)
return false;
read_buffer = std::make_unique<u8[]>(BLOCK_TOTAL_SIZE);
}
auto read_buffer = std::make_unique<u8[]>(BLOCK_TOTAL_SIZE);
while (length > 0)
{
// Calculate offsets
u64 block_offset_on_disc = partition.offset + *partition_details.data_offset +
offset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE;
u64 block_offset_on_disc = partition_data_offset + offset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE;
u64 data_offset_in_block = offset % BLOCK_DATA_SIZE;
if (m_last_decrypted_block != block_offset_on_disc)
{
// Read the current block
if (!m_reader->Read(block_offset_on_disc, BLOCK_TOTAL_SIZE, read_buffer.get()))
return false;
if (m_has_encryption)
{
// Read the current block
if (!m_reader->Read(block_offset_on_disc, BLOCK_TOTAL_SIZE, read_buffer.get()))
return false;
// Decrypt the block's data
DecryptBlockData(read_buffer.get(), m_last_decrypted_block_data, aes_context);
}
else
{
// Read the current block
if (!m_reader->Read(block_offset_on_disc + BLOCK_HEADER_SIZE, BLOCK_DATA_SIZE,
m_last_decrypted_block_data))
{
return false;
}
}
// Decrypt the block's data
DecryptBlockData(read_buffer.get(), m_last_decrypted_block_data, aes_context);
m_last_decrypted_block = block_offset_on_disc;
}
@ -216,9 +240,14 @@ bool VolumeWii::Read(u64 offset, u64 length, u8* buffer, const Partition& partit
return true;
}
bool VolumeWii::IsEncryptedAndHashed() const
bool VolumeWii::HasWiiHashes() const
{
return m_encrypted;
return m_has_hashes;
}
bool VolumeWii::HasWiiEncryption() const
{
return m_has_encryption;
}
std::vector<Partition> VolumeWii::GetPartitions() const
@ -272,8 +301,8 @@ const FileSystem* VolumeWii::GetFileSystem(const Partition& partition) const
return it != m_partitions.end() ? it->second.file_system->get() : nullptr;
}
u64 VolumeWii::EncryptedPartitionOffsetToRawOffset(u64 offset, const Partition& partition,
u64 partition_data_offset)
u64 VolumeWii::OffsetInHashedPartitionToRawOffset(u64 offset, const Partition& partition,
u64 partition_data_offset)
{
if (partition == PARTITION_NONE)
return offset;
@ -289,10 +318,10 @@ u64 VolumeWii::PartitionOffsetToRawOffset(u64 offset, const Partition& partition
return offset;
const u64 data_offset = *it->second.data_offset;
if (!m_encrypted)
if (!m_has_hashes)
return partition.offset + data_offset + offset;
return EncryptedPartitionOffsetToRawOffset(offset, partition, data_offset);
return OffsetInHashedPartitionToRawOffset(offset, partition, data_offset);
}
std::string VolumeWii::GetGameTDBID(const Partition& partition) const
@ -415,23 +444,37 @@ bool VolumeWii::CheckBlockIntegrity(u64 block_index, const u8* encrypted_data,
if (block_index / BLOCKS_PER_GROUP * Common::SHA1::DIGEST_LEN >=
partition_details.h3_table->size())
{
return false;
auto aes_context = partition_details.key->get();
if (!aes_context)
return false;
}
HashBlock hashes;
DecryptBlockHashes(encrypted_data, &hashes, aes_context);
u8 cluster_data_buffer[BLOCK_DATA_SIZE];
const u8* cluster_data;
auto cluster_data = std::make_unique<u8[]>(BLOCK_DATA_SIZE);
DecryptBlockData(encrypted_data, cluster_data.get(), aes_context);
if (m_has_encryption)
{
Common::AES::Context* aes_context = partition_details.key->get();
if (!aes_context)
return false;
DecryptBlockHashes(encrypted_data, &hashes, aes_context);
DecryptBlockData(encrypted_data, cluster_data_buffer, aes_context);
cluster_data = cluster_data_buffer;
}
else
{
std::memcpy(&hashes, encrypted_data, BLOCK_HEADER_SIZE);
cluster_data = encrypted_data + BLOCK_HEADER_SIZE;
}
for (u32 hash_index = 0; hash_index < 31; ++hash_index)
{
if (Common::SHA1::CalculateDigest(&cluster_data[hash_index * 0x400], 0x400) !=
hashes.h0[hash_index])
{
return false;
}
}
if (Common::SHA1::CalculateDigest(hashes.h0) != hashes.h1[block_index % 8])

View file

@ -60,7 +60,8 @@ public:
VolumeWii(std::unique_ptr<BlobReader> reader);
~VolumeWii();
bool Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const override;
bool IsEncryptedAndHashed() const override;
bool HasWiiHashes() const override;
bool HasWiiEncryption() const override;
std::vector<Partition> GetPartitions() const override;
Partition GetGamePartition() const override;
std::optional<u32> GetPartitionType(const Partition& partition) const override;
@ -69,8 +70,8 @@ public:
const IOS::ES::TMDReader& GetTMD(const Partition& partition) const override;
const std::vector<u8>& GetCertificateChain(const Partition& partition) const override;
const FileSystem* GetFileSystem(const Partition& partition) const override;
static u64 EncryptedPartitionOffsetToRawOffset(u64 offset, const Partition& partition,
u64 partition_data_offset);
static u64 OffsetInHashedPartitionToRawOffset(u64 offset, const Partition& partition,
u64 partition_data_offset);
u64 PartitionOffsetToRawOffset(u64 offset, const Partition& partition) const override;
std::string GetGameTDBID(const Partition& partition = PARTITION_NONE) const override;
std::map<Language, std::string> GetLongNames() const override;
@ -78,7 +79,6 @@ public:
Platform GetVolumeType() const override;
bool IsDatelDisc() const override;
bool SupportsIntegrityCheck() const override { return m_encrypted; }
bool CheckH3TableIntegrity(const Partition& partition) const override;
bool CheckBlockIntegrity(u64 block_index, const u8* encrypted_data,
const Partition& partition) const override;
@ -128,7 +128,8 @@ private:
std::unique_ptr<BlobReader> m_reader;
std::map<Partition, PartitionDetails> m_partitions;
Partition m_game_partition;
bool m_encrypted;
bool m_has_hashes;
bool m_has_encryption;
mutable u64 m_last_decrypted_block;
mutable u8 m_last_decrypted_block_data[BLOCK_DATA_SIZE]{};

View file

@ -925,7 +925,7 @@ ConversionResultCode WIARVZFileReader<RVZ>::SetUpDataEntriesForWriting(
std::vector<DataEntry>* data_entries, std::vector<const FileSystem*>* partition_file_systems)
{
std::vector<Partition> partitions;
if (volume && volume->IsEncryptedAndHashed())
if (volume && volume->HasWiiHashes() && volume->HasWiiEncryption())
partitions = volume->GetPartitions();
std::sort(partitions.begin(), partitions.end(),