Initial commit

This commit is contained in:
Jack 2023-06-04 14:00:46 +08:00
parent 7abcedb7e5
commit 51f790a9f9
4 changed files with 137 additions and 3 deletions

View file

@ -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<tc::ByteData> mRawTitleKey;
tc::Optional<tc::ByteData> mDepersonalisedTitleKey;
tc::Optional<tc::ByteData> 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<tc::ByteData> mRawTitleKey;
tc::Optional<tc::ByteData> mDepersonalisedTitleKey;
tc::Optional<tc::ByteData> 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);

View file

@ -32,7 +32,12 @@ private:
pie::hac::es::SignedData<pie::hac::es::TicketBody_V2> mTik;
tc::Optional<tc::ByteData> mRawTitleKey;
tc::Optional<tc::ByteData> mDepersonalisedTitleKey;
tc::Optional<tc::ByteData> mDecryptedTitleKey;
void importTicket();
void decryptTitleKey();
void verifyTicket();
void displayTicket();
std::string getSignTypeStr(uint32_t type) const;

View file

@ -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<std::string> kNrrCertBase = { "nrr_certificate" };
std::vector<std::string> kPkiRootBase = { "pki_root" };
std::vector<std::string> kTicketCommonKeyBase = { "ticket_commonkey", "titlekek" };
std::vector<std::string> kTicketDeviceKeyBase = { "ticket_devicekey", "eticket_rsa" };
std::vector<std::string> kNcaKeyAreaEncKeyBase = { "nca_key_area_key", "key_area_key", "nca_body_keak" };
std::vector<std::string> kNcaKeyAreaEncKeyHwBase = { "nca_key_area_key_hw", "key_area_hw_key" };
std::vector<std::string> 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<std::string> 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())
{

View file

@ -53,6 +53,8 @@ struct KeyBag
// ticket
std::map<key_generation_t, aes128_key_t> etik_common_key;
tc::Optional<rsa_key_t> 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<byte_t, 0x100> private_exponent;
std::array<byte_t, 0x100> modulus;
std::array<byte_t, 0x004> public_exponent;
tc::bn::pad<0xC> reserved;
};
#pragma pack(pop)
};
}