diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index a59a7e1f5..fd0786068 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -98,7 +98,7 @@ std::array DecryptKeyblob(const std::array& encrypted_keyblob, return keyblob; } -void KeyManager::DeriveGeneralPurposeKeys(u8 crypto_revision) { +void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) { const auto kek_generation_source = GetKey(S128KeyType::Source, static_cast(SourceKeyType::AESKekGeneration)); const auto key_generation_source = @@ -147,31 +147,38 @@ boost::optional DeriveSDSeed() { "rb+"); if (!save_43.IsOpen()) return boost::none; + const FileUtil::IOFile sd_private( FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); if (!sd_private.IsOpen()) return boost::none; - sd_private.Seek(0, SEEK_SET); std::array private_seed{}; - if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10) + if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) { return boost::none; + } std::array buffer{}; std::size_t offset = 0; for (; offset + 0x10 < save_43.GetSize(); ++offset) { - save_43.Seek(offset, SEEK_SET); + if (!save_43.Seek(offset, SEEK_SET)) { + return boost::none; + } + save_43.ReadBytes(buffer.data(), buffer.size()); - if (buffer == private_seed) + if (buffer == private_seed) { break; + } } - if (offset + 0x10 >= save_43.GetSize()) + if (!save_43.Seek(offset + 0x10, SEEK_SET)) { return boost::none; + } Key128 seed{}; - save_43.Seek(offset + 0x10, SEEK_SET); - save_43.ReadBytes(seed.data(), seed.size()); + if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { + return boost::none; + } return seed; } @@ -234,7 +241,9 @@ std::vector GetTicketblob(const FileUtil::IOFile& ticket_save) { return {}; std::vector buffer(ticket_save.GetSize()); - ticket_save.ReadBytes(buffer.data(), buffer.size()); + if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { + return {}; + } std::vector out; u32 magic{}; @@ -261,6 +270,9 @@ static std::array operator^(const std::array& lhs, template static std::array MGF1(const std::array& seed) { + // Avoids truncation overflow within the loop below. + static_assert(target_size <= 0xFF); + std::array seed_exp{}; std::memcpy(seed_exp.data(), seed.data(), in_size); @@ -268,7 +280,7 @@ static std::array MGF1(const std::array& seed) { size_t i = 0; while (out.size() < target_size) { out.resize(out.size() + 0x20); - seed_exp[in_size + 3] = i; + seed_exp[in_size + 3] = static_cast(i); mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0); ++i; } @@ -299,10 +311,11 @@ boost::optional> ParseTicket(const TicketRaw& ticket, std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority)); if (cert_authority == 0) return boost::none; - if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) + if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) { LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority {:08X}.", cert_authority); + } Key128 rights_id; std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128)); @@ -871,9 +884,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) { "/system/save/80000000000000e2", "rb+"); + const auto blob2 = GetTicketblob(save2); auto res = GetTicketblob(save1); - const auto res2 = GetTicketblob(save2); - std::copy(res2.begin(), res2.end(), std::back_inserter(res)); + res.insert(res.end(), blob2.begin(), blob2.end()); for (const auto& raw : res) { const auto pair = ParseTicket(raw, rsa_key); diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index a41abbdfc..cccb3c0ae 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -175,7 +175,7 @@ private: void WriteKeyToFile(KeyCategory category, std::string_view keyname, const std::array& key); - void DeriveGeneralPurposeKeys(u8 crypto_revision); + void DeriveGeneralPurposeKeys(std::size_t crypto_revision); void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index d1c04e98d..ed5e2b145 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include "common/assert.h" #include "common/common_funcs.h" @@ -19,7 +18,7 @@ #include "common/hex_util.h" #include "common/logging/log.h" #include "common/string_util.h" -#include "core/crypto/ctr_encryption_layer.h" +#include "common/swap.h" #include "core/crypto/key_manager.h" #include "core/crypto/partition_data_manager.h" #include "core/crypto/xts_encryption_layer.h" @@ -302,7 +301,7 @@ FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, return nullptr; } -PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir) +PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir) : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")), fuses(FindFileInDirWithNames(sysdata_dir, "fuse")), kfuses(FindFileInDirWithNames(sysdata_dir, "kfuse")), @@ -314,13 +313,14 @@ PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir) FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"), FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"), }), + prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")), secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")), package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")), secure_monitor_bytes(secure_monitor == nullptr ? std::vector{} : secure_monitor->ReadAllBytes()), package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector{} - : package1_decrypted->ReadAllBytes()), - prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")) {} + : package1_decrypted->ReadAllBytes()) { +} PartitionDataManager::~PartitionDataManager() = default; @@ -332,18 +332,19 @@ FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const { return boot0; } -std::array PartitionDataManager::GetEncryptedKeyblob(u8 index) const { - if (HasBoot0() && index < 32) +PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob( + std::size_t index) const { + if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS) return GetEncryptedKeyblobs()[index]; return {}; } -std::array, 32> PartitionDataManager::GetEncryptedKeyblobs() const { +PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const { if (!HasBoot0()) return {}; - std::array, 32> out{}; - for (size_t i = 0; i < 0x20; ++i) + EncryptedKeyBlobs out{}; + for (size_t i = 0; i < out.size(); ++i) boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200); return out; } @@ -389,7 +390,7 @@ std::array PartitionDataManager::GetKeyblobMACKeySource() const { return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]); } -std::array PartitionDataManager::GetKeyblobKeySource(u8 revision) const { +std::array PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const { if (keyblob_source_hashes[revision] == SHA256Hash{}) { LOG_WARNING(Crypto, "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...", @@ -446,7 +447,7 @@ bool AttemptDecrypt(const std::array& key, Package2Header& header) { return false; } -void PartitionDataManager::DecryptPackage2(std::array, 0x20> package2_keys, +void PartitionDataManager::DecryptPackage2(const std::array& package2_keys, Package2Type type) { FileSys::VirtualFile file = std::make_shared( package2[static_cast(type)], @@ -456,43 +457,38 @@ void PartitionDataManager::DecryptPackage2(std::array, 0x20> if (file->ReadObject(&header) != sizeof(Package2Header)) return; - u8 revision = 0xFF; + std::size_t revision = 0xFF; if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) { - for (size_t i = 0; i < package2_keys.size(); ++i) { - if (AttemptDecrypt(package2_keys[i], header)) + for (std::size_t i = 0; i < package2_keys.size(); ++i) { + if (AttemptDecrypt(package2_keys[i], header)) { revision = i; + } } } if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) return; - const std::vector s1_iv(header.section_ctr[1].begin(), header.section_ctr[1].end()); - const auto a = std::make_shared( file, header.section_size[1], header.section_size[0] + sizeof(Package2Header)); auto c = a->ReadAllBytes(); AESCipher cipher(package2_keys[revision], Mode::CTR); - cipher.SetIV(s1_iv); + cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()}); cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); - // package2_decrypted[static_cast(type)] = s1; - INIHeader ini; std::memcpy(&ini, c.data(), sizeof(INIHeader)); if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1')) return; - std::map kips{}; u64 offset = sizeof(INIHeader); for (size_t i = 0; i < ini.process_count; ++i) { KIPHeader kip; std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader)); if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1')) return; - kips.emplace(offset, kip); const auto name = Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size()); @@ -503,33 +499,29 @@ void PartitionDataManager::DecryptPackage2(std::array, 0x20> continue; } - std::vector text(kip.sections[0].size_compressed); - std::vector rodata(kip.sections[1].size_compressed); - std::vector data(kip.sections[2].size_compressed); + const u64 initial_offset = sizeof(KIPHeader) + offset; + const auto text_begin = c.cbegin() + initial_offset; + const auto text_end = text_begin + kip.sections[0].size_compressed; + const std::vector text = DecompressBLZ({text_begin, text_end}); - u64 offset_sec = sizeof(KIPHeader) + offset; - std::memcpy(text.data(), c.data() + offset_sec, text.size()); - offset_sec += text.size(); - std::memcpy(rodata.data(), c.data() + offset_sec, rodata.size()); - offset_sec += rodata.size(); - std::memcpy(data.data(), c.data() + offset_sec, data.size()); + const auto rodata_end = text_end + kip.sections[1].size_compressed; + const std::vector rodata = DecompressBLZ({text_end, rodata_end}); - offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + - kip.sections[1].size_compressed + kip.sections[2].size_compressed; + const auto data_end = rodata_end + kip.sections[2].size_compressed; + const std::vector data = DecompressBLZ({rodata_end, data_end}); - text = DecompressBLZ(text); - rodata = DecompressBLZ(rodata); - data = DecompressBLZ(data); + std::vector out; + out.reserve(text.size() + rodata.size() + data.size()); + out.insert(out.end(), text.begin(), text.end()); + out.insert(out.end(), rodata.begin(), rodata.end()); + out.insert(out.end(), data.begin(), data.end()); - std::vector out(text.size() + rodata.size() + data.size()); - std::memcpy(out.data(), text.data(), text.size()); - std::memcpy(out.data() + text.size(), rodata.data(), rodata.size()); - std::memcpy(out.data() + text.size() + rodata.size(), data.data(), data.size()); + offset += sizeof(KIPHeader) + out.size(); if (name == "FS") - package2_fs[static_cast(type)] = out; + package2_fs[static_cast(type)] = std::move(out); else if (name == "spl") - package2_spl[static_cast(type)] = out; + package2_spl[static_cast(type)] = std::move(out); } } diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h index 45c7fecfa..0ad007c72 100644 --- a/src/core/crypto/partition_data_manager.h +++ b/src/core/crypto/partition_data_manager.h @@ -5,9 +5,7 @@ #pragma once #include -#include "common/common_funcs.h" #include "common/common_types.h" -#include "common/swap.h" #include "core/file_sys/vfs_types.h" namespace Core::Crypto { @@ -24,15 +22,20 @@ enum class Package2Type { class PartitionDataManager { public: static const u8 MAX_KEYBLOB_SOURCE_HASH; + static constexpr std::size_t NUM_ENCRYPTED_KEYBLOBS = 32; + static constexpr std::size_t ENCRYPTED_KEYBLOB_SIZE = 0xB0; - explicit PartitionDataManager(FileSys::VirtualDir sysdata_dir); + using EncryptedKeyBlob = std::array; + using EncryptedKeyBlobs = std::array; + + explicit PartitionDataManager(const FileSys::VirtualDir& sysdata_dir); ~PartitionDataManager(); // BOOT0 bool HasBoot0() const; FileSys::VirtualFile GetBoot0Raw() const; - std::array GetEncryptedKeyblob(u8 index) const; - std::array, 0x20> GetEncryptedKeyblobs() const; + EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const; + EncryptedKeyBlobs GetEncryptedKeyblobs() const; std::vector GetSecureMonitor() const; std::array GetPackage2KeySource() const; std::array GetAESKekGenerationSource() const; @@ -43,7 +46,7 @@ public: std::vector GetPackage1Decrypted() const; std::array GetMasterKeySource() const; std::array GetKeyblobMACKeySource() const; - std::array GetKeyblobKeySource(u8 revision) const; + std::array GetKeyblobKeySource(std::size_t revision) const; // Fuses bool HasFuses() const; @@ -57,7 +60,8 @@ public: // Package2 bool HasPackage2(Package2Type type = Package2Type::NormalMain) const; FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const; - void DecryptPackage2(std::array, 0x20> package2, Package2Type type); + void DecryptPackage2(const std::array, 0x20>& package2_keys, + Package2Type type); const std::vector& GetPackage2FSDecompressed( Package2Type type = Package2Type::NormalMain) const; std::array GetKeyAreaKeyApplicationSource(