summaryrefslogtreecommitdiff
path: root/Source/Core/DiscIO/VolumeWii.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/DiscIO/VolumeWii.cpp')
-rw-r--r--Source/Core/DiscIO/VolumeWii.cpp179
1 files changed, 160 insertions, 19 deletions
diff --git a/Source/Core/DiscIO/VolumeWii.cpp b/Source/Core/DiscIO/VolumeWii.cpp
index d2a1fba3db..df6cfebee1 100644
--- a/Source/Core/DiscIO/VolumeWii.cpp
+++ b/Source/Core/DiscIO/VolumeWii.cpp
@@ -8,15 +8,19 @@
#include <array>
#include <cstddef>
#include <cstring>
+#include <future>
#include <map>
-#include <mbedtls/aes.h>
-#include <mbedtls/sha1.h>
#include <memory>
#include <optional>
#include <string>
+#include <thread>
#include <utility>
#include <vector>
+#include <mbedtls/aes.h>
+#include <mbedtls/sha1.h>
+
+#include "Common/Align.h"
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
@@ -126,7 +130,7 @@ VolumeWii::VolumeWii(std::unique_ptr<BlobReader> reader)
const IOS::ES::TicketReader& ticket = *m_partitions[partition].ticket;
if (!ticket.IsValid())
return nullptr;
- const std::array<u8, 16> key = ticket.GetTitleKey();
+ const std::array<u8, AES_KEY_SIZE> key = ticket.GetTitleKey();
std::unique_ptr<mbedtls_aes_context> aes_context = std::make_unique<mbedtls_aes_context>();
mbedtls_aes_setkey_dec(aes_context.get(), key.data(), 128);
return aes_context;
@@ -162,14 +166,17 @@ bool VolumeWii::Read(u64 offset, u64 length, u8* buffer, const Partition& partit
if (partition == PARTITION_NONE)
return m_reader->Read(offset, length, buffer);
- if (m_reader->SupportsReadWiiDecrypted())
- return m_reader->ReadWiiDecrypted(offset, length, buffer, partition.offset);
-
auto it = m_partitions.find(partition);
if (it == m_partitions.end())
return false;
const PartitionDetails& partition_details = it->second;
+ if (m_reader->SupportsReadWiiDecrypted())
+ {
+ return m_reader->ReadWiiDecrypted(offset, length, buffer,
+ partition.offset + *partition_details.data_offset);
+ }
+
if (!m_encrypted)
{
return m_reader->Read(partition.offset + *partition_details.data_offset + offset, length,
@@ -462,44 +469,43 @@ bool VolumeWii::CheckBlockIntegrity(u64 block_index, const std::vector<u8>& encr
return false;
const PartitionDetails& partition_details = it->second;
- constexpr size_t SHA1_SIZE = 20;
- if (block_index / 64 * SHA1_SIZE >= partition_details.h3_table->size())
+ if (block_index / BLOCKS_PER_GROUP * SHA1_SIZE >= partition_details.h3_table->size())
return false;
mbedtls_aes_context* aes_context = partition_details.key->get();
if (!aes_context)
return false;
- u8 cluster_metadata[BLOCK_HEADER_SIZE];
+ HashBlock hashes;
u8 iv[16] = {0};
- mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, BLOCK_HEADER_SIZE, iv,
- encrypted_data.data(), cluster_metadata);
+ mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, sizeof(HashBlock), iv,
+ encrypted_data.data(), reinterpret_cast<u8*>(&hashes));
u8 cluster_data[BLOCK_DATA_SIZE];
std::memcpy(iv, encrypted_data.data() + 0x3D0, 16);
- mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, BLOCK_DATA_SIZE, iv,
- encrypted_data.data() + BLOCK_HEADER_SIZE, cluster_data);
+ mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, sizeof(cluster_data), iv,
+ encrypted_data.data() + sizeof(HashBlock), cluster_data);
for (u32 hash_index = 0; hash_index < 31; ++hash_index)
{
u8 h0_hash[SHA1_SIZE];
mbedtls_sha1_ret(cluster_data + hash_index * 0x400, 0x400, h0_hash);
- if (memcmp(h0_hash, cluster_metadata + hash_index * SHA1_SIZE, SHA1_SIZE))
+ if (memcmp(h0_hash, hashes.h0[hash_index], SHA1_SIZE))
return false;
}
u8 h1_hash[SHA1_SIZE];
- mbedtls_sha1_ret(cluster_metadata, SHA1_SIZE * 31, h1_hash);
- if (memcmp(h1_hash, cluster_metadata + 0x280 + (block_index % 8) * SHA1_SIZE, SHA1_SIZE))
+ mbedtls_sha1_ret(reinterpret_cast<u8*>(hashes.h0), sizeof(hashes.h0), h1_hash);
+ if (memcmp(h1_hash, hashes.h1[block_index % 8], SHA1_SIZE))
return false;
u8 h2_hash[SHA1_SIZE];
- mbedtls_sha1_ret(cluster_metadata + 0x280, SHA1_SIZE * 8, h2_hash);
- if (memcmp(h2_hash, cluster_metadata + 0x340 + (block_index / 8 % 8) * SHA1_SIZE, SHA1_SIZE))
+ mbedtls_sha1_ret(reinterpret_cast<u8*>(hashes.h1), sizeof(hashes.h1), h2_hash);
+ if (memcmp(h2_hash, hashes.h2[block_index / 8 % 8], SHA1_SIZE))
return false;
u8 h3_hash[SHA1_SIZE];
- mbedtls_sha1_ret(cluster_metadata + 0x340, SHA1_SIZE * 8, h3_hash);
+ mbedtls_sha1_ret(reinterpret_cast<u8*>(hashes.h2), sizeof(hashes.h2), h3_hash);
if (memcmp(h3_hash, partition_details.h3_table->data() + block_index / 64 * SHA1_SIZE, SHA1_SIZE))
return false;
@@ -521,4 +527,139 @@ bool VolumeWii::CheckBlockIntegrity(u64 block_index, const Partition& partition)
return CheckBlockIntegrity(block_index, cluster, partition);
}
+bool VolumeWii::EncryptGroup(u64 offset, u64 partition_data_offset,
+ u64 partition_data_decrypted_size,
+ const std::array<u8, AES_KEY_SIZE>& key, BlobReader* blob,
+ std::array<u8, GROUP_TOTAL_SIZE>* out)
+{
+ std::vector<std::array<u8, BLOCK_DATA_SIZE>> unencrypted_data(BLOCKS_PER_GROUP);
+ std::vector<HashBlock> unencrypted_hashes(BLOCKS_PER_GROUP);
+
+ std::array<std::future<void>, BLOCKS_PER_GROUP> hash_futures;
+ bool error_occurred = false;
+
+ for (size_t i = 0; i < BLOCKS_PER_GROUP; ++i)
+ {
+ if (!error_occurred)
+ {
+ if (offset + (i + 1) * BLOCK_DATA_SIZE <= partition_data_decrypted_size)
+ {
+ if (!blob->ReadWiiDecrypted(offset + i * BLOCK_DATA_SIZE, BLOCK_DATA_SIZE,
+ unencrypted_data[i].data(), partition_data_offset))
+ {
+ error_occurred = true;
+ }
+ }
+ else
+ {
+ unencrypted_data[i].fill(0);
+ }
+ }
+
+ hash_futures[i] = std::async(std::launch::async, [&unencrypted_data, &unencrypted_hashes,
+ &hash_futures, error_occurred, i]() {
+ const size_t h1_base = Common::AlignDown(i, 8);
+
+ if (!error_occurred)
+ {
+ // H0 hashes
+ for (size_t j = 0; j < 31; ++j)
+ {
+ mbedtls_sha1_ret(unencrypted_data[i].data() + j * 0x400, 0x400,
+ unencrypted_hashes[i].h0[j]);
+ }
+
+ // H0 padding
+ std::memset(unencrypted_hashes[i].padding_0, 0, sizeof(HashBlock::padding_0));
+
+ // H1 hash
+ mbedtls_sha1_ret(reinterpret_cast<u8*>(unencrypted_hashes[i].h0), sizeof(HashBlock::h0),
+ unencrypted_hashes[h1_base].h1[i - h1_base]);
+ }
+
+ if (i % 8 == 7)
+ {
+ for (size_t j = 0; j < 7; ++j)
+ hash_futures[h1_base + j].get();
+
+ if (!error_occurred)
+ {
+ // H1 padding
+ std::memset(unencrypted_hashes[h1_base].padding_1, 0, sizeof(HashBlock::padding_1));
+
+ // H1 copies
+ for (size_t j = 1; j < 8; ++j)
+ {
+ std::memcpy(unencrypted_hashes[h1_base + j].h1, unencrypted_hashes[h1_base].h1,
+ sizeof(HashBlock::h1));
+ }
+
+ // H2 hash
+ mbedtls_sha1_ret(reinterpret_cast<u8*>(unencrypted_hashes[i].h1), sizeof(HashBlock::h1),
+ unencrypted_hashes[0].h2[h1_base / 8]);
+ }
+
+ if (i == BLOCKS_PER_GROUP - 1)
+ {
+ for (size_t j = 0; j < 7; ++j)
+ hash_futures[j * 8 + 7].get();
+
+ if (!error_occurred)
+ {
+ // H2 padding
+ std::memset(unencrypted_hashes[0].padding_2, 0, sizeof(HashBlock::padding_2));
+
+ // H2 copies
+ for (size_t j = 1; j < BLOCKS_PER_GROUP; ++j)
+ {
+ std::memcpy(unencrypted_hashes[j].h2, unencrypted_hashes[0].h2,
+ sizeof(HashBlock::h2));
+ }
+ }
+ }
+ }
+ });
+ }
+
+ // Wait for all the async tasks to finish
+ hash_futures.back().get();
+
+ if (error_occurred)
+ return false;
+
+ const unsigned int threads =
+ std::min(BLOCKS_PER_GROUP, std::max<unsigned int>(1, std::thread::hardware_concurrency()));
+
+ std::vector<std::future<void>> encryption_futures(threads);
+
+ mbedtls_aes_context aes_context;
+ mbedtls_aes_setkey_enc(&aes_context, key.data(), 128);
+
+ for (size_t i = 0; i < threads; ++i)
+ {
+ encryption_futures[i] = std::async(
+ std::launch::async,
+ [&unencrypted_data, &unencrypted_hashes, &aes_context, &out](size_t start, size_t end) {
+ for (size_t i = start; i < end; ++i)
+ {
+ u8* out_ptr = out->data() + i * BLOCK_TOTAL_SIZE;
+
+ u8 iv[16] = {};
+ mbedtls_aes_crypt_cbc(&aes_context, MBEDTLS_AES_ENCRYPT, BLOCK_HEADER_SIZE, iv,
+ reinterpret_cast<u8*>(&unencrypted_hashes[i]), out_ptr);
+
+ std::memcpy(iv, out_ptr + 0x3D0, sizeof(iv));
+ mbedtls_aes_crypt_cbc(&aes_context, MBEDTLS_AES_ENCRYPT, BLOCK_DATA_SIZE, iv,
+ unencrypted_data[i].data(), out_ptr + BLOCK_HEADER_SIZE);
+ }
+ },
+ i * BLOCKS_PER_GROUP / threads, (i + 1) * BLOCKS_PER_GROUP / threads);
+ }
+
+ for (std::future<void>& future : encryption_futures)
+ future.get();
+
+ return true;
+}
+
} // namespace DiscIO