diff --git a/src/EsTikProcess.cpp b/src/EsTikProcess.cpp index eea7afc..b88063a 100644 --- a/src/EsTikProcess.cpp +++ b/src/EsTikProcess.cpp @@ -14,6 +14,7 @@ nstool::EsTikProcess::EsTikProcess() : void nstool::EsTikProcess::process() { importTicket(); + decryptTitleKey(); if (mVerify) verifyTicket(); @@ -102,9 +103,66 @@ void nstool::EsTikProcess::verifyTicket() } } +void nstool::EsTikProcess::decryptTitleKey() +{ + const pie::hac::es::TicketBody_V2& body = mTik.getBody(); + + tc::ByteData raw_title_key = tc::ByteData(body.getEncTitleKey(), 0x100); + tc::ByteData depersonalised_key = tc::ByteData(0x10); + tc::ByteData decrypted_key = tc::ByteData(0x10); + + try + { + // check if enc type is supported + if (body.getTitleKeyEncType() != pie::hac::es::ticket::RSA2048 && body.getTitleKeyEncType() != pie::hac::es::ticket::AES128_CBC) + { + throw tc::Exception(fmt::format("Unsupported TitleKeyEncType: {:d}", (byte_t)body.getTitleKeyEncType())); + } + + // strip personalised enc layer + if (body.getTitleKeyEncType() == pie::hac::es::ticket::RSA2048) + { + if (mKeyCfg.etik_device_key.isSet()) + { + tc::crypto::RsaKey console_eticket_rsa_key = mKeyCfg.etik_device_key.get(); + size_t dec_data_len = 0x10; + bool depersonalise_successful = false; + + depersonalise_successful = tc::crypto::DecryptRsa2048OaepSha2256(depersonalised_key.data(), dec_data_len, depersonalised_key.size(), raw_title_key.data(), console_eticket_rsa_key, nullptr, 0, false); + + if (depersonalise_successful != true) + { + throw tc::Exception("Decrypting personalisation layer failed, check ticket device RSA key is correct"); + } + } + else + { + throw tc::Exception("Decrypting personalisation layer failed, no ticket device RSA key was provided"); + } + } + + // strip common enc layer + // TODO + + + // set class key state variables + // TODO + /* + tc::Optional mRawTitleKey; + tc::Optional mDepersonalisedTitleKey; + tc::Optional mDecryptedTitleKey; + */ + } catch (tc::Exception& e) + { + fmt::print("[WARNING] Ticket title key could not be decrypted ({:s})\n", e.error()); + } + + +} + void nstool::EsTikProcess::displayTicket() { - const pie::hac::es::TicketBody_V2& body = mTik.getBody(); + const pie::hac::es::TicketBody_V2& body = mTik.getBody(); fmt::print("[ES Ticket]\n"); fmt::print(" SignType: {:s}", getSignTypeStr(mTik.getSignature().getSignType())); @@ -116,10 +174,36 @@ void nstool::EsTikProcess::displayTicket() fmt::print(" Title Key:\n"); fmt::print(" EncMode: {:s}\n", getTitleKeyPersonalisationStr(body.getTitleKeyEncType())); fmt::print(" KeyGeneration: {:d}\n", (uint32_t)body.getCommonKeyId()); + + // TODO refer to these variables for showing titlekey in it's various states of encryption + /* + tc::Optional mRawTitleKey; + tc::Optional mDepersonalisedTitleKey; + tc::Optional mDecryptedTitleKey; + */ if (body.getTitleKeyEncType() == pie::hac::es::ticket::RSA2048) { fmt::print(" Data:\n"); fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(body.getEncTitleKey(), 0x100, true, "", 0x10, 6, false)); + + // DEBUG ONLY - REMOVE LATER + if (mKeyCfg.etik_device_key.isSet()) + { + tc::crypto::RsaKey console_eticket_rsa_key = mKeyCfg.etik_device_key.get(); + + tc::ByteData enc_data = tc::ByteData(body.getEncTitleKey(), 0x100); + size_t dec_data_len = 0x100; + tc::ByteData dec_data = tc::ByteData(dec_data_len); + + tc::crypto::DecryptRsa2048OaepSha2256(dec_data.data(), dec_data_len, dec_data.size(), enc_data.data(), console_eticket_rsa_key, nullptr, 0, false); + + fmt::print(" De-Personalised TitleKey:\n"); + fmt::print(" Len: 0x{:x}\n", dec_data_len); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(dec_data.data(), dec_data_len, true, "", 0x10, 6, false)); + + + } + // DEBUG ONLY - REMOVE LATER } else if (body.getTitleKeyEncType() == pie::hac::es::ticket::AES128_CBC) { @@ -153,6 +237,9 @@ void nstool::EsTikProcess::displayTicket() if (body.getDeviceId() != 0 || mCliOutputMode.show_extended_info) fmt::print(" DeviceId: 0x{:016x}\n", body.getDeviceId()); + if (body.getAccountId() != 0 || mCliOutputMode.show_extended_info) + fmt::print(" AccountId: 0x{:08x}\n", body.getAccountId()); + fmt::print(" RightsId: \n"); fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getRightsId(), 16, true, "")); @@ -198,10 +285,10 @@ std::string nstool::EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) con switch(flag) { case (pie::hac::es::ticket::AES128_CBC): - str = "Generic (AESCBC)"; + str = "Generic"; break; case (pie::hac::es::ticket::RSA2048): - str = "Personalised (RSA2048)"; + str = "Personalised"; break; default: str = fmt::format("Unknown ({:d})", flag); diff --git a/src/EsTikProcess.h b/src/EsTikProcess.h index c094760..7bd0aad 100644 --- a/src/EsTikProcess.h +++ b/src/EsTikProcess.h @@ -32,7 +32,12 @@ private: pie::hac::es::SignedData mTik; + tc::Optional mRawTitleKey; + tc::Optional mDepersonalisedTitleKey; + tc::Optional mDecryptedTitleKey; + void importTicket(); + void decryptTitleKey(); void verifyTicket(); void displayTicket(); std::string getSignTypeStr(uint32_t type) const; diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 992eeda..d8a9486 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -117,6 +117,25 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa } \ } \ } + +#define _SAVE_ETIK_DEVICE_RSAKEY(key_name, dst) \ + { \ + std::string key,val; \ + tc::ByteData dec_val; \ + sTicketDevicePersonalisationRsaKeypair* device_keypair = nullptr; \ + key = (key_name); \ + val = keyfile_dict[key]; \ + if (val.empty() == false) { \ + dec_val = tc::cli::FormatUtil::hexStringToBytes(val); \ + if (dec_val.size() == sizeof(sTicketDevicePersonalisationRsaKeypair)) { \ + device_keypair = (sTicketDevicePersonalisationRsaKeypair*)dec_val.data(); \ + (dst) = tc::crypto::RsaPrivateKey(device_keypair->modulus.data(), device_keypair->modulus.size(), device_keypair->private_exponent.data(), device_keypair->private_exponent.size()); \ + } \ + else {\ + fmt::print("[WARNING] Key: \"{:s}\" has incorrect length (was: {:d}, expected {:d})\n", key, val.size(), (sizeof(sTicketDevicePersonalisationRsaKeypair))*2); \ + } \ + } \ + } // keynames enum NameVariantIndex @@ -139,6 +158,7 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa std::vector kNrrCertBase = { "nrr_certificate" }; std::vector kPkiRootBase = { "pki_root" }; std::vector kTicketCommonKeyBase = { "ticket_commonkey", "titlekek" }; + std::vector kTicketDeviceKeyBase = { "ticket_devicekey", "eticket_rsa" }; std::vector kNcaKeyAreaEncKeyBase = { "nca_key_area_key", "key_area_key", "nca_body_keak" }; std::vector kNcaKeyAreaEncKeyHwBase = { "nca_key_area_key_hw", "key_area_hw_key" }; std::vector kKekGenBase = { "aes_kek_generation" }; @@ -147,10 +167,12 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa // misc str const std::string kKeyStr = "key"; const std::string kKekStr = "kek"; + const std::string kKeKekStr = "kekek"; const std::string kSourceStr = "source"; const std::string kSignKey = "sign_key"; const std::string kModulusStr = "modulus"; const std::string kPrivateStr = "private"; + const std::string kKeypairStr = "keypair"; std::vector kNcaKeyAreaKeyIndexStr = { "application", "ocean", "system" }; static const size_t kKeyGenerationNum = 0x100; @@ -261,6 +283,13 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa } } + // ticket device key + if (name_idx < kTicketDeviceKeyBase.size()) + { + //fmt::print("{:s}_{:s}\n", kTicketDeviceKeyBase[name_idx], kKeypairStr); + _SAVE_ETIK_DEVICE_RSAKEY(fmt::format("{:s}_{:s}", kTicketDeviceKeyBase[name_idx], kKeypairStr), etik_device_key); + } + /* NCA keys */ if (name_idx < kContentArchiveHeaderBase.size()) { diff --git a/src/KeyBag.h b/src/KeyBag.h index a29baa1..bbaadfe 100644 --- a/src/KeyBag.h +++ b/src/KeyBag.h @@ -53,6 +53,8 @@ struct KeyBag // ticket std::map etik_common_key; + tc::Optional etik_device_key; + // BroadOn signer profiles (for es cert and es tik) // BroadOn Keys @@ -80,6 +82,17 @@ private: void importTicket(const tc::io::Path& tik_path); void importKnownKeys(bool isDev); + +#pragma pack(push,1) + struct sTicketDevicePersonalisationRsaKeypair + { + std::array private_exponent; + std::array modulus; + std::array public_exponent; + tc::bn::pad<0xC> reserved; + }; +#pragma pack(pop) + }; } \ No newline at end of file