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(),