From b5b2fa32cb3f8486a4c57a5abb26a20346bfdcf6 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Thu, 30 Apr 2020 20:03:54 +0800 Subject: [PATCH] core/ncch: Add back secondary key derivation from Citra Along with RomFS CTR, etc. --- src/core/ncch/ncch_container.cpp | 76 +++++++++++++++++++++++++++++--- src/core/ncch/ncch_container.h | 3 ++ src/core/ncch/seed_db.cpp | 22 +++++++++ src/core/ncch/seed_db.h | 7 +++ 4 files changed, 101 insertions(+), 7 deletions(-) diff --git a/src/core/ncch/ncch_container.cpp b/src/core/ncch/ncch_container.cpp index 2b672f9..95043ce 100644 --- a/src/core/ncch/ncch_container.cpp +++ b/src/core/ncch/ncch_container.cpp @@ -17,6 +17,7 @@ #include "core/data_container.h" #include "core/key/key.h" #include "core/ncch/ncch_container.h" +#include "core/ncch/seed_db.h" namespace Core { @@ -67,22 +68,77 @@ ResultStatus NCCHContainer::Load() { if (!ncch_header.no_crypto) { is_encrypted = true; - // Find primary key + // Find primary and secondary keys if (ncch_header.fixed_key) { LOG_DEBUG(Service_FS, "Fixed-key crypto"); primary_key.fill(0); + secondary_key.fill(0); } else { - std::array key_y_primary; + std::array key_y_primary, key_y_secondary; std::copy(ncch_header.signature, ncch_header.signature + key_y_primary.size(), key_y_primary.begin()); - Key::SetKeyY(Key::KeySlotID::NCCHSecure1, key_y_primary); - if (!Key::IsNormalKeyAvailable(Key::KeySlotID::NCCHSecure1)) { + if (!ncch_header.seed_crypto) { + key_y_secondary = key_y_primary; + } else { + auto opt{Seeds::GetSeed(ncch_header.program_id)}; + if (!opt.has_value()) { + LOG_ERROR(Service_FS, "Seed for program {:016X} not found", + ncch_header.program_id); + failed_to_decrypt = true; + } else { + auto seed{*opt}; + std::array input; + std::memcpy(input.data(), key_y_primary.data(), key_y_primary.size()); + std::memcpy(input.data() + key_y_primary.size(), seed.data(), seed.size()); + CryptoPP::SHA256 sha; + std::array hash; + sha.CalculateDigest(hash.data(), input.data(), input.size()); + std::memcpy(key_y_secondary.data(), hash.data(), key_y_secondary.size()); + } + } + + Key::SetKeyY(Key::NCCHSecure1, key_y_primary); + if (!Key::IsNormalKeyAvailable(Key::NCCHSecure1)) { LOG_ERROR(Service_FS, "Secure1 KeyX missing"); failed_to_decrypt = true; } - primary_key = Key::GetNormalKey(Key::KeySlotID::NCCHSecure1); + primary_key = Key::GetNormalKey(Key::NCCHSecure1); + + switch (ncch_header.secondary_key_slot) { + case 0: + LOG_DEBUG(Service_FS, "Secure1 crypto"); + secondary_key = primary_key; + break; + case 1: + LOG_DEBUG(Service_FS, "Secure2 crypto"); + Key::SetKeyY(Key::NCCHSecure2, key_y_secondary); + if (!Key::IsNormalKeyAvailable(Key::NCCHSecure2)) { + LOG_ERROR(Service_FS, "Secure2 KeyX missing"); + failed_to_decrypt = true; + } + secondary_key = Key::GetNormalKey(Key::NCCHSecure2); + break; + case 10: + LOG_DEBUG(Service_FS, "Secure3 crypto"); + Key::SetKeyY(Key::NCCHSecure3, key_y_secondary); + if (!Key::IsNormalKeyAvailable(Key::NCCHSecure3)) { + LOG_ERROR(Service_FS, "Secure3 KeyX missing"); + failed_to_decrypt = true; + } + secondary_key = Key::GetNormalKey(Key::NCCHSecure3); + break; + case 11: + LOG_DEBUG(Service_FS, "Secure4 crypto"); + Key::SetKeyY(Key::NCCHSecure4, key_y_secondary); + if (!Key::IsNormalKeyAvailable(Key::NCCHSecure4)) { + LOG_ERROR(Service_FS, "Secure4 KeyX missing"); + failed_to_decrypt = true; + } + secondary_key = Key::GetNormalKey(Key::NCCHSecure4); + break; + } } // Find CTR for each section @@ -94,16 +150,17 @@ ResultStatus NCCHContainer::Load() { // (reverse order) std::reverse_copy(ncch_header.partition_id, ncch_header.partition_id + 8, exheader_ctr.begin()); - exefs_ctr = exheader_ctr; + exefs_ctr = romfs_ctr = exheader_ctr; exheader_ctr[8] = 1; exefs_ctr[8] = 2; + romfs_ctr[8] = 3; } else if (ncch_header.version == 1) { LOG_DEBUG(Loader, "NCCH version 1"); // In this version, CTR for each section is the section offset prefixed by partition // ID, as if the entire NCCH image is encrypted using a single CTR stream. std::copy(ncch_header.partition_id, ncch_header.partition_id + 8, exheader_ctr.begin()); - exefs_ctr = exheader_ctr; + exefs_ctr = romfs_ctr = exheader_ctr; auto u32ToBEArray = [](u32 value) -> std::array { return std::array{ static_cast(value >> 24), @@ -114,9 +171,11 @@ ResultStatus NCCHContainer::Load() { }; auto offset_exheader = u32ToBEArray(0x200); // exheader offset auto offset_exefs = u32ToBEArray(ncch_header.exefs_offset * kBlockSize); + auto offset_romfs = u32ToBEArray(ncch_header.romfs_offset * kBlockSize); std::copy(offset_exheader.begin(), offset_exheader.end(), exheader_ctr.begin() + 12); std::copy(offset_exefs.begin(), offset_exefs.end(), exefs_ctr.begin() + 12); + std::copy(offset_romfs.begin(), offset_romfs.end(), romfs_ctr.begin() + 12); } else { LOG_ERROR(Service_FS, "Unknown NCCH version {}", ncch_header.version); failed_to_decrypt = true; @@ -203,6 +262,9 @@ ResultStatus NCCHContainer::Load() { exefs_file = SDMCFile(root_folder, filepath, "rb"); has_exefs = true; } + + if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) + has_romfs = true; } is_loaded = true; diff --git a/src/core/ncch/ncch_container.h b/src/core/ncch/ncch_container.h index f6a96e8..fdc079a 100644 --- a/src/core/ncch/ncch_container.h +++ b/src/core/ncch/ncch_container.h @@ -273,14 +273,17 @@ private: bool has_header = false; bool has_exheader = false; bool has_exefs = false; + bool has_romfs = false; bool is_loaded = false; bool is_encrypted = false; // for decrypting exheader, exefs header and icon/banner section std::array primary_key{}; + std::array secondary_key{}; // for decrypting romfs and .code section std::array exheader_ctr{}; std::array exefs_ctr{}; + std::array romfs_ctr{}; u32 exefs_offset = 0; diff --git a/src/core/ncch/seed_db.cpp b/src/core/ncch/seed_db.cpp index af04432..a5e57ba 100644 --- a/src/core/ncch/seed_db.cpp +++ b/src/core/ncch/seed_db.cpp @@ -114,4 +114,26 @@ std::optional SeedDB::Get(u64 title_id) const { return std::nullopt; } +namespace Seeds { + +static SeedDB g_seeddb; +static bool g_seeddb_loaded = false; + +void Init(bool force) { + if (force || !g_seeddb_loaded) { + g_seeddb.Load( + fmt::format("{}/seeddb.bin", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir))); + } + g_seeddb_loaded = true; +} + +std::optional GetSeed(u64 title_id) { + if (!g_seeddb_loaded) { + Init(); + } + return g_seeddb.Get(title_id); +} + +} // namespace Seeds + } // namespace Core diff --git a/src/core/ncch/seed_db.h b/src/core/ncch/seed_db.h index c4ddafe..4305489 100644 --- a/src/core/ncch/seed_db.h +++ b/src/core/ncch/seed_db.h @@ -51,4 +51,11 @@ private: std::vector seeds; }; +namespace Seeds { + +void Init(bool force = false); +std::optional GetSeed(u64 title_id); + +} // namespace Seeds + } // namespace Core