diff options
| author | 2023-09-05 12:09:42 -0700 | |
|---|---|---|
| committer | 2023-09-05 21:09:42 +0200 | |
| commit | 0a51fe78544daafceadbd5418e406bf11e703851 (patch) | |
| tree | 83c7ae92e1c46ec70ec8ab79420362e5e4b0212d /src/core/crypto/key_manager.cpp | |
| parent | Merge pull request #11427 from zhaobot/tx-update-20230901020727 (diff) | |
| download | yuzu-0a51fe78544daafceadbd5418e406bf11e703851.tar.gz yuzu-0a51fe78544daafceadbd5418e406bf11e703851.tar.xz yuzu-0a51fe78544daafceadbd5418e406bf11e703851.zip | |
core: Add support for loading NSPs with personalized tickets. (#10048)
Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com>
Diffstat (limited to 'src/core/crypto/key_manager.cpp')
| -rw-r--r-- | src/core/crypto/key_manager.cpp | 226 |
1 files changed, 137 insertions, 89 deletions
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 4ff2c50e5..e13c5cdc7 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -35,7 +35,6 @@ namespace Core::Crypto { | |||
| 35 | namespace { | 35 | namespace { |
| 36 | 36 | ||
| 37 | constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; | 37 | constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; |
| 38 | constexpr u64 FULL_TICKET_SIZE = 0x400; | ||
| 39 | 38 | ||
| 40 | using Common::AsArray; | 39 | using Common::AsArray; |
| 41 | 40 | ||
| @@ -156,6 +155,10 @@ u64 GetSignatureTypePaddingSize(SignatureType type) { | |||
| 156 | UNREACHABLE(); | 155 | UNREACHABLE(); |
| 157 | } | 156 | } |
| 158 | 157 | ||
| 158 | bool Ticket::IsValid() const { | ||
| 159 | return !std::holds_alternative<std::monostate>(data); | ||
| 160 | } | ||
| 161 | |||
| 159 | SignatureType Ticket::GetSignatureType() const { | 162 | SignatureType Ticket::GetSignatureType() const { |
| 160 | if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) { | 163 | if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) { |
| 161 | return ticket->sig_type; | 164 | return ticket->sig_type; |
| @@ -210,6 +213,54 @@ Ticket Ticket::SynthesizeCommon(Key128 title_key, const std::array<u8, 16>& righ | |||
| 210 | return Ticket{out}; | 213 | return Ticket{out}; |
| 211 | } | 214 | } |
| 212 | 215 | ||
| 216 | Ticket Ticket::Read(const FileSys::VirtualFile& file) { | ||
| 217 | // Attempt to read up to the largest ticket size, and make sure we read at least a signature | ||
| 218 | // type. | ||
| 219 | std::array<u8, sizeof(RSA4096Ticket)> raw_data{}; | ||
| 220 | auto read_size = file->Read(raw_data.data(), raw_data.size(), 0); | ||
| 221 | if (read_size < sizeof(SignatureType)) { | ||
| 222 | LOG_WARNING(Crypto, "Attempted to read ticket file with invalid size {}.", read_size); | ||
| 223 | return Ticket{std::monostate()}; | ||
| 224 | } | ||
| 225 | return Read(std::span{raw_data}); | ||
| 226 | } | ||
| 227 | |||
| 228 | Ticket Ticket::Read(std::span<const u8> raw_data) { | ||
| 229 | // Some tools read only 0x180 bytes of ticket data instead of 0x2C0, so | ||
| 230 | // just make sure we have at least the bare minimum of data to work with. | ||
| 231 | SignatureType sig_type; | ||
| 232 | if (raw_data.size() < sizeof(SignatureType)) { | ||
| 233 | LOG_WARNING(Crypto, "Attempted to parse ticket buffer with invalid size {}.", | ||
| 234 | raw_data.size()); | ||
| 235 | return Ticket{std::monostate()}; | ||
| 236 | } | ||
| 237 | std::memcpy(&sig_type, raw_data.data(), sizeof(sig_type)); | ||
| 238 | |||
| 239 | switch (sig_type) { | ||
| 240 | case SignatureType::RSA_4096_SHA1: | ||
| 241 | case SignatureType::RSA_4096_SHA256: { | ||
| 242 | RSA4096Ticket ticket{}; | ||
| 243 | std::memcpy(&ticket, raw_data.data(), sizeof(ticket)); | ||
| 244 | return Ticket{ticket}; | ||
| 245 | } | ||
| 246 | case SignatureType::RSA_2048_SHA1: | ||
| 247 | case SignatureType::RSA_2048_SHA256: { | ||
| 248 | RSA2048Ticket ticket{}; | ||
| 249 | std::memcpy(&ticket, raw_data.data(), sizeof(ticket)); | ||
| 250 | return Ticket{ticket}; | ||
| 251 | } | ||
| 252 | case SignatureType::ECDSA_SHA1: | ||
| 253 | case SignatureType::ECDSA_SHA256: { | ||
| 254 | ECDSATicket ticket{}; | ||
| 255 | std::memcpy(&ticket, raw_data.data(), sizeof(ticket)); | ||
| 256 | return Ticket{ticket}; | ||
| 257 | } | ||
| 258 | default: | ||
| 259 | LOG_WARNING(Crypto, "Attempted to parse ticket buffer with invalid type {}.", sig_type); | ||
| 260 | return Ticket{std::monostate()}; | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 213 | Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { | 264 | Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { |
| 214 | Key128 out{}; | 265 | Key128 out{}; |
| 215 | 266 | ||
| @@ -290,9 +341,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) { | |||
| 290 | } | 341 | } |
| 291 | } | 342 | } |
| 292 | 343 | ||
| 293 | RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { | 344 | void KeyManager::DeriveETicketRSAKey() { |
| 294 | if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) { | 345 | if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) { |
| 295 | return {}; | 346 | return; |
| 296 | } | 347 | } |
| 297 | 348 | ||
| 298 | const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); | 349 | const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); |
| @@ -304,12 +355,12 @@ RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { | |||
| 304 | rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10, | 355 | rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10, |
| 305 | extended_dec.data(), Op::Decrypt); | 356 | extended_dec.data(), Op::Decrypt); |
| 306 | 357 | ||
| 307 | RSAKeyPair<2048> rsa_key{}; | 358 | std::memcpy(eticket_rsa_keypair.decryption_key.data(), extended_dec.data(), |
| 308 | std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size()); | 359 | eticket_rsa_keypair.decryption_key.size()); |
| 309 | std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size()); | 360 | std::memcpy(eticket_rsa_keypair.modulus.data(), extended_dec.data() + 0x100, |
| 310 | std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size()); | 361 | eticket_rsa_keypair.modulus.size()); |
| 311 | 362 | std::memcpy(eticket_rsa_keypair.exponent.data(), extended_dec.data() + 0x200, | |
| 312 | return rsa_key; | 363 | eticket_rsa_keypair.exponent.size()); |
| 313 | } | 364 | } |
| 314 | 365 | ||
| 315 | Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) { | 366 | Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) { |
| @@ -447,10 +498,12 @@ std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) { | |||
| 447 | for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { | 498 | for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { |
| 448 | if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && | 499 | if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && |
| 449 | buffer[offset + 3] == 0x0) { | 500 | buffer[offset + 3] == 0x0) { |
| 450 | out.emplace_back(); | 501 | // NOTE: Assumes ticket blob will only contain RSA-2048 tickets. |
| 451 | auto& next = out.back(); | 502 | auto ticket = Ticket::Read(std::span{buffer.data() + offset, sizeof(RSA2048Ticket)}); |
| 452 | std::memcpy(&next, buffer.data() + offset, sizeof(Ticket)); | 503 | offset += sizeof(RSA2048Ticket); |
| 453 | offset += FULL_TICKET_SIZE; | 504 | if (ticket.IsValid()) { |
| 505 | out.push_back(ticket); | ||
| 506 | } | ||
| 454 | } | 507 | } |
| 455 | } | 508 | } |
| 456 | 509 | ||
| @@ -503,25 +556,36 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { | |||
| 503 | return offset; | 556 | return offset; |
| 504 | } | 557 | } |
| 505 | 558 | ||
| 506 | std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | 559 | std::optional<Key128> KeyManager::ParseTicketTitleKey(const Ticket& ticket) { |
| 507 | const RSAKeyPair<2048>& key) { | 560 | if (!ticket.IsValid()) { |
| 561 | LOG_WARNING(Crypto, "Attempted to parse title key of invalid ticket."); | ||
| 562 | return std::nullopt; | ||
| 563 | } | ||
| 564 | |||
| 565 | if (ticket.GetData().rights_id == Key128{}) { | ||
| 566 | LOG_WARNING(Crypto, "Attempted to parse title key of ticket with no rights ID."); | ||
| 567 | return std::nullopt; | ||
| 568 | } | ||
| 569 | |||
| 508 | const auto issuer = ticket.GetData().issuer; | 570 | const auto issuer = ticket.GetData().issuer; |
| 509 | if (IsAllZeroArray(issuer)) { | 571 | if (IsAllZeroArray(issuer)) { |
| 572 | LOG_WARNING(Crypto, "Attempted to parse title key of ticket with invalid issuer."); | ||
| 510 | return std::nullopt; | 573 | return std::nullopt; |
| 511 | } | 574 | } |
| 575 | |||
| 512 | if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { | 576 | if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { |
| 513 | LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); | 577 | LOG_WARNING(Crypto, "Parsing ticket with non-standard certificate authority."); |
| 514 | } | 578 | } |
| 515 | 579 | ||
| 516 | Key128 rights_id = ticket.GetData().rights_id; | 580 | if (ticket.GetData().type == TitleKeyType::Common) { |
| 517 | 581 | return ticket.GetData().title_key_common; | |
| 518 | if (rights_id == Key128{}) { | ||
| 519 | return std::nullopt; | ||
| 520 | } | 582 | } |
| 521 | 583 | ||
| 522 | if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), | 584 | if (eticket_rsa_keypair == RSAKeyPair<2048>{}) { |
| 523 | ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { | 585 | LOG_WARNING( |
| 524 | return std::make_pair(rights_id, ticket.GetData().title_key_common); | 586 | Crypto, |
| 587 | "Skipping personalized ticket title key parsing due to missing ETicket RSA key-pair."); | ||
| 588 | return std::nullopt; | ||
| 525 | } | 589 | } |
| 526 | 590 | ||
| 527 | mbedtls_mpi D; // RSA Private Exponent | 591 | mbedtls_mpi D; // RSA Private Exponent |
| @@ -534,9 +598,12 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | |||
| 534 | mbedtls_mpi_init(&S); | 598 | mbedtls_mpi_init(&S); |
| 535 | mbedtls_mpi_init(&M); | 599 | mbedtls_mpi_init(&M); |
| 536 | 600 | ||
| 537 | mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size()); | 601 | const auto& title_key_block = ticket.GetData().title_key_block; |
| 538 | mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size()); | 602 | mbedtls_mpi_read_binary(&D, eticket_rsa_keypair.decryption_key.data(), |
| 539 | mbedtls_mpi_read_binary(&S, ticket.GetData().title_key_block.data(), 0x100); | 603 | eticket_rsa_keypair.decryption_key.size()); |
| 604 | mbedtls_mpi_read_binary(&N, eticket_rsa_keypair.modulus.data(), | ||
| 605 | eticket_rsa_keypair.modulus.size()); | ||
| 606 | mbedtls_mpi_read_binary(&S, title_key_block.data(), title_key_block.size()); | ||
| 540 | 607 | ||
| 541 | mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr); | 608 | mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr); |
| 542 | 609 | ||
| @@ -564,8 +631,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | |||
| 564 | 631 | ||
| 565 | Key128 key_temp{}; | 632 | Key128 key_temp{}; |
| 566 | std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); | 633 | std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); |
| 567 | 634 | return key_temp; | |
| 568 | return std::make_pair(rights_id, key_temp); | ||
| 569 | } | 635 | } |
| 570 | 636 | ||
| 571 | KeyManager::KeyManager() { | 637 | KeyManager::KeyManager() { |
| @@ -669,6 +735,14 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti | |||
| 669 | encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); | 735 | encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); |
| 670 | } else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) { | 736 | } else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) { |
| 671 | eticket_extended_kek = Common::HexStringToArray<576>(out[1]); | 737 | eticket_extended_kek = Common::HexStringToArray<576>(out[1]); |
| 738 | } else if (out[0].compare(0, 19, "eticket_rsa_keypair") == 0) { | ||
| 739 | const auto key_data = Common::HexStringToArray<528>(out[1]); | ||
| 740 | std::memcpy(eticket_rsa_keypair.decryption_key.data(), key_data.data(), | ||
| 741 | eticket_rsa_keypair.decryption_key.size()); | ||
| 742 | std::memcpy(eticket_rsa_keypair.modulus.data(), key_data.data() + 0x100, | ||
| 743 | eticket_rsa_keypair.modulus.size()); | ||
| 744 | std::memcpy(eticket_rsa_keypair.exponent.data(), key_data.data() + 0x200, | ||
| 745 | eticket_rsa_keypair.exponent.size()); | ||
| 672 | } else { | 746 | } else { |
| 673 | for (const auto& kv : KEYS_VARIABLE_LENGTH) { | 747 | for (const auto& kv : KEYS_VARIABLE_LENGTH) { |
| 674 | if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) { | 748 | if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) { |
| @@ -1110,56 +1184,38 @@ void KeyManager::DeriveETicket(PartitionDataManager& data, | |||
| 1110 | 1184 | ||
| 1111 | eticket_extended_kek = data.GetETicketExtendedKek(); | 1185 | eticket_extended_kek = data.GetETicketExtendedKek(); |
| 1112 | WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek); | 1186 | WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek); |
| 1187 | DeriveETicketRSAKey(); | ||
| 1113 | PopulateTickets(); | 1188 | PopulateTickets(); |
| 1114 | } | 1189 | } |
| 1115 | 1190 | ||
| 1116 | void KeyManager::PopulateTickets() { | 1191 | void KeyManager::PopulateTickets() { |
| 1117 | const auto rsa_key = GetETicketRSAKey(); | 1192 | if (ticket_databases_loaded) { |
| 1118 | |||
| 1119 | if (rsa_key == RSAKeyPair<2048>{}) { | ||
| 1120 | return; | 1193 | return; |
| 1121 | } | 1194 | } |
| 1195 | ticket_databases_loaded = true; | ||
| 1122 | 1196 | ||
| 1123 | if (!common_tickets.empty() && !personal_tickets.empty()) { | 1197 | std::vector<Ticket> tickets; |
| 1124 | return; | ||
| 1125 | } | ||
| 1126 | 1198 | ||
| 1127 | const auto system_save_e1_path = | 1199 | const auto system_save_e1_path = |
| 1128 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e1"; | 1200 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e1"; |
| 1129 | 1201 | if (Common::FS::Exists(system_save_e1_path)) { | |
| 1130 | const Common::FS::IOFile save_e1{system_save_e1_path, Common::FS::FileAccessMode::Read, | 1202 | const Common::FS::IOFile save_e1{system_save_e1_path, Common::FS::FileAccessMode::Read, |
| 1131 | Common::FS::FileType::BinaryFile}; | 1203 | Common::FS::FileType::BinaryFile}; |
| 1204 | const auto blob1 = GetTicketblob(save_e1); | ||
| 1205 | tickets.insert(tickets.end(), blob1.begin(), blob1.end()); | ||
| 1206 | } | ||
| 1132 | 1207 | ||
| 1133 | const auto system_save_e2_path = | 1208 | const auto system_save_e2_path = |
| 1134 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e2"; | 1209 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e2"; |
| 1210 | if (Common::FS::Exists(system_save_e2_path)) { | ||
| 1211 | const Common::FS::IOFile save_e2{system_save_e2_path, Common::FS::FileAccessMode::Read, | ||
| 1212 | Common::FS::FileType::BinaryFile}; | ||
| 1213 | const auto blob2 = GetTicketblob(save_e2); | ||
| 1214 | tickets.insert(tickets.end(), blob2.begin(), blob2.end()); | ||
| 1215 | } | ||
| 1135 | 1216 | ||
| 1136 | const Common::FS::IOFile save_e2{system_save_e2_path, Common::FS::FileAccessMode::Read, | 1217 | for (const auto& ticket : tickets) { |
| 1137 | Common::FS::FileType::BinaryFile}; | 1218 | AddTicket(ticket); |
| 1138 | |||
| 1139 | const auto blob2 = GetTicketblob(save_e2); | ||
| 1140 | auto res = GetTicketblob(save_e1); | ||
| 1141 | |||
| 1142 | const auto idx = res.size(); | ||
| 1143 | res.insert(res.end(), blob2.begin(), blob2.end()); | ||
| 1144 | |||
| 1145 | for (std::size_t i = 0; i < res.size(); ++i) { | ||
| 1146 | const auto common = i < idx; | ||
| 1147 | const auto pair = ParseTicket(res[i], rsa_key); | ||
| 1148 | if (!pair) { | ||
| 1149 | continue; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | const auto& [rid, key] = *pair; | ||
| 1153 | u128 rights_id; | ||
| 1154 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | ||
| 1155 | |||
| 1156 | if (common) { | ||
| 1157 | common_tickets[rights_id] = res[i]; | ||
| 1158 | } else { | ||
| 1159 | personal_tickets[rights_id] = res[i]; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 1163 | } | 1219 | } |
| 1164 | } | 1220 | } |
| 1165 | 1221 | ||
| @@ -1291,41 +1347,33 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const { | |||
| 1291 | return personal_tickets; | 1347 | return personal_tickets; |
| 1292 | } | 1348 | } |
| 1293 | 1349 | ||
| 1294 | bool KeyManager::AddTicketCommon(Ticket raw) { | 1350 | bool KeyManager::AddTicket(const Ticket& ticket) { |
| 1295 | const auto rsa_key = GetETicketRSAKey(); | 1351 | if (!ticket.IsValid()) { |
| 1296 | if (rsa_key == RSAKeyPair<2048>{}) { | 1352 | LOG_WARNING(Crypto, "Attempted to add invalid ticket."); |
| 1297 | return false; | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | const auto pair = ParseTicket(raw, rsa_key); | ||
| 1301 | if (!pair) { | ||
| 1302 | return false; | 1353 | return false; |
| 1303 | } | 1354 | } |
| 1304 | 1355 | ||
| 1305 | const auto& [rid, key] = *pair; | 1356 | const auto& rid = ticket.GetData().rights_id; |
| 1306 | u128 rights_id; | 1357 | u128 rights_id; |
| 1307 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | 1358 | std::memcpy(rights_id.data(), rid.data(), rid.size()); |
| 1308 | common_tickets[rights_id] = raw; | 1359 | if (ticket.GetData().type == Core::Crypto::TitleKeyType::Common) { |
| 1309 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | 1360 | common_tickets[rights_id] = ticket; |
| 1310 | return true; | 1361 | } else { |
| 1311 | } | 1362 | personal_tickets[rights_id] = ticket; |
| 1363 | } | ||
| 1312 | 1364 | ||
| 1313 | bool KeyManager::AddTicketPersonalized(Ticket raw) { | 1365 | if (HasKey(S128KeyType::Titlekey, rights_id[1], rights_id[0])) { |
| 1314 | const auto rsa_key = GetETicketRSAKey(); | 1366 | LOG_DEBUG(Crypto, |
| 1315 | if (rsa_key == RSAKeyPair<2048>{}) { | 1367 | "Skipping parsing title key from ticket for known rights ID {:016X}{:016X}.", |
| 1316 | return false; | 1368 | rights_id[1], rights_id[0]); |
| 1369 | return true; | ||
| 1317 | } | 1370 | } |
| 1318 | 1371 | ||
| 1319 | const auto pair = ParseTicket(raw, rsa_key); | 1372 | const auto key = ParseTicketTitleKey(ticket); |
| 1320 | if (!pair) { | 1373 | if (!key) { |
| 1321 | return false; | 1374 | return false; |
| 1322 | } | 1375 | } |
| 1323 | 1376 | SetKey(S128KeyType::Titlekey, key.value(), rights_id[1], rights_id[0]); | |
| 1324 | const auto& [rid, key] = *pair; | ||
| 1325 | u128 rights_id; | ||
| 1326 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | ||
| 1327 | common_tickets[rights_id] = raw; | ||
| 1328 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 1329 | return true; | 1377 | return true; |
| 1330 | } | 1378 | } |
| 1331 | } // namespace Core::Crypto | 1379 | } // namespace Core::Crypto |