mirror of
https://github.com/jakcron/nstool
synced 2024-12-25 14:11:14 +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()
|
||||
{
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
13
src/KeyBag.h
13
src/KeyBag.h
|
@ -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)
|
||||
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue