mirror of
https://github.com/jakcron/nstool
synced 2024-11-15 02:06:40 +00:00
[nstool] First stage integrate KeyConfiguration
This commit is contained in:
parent
d4435b7559
commit
67a13b9d34
3 changed files with 90 additions and 225 deletions
|
@ -1,6 +1,7 @@
|
||||||
#include "UserSettings.h"
|
#include "UserSettings.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "PkiValidator.h"
|
#include "PkiValidator.h"
|
||||||
|
#include "KeyConfiguration.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -385,36 +386,25 @@ void UserSettings::populateCmdArgs(const std::vector<std::string>& arg_list, sCm
|
||||||
|
|
||||||
void UserSettings::populateKeyset(sCmdArgs& args)
|
void UserSettings::populateKeyset(sCmdArgs& args)
|
||||||
{
|
{
|
||||||
fnd::aes::sAes128Key zeros_aes_key;
|
|
||||||
fnd::aes::sAesXts128Key zeros_aes_xts_key;
|
|
||||||
memset((void*)&zeros_aes_key, 0, sizeof(fnd::aes::sAes128Key));
|
|
||||||
memset((void*)&zeros_aes_xts_key, 0, sizeof(fnd::aes::sAesXts128Key));
|
|
||||||
memset((void*)&mKeyset, 0, sizeof(sKeyset));
|
memset((void*)&mKeyset, 0, sizeof(sKeyset));
|
||||||
|
|
||||||
fnd::ResourceFileReader res;
|
|
||||||
if (args.keyset_path.isSet)
|
if (args.keyset_path.isSet)
|
||||||
{
|
{
|
||||||
res.processFile(*args.keyset_path);
|
mKeyCfg.importHactoolGenericKeyfile(*args.keyset_path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// open other resource files in $HOME/.switch/prod.keys (or $HOME/.switch/dev.keys if -d/--dev is set).
|
// open other resource files in $HOME/.switch/prod.keys (or $HOME/.switch/dev.keys if -d/--dev is set).
|
||||||
std::string home;
|
|
||||||
if (home.empty()) fnd::io::getEnvironVar(home, "HOME");
|
|
||||||
if (home.empty()) fnd::io::getEnvironVar(home, "USERPROFILE");
|
|
||||||
if (home.empty()) return;
|
|
||||||
|
|
||||||
const std::string kKeysetNameStr[2] = {"prod.keys", "dev.keys"};
|
|
||||||
const std::string kHomeSwitchDirStr = ".switch";
|
|
||||||
|
|
||||||
std::string keyset_path;
|
std::string keyset_path;
|
||||||
fnd::io::appendToPath(keyset_path, home);
|
getSwitchPath(keyset_path);
|
||||||
fnd::io::appendToPath(keyset_path, kHomeSwitchDirStr);
|
if (keyset_path.empty())
|
||||||
fnd::io::appendToPath(keyset_path, kKeysetNameStr[args.devkit_keys.isSet ? *args.devkit_keys : 0]);
|
return;
|
||||||
|
|
||||||
|
fnd::io::appendToPath(keyset_path, kGeneralKeyfileName[args.devkit_keys.isSet]);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
res.processFile(keyset_path);
|
mKeyCfg.importHactoolGenericKeyfile(keyset_path);
|
||||||
}
|
}
|
||||||
catch (const fnd::Exception&)
|
catch (const fnd::Exception&)
|
||||||
{
|
{
|
||||||
|
@ -423,131 +413,43 @@ void UserSettings::populateKeyset(sCmdArgs& args)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// suffix
|
mKeyCfg.getPkiRootSignKey(nn::pki::sign::kRootIssuerStr, mKeyset.pki.root_sign_key);
|
||||||
const std::string kKeyStr = "key";
|
mKeyCfg.getAcidSignKey(mKeyset.acid_sign_key);
|
||||||
const std::string kKekStr = "kek";
|
mKeyCfg.getNcaHeader0SignKey(mKeyset.nca.header_sign_key);
|
||||||
const std::string kSourceStr = "source";
|
mKeyCfg.getNcaHeaderKey(mKeyset.nca.header_key);
|
||||||
const std::string kRsaKeySuffix[2] = {"sign_key_private", "sign_key_modulus"};
|
mKeyCfg.getXciHeaderSignKey(mKeyset.xci.header_sign_key);
|
||||||
const std::string kKeyIndex[kMasterKeyNum] = {"00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f"};
|
mKeyCfg.getXciHeaderKey(mKeyset.xci.header_key);
|
||||||
|
mKeyCfg.getPkg2SignKey(mKeyset.package2_sign_key);
|
||||||
// keyname bases
|
for (size_t mkeyidx = 0; mkeyidx < 32; mkeyidx++)
|
||||||
const std::string kMasterBase = "master";
|
|
||||||
const std::string kPackage1Base = "package1";
|
|
||||||
const std::string kPackage2Base = "package2";
|
|
||||||
const std::string kXciHeaderBase = "xci_header";
|
|
||||||
const std::string kNcaHeaderBase[2] = {"header", "nca_header"};
|
|
||||||
const std::string kKekGenSource = "aes_kek_generation";
|
|
||||||
const std::string kKeyGenSource = "aes_key_generation";
|
|
||||||
const std::string kAcidBase = "acid";
|
|
||||||
const std::string kPkiRootBase = "pki_root";
|
|
||||||
const std::string kTicketCommonKeyBase[2] = { "titlekek", "ticket_commonkey" };
|
|
||||||
const std::string kNcaBodyBase[2] = {"key_area_key", "nca_body_keak"};
|
|
||||||
const std::string kNcaBodyKeakIndexName[3] = {"application", "ocean", "system"};
|
|
||||||
|
|
||||||
|
|
||||||
// sources
|
|
||||||
fnd::aes::sAes128Key master_key[kMasterKeyNum] = { zeros_aes_key };
|
|
||||||
fnd::aes::sAes128Key package2_key_source = zeros_aes_key;
|
|
||||||
fnd::aes::sAes128Key ticket_titlekek_source = zeros_aes_key;
|
|
||||||
fnd::aes::sAes128Key key_area_key_source[3] = { zeros_aes_key, zeros_aes_key, zeros_aes_key };
|
|
||||||
fnd::aes::sAes128Key aes_kek_generation_source = zeros_aes_key;
|
|
||||||
fnd::aes::sAes128Key aes_key_generation_source = zeros_aes_key;
|
|
||||||
fnd::aes::sAes128Key nca_header_kek_source = zeros_aes_key;
|
|
||||||
fnd::aes::sAesXts128Key nca_header_key_source = zeros_aes_xts_key;
|
|
||||||
|
|
||||||
|
|
||||||
#define _CONCAT_2_STRINGS(str1, str2) ((str1) + "_" + (str2))
|
|
||||||
#define _CONCAT_3_STRINGS(str1, str2, str3) ((str1) + "_"+ (str2) + "_" + (str3))
|
|
||||||
|
|
||||||
std::string key,val;
|
|
||||||
|
|
||||||
#define _SAVE_KEYDATA(key_name, array, len) \
|
|
||||||
key = (key_name); \
|
|
||||||
val = res[key]; \
|
|
||||||
if (val.empty() == false) { \
|
|
||||||
decodeHexStringToBytes(key, val, (byte_t*)array, len); \
|
|
||||||
}
|
|
||||||
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPackage2Base, kKeyStr, kSourceStr), package2_key_source.key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[0], kSourceStr), ticket_titlekek_source.key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[1], kSourceStr), ticket_titlekek_source.key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[0], kSourceStr), key_area_key_source[0].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[1], kSourceStr), key_area_key_source[1].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[2], kSourceStr), key_area_key_source[2].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[0], kSourceStr), key_area_key_source[0].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[1], kSourceStr), key_area_key_source[1].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[2], kSourceStr), key_area_key_source[2].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kKekGenSource, kSourceStr), aes_kek_generation_source.key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kKeyGenSource, kSourceStr), aes_key_generation_source.key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaHeaderBase[0], kKekStr, kSourceStr), nca_header_kek_source.key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaHeaderBase[0], kKeyStr, kSourceStr), nca_header_key_source.key, 0x20);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaHeaderBase[1], kKekStr, kSourceStr), nca_header_kek_source.key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaHeaderBase[1], kKeyStr, kSourceStr), nca_header_key_source.key, 0x20);
|
|
||||||
|
|
||||||
// Store Key Variants/Derivatives
|
|
||||||
for (size_t i = 0; i < kMasterKeyNum; i++)
|
|
||||||
{
|
{
|
||||||
|
mKeyCfg.getPkg1Key(mkeyidx, mKeyset.package1_key[mkeyidx]);
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kMasterBase, kKeyStr, kKeyIndex[i]), master_key[i].key, 0x10);
|
mKeyCfg.getPkg2Key(mkeyidx, mKeyset.package1_key[mkeyidx]);
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPackage1Base, kKeyStr, kKeyIndex[i]), mKeyset.package1_key[i].key, 0x10);
|
mKeyCfg.getNcaKeyAreaEncryptionKey(mkeyidx, 0, mKeyset.nca.key_area_key[0][mkeyidx]);
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPackage2Base, kKeyStr, kKeyIndex[i]), mKeyset.package2_key[i].key, 0x10);
|
mKeyCfg.getNcaKeyAreaEncryptionKey(mkeyidx, 1, mKeyset.nca.key_area_key[1][mkeyidx]);
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[0], kKeyIndex[i]), mKeyset.ticket.titlekey_kek[i].key, 0x10);
|
mKeyCfg.getNcaKeyAreaEncryptionKey(mkeyidx, 2, mKeyset.nca.key_area_key[2][mkeyidx]);
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[1], kKeyIndex[i]), mKeyset.ticket.titlekey_kek[i].key, 0x10);
|
mKeyCfg.getETicketCommonKey(mkeyidx, mKeyset.ticket.titlekey_kek[mkeyidx]);
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[0], kKeyIndex[i]), mKeyset.nca.key_area_key[0][i].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[1], kKeyIndex[i]), mKeyset.nca.key_area_key[1][i].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[2], kKeyIndex[i]), mKeyset.nca.key_area_key[2][i].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[0], kKeyIndex[i]), mKeyset.nca.key_area_key[0][i].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[1], kKeyIndex[i]), mKeyset.nca.key_area_key[1][i].key, 0x10);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[2], kKeyIndex[i]), mKeyset.nca.key_area_key[2][i].key, 0x10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// store nca header key
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[0], kKeyStr), mKeyset.nca.header_key.key[0], 0x20);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kKeyStr), mKeyset.nca.header_key.key[0], 0x20);
|
|
||||||
|
|
||||||
// store xci header key
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kKeyStr), mKeyset.xci.header_key.key, 0x10);
|
|
||||||
|
|
||||||
// store rsa keys
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[0]), mKeyset.nca.header_sign_key.priv_exponent, fnd::rsa::kRsa2048Size);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[1]), mKeyset.nca.header_sign_key.modulus, fnd::rsa::kRsa2048Size);
|
|
||||||
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[0]), mKeyset.xci.header_sign_key.priv_exponent, fnd::rsa::kRsa2048Size);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[1]), mKeyset.xci.header_sign_key.modulus, fnd::rsa::kRsa2048Size);
|
|
||||||
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[0]), mKeyset.acid_sign_key.priv_exponent, fnd::rsa::kRsa2048Size);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[1]), mKeyset.acid_sign_key.modulus, fnd::rsa::kRsa2048Size);
|
|
||||||
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[0]), mKeyset.package2_sign_key.priv_exponent, fnd::rsa::kRsa2048Size);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[1]), mKeyset.package2_sign_key.modulus, fnd::rsa::kRsa2048Size);
|
|
||||||
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[0]), mKeyset.pki.root_sign_key.priv_exponent, fnd::rsa::kRsa4096Size);
|
|
||||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[1]), mKeyset.pki.root_sign_key.modulus, fnd::rsa::kRsa4096Size);
|
|
||||||
|
|
||||||
|
|
||||||
// save keydata from input args
|
|
||||||
if (args.nca_bodykey.isSet)
|
if (args.nca_bodykey.isSet)
|
||||||
{
|
{
|
||||||
if (args.nca_bodykey.var.length() == (sizeof(fnd::aes::sAes128Key)*2))
|
fnd::aes::sAes128Key tmp_key;
|
||||||
{
|
fnd::Vec<byte_t> tmp_raw;
|
||||||
decodeHexStringToBytes("--bodykey", args.nca_bodykey.var, mKeyset.nca.manual_body_key_aesctr.key, sizeof(fnd::aes::sAes128Key));
|
fnd::SimpleTextOutput::stringToArray(args.nca_bodykey.var, tmp_raw);
|
||||||
}
|
if (tmp_raw.size() != sizeof(fnd::aes::sAes128Key))
|
||||||
else
|
throw fnd::Exception(kModuleName, "Key: \"--bodykey\" has incorrect length");
|
||||||
{
|
memcpy(tmp_key.key, tmp_raw.data(), 16);
|
||||||
decodeHexStringToBytes("--bodykey", args.nca_bodykey.var, mKeyset.nca.manual_body_key_aesxts.key[0], sizeof(fnd::aes::sAesXts128Key));
|
mKeyCfg.addNcaExternalContentKey(kDummyRightsIdForUserBodyKey, tmp_key);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.nca_titlekey.isSet)
|
if (args.nca_titlekey.isSet)
|
||||||
{
|
{
|
||||||
if (args.nca_titlekey.var.length() == (sizeof(fnd::aes::sAes128Key)*2))
|
fnd::aes::sAes128Key tmp_key;
|
||||||
{
|
fnd::Vec<byte_t> tmp_raw;
|
||||||
decodeHexStringToBytes("--titlekey", args.nca_titlekey.var, mKeyset.nca.manual_title_key_aesctr.key, sizeof(fnd::aes::sAes128Key));
|
fnd::SimpleTextOutput::stringToArray(args.nca_bodykey.var, tmp_raw);
|
||||||
}
|
if (tmp_raw.size() != sizeof(fnd::aes::sAes128Key))
|
||||||
else
|
throw fnd::Exception(kModuleName, "Key: \"--titlekey\" has incorrect length");
|
||||||
{
|
memcpy(tmp_key.key, tmp_raw.data(), 16);
|
||||||
decodeHexStringToBytes("--titlekey", args.nca_titlekey.var, mKeyset.nca.manual_title_key_aesxts.key[0], sizeof(fnd::aes::sAesXts128Key));
|
mKeyCfg.addNcaExternalContentKey(kDummyRightsIdForUserTitleKey, tmp_key);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// import certificate chain
|
// import certificate chain
|
||||||
|
@ -615,7 +517,19 @@ void UserSettings::populateKeyset(sCmdArgs& args)
|
||||||
// extract title key
|
// extract title key
|
||||||
if (tik.getBody().getTitleKeyEncType() == nn::es::ticket::AES128_CBC)
|
if (tik.getBody().getTitleKeyEncType() == nn::es::ticket::AES128_CBC)
|
||||||
{
|
{
|
||||||
memcpy(mKeyset.nca.manual_title_key_aesctr.key, tik.getBody().getEncTitleKey(), fnd::aes::kAes128KeySize);
|
fnd::aes::sAes128Key enc_title_key;
|
||||||
|
memcpy(enc_title_key.key, tik.getBody().getEncTitleKey(), 16);
|
||||||
|
mKeyCfg.addNcaExternalContentKey(kDummyRightsIdForUserTitleKey, enc_title_key);
|
||||||
|
fnd::aes::sAes128Key common_key, external_content_key;
|
||||||
|
if (mKeyCfg.getETicketCommonKey(nn::hac::NcaUtils::getMasterKeyRevisionFromKeyGeneration(tik.getBody().getCommonKeyId()), common_key) == true)
|
||||||
|
{
|
||||||
|
nn::hac::AesKeygen::generateKey(external_content_key.key, tik.getBody().getEncTitleKey(), common_key.key);
|
||||||
|
mKeyCfg.addNcaExternalContentKey(tik.getBody().getRightsId(), external_content_key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "[WARNING] Titlekey not imported from ticket because commonkey was not available" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -623,67 +537,9 @@ void UserSettings::populateKeyset(sCmdArgs& args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef _SAVE_KEYDATA
|
// replicate keys into old keyset
|
||||||
#undef _CONCAT_3_STRINGS
|
mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserBodyKey, mKeyset.nca.manual_body_key_aesctr);
|
||||||
#undef _CONCAT_2_STRINGS
|
mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserTitleKey, mKeyset.nca.manual_title_key_aesctr);
|
||||||
|
|
||||||
// Derive keys
|
|
||||||
for (size_t i = 0; i < kMasterKeyNum; i++)
|
|
||||||
{
|
|
||||||
if (master_key[i] != zeros_aes_key)
|
|
||||||
{
|
|
||||||
if (aes_kek_generation_source != zeros_aes_key && aes_key_generation_source != zeros_aes_key)
|
|
||||||
{
|
|
||||||
if (i == 0 && nca_header_kek_source != zeros_aes_key && nca_header_key_source != zeros_aes_xts_key)
|
|
||||||
{
|
|
||||||
if (mKeyset.nca.header_key == zeros_aes_xts_key)
|
|
||||||
{
|
|
||||||
fnd::aes::sAes128Key nca_header_kek;
|
|
||||||
nn::hac::AesKeygen::generateKey(nca_header_kek.key, aes_kek_generation_source.key, nca_header_kek_source.key, aes_key_generation_source.key, master_key[i].key);
|
|
||||||
nn::hac::AesKeygen::generateKey(mKeyset.nca.header_key.key[0], nca_header_key_source.key[0], nca_header_kek.key);
|
|
||||||
nn::hac::AesKeygen::generateKey(mKeyset.nca.header_key.key[1], nca_header_key_source.key[1], nca_header_kek.key);
|
|
||||||
//printf("nca header key[0] ");
|
|
||||||
//fnd::SimpleTextOutput::hexDump(mKeyset.nca.header_key.key[0], 0x10);
|
|
||||||
//printf("nca header key[1] ");
|
|
||||||
//fnd::SimpleTextOutput::hexDump(mKeyset.nca.header_key.key[1], 0x10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t j = 0; j < nn::hac::nca::kKeyAreaEncryptionKeyNum; j++)
|
|
||||||
{
|
|
||||||
if (key_area_key_source[j] != zeros_aes_key && mKeyset.nca.key_area_key[j][i] == zeros_aes_key)
|
|
||||||
{
|
|
||||||
nn::hac::AesKeygen::generateKey(mKeyset.nca.key_area_key[j][i].key, aes_kek_generation_source.key, key_area_key_source[j].key, aes_key_generation_source.key, master_key[i].key);
|
|
||||||
//printf("nca keak %d/%02d ", j, i);
|
|
||||||
//fnd::SimpleTextOutput::hexDump(mKeyset.nca.key_area_key[j][i].key, 0x10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ticket_titlekek_source != zeros_aes_key && mKeyset.ticket.titlekey_kek[i] == zeros_aes_key)
|
|
||||||
{
|
|
||||||
nn::hac::AesKeygen::generateKey(mKeyset.ticket.titlekey_kek[i].key, ticket_titlekek_source.key, master_key[i].key);
|
|
||||||
//printf("ticket titlekek %02d ", i);
|
|
||||||
//fnd::SimpleTextOutput::hexDump(mKeyset.ticket.titlekey_kek[i].key, 0x10);
|
|
||||||
}
|
|
||||||
if (package2_key_source != zeros_aes_key && mKeyset.package2_key[i] == zeros_aes_key)
|
|
||||||
{
|
|
||||||
nn::hac::AesKeygen::generateKey(mKeyset.package2_key[i].key, package2_key_source.key, master_key[i].key);
|
|
||||||
//printf("package2 key %02d ", i);
|
|
||||||
//fnd::SimpleTextOutput::hexDump(mKeyset.package2_key[i].key, 0x10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
for (size_t j = 0; j < nn::hac::nca::kKeyAreaEncryptionKeyNum; j++)
|
|
||||||
{
|
|
||||||
if (mKeyset.nca.key_area_key[j][i] != zeros_aes_key)
|
|
||||||
{
|
|
||||||
printf("nca body keak %d/%02d ", j, i);
|
|
||||||
fnd::SimpleTextOutput::hexDump(mKeyset.nca.key_area_key[j][i].key, 0x10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserSettings::populateUserSettings(sCmdArgs& args)
|
void UserSettings::populateUserSettings(sCmdArgs& args)
|
||||||
|
@ -747,21 +603,6 @@ void UserSettings::populateUserSettings(sCmdArgs& args)
|
||||||
throw fnd::Exception(kModuleName, "Unknown file type.");
|
throw fnd::Exception(kModuleName, "Unknown file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UserSettings::decodeHexStringToBytes(const std::string& name, const std::string& str, byte_t* out, size_t out_len)
|
|
||||||
{
|
|
||||||
size_t size = str.size();
|
|
||||||
if ((size % 2) || ((size / 2) != out_len))
|
|
||||||
{
|
|
||||||
throw fnd::Exception(kModuleName, "Key: \"" + name + "\" has incorrect length");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < out_len; i++)
|
|
||||||
{
|
|
||||||
out[i] = (charToByte(str[i * 2]) << 4) | charToByte(str[(i * 2) + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileType UserSettings::getFileTypeFromString(const std::string& type_str)
|
FileType UserSettings::getFileTypeFromString(const std::string& type_str)
|
||||||
{
|
{
|
||||||
std::string str = type_str;
|
std::string str = type_str;
|
||||||
|
@ -1019,3 +860,25 @@ nn::hac::npdm::InstructionType UserSettings::getInstructionTypeFromString(const
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UserSettings::getHomePath(std::string& path) const
|
||||||
|
{
|
||||||
|
// open other resource files in $HOME/.switch/prod.keys (or $HOME/.switch/dev.keys if -d/--dev is set).
|
||||||
|
path.clear();
|
||||||
|
if (path.empty()) fnd::io::getEnvironVar(path, "HOME");
|
||||||
|
if (path.empty()) fnd::io::getEnvironVar(path, "USERPROFILE");
|
||||||
|
if (path.empty()) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserSettings::getSwitchPath(std::string& path) const
|
||||||
|
{
|
||||||
|
std::string home;
|
||||||
|
home.clear();
|
||||||
|
getHomePath(home);
|
||||||
|
if (home.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
path.clear();
|
||||||
|
fnd::io::appendToPath(path, home);
|
||||||
|
fnd::io::appendToPath(path, kHomeSwitchDirStr);
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
#include <nn/pki/CertificateBody.h>
|
#include <nn/pki/CertificateBody.h>
|
||||||
#include <nn/hac/npdm.h>
|
#include <nn/hac/npdm.h>
|
||||||
#include "nstool.h"
|
#include "nstool.h"
|
||||||
|
#include "KeyConfiguration.h"
|
||||||
|
|
||||||
class UserSettings
|
class UserSettings
|
||||||
{
|
{
|
||||||
|
@ -19,6 +20,7 @@ public:
|
||||||
|
|
||||||
// generic options
|
// generic options
|
||||||
const std::string getInputPath() const;
|
const std::string getInputPath() const;
|
||||||
|
const KeyConfiguration& getKeyCfg() const;
|
||||||
const sKeyset& getKeyset() const;
|
const sKeyset& getKeyset() const;
|
||||||
FileType getFileType() const;
|
FileType getFileType() const;
|
||||||
bool isVerifyFile() const;
|
bool isVerifyFile() const;
|
||||||
|
@ -44,9 +46,16 @@ public:
|
||||||
const sOptional<std::string>& getAssetNacpPath() const;
|
const sOptional<std::string>& getAssetNacpPath() const;
|
||||||
const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& getCertificateChain() const;
|
const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& getCertificateChain() const;
|
||||||
|
|
||||||
|
void dumpKeys() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string kModuleName = "UserSettings";
|
const std::string kModuleName = "UserSettings";
|
||||||
|
|
||||||
|
const std::string kHomeSwitchDirStr = ".switch";
|
||||||
|
const std::string kGeneralKeyfileName[2] = { "prod.keys", "dev.keys" };
|
||||||
|
const std::string kTitleKeyfileName = "title.keys";
|
||||||
|
|
||||||
|
|
||||||
struct sCmdArgs
|
struct sCmdArgs
|
||||||
{
|
{
|
||||||
sCmdArgs() {}
|
sCmdArgs() {}
|
||||||
|
@ -82,6 +91,7 @@ private:
|
||||||
std::string mInputPath;
|
std::string mInputPath;
|
||||||
FileType mFileType;
|
FileType mFileType;
|
||||||
sKeyset mKeyset;
|
sKeyset mKeyset;
|
||||||
|
KeyConfiguration mKeyCfg;
|
||||||
bool mVerifyFile;
|
bool mVerifyFile;
|
||||||
CliOutputMode mOutputMode;
|
CliOutputMode mOutputMode;
|
||||||
|
|
||||||
|
@ -109,7 +119,6 @@ private:
|
||||||
void populateCmdArgs(const std::vector<std::string>& arg_list, sCmdArgs& cmd_args);
|
void populateCmdArgs(const std::vector<std::string>& arg_list, sCmdArgs& cmd_args);
|
||||||
void populateKeyset(sCmdArgs& args);
|
void populateKeyset(sCmdArgs& args);
|
||||||
void populateUserSettings(sCmdArgs& args);
|
void populateUserSettings(sCmdArgs& args);
|
||||||
void decodeHexStringToBytes(const std::string& name, const std::string& str, byte_t* out, size_t out_len);
|
|
||||||
FileType getFileTypeFromString(const std::string& type_str);
|
FileType getFileTypeFromString(const std::string& type_str);
|
||||||
FileType determineFileTypeFromFile(const std::string& path);
|
FileType determineFileTypeFromFile(const std::string& path);
|
||||||
bool determineValidNcaFromSample(const fnd::Vec<byte_t>& sample) const;
|
bool determineValidNcaFromSample(const fnd::Vec<byte_t>& sample) const;
|
||||||
|
@ -118,4 +127,6 @@ private:
|
||||||
bool determineValidEsCertFromSample(const fnd::Vec<byte_t>& sample) const;
|
bool determineValidEsCertFromSample(const fnd::Vec<byte_t>& sample) const;
|
||||||
bool determineValidEsTikFromSample(const fnd::Vec<byte_t>& sample) const;
|
bool determineValidEsTikFromSample(const fnd::Vec<byte_t>& sample) const;
|
||||||
nn::hac::npdm::InstructionType getInstructionTypeFromString(const std::string& type_str);
|
nn::hac::npdm::InstructionType getInstructionTypeFromString(const std::string& type_str);
|
||||||
|
void getHomePath(std::string& path) const;
|
||||||
|
void getSwitchPath(std::string& path) const;
|
||||||
};
|
};
|
|
@ -1,5 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#pragma once
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fnd/types.h>
|
#include <fnd/types.h>
|
||||||
#include <fnd/aes.h>
|
#include <fnd/aes.h>
|
||||||
|
@ -61,6 +60,9 @@ struct sOptional
|
||||||
inline T& operator*() { return var; }
|
inline T& operator*() { return var; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const byte_t kDummyRightsIdForUserTitleKey[nn::hac::nca::kRightsIdLen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||||
|
const byte_t kDummyRightsIdForUserBodyKey[nn::hac::nca::kRightsIdLen] = {0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
|
||||||
|
|
||||||
struct sKeyset
|
struct sKeyset
|
||||||
{
|
{
|
||||||
fnd::rsa::sRsa2048Key acid_sign_key;
|
fnd::rsa::sRsa2048Key acid_sign_key;
|
||||||
|
@ -97,14 +99,3 @@ struct sKeyset
|
||||||
fnd::rsa::sRsa4096Key root_sign_key;
|
fnd::rsa::sRsa4096Key root_sign_key;
|
||||||
} pki;
|
} pki;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline byte_t charToByte(char chr)
|
|
||||||
{
|
|
||||||
if (chr >= 'a' && chr <= 'f')
|
|
||||||
return (chr - 'a') + 0xa;
|
|
||||||
else if (chr >= 'A' && chr <= 'F')
|
|
||||||
return (chr - 'A') + 0xa;
|
|
||||||
else if (chr >= '0' && chr <= '9')
|
|
||||||
return chr - '0';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue