Merge pull request #1486 from lioncash/file

key_manager/partition_data_manager: Minor changes
master
bunnei 2018-10-14 14:46:47 +07:00 committed by GitHub
commit b3cca34f50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 63 deletions

@ -98,7 +98,7 @@ std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
return keyblob; return keyblob;
} }
void KeyManager::DeriveGeneralPurposeKeys(u8 crypto_revision) { void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
const auto kek_generation_source = const auto kek_generation_source =
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)); GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
const auto key_generation_source = const auto key_generation_source =
@ -147,31 +147,38 @@ boost::optional<Key128> DeriveSDSeed() {
"rb+"); "rb+");
if (!save_43.IsOpen()) if (!save_43.IsOpen())
return boost::none; return boost::none;
const FileUtil::IOFile sd_private( const FileUtil::IOFile sd_private(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
if (!sd_private.IsOpen()) if (!sd_private.IsOpen())
return boost::none; return boost::none;
sd_private.Seek(0, SEEK_SET);
std::array<u8, 0x10> private_seed{}; std::array<u8, 0x10> 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; return boost::none;
}
std::array<u8, 0x10> buffer{}; std::array<u8, 0x10> buffer{};
std::size_t offset = 0; std::size_t offset = 0;
for (; offset + 0x10 < save_43.GetSize(); ++offset) { 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()); save_43.ReadBytes(buffer.data(), buffer.size());
if (buffer == private_seed) if (buffer == private_seed) {
break; break;
}
} }
if (offset + 0x10 >= save_43.GetSize()) if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
return boost::none; return boost::none;
}
Key128 seed{}; Key128 seed{};
save_43.Seek(offset + 0x10, SEEK_SET); if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
save_43.ReadBytes(seed.data(), seed.size()); return boost::none;
}
return seed; return seed;
} }
@ -234,7 +241,9 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
return {}; return {};
std::vector<u8> buffer(ticket_save.GetSize()); std::vector<u8> buffer(ticket_save.GetSize());
ticket_save.ReadBytes(buffer.data(), buffer.size()); if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
return {};
}
std::vector<TicketRaw> out; std::vector<TicketRaw> out;
u32 magic{}; u32 magic{};
@ -261,6 +270,9 @@ static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
template <size_t target_size, size_t in_size> template <size_t target_size, size_t in_size>
static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) { static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
// Avoids truncation overflow within the loop below.
static_assert(target_size <= 0xFF);
std::array<u8, in_size + 4> seed_exp{}; std::array<u8, in_size + 4> seed_exp{};
std::memcpy(seed_exp.data(), seed.data(), in_size); std::memcpy(seed_exp.data(), seed.data(), in_size);
@ -268,7 +280,7 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
size_t i = 0; size_t i = 0;
while (out.size() < target_size) { while (out.size() < target_size) {
out.resize(out.size() + 0x20); out.resize(out.size() + 0x20);
seed_exp[in_size + 3] = i; seed_exp[in_size + 3] = static_cast<u8>(i);
mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0); mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
++i; ++i;
} }
@ -299,10 +311,11 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority)); std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
if (cert_authority == 0) if (cert_authority == 0)
return boost::none; return boost::none;
if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
LOG_INFO(Crypto, LOG_INFO(Crypto,
"Attempting to parse ticket with non-standard certificate authority {:08X}.", "Attempting to parse ticket with non-standard certificate authority {:08X}.",
cert_authority); cert_authority);
}
Key128 rights_id; Key128 rights_id;
std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128)); std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
@ -871,9 +884,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
"/system/save/80000000000000e2", "/system/save/80000000000000e2",
"rb+"); "rb+");
const auto blob2 = GetTicketblob(save2);
auto res = GetTicketblob(save1); auto res = GetTicketblob(save1);
const auto res2 = GetTicketblob(save2); res.insert(res.end(), blob2.begin(), blob2.end());
std::copy(res2.begin(), res2.end(), std::back_inserter(res));
for (const auto& raw : res) { for (const auto& raw : res) {
const auto pair = ParseTicket(raw, rsa_key); const auto pair = ParseTicket(raw, rsa_key);

@ -175,7 +175,7 @@ private:
void WriteKeyToFile(KeyCategory category, std::string_view keyname, void WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key); const std::array<u8, Size>& 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(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);

@ -11,7 +11,6 @@
#include <array> #include <array>
#include <cctype> #include <cctype>
#include <cstring> #include <cstring>
#include <boost/optional/optional.hpp>
#include <mbedtls/sha256.h> #include <mbedtls/sha256.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
@ -19,7 +18,7 @@
#include "common/hex_util.h" #include "common/hex_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/string_util.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/key_manager.h"
#include "core/crypto/partition_data_manager.h" #include "core/crypto/partition_data_manager.h"
#include "core/crypto/xts_encryption_layer.h" #include "core/crypto/xts_encryption_layer.h"
@ -302,7 +301,7 @@ FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
return nullptr; return nullptr;
} }
PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir) PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
: boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")), : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
fuses(FindFileInDirWithNames(sysdata_dir, "fuse")), fuses(FindFileInDirWithNames(sysdata_dir, "fuse")),
kfuses(FindFileInDirWithNames(sysdata_dir, "kfuse")), 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-5-Repair-Main"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"), FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),
}), }),
prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")),
secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")), secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),
package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")), package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),
secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{} secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}
: secure_monitor->ReadAllBytes()), : secure_monitor->ReadAllBytes()),
package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{} package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{}
: package1_decrypted->ReadAllBytes()), : package1_decrypted->ReadAllBytes()) {
prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")) {} }
PartitionDataManager::~PartitionDataManager() = default; PartitionDataManager::~PartitionDataManager() = default;
@ -332,18 +332,19 @@ FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
return boot0; return boot0;
} }
std::array<u8, 176> PartitionDataManager::GetEncryptedKeyblob(u8 index) const { PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob(
if (HasBoot0() && index < 32) std::size_t index) const {
if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)
return GetEncryptedKeyblobs()[index]; return GetEncryptedKeyblobs()[index];
return {}; return {};
} }
std::array<std::array<u8, 176>, 32> PartitionDataManager::GetEncryptedKeyblobs() const { PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {
if (!HasBoot0()) if (!HasBoot0())
return {}; return {};
std::array<std::array<u8, 176>, 32> out{}; EncryptedKeyBlobs out{};
for (size_t i = 0; i < 0x20; ++i) for (size_t i = 0; i < out.size(); ++i)
boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200); boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
return out; return out;
} }
@ -389,7 +390,7 @@ std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]); return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
} }
std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(u8 revision) const { std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const {
if (keyblob_source_hashes[revision] == SHA256Hash{}) { if (keyblob_source_hashes[revision] == SHA256Hash{}) {
LOG_WARNING(Crypto, LOG_WARNING(Crypto,
"No keyblob source hash for crypto revision {:02X}! Cannot derive keys...", "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...",
@ -446,7 +447,7 @@ bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
return false; return false;
} }
void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2_keys, void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& package2_keys,
Package2Type type) { Package2Type type) {
FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>( FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(
package2[static_cast<size_t>(type)], package2[static_cast<size_t>(type)],
@ -456,43 +457,38 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>
if (file->ReadObject(&header) != sizeof(Package2Header)) if (file->ReadObject(&header) != sizeof(Package2Header))
return; return;
u8 revision = 0xFF; std::size_t revision = 0xFF;
if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) { if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
for (size_t i = 0; i < package2_keys.size(); ++i) { for (std::size_t i = 0; i < package2_keys.size(); ++i) {
if (AttemptDecrypt(package2_keys[i], header)) if (AttemptDecrypt(package2_keys[i], header)) {
revision = i; revision = i;
}
} }
} }
if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))
return; return;
const std::vector<u8> s1_iv(header.section_ctr[1].begin(), header.section_ctr[1].end());
const auto a = std::make_shared<FileSys::OffsetVfsFile>( const auto a = std::make_shared<FileSys::OffsetVfsFile>(
file, header.section_size[1], header.section_size[0] + sizeof(Package2Header)); file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));
auto c = a->ReadAllBytes(); auto c = a->ReadAllBytes();
AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); AESCipher<Key128> 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); cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
// package2_decrypted[static_cast<size_t>(type)] = s1;
INIHeader ini; INIHeader ini;
std::memcpy(&ini, c.data(), sizeof(INIHeader)); std::memcpy(&ini, c.data(), sizeof(INIHeader));
if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1')) if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
return; return;
std::map<u64, KIPHeader> kips{};
u64 offset = sizeof(INIHeader); u64 offset = sizeof(INIHeader);
for (size_t i = 0; i < ini.process_count; ++i) { for (size_t i = 0; i < ini.process_count; ++i) {
KIPHeader kip; KIPHeader kip;
std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader)); std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1')) if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
return; return;
kips.emplace(offset, kip);
const auto name = const auto name =
Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size()); Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
@ -503,33 +499,29 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>
continue; continue;
} }
std::vector<u8> text(kip.sections[0].size_compressed); const u64 initial_offset = sizeof(KIPHeader) + offset;
std::vector<u8> rodata(kip.sections[1].size_compressed); const auto text_begin = c.cbegin() + initial_offset;
std::vector<u8> data(kip.sections[2].size_compressed); const auto text_end = text_begin + kip.sections[0].size_compressed;
const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
u64 offset_sec = sizeof(KIPHeader) + offset; const auto rodata_end = text_end + kip.sections[1].size_compressed;
std::memcpy(text.data(), c.data() + offset_sec, text.size()); const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
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());
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + const auto data_end = rodata_end + kip.sections[2].size_compressed;
kip.sections[1].size_compressed + kip.sections[2].size_compressed; const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
text = DecompressBLZ(text); std::vector<u8> out;
rodata = DecompressBLZ(rodata); out.reserve(text.size() + rodata.size() + data.size());
data = DecompressBLZ(data); 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<u8> out(text.size() + rodata.size() + data.size()); offset += sizeof(KIPHeader) + out.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());
if (name == "FS") if (name == "FS")
package2_fs[static_cast<size_t>(type)] = out; package2_fs[static_cast<size_t>(type)] = std::move(out);
else if (name == "spl") else if (name == "spl")
package2_spl[static_cast<size_t>(type)] = out; package2_spl[static_cast<size_t>(type)] = std::move(out);
} }
} }

@ -5,9 +5,7 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs_types.h" #include "core/file_sys/vfs_types.h"
namespace Core::Crypto { namespace Core::Crypto {
@ -24,15 +22,20 @@ enum class Package2Type {
class PartitionDataManager { class PartitionDataManager {
public: public:
static const u8 MAX_KEYBLOB_SOURCE_HASH; 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<u8, ENCRYPTED_KEYBLOB_SIZE>;
using EncryptedKeyBlobs = std::array<EncryptedKeyBlob, NUM_ENCRYPTED_KEYBLOBS>;
explicit PartitionDataManager(const FileSys::VirtualDir& sysdata_dir);
~PartitionDataManager(); ~PartitionDataManager();
// BOOT0 // BOOT0
bool HasBoot0() const; bool HasBoot0() const;
FileSys::VirtualFile GetBoot0Raw() const; FileSys::VirtualFile GetBoot0Raw() const;
std::array<u8, 0xB0> GetEncryptedKeyblob(u8 index) const; EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const;
std::array<std::array<u8, 0xB0>, 0x20> GetEncryptedKeyblobs() const; EncryptedKeyBlobs GetEncryptedKeyblobs() const;
std::vector<u8> GetSecureMonitor() const; std::vector<u8> GetSecureMonitor() const;
std::array<u8, 0x10> GetPackage2KeySource() const; std::array<u8, 0x10> GetPackage2KeySource() const;
std::array<u8, 0x10> GetAESKekGenerationSource() const; std::array<u8, 0x10> GetAESKekGenerationSource() const;
@ -43,7 +46,7 @@ public:
std::vector<u8> GetPackage1Decrypted() const; std::vector<u8> GetPackage1Decrypted() const;
std::array<u8, 0x10> GetMasterKeySource() const; std::array<u8, 0x10> GetMasterKeySource() const;
std::array<u8, 0x10> GetKeyblobMACKeySource() const; std::array<u8, 0x10> GetKeyblobMACKeySource() const;
std::array<u8, 0x10> GetKeyblobKeySource(u8 revision) const; std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;
// Fuses // Fuses
bool HasFuses() const; bool HasFuses() const;
@ -57,7 +60,8 @@ public:
// Package2 // Package2
bool HasPackage2(Package2Type type = Package2Type::NormalMain) const; bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const; FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
void DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2, Package2Type type); void DecryptPackage2(const std::array<std::array<u8, 16>, 0x20>& package2_keys,
Package2Type type);
const std::vector<u8>& GetPackage2FSDecompressed( const std::vector<u8>& GetPackage2FSDecompressed(
Package2Type type = Package2Type::NormalMain) const; Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetKeyAreaKeyApplicationSource( std::array<u8, 0x10> GetKeyAreaKeyApplicationSource(