key_manager: Add accessors/helpers for ticket management
This commit is contained in:
parent
5275fd2789
commit
e35fac2054
2 changed files with 100 additions and 14 deletions
|
@ -135,6 +135,28 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RSAKeyPair<2048> KeyManager::GetETicketRSAKey() {
|
||||||
|
if (eticket_extended_kek == std::array<u8, 576>{} || !HasKey(S128KeyType::ETicketRSAKek))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek);
|
||||||
|
|
||||||
|
std::vector<u8> extended_iv(0x10);
|
||||||
|
std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size());
|
||||||
|
std::array<u8, 0x230> extended_dec{};
|
||||||
|
AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
|
||||||
|
rsa_1.SetIV(extended_iv);
|
||||||
|
rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
|
||||||
|
extended_dec.data(), Op::Decrypt);
|
||||||
|
|
||||||
|
RSAKeyPair<2048> rsa_key{};
|
||||||
|
std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
|
||||||
|
std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
|
||||||
|
std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
|
||||||
|
|
||||||
|
return rsa_key;
|
||||||
|
}
|
||||||
|
|
||||||
Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) {
|
Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) {
|
||||||
AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB);
|
AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB);
|
||||||
Key128 mac_key{};
|
Key128 mac_key{};
|
||||||
|
@ -450,6 +472,8 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
|
||||||
|
|
||||||
const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
|
const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
|
||||||
encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
|
encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
|
||||||
|
} else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) {
|
||||||
|
eticket_extended_kek = Common::HexStringToArray<576>(out[1]);
|
||||||
} else {
|
} else {
|
||||||
for (const auto& kv : KEYS_VARIABLE_LENGTH) {
|
for (const auto& kv : KEYS_VARIABLE_LENGTH) {
|
||||||
if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2))
|
if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2))
|
||||||
|
@ -862,20 +886,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
|
||||||
// Titlekeys
|
// Titlekeys
|
||||||
data.DecryptProdInfo(GetBISKey(0));
|
data.DecryptProdInfo(GetBISKey(0));
|
||||||
|
|
||||||
const auto eticket_extended_kek = data.GetETicketExtendedKek();
|
eticket_extended_kek = data.GetETicketExtendedKek();
|
||||||
|
WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek);
|
||||||
|
PopulateTickets();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<u8> extended_iv(0x10);
|
void KeyManager::PopulateTickets() {
|
||||||
std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size());
|
const auto rsa_key = GetETicketRSAKey();
|
||||||
std::array<u8, 0x230> extended_dec{};
|
|
||||||
AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
|
|
||||||
rsa_1.SetIV(extended_iv);
|
|
||||||
rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
|
|
||||||
extended_dec.data(), Op::Decrypt);
|
|
||||||
|
|
||||||
RSAKeyPair<2048> rsa_key{};
|
if (rsa_key == RSAKeyPair<2048>{})
|
||||||
std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
|
return;
|
||||||
std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
|
|
||||||
std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
|
if (!common_tickets.empty() && !personal_tickets.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||||
"/system/save/80000000000000e1",
|
"/system/save/80000000000000e1",
|
||||||
|
@ -886,15 +909,24 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
|
||||||
|
|
||||||
const auto blob2 = GetTicketblob(save2);
|
const auto blob2 = GetTicketblob(save2);
|
||||||
auto res = GetTicketblob(save1);
|
auto res = GetTicketblob(save1);
|
||||||
|
const auto idx = res.size();
|
||||||
res.insert(res.end(), blob2.begin(), blob2.end());
|
res.insert(res.end(), blob2.begin(), blob2.end());
|
||||||
|
|
||||||
for (const auto& raw : res) {
|
for (std::size_t i = 0; i < res.size(); ++i) {
|
||||||
const auto pair = ParseTicket(raw, rsa_key);
|
const auto common = i < idx;
|
||||||
|
const auto pair = ParseTicket(res[i], rsa_key);
|
||||||
if (!pair)
|
if (!pair)
|
||||||
continue;
|
continue;
|
||||||
const auto& [rid, key] = *pair;
|
const auto& [rid, key] = *pair;
|
||||||
u128 rights_id;
|
u128 rights_id;
|
||||||
std::memcpy(rights_id.data(), rid.data(), rid.size());
|
std::memcpy(rights_id.data(), rid.data(), rid.size());
|
||||||
|
|
||||||
|
if (common) {
|
||||||
|
common_tickets[rights_id] = res[i];
|
||||||
|
} else {
|
||||||
|
personal_tickets[rights_id] = res[i];
|
||||||
|
}
|
||||||
|
|
||||||
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
|
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -997,6 +1029,46 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
|
||||||
DeriveBase();
|
DeriveBase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::map<u128, TicketRaw>& KeyManager::GetCommonTickets() const {
|
||||||
|
return common_tickets;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::map<u128, TicketRaw>& KeyManager::GetPersonalizedTickets() const {
|
||||||
|
return personal_tickets;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KeyManager::AddTicketCommon(TicketRaw raw) {
|
||||||
|
const auto rsa_key = GetETicketRSAKey();
|
||||||
|
if (rsa_key == RSAKeyPair<2048>{})
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto pair = ParseTicket(raw, rsa_key);
|
||||||
|
if (!pair)
|
||||||
|
return false;
|
||||||
|
const auto& [rid, key] = *pair;
|
||||||
|
u128 rights_id;
|
||||||
|
std::memcpy(rights_id.data(), rid.data(), rid.size());
|
||||||
|
common_tickets[rights_id] = raw;
|
||||||
|
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KeyManager::AddTicketPersonalized(TicketRaw raw) {
|
||||||
|
const auto rsa_key = GetETicketRSAKey();
|
||||||
|
if (rsa_key == RSAKeyPair<2048>{})
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto pair = ParseTicket(raw, rsa_key);
|
||||||
|
if (!pair)
|
||||||
|
return false;
|
||||||
|
const auto& [rid, key] = *pair;
|
||||||
|
u128 rights_id;
|
||||||
|
std::memcpy(rights_id.data(), rid.data(), rid.size());
|
||||||
|
common_tickets[rights_id] = raw;
|
||||||
|
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
|
const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
|
||||||
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
|
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
|
||||||
{"eticket_rsa_kek_source",
|
{"eticket_rsa_kek_source",
|
||||||
|
|
|
@ -165,15 +165,27 @@ public:
|
||||||
bool BaseDeriveNecessary() const;
|
bool BaseDeriveNecessary() const;
|
||||||
void DeriveBase();
|
void DeriveBase();
|
||||||
void DeriveETicket(PartitionDataManager& data);
|
void DeriveETicket(PartitionDataManager& data);
|
||||||
|
void PopulateTickets();
|
||||||
|
|
||||||
void PopulateFromPartitionData(PartitionDataManager& data);
|
void PopulateFromPartitionData(PartitionDataManager& data);
|
||||||
|
|
||||||
|
const std::map<u128, TicketRaw>& GetCommonTickets() const;
|
||||||
|
const std::map<u128, TicketRaw>& GetPersonalizedTickets() const;
|
||||||
|
|
||||||
|
bool AddTicketCommon(TicketRaw raw);
|
||||||
|
bool AddTicketPersonalized(TicketRaw raw);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
|
std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
|
||||||
std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
|
std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
|
||||||
|
|
||||||
|
// Map from rights ID to ticket
|
||||||
|
std::map<u128, TicketRaw> common_tickets;
|
||||||
|
std::map<u128, TicketRaw> personal_tickets;
|
||||||
|
|
||||||
std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
|
std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
|
||||||
std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
|
std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
|
||||||
|
std::array<u8, 576> eticket_extended_kek{};
|
||||||
|
|
||||||
bool dev_mode;
|
bool dev_mode;
|
||||||
void LoadFromFile(const std::string& filename, bool is_title_keys);
|
void LoadFromFile(const std::string& filename, bool is_title_keys);
|
||||||
|
@ -185,6 +197,8 @@ private:
|
||||||
|
|
||||||
void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
|
void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
|
||||||
|
|
||||||
|
RSAKeyPair<2048> GetETicketRSAKey();
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue