mirror of
https://github.com/jakcron/nstool
synced 2024-11-23 05:59:29 +00:00
Initial commit
This commit is contained in:
parent
7abcedb7e5
commit
51f790a9f9
4 changed files with 137 additions and 3 deletions
|
@ -14,6 +14,7 @@ nstool::EsTikProcess::EsTikProcess() :
|
||||||
void nstool::EsTikProcess::process()
|
void nstool::EsTikProcess::process()
|
||||||
{
|
{
|
||||||
importTicket();
|
importTicket();
|
||||||
|
decryptTitleKey();
|
||||||
|
|
||||||
if (mVerify)
|
if (mVerify)
|
||||||
verifyTicket();
|
verifyTicket();
|
||||||
|
@ -102,6 +103,63 @@ 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()
|
void nstool::EsTikProcess::displayTicket()
|
||||||
{
|
{
|
||||||
const pie::hac::es::TicketBody_V2& body = mTik.getBody();
|
const pie::hac::es::TicketBody_V2& body = mTik.getBody();
|
||||||
|
@ -116,10 +174,36 @@ void nstool::EsTikProcess::displayTicket()
|
||||||
fmt::print(" Title Key:\n");
|
fmt::print(" Title Key:\n");
|
||||||
fmt::print(" EncMode: {:s}\n", getTitleKeyPersonalisationStr(body.getTitleKeyEncType()));
|
fmt::print(" EncMode: {:s}\n", getTitleKeyPersonalisationStr(body.getTitleKeyEncType()));
|
||||||
fmt::print(" KeyGeneration: {:d}\n", (uint32_t)body.getCommonKeyId());
|
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)
|
if (body.getTitleKeyEncType() == pie::hac::es::ticket::RSA2048)
|
||||||
{
|
{
|
||||||
fmt::print(" Data:\n");
|
fmt::print(" Data:\n");
|
||||||
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(body.getEncTitleKey(), 0x100, true, "", 0x10, 6, false));
|
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)
|
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)
|
if (body.getDeviceId() != 0 || mCliOutputMode.show_extended_info)
|
||||||
fmt::print(" DeviceId: 0x{:016x}\n", body.getDeviceId());
|
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(" RightsId: \n");
|
||||||
fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getRightsId(), 16, true, ""));
|
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)
|
switch(flag)
|
||||||
{
|
{
|
||||||
case (pie::hac::es::ticket::AES128_CBC):
|
case (pie::hac::es::ticket::AES128_CBC):
|
||||||
str = "Generic (AESCBC)";
|
str = "Generic";
|
||||||
break;
|
break;
|
||||||
case (pie::hac::es::ticket::RSA2048):
|
case (pie::hac::es::ticket::RSA2048):
|
||||||
str = "Personalised (RSA2048)";
|
str = "Personalised";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
str = fmt::format("Unknown ({:d})", flag);
|
str = fmt::format("Unknown ({:d})", flag);
|
||||||
|
|
|
@ -32,7 +32,12 @@ private:
|
||||||
|
|
||||||
pie::hac::es::SignedData<pie::hac::es::TicketBody_V2> mTik;
|
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 importTicket();
|
||||||
|
void decryptTitleKey();
|
||||||
void verifyTicket();
|
void verifyTicket();
|
||||||
void displayTicket();
|
void displayTicket();
|
||||||
std::string getSignTypeStr(uint32_t type) const;
|
std::string getSignTypeStr(uint32_t type) const;
|
||||||
|
|
|
@ -118,6 +118,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
|
// keynames
|
||||||
enum NameVariantIndex
|
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> kNrrCertBase = { "nrr_certificate" };
|
||||||
std::vector<std::string> kPkiRootBase = { "pki_root" };
|
std::vector<std::string> kPkiRootBase = { "pki_root" };
|
||||||
std::vector<std::string> kTicketCommonKeyBase = { "ticket_commonkey", "titlekek" };
|
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> 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> kNcaKeyAreaEncKeyHwBase = { "nca_key_area_key_hw", "key_area_hw_key" };
|
||||||
std::vector<std::string> kKekGenBase = { "aes_kek_generation" };
|
std::vector<std::string> kKekGenBase = { "aes_kek_generation" };
|
||||||
|
@ -147,10 +167,12 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa
|
||||||
// misc str
|
// misc str
|
||||||
const std::string kKeyStr = "key";
|
const std::string kKeyStr = "key";
|
||||||
const std::string kKekStr = "kek";
|
const std::string kKekStr = "kek";
|
||||||
|
const std::string kKeKekStr = "kekek";
|
||||||
const std::string kSourceStr = "source";
|
const std::string kSourceStr = "source";
|
||||||
const std::string kSignKey = "sign_key";
|
const std::string kSignKey = "sign_key";
|
||||||
const std::string kModulusStr = "modulus";
|
const std::string kModulusStr = "modulus";
|
||||||
const std::string kPrivateStr = "private";
|
const std::string kPrivateStr = "private";
|
||||||
|
const std::string kKeypairStr = "keypair";
|
||||||
std::vector<std::string> kNcaKeyAreaKeyIndexStr = { "application", "ocean", "system" };
|
std::vector<std::string> kNcaKeyAreaKeyIndexStr = { "application", "ocean", "system" };
|
||||||
|
|
||||||
static const size_t kKeyGenerationNum = 0x100;
|
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 */
|
/* NCA keys */
|
||||||
if (name_idx < kContentArchiveHeaderBase.size())
|
if (name_idx < kContentArchiveHeaderBase.size())
|
||||||
{
|
{
|
||||||
|
|
13
src/KeyBag.h
13
src/KeyBag.h
|
@ -53,6 +53,8 @@ struct KeyBag
|
||||||
|
|
||||||
// ticket
|
// ticket
|
||||||
std::map<key_generation_t, aes128_key_t> etik_common_key;
|
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 signer profiles (for es cert and es tik)
|
||||||
// BroadOn Keys
|
// BroadOn Keys
|
||||||
|
@ -80,6 +82,17 @@ private:
|
||||||
void importTicket(const tc::io::Path& tik_path);
|
void importTicket(const tc::io::Path& tik_path);
|
||||||
|
|
||||||
void importKnownKeys(bool isDev);
|
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)
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue