From bb27d4cc95ccdaacfb4dce9a2534534d263ce20b Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 31 Jul 2022 13:28:01 +0200 Subject: [PATCH] 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. --- Source/Core/Core/HW/DVD/DVDInterface.cpp | 7 +- Source/Core/Core/HW/DVD/DVDThread.cpp | 6 +- Source/Core/Core/HW/DVD/DVDThread.h | 2 +- Source/Core/DiscIO/DirectoryBlob.cpp | 4 +- Source/Core/DiscIO/DiscExtractor.cpp | 2 +- Source/Core/DiscIO/DiscScrubber.cpp | 2 +- Source/Core/DiscIO/Volume.h | 4 +- Source/Core/DiscIO/VolumeVerifier.cpp | 13 ++- Source/Core/DiscIO/VolumeWii.cpp | 107 ++++++++++++++++------- Source/Core/DiscIO/VolumeWii.h | 11 +-- Source/Core/DiscIO/WIABlob.cpp | 2 +- 11 files changed, 101 insertions(+), 59 deletions(-) diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 857c6edb91..190f0b1213 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -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 { diff --git a/Source/Core/Core/HW/DVD/DVDThread.cpp b/Source/Core/Core/HW/DVD/DVDThread.cpp index 4d37f823ec..5590f58b6e 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.cpp +++ b/Source/Core/Core/HW/DVD/DVDThread.cpp @@ -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() diff --git a/Source/Core/Core/HW/DVD/DVDThread.h b/Source/Core/Core/HW/DVD/DVDThread.h index 420ab3a3d8..fe427e2e57 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.h +++ b/Source/Core/Core/HW/DVD/DVDThread.h @@ -41,7 +41,7 @@ void DoState(PointerWrap& p); void SetDisc(std::unique_ptr 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); diff --git a/Source/Core/DiscIO/DirectoryBlob.cpp b/Source/Core/DiscIO/DirectoryBlob.cpp index 7decaac2e3..295235c605 100644 --- a/Source/Core/DiscIO/DirectoryBlob.cpp +++ b/Source/Core/DiscIO/DirectoryBlob.cpp @@ -668,7 +668,7 @@ void DirectoryBlobReader::SetPartitions(std::vector&& 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 offset = m_wrapped_volume->ReadSwappedAndShifted( wrapped_partition->offset + WII_PARTITION_H3_OFFSET_ADDRESS, PARTITION_NONE); diff --git a/Source/Core/DiscIO/DiscExtractor.cpp b/Source/Core/DiscIO/DiscExtractor.cpp index d0453d7669..229e2a59cc 100644 --- a/Source/Core/DiscIO/DiscExtractor.cpp +++ b/Source/Core/DiscIO/DiscExtractor.cpp @@ -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"); } diff --git a/Source/Core/DiscIO/DiscScrubber.cpp b/Source/Core/DiscIO/DiscScrubber.cpp index de0f17bb64..b79be16315 100644 --- a/Source/Core/DiscIO/DiscScrubber.cpp +++ b/Source/Core/DiscIO/DiscScrubber.cpp @@ -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); diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 2c974f447d..c49f2ee24e 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -63,7 +63,8 @@ public: return static_cast(*temp) << GetOffsetShift(); } - virtual bool IsEncryptedAndHashed() const { return false; } + virtual bool HasWiiHashes() const { return false; } + virtual bool HasWiiEncryption() const { return false; } virtual std::vector GetPartitions() const { return {}; } virtual Partition GetGamePartition() const { return PARTITION_NONE; } virtual std::optional 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 diff --git a/Source/Core/DiscIO/VolumeVerifier.cpp b/Source/Core/DiscIO/VolumeVerifier.cpp index c4c5539fe2..460fd2a46b 100644 --- a/Source/Core/DiscIO/VolumeVerifier.cpp +++ b/Source/Core/DiscIO/VolumeVerifier.cpp @@ -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 partitions = CheckPartitions(); @@ -492,7 +491,7 @@ std::vector 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; diff --git a/Source/Core/DiscIO/VolumeWii.cpp b/Source/Core/DiscIO/VolumeWii.cpp index da7254e845..1b56e61ed6 100644 --- a/Source/Core/DiscIO/VolumeWii.cpp +++ b/Source/Core/DiscIO/VolumeWii.cpp @@ -41,7 +41,11 @@ VolumeWii::VolumeWii(std::unique_ptr reader) { ASSERT(m_reader); - m_encrypted = m_reader->ReadSwapped(0x60) == u32(0); + m_has_hashes = m_reader->ReadSwapped(0x60) == u8(0); + m_has_encryption = m_reader->ReadSwapped(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 reader) }; auto get_h3_table = [this, partition]() -> std::vector { - if (!m_encrypted) + if (!m_has_hashes) return {}; const std::optional 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 read_buffer = nullptr; + if (m_has_encryption) + { + aes_context = partition_details.key->get(); + if (!aes_context) + return false; + + read_buffer = std::make_unique(BLOCK_TOTAL_SIZE); + } - auto read_buffer = std::make_unique(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 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(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]) diff --git a/Source/Core/DiscIO/VolumeWii.h b/Source/Core/DiscIO/VolumeWii.h index d4837a26b6..05b43acaff 100644 --- a/Source/Core/DiscIO/VolumeWii.h +++ b/Source/Core/DiscIO/VolumeWii.h @@ -60,7 +60,8 @@ public: VolumeWii(std::unique_ptr 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 GetPartitions() const override; Partition GetGamePartition() const override; std::optional GetPartitionType(const Partition& partition) const override; @@ -69,8 +70,8 @@ public: const IOS::ES::TMDReader& GetTMD(const Partition& partition) const override; const std::vector& 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 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 m_reader; std::map 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]{}; diff --git a/Source/Core/DiscIO/WIABlob.cpp b/Source/Core/DiscIO/WIABlob.cpp index 35b6407d66..77accc590d 100644 --- a/Source/Core/DiscIO/WIABlob.cpp +++ b/Source/Core/DiscIO/WIABlob.cpp @@ -925,7 +925,7 @@ ConversionResultCode WIARVZFileReader::SetUpDataEntriesForWriting( std::vector* data_entries, std::vector* partition_file_systems) { std::vector partitions; - if (volume && volume->IsEncryptedAndHashed()) + if (volume && volume->HasWiiHashes() && volume->HasWiiEncryption()) partitions = volume->GetPartitions(); std::sort(partitions.begin(), partitions.end(),