mirror of
https://github.com/jakcron/nstool
synced 2024-12-27 15:11:12 +00:00
Merge pull request #46 from jakcron/nstool-keycfg
Separate class for Key management.
This commit is contained in:
commit
a1c5f91dc0
21 changed files with 840 additions and 389 deletions
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <fnd/types.h>
|
||||
#include <fnd/Vec.h>
|
||||
|
||||
namespace fnd
|
||||
{
|
||||
|
@ -12,6 +13,8 @@ namespace fnd
|
|||
static void hexDump(const byte_t* data, size_t len, size_t row_len, size_t indent_len);
|
||||
static void hexDump(const byte_t* data, size_t len);
|
||||
static std::string arrayToString(const byte_t* data, size_t len, bool upper_case, const std::string& separator);
|
||||
static void stringToArray(const std::string& str, fnd::Vec<byte_t>& array);
|
||||
|
||||
private:
|
||||
static const size_t kDefaultRowLen = 0x10;
|
||||
static const size_t kDefaultByteGroupingSize = 1;
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace fnd
|
|||
|
||||
void operator=(const sEcdsa240PrivateKey& other)
|
||||
{
|
||||
memcpy(this->k, k, kEcdsa240Size);
|
||||
memcpy(this->k, other.k, kEcdsa240Size);
|
||||
}
|
||||
|
||||
bool operator==(const sEcdsa240PrivateKey& other) const
|
||||
|
@ -56,6 +56,28 @@ namespace fnd
|
|||
return !operator==(other);
|
||||
}
|
||||
};
|
||||
|
||||
struct sEcdsa240Key
|
||||
{
|
||||
sEcdsa240Point pub;
|
||||
sEcdsa240PrivateKey pvt;
|
||||
|
||||
void operator=(const sEcdsa240Key& other)
|
||||
{
|
||||
this->pub = other.pub;
|
||||
this->pvt = other.pvt;
|
||||
}
|
||||
|
||||
bool operator==(const sEcdsa240Key& other) const
|
||||
{
|
||||
return this->pub == other.pub && this->pvt == other.pvt;
|
||||
}
|
||||
|
||||
bool operator!=(const sEcdsa240Key& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
};
|
||||
#pragma pack (pop)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,4 +87,31 @@ std::string fnd::SimpleTextOutput::arrayToString(const byte_t* data, size_t len,
|
|||
ss << separator;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void fnd::SimpleTextOutput::stringToArray(const std::string& str, fnd::Vec<byte_t>& array)
|
||||
{
|
||||
size_t size = str.size();
|
||||
if ((size % 2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
array.alloc(size/2);
|
||||
|
||||
for (size_t i = 0; i < array.size(); i++)
|
||||
{
|
||||
array[i] = (charToByte(str[i * 2]) << 4) | charToByte(str[(i * 2) + 1]);
|
||||
}
|
||||
}
|
|
@ -41,9 +41,9 @@ void EsTikProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
|||
mOwnIFile = ownIFile;
|
||||
}
|
||||
|
||||
void EsTikProcess::setKeyset(const sKeyset* keyset)
|
||||
void EsTikProcess::setKeyCfg(const KeyConfiguration& keycfg)
|
||||
{
|
||||
mKeyset = keyset;
|
||||
mKeyCfg = keycfg;
|
||||
}
|
||||
|
||||
void EsTikProcess::setCertificateChain(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs)
|
||||
|
@ -95,7 +95,7 @@ void EsTikProcess::verifyTicket()
|
|||
|
||||
try
|
||||
{
|
||||
pki_validator.setRootKey(mKeyset->pki.root_sign_key);
|
||||
pki_validator.setKeyCfg(mKeyCfg);
|
||||
pki_validator.addCertificates(mCerts);
|
||||
pki_validator.validateSignature(mTik.getBody().getIssuer(), mTik.getSignature().getSignType(), mTik.getSignature().getSignature(), tik_hash);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <nn/pki/SignedData.h>
|
||||
#include <nn/pki/CertificateBody.h>
|
||||
#include <nn/es/TicketBody_V2.h>
|
||||
#include "KeyConfiguration.h"
|
||||
#include "nstool.h"
|
||||
|
||||
class EsTikProcess
|
||||
|
@ -17,7 +18,7 @@ public:
|
|||
void process();
|
||||
|
||||
void setInputFile(fnd::IFile* file, bool ownIFile);
|
||||
void setKeyset(const sKeyset* keyset);
|
||||
void setKeyCfg(const KeyConfiguration& keycfg);
|
||||
void setCertificateChain(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs);
|
||||
void setCliOutputMode(CliOutputMode mode);
|
||||
void setVerifyMode(bool verify);
|
||||
|
@ -27,7 +28,7 @@ private:
|
|||
|
||||
fnd::IFile* mFile;
|
||||
bool mOwnIFile;
|
||||
const sKeyset* mKeyset;
|
||||
KeyConfiguration mKeyCfg;
|
||||
CliOutputMode mCliOutputMode;
|
||||
bool mVerify;
|
||||
|
||||
|
|
376
programs/nstool/source/KeyConfiguration.cpp
Normal file
376
programs/nstool/source/KeyConfiguration.cpp
Normal file
|
@ -0,0 +1,376 @@
|
|||
#include "KeyConfiguration.h"
|
||||
#include <fnd/ResourceFileReader.h>
|
||||
#include <fnd/SimpleTextOutput.h>
|
||||
#include <nn/hac/AesKeygen.h>
|
||||
#include <nn/hac/NcaUtils.h>
|
||||
|
||||
KeyConfiguration::KeyConfiguration()
|
||||
{
|
||||
clearGeneralKeyConfiguration();
|
||||
clearNcaExternalKeys();
|
||||
}
|
||||
|
||||
KeyConfiguration::KeyConfiguration(const KeyConfiguration& other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
void KeyConfiguration::operator=(const KeyConfiguration& other)
|
||||
{
|
||||
mAcidSignKey = other.mAcidSignKey;
|
||||
mPkg2SignKey = other.mPkg2SignKey;
|
||||
mNcaHeader0SignKey = other.mNcaHeader0SignKey;
|
||||
mXciHeaderSignKey = other.mXciHeaderSignKey;
|
||||
|
||||
mNcaHeaderKey = other.mNcaHeaderKey;
|
||||
mXciHeaderKey = other.mXciHeaderKey;
|
||||
|
||||
for (size_t i = 0; i < kMasterKeyNum; i++)
|
||||
{
|
||||
mPkg2Key[i] = other.mPkg2Key[i];
|
||||
mPkg1Key[i] = other.mPkg1Key[i];
|
||||
mNcaKeyAreaEncryptionKey[0][i] = other.mNcaKeyAreaEncryptionKey[0][i];
|
||||
mNcaKeyAreaEncryptionKey[1][i] = other.mNcaKeyAreaEncryptionKey[1][i];
|
||||
mNcaKeyAreaEncryptionKey[2][i] = other.mNcaKeyAreaEncryptionKey[2][i];
|
||||
mNcaKeyAreaEncryptionKeyHw[0][i] = other.mNcaKeyAreaEncryptionKeyHw[0][i];
|
||||
mNcaKeyAreaEncryptionKeyHw[1][i] = other.mNcaKeyAreaEncryptionKeyHw[1][i];
|
||||
mNcaKeyAreaEncryptionKeyHw[2][i] = other.mNcaKeyAreaEncryptionKeyHw[2][i];
|
||||
mETicketCommonKey[i] = other.mETicketCommonKey[i];
|
||||
}
|
||||
|
||||
mPkiRootKeyList = other.mPkiRootKeyList;
|
||||
|
||||
mNcaExternalContentKeyList = other.mNcaExternalContentKeyList;
|
||||
}
|
||||
|
||||
void KeyConfiguration::importHactoolGenericKeyfile(const std::string& path)
|
||||
{
|
||||
clearGeneralKeyConfiguration();
|
||||
|
||||
fnd::ResourceFileReader res;
|
||||
try
|
||||
{
|
||||
res.processFile(path);
|
||||
}
|
||||
catch (const fnd::Exception&)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Failed to open key file: " + path);
|
||||
}
|
||||
|
||||
// internally used sources
|
||||
fnd::aes::sAes128Key master_key[kMasterKeyNum] = { kNullAesKey };
|
||||
fnd::aes::sAes128Key package2_key_source = kNullAesKey;
|
||||
fnd::aes::sAes128Key ticket_titlekek_source = kNullAesKey;
|
||||
fnd::aes::sAes128Key key_area_key_source[kNcaKeakNum] = { kNullAesKey, kNullAesKey, kNullAesKey };
|
||||
fnd::aes::sAes128Key aes_kek_generation_source = kNullAesKey;
|
||||
fnd::aes::sAes128Key aes_key_generation_source = kNullAesKey;
|
||||
fnd::aes::sAes128Key nca_header_kek_source = kNullAesKey;
|
||||
fnd::aes::sAesXts128Key nca_header_key_source = kNullAesXtsKey;
|
||||
fnd::rsa::sRsa4096Key pki_root_sign_key = kNullRsa4096Key;
|
||||
|
||||
#define _CONCAT_2_STRINGS(str1, str2) ((str1) + "_" + (str2))
|
||||
#define _CONCAT_3_STRINGS(str1, str2, str3) _CONCAT_2_STRINGS(_CONCAT_2_STRINGS(str1, str2), str3)
|
||||
|
||||
std::string key,val;
|
||||
fnd::Vec<byte_t> dec_array;
|
||||
|
||||
#define _SAVE_KEYDATA(key_name, array, len) \
|
||||
key = (key_name); \
|
||||
val = res[key]; \
|
||||
if (val.empty() == false) { \
|
||||
fnd::SimpleTextOutput::stringToArray(val, dec_array); \
|
||||
if (dec_array.size() != len) \
|
||||
throw fnd::Exception(kModuleName, "Key: \"" + key_name + "\" has incorrect length"); \
|
||||
memcpy(array, dec_array.data(), len); \
|
||||
}
|
||||
|
||||
for (size_t nameidx = 0; nameidx < kNameVariantNum; nameidx++)
|
||||
{
|
||||
// import sources
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kKeyStr, kSourceStr), package2_key_source.key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[nameidx], kSourceStr), ticket_titlekek_source.key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kSourceStr), key_area_key_source[0].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kSourceStr), key_area_key_source[1].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kSourceStr), key_area_key_source[2].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kKekGenBase[nameidx], kSourceStr), aes_kek_generation_source.key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kKeyGenBase[nameidx], kSourceStr), aes_key_generation_source.key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kXciHeaderBase[nameidx], kKekStr, kSourceStr), nca_header_kek_source.key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kXciHeaderBase[nameidx], kKeyStr, kSourceStr), nca_header_key_source.key, 0x20);
|
||||
|
||||
// Store Key Variants/Derivatives
|
||||
for (size_t mkeyidx = 0; mkeyidx < kMasterKeyNum; mkeyidx++)
|
||||
{
|
||||
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kMasterBase[nameidx], kKeyStr, kKeyIndex[mkeyidx]), master_key[mkeyidx].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg1Base[nameidx], kKeyStr, kKeyIndex[mkeyidx]), mPkg1Key[mkeyidx].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kKeyStr, kKeyIndex[mkeyidx]), mPkg2Key[mkeyidx].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[nameidx], kKeyIndex[mkeyidx]), mETicketCommonKey[mkeyidx].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[0][mkeyidx].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[1][mkeyidx].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[2][mkeyidx].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[0][mkeyidx].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[1][mkeyidx].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[2][mkeyidx].key, 0x10);
|
||||
}
|
||||
|
||||
// store nca header key
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kKeyStr), mNcaHeaderKey.key[0], 0x20);
|
||||
|
||||
// store xci header key
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kKeyStr), mXciHeaderKey.key, 0x10);
|
||||
|
||||
// store rsa keys
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kRsaKeyPrivate), mNcaHeader0SignKey.priv_exponent, fnd::rsa::kRsa2048Size);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kRsaKeyModulus), mNcaHeader0SignKey.modulus, fnd::rsa::kRsa2048Size);
|
||||
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kRsaKeyPrivate), mXciHeaderSignKey.priv_exponent, fnd::rsa::kRsa2048Size);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kRsaKeyModulus), mXciHeaderSignKey.modulus, fnd::rsa::kRsa2048Size);
|
||||
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase[nameidx], kRsaKeyPrivate), mAcidSignKey.priv_exponent, fnd::rsa::kRsa2048Size);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase[nameidx], kRsaKeyModulus), mAcidSignKey.modulus, fnd::rsa::kRsa2048Size);
|
||||
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkg2Base[nameidx], kRsaKeyPrivate), mPkg2SignKey.priv_exponent, fnd::rsa::kRsa2048Size);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkg2Base[nameidx], kRsaKeyModulus), mPkg2SignKey.modulus, fnd::rsa::kRsa2048Size);
|
||||
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase[nameidx], kRsaKeyPrivate), pki_root_sign_key.priv_exponent, fnd::rsa::kRsa4096Size);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase[nameidx], kRsaKeyModulus), pki_root_sign_key.modulus, fnd::rsa::kRsa4096Size);
|
||||
}
|
||||
|
||||
#undef _SAVE_KEYDATA
|
||||
#undef _CONCAT_3_STRINGS
|
||||
#undef _CONCAT_2_STRINGS
|
||||
|
||||
// Derive keys
|
||||
for (size_t i = 0; i < kMasterKeyNum; i++)
|
||||
{
|
||||
if (master_key[i] != kNullAesKey)
|
||||
{
|
||||
if (aes_kek_generation_source != kNullAesKey && aes_key_generation_source != kNullAesKey)
|
||||
{
|
||||
if (i == 0 && nca_header_kek_source != kNullAesKey && nca_header_key_source != kNullAesXtsKey)
|
||||
{
|
||||
if (mNcaHeaderKey == kNullAesXtsKey)
|
||||
{
|
||||
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(mNcaHeaderKey.key[0], nca_header_key_source.key[0], nca_header_kek.key);
|
||||
nn::hac::AesKeygen::generateKey(mNcaHeaderKey.key[1], nca_header_key_source.key[1], nca_header_kek.key);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < nn::hac::nca::kKeyAreaEncryptionKeyNum; j++)
|
||||
{
|
||||
if (key_area_key_source[j] != kNullAesKey && mNcaKeyAreaEncryptionKey[j][i] == kNullAesKey)
|
||||
{
|
||||
nn::hac::AesKeygen::generateKey(mNcaKeyAreaEncryptionKey[j][i].key, aes_kek_generation_source.key, key_area_key_source[j].key, aes_key_generation_source.key, master_key[i].key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ticket_titlekek_source != kNullAesKey && mETicketCommonKey[i] == kNullAesKey)
|
||||
{
|
||||
nn::hac::AesKeygen::generateKey(mETicketCommonKey[i].key, ticket_titlekek_source.key, master_key[i].key);
|
||||
}
|
||||
if (package2_key_source != kNullAesKey && mPkg2Key[i] == kNullAesKey)
|
||||
{
|
||||
nn::hac::AesKeygen::generateKey(mPkg2Key[i].key, package2_key_source.key, master_key[i].key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// populate pki root keys
|
||||
if (pki_root_sign_key != kNullRsa4096Key)
|
||||
{
|
||||
sPkiRootKey tmp;
|
||||
|
||||
tmp.name = nn::pki::sign::kRootIssuerStr;
|
||||
tmp.key_type = nn::pki::sign::SIGN_ALGO_RSA4096;
|
||||
tmp.rsa4096_key = pki_root_sign_key;
|
||||
|
||||
mPkiRootKeyList.addElement(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void KeyConfiguration::clearGeneralKeyConfiguration()
|
||||
{
|
||||
mAcidSignKey = kNullRsa2048Key;
|
||||
mPkg2SignKey = kNullRsa2048Key;
|
||||
mNcaHeader0SignKey = kNullRsa2048Key;
|
||||
mXciHeaderSignKey = kNullRsa2048Key;
|
||||
mPkiRootKeyList.clear();
|
||||
|
||||
mNcaHeaderKey = kNullAesXtsKey;
|
||||
mXciHeaderKey = kNullAesKey;
|
||||
|
||||
for (size_t i = 0; i < kMasterKeyNum; i++)
|
||||
{
|
||||
mPkg1Key[i] = kNullAesKey;
|
||||
mPkg2Key[i] = kNullAesKey;
|
||||
mETicketCommonKey[i] = kNullAesKey;
|
||||
for (size_t j = 0; j < kNcaKeakNum; j++)
|
||||
{
|
||||
mNcaKeyAreaEncryptionKey[j][i] = kNullAesKey;
|
||||
mNcaKeyAreaEncryptionKey[j][i] = kNullAesKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyConfiguration::clearNcaExternalKeys()
|
||||
{
|
||||
mNcaExternalContentKeyList.clear();
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getNcaHeaderKey(fnd::aes::sAesXts128Key& key) const
|
||||
{
|
||||
return copyOutKeyResourceIfExists(mNcaHeaderKey, key, kNullAesXtsKey);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getNcaHeader0SignKey(fnd::rsa::sRsa2048Key& key) const
|
||||
{
|
||||
return copyOutKeyResourceIfExists(mNcaHeader0SignKey, key, kNullRsa2048Key);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getAcidSignKey(fnd::rsa::sRsa2048Key& key) const
|
||||
{
|
||||
return copyOutKeyResourceIfExists(mAcidSignKey, key, kNullRsa2048Key);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getNcaKeyAreaEncryptionKey(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const
|
||||
{
|
||||
if (keak_type >= kNcaKeakNum || masterkey_index >= kMasterKeyNum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return copyOutKeyResourceIfExists(mNcaKeyAreaEncryptionKey[keak_type][masterkey_index], key, kNullAesKey);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getNcaKeyAreaEncryptionKeyHw(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const
|
||||
{
|
||||
if (keak_type >= kNcaKeakNum || masterkey_index >= kMasterKeyNum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return copyOutKeyResourceIfExists(mNcaKeyAreaEncryptionKeyHw[keak_type][masterkey_index], key, kNullAesKey);
|
||||
}
|
||||
|
||||
void KeyConfiguration::addNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], const fnd::aes::sAes128Key& key)
|
||||
{
|
||||
sNcaExternalContentKey tmp;
|
||||
memcpy(tmp.rights_id.data, rights_id, nn::hac::nca::kRightsIdLen);
|
||||
tmp.key = key;
|
||||
|
||||
if (mNcaExternalContentKeyList.hasElement(tmp))
|
||||
return;
|
||||
|
||||
mNcaExternalContentKeyList.addElement(tmp);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], fnd::aes::sAes128Key& key) const
|
||||
{
|
||||
sRightsId id;
|
||||
bool res_exists = false;
|
||||
|
||||
memcpy(id.data, rights_id, nn::hac::nca::kRightsIdLen);
|
||||
for (size_t i = 0; i < mNcaExternalContentKeyList.size(); i++)
|
||||
{
|
||||
if (mNcaExternalContentKeyList[i].rights_id == id)
|
||||
{
|
||||
res_exists = true;
|
||||
key = mNcaExternalContentKeyList[i].key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res_exists;
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getPkg1Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const
|
||||
{
|
||||
if (masterkey_index >= kMasterKeyNum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return copyOutKeyResourceIfExists(mPkg1Key[masterkey_index], key, kNullAesKey);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getPkg2Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const
|
||||
{
|
||||
if (masterkey_index >= kMasterKeyNum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return copyOutKeyResourceIfExists(mPkg2Key[masterkey_index], key, kNullAesKey);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getPkg2SignKey(fnd::rsa::sRsa2048Key& key) const
|
||||
{
|
||||
return copyOutKeyResourceIfExists(mPkg2SignKey, key, kNullRsa2048Key);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getXciHeaderSignKey(fnd::rsa::sRsa2048Key& key) const
|
||||
{
|
||||
return copyOutKeyResourceIfExists(mXciHeaderSignKey, key, kNullRsa2048Key);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getXciHeaderKey(fnd::aes::sAes128Key& key) const
|
||||
{
|
||||
return copyOutKeyResourceIfExists(mXciHeaderKey, key, kNullAesKey);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getETicketCommonKey(byte_t masterkey_index, fnd::aes::sAes128Key& key) const
|
||||
{
|
||||
if (masterkey_index >= kMasterKeyNum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return copyOutKeyResourceIfExists(mETicketCommonKey[masterkey_index], key, kNullAesKey);
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa4096Key& key) const
|
||||
{
|
||||
bool res_exists = false;
|
||||
for (size_t i = 0; i < mPkiRootKeyList.size(); i++)
|
||||
{
|
||||
if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_RSA4096)
|
||||
{
|
||||
res_exists = true;
|
||||
key = mPkiRootKeyList[i].rsa4096_key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res_exists;
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa2048Key& key) const
|
||||
{
|
||||
bool res_exists = false;
|
||||
for (size_t i = 0; i < mPkiRootKeyList.size(); i++)
|
||||
{
|
||||
if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_RSA2048)
|
||||
{
|
||||
res_exists = true;
|
||||
key = mPkiRootKeyList[i].rsa2048_key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res_exists;
|
||||
}
|
||||
|
||||
bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::ecdsa::sEcdsa240Key& key) const
|
||||
{
|
||||
bool res_exists = false;
|
||||
for (size_t i = 0; i < mPkiRootKeyList.size(); i++)
|
||||
{
|
||||
if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_ECDSA240)
|
||||
{
|
||||
res_exists = true;
|
||||
key = mPkiRootKeyList[i].ecdsa240_key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res_exists;
|
||||
}
|
209
programs/nstool/source/KeyConfiguration.h
Normal file
209
programs/nstool/source/KeyConfiguration.h
Normal file
|
@ -0,0 +1,209 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <fnd/types.h>
|
||||
#include <fnd/aes.h>
|
||||
#include <fnd/rsa.h>
|
||||
#include <fnd/ecdsa.h>
|
||||
#include <nn/hac/nca.h>
|
||||
#include <nn/pki/SignedData.h>
|
||||
#include <nn/es/TicketBody_V2.h>
|
||||
|
||||
class KeyConfiguration
|
||||
{
|
||||
public:
|
||||
KeyConfiguration();
|
||||
KeyConfiguration(const KeyConfiguration& other);
|
||||
|
||||
void operator=(const KeyConfiguration& other);
|
||||
|
||||
void importHactoolGenericKeyfile(const std::string& path);
|
||||
//void importHactoolTitleKeyfile(const std::string& path);
|
||||
|
||||
void clearGeneralKeyConfiguration();
|
||||
void clearNcaExternalKeys();
|
||||
|
||||
// nca keys
|
||||
bool getNcaHeaderKey(fnd::aes::sAesXts128Key& key) const;
|
||||
bool getNcaHeader0SignKey(fnd::rsa::sRsa2048Key& key) const;
|
||||
bool getAcidSignKey(fnd::rsa::sRsa2048Key& key) const;
|
||||
bool getNcaKeyAreaEncryptionKey(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const;
|
||||
bool getNcaKeyAreaEncryptionKeyHw(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const;
|
||||
|
||||
// external content keys
|
||||
void addNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], const fnd::aes::sAes128Key& key);
|
||||
bool getNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], fnd::aes::sAes128Key& key) const;
|
||||
|
||||
// pkg1/pkg2
|
||||
bool getPkg1Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const;
|
||||
bool getPkg2Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const;
|
||||
bool getPkg2SignKey(fnd::rsa::sRsa2048Key& key) const;
|
||||
|
||||
// xci keys
|
||||
bool getXciHeaderSignKey(fnd::rsa::sRsa2048Key& key) const;
|
||||
bool getXciHeaderKey(fnd::aes::sAes128Key& key) const;
|
||||
|
||||
// ticket
|
||||
bool getETicketCommonKey(byte_t masterkey_index, fnd::aes::sAes128Key& key) const;
|
||||
|
||||
// pki
|
||||
bool getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa4096Key& key) const;
|
||||
bool getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa2048Key& key) const;
|
||||
bool getPkiRootSignKey(const std::string& root_name, fnd::ecdsa::sEcdsa240Key& key) const;
|
||||
private:
|
||||
const std::string kModuleName = "KeyConfiguration";
|
||||
const fnd::aes::sAes128Key kNullAesKey = {{0}};
|
||||
const fnd::aes::sAesXts128Key kNullAesXtsKey = {{{0}}};
|
||||
const fnd::rsa::sRsa4096Key kNullRsa4096Key = {{0}, {0}, {0}};
|
||||
const fnd::rsa::sRsa2048Key kNullRsa2048Key = {{0}, {0}, {0}};
|
||||
static const size_t kMasterKeyNum = 0x20;
|
||||
static const size_t kNcaKeakNum = nn::hac::nca::kKeyAreaEncryptionKeyNum;
|
||||
|
||||
// keynames
|
||||
enum NameVariantIndex
|
||||
{
|
||||
NNTOOLS,
|
||||
LEGACY_HACTOOL,
|
||||
LEGACY_0
|
||||
};
|
||||
static const size_t kNameVariantNum = 3;
|
||||
const std::string kMasterBase[kNameVariantNum] = { "master", "master", "master" };
|
||||
const std::string kPkg1Base[kNameVariantNum] = { "package1", "package1", "package1" };
|
||||
const std::string kPkg2Base[kNameVariantNum] = { "package2", "package2", "package2" };
|
||||
const std::string kXciHeaderBase[kNameVariantNum] = { "xci_header", "xci_header", "xci_header" };
|
||||
const std::string kNcaHeaderBase[kNameVariantNum] = { "nca_header", "header", "nca_header" };
|
||||
const std::string kAcidBase[kNameVariantNum] = { "acid", "acid", "acid" };
|
||||
const std::string kPkiRootBase[kNameVariantNum] = { "pki_root", "pki_root", "pki_root" };
|
||||
const std::string kTicketCommonKeyBase[kNameVariantNum] = { "ticket_commonkey", "titlekek", "ticket_commonkey" };
|
||||
const std::string kNcaKeyAreaEncKeyBase[kNameVariantNum] = { "nca_body_keak", "key_area_key", "nca_body_keak" };
|
||||
const std::string kNcaKeyAreaEncKeyHwBase[kNameVariantNum] = { "nca_body_keakhw", "key_area_hw_key", "nca_body_keakhw" };
|
||||
const std::string kKekGenBase[kNameVariantNum] = { "aes_kek_generation", "aes_kek_generation", "aes_kek_generation" };
|
||||
const std::string kKeyGenBase[kNameVariantNum] = { "aes_key_generation", "aes_key_generation", "aes_key_generation" };
|
||||
|
||||
// misc str
|
||||
const std::string kKeyStr = "key";
|
||||
const std::string kKekStr = "kek";
|
||||
const std::string kSourceStr = "source";
|
||||
const std::string kRsaKeyModulus = "sign_key_modulus";
|
||||
const std::string kRsaKeyPrivate = "sign_key_private";
|
||||
const std::string kNcaKeyAreaKeyIndexStr[kNcaKeakNum] = { "application", "ocean", "system" };
|
||||
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"};
|
||||
|
||||
struct sRightsId
|
||||
{
|
||||
byte_t data[nn::hac::nca::kRightsIdLen];
|
||||
|
||||
void operator=(const sRightsId& other)
|
||||
{
|
||||
memcpy(this->data, other.data, nn::hac::nca::kRightsIdLen);
|
||||
}
|
||||
|
||||
bool operator==(const sRightsId& other) const
|
||||
{
|
||||
return memcmp(this->data, other.data, nn::hac::nca::kRightsIdLen) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const sRightsId& other) const
|
||||
{
|
||||
return !(operator==(other));
|
||||
}
|
||||
};
|
||||
|
||||
struct sNcaExternalContentKey
|
||||
{
|
||||
sRightsId rights_id;
|
||||
fnd::aes::sAes128Key key;
|
||||
|
||||
void operator=(const sNcaExternalContentKey& other)
|
||||
{
|
||||
rights_id = other.rights_id;
|
||||
key = other.key;
|
||||
}
|
||||
|
||||
bool operator==(const sNcaExternalContentKey& other) const
|
||||
{
|
||||
return (rights_id == other.rights_id) \
|
||||
&& (key == other.key);
|
||||
}
|
||||
|
||||
bool operator!=(const sNcaExternalContentKey& other) const
|
||||
{
|
||||
return !(operator==(other));
|
||||
}
|
||||
};
|
||||
|
||||
struct sPkiRootKey
|
||||
{
|
||||
std::string name;
|
||||
nn::pki::sign::SignatureAlgo key_type;
|
||||
fnd::rsa::sRsa4096Key rsa4096_key;
|
||||
fnd::rsa::sRsa2048Key rsa2048_key;
|
||||
fnd::ecdsa::sEcdsa240Key ecdsa240_key;
|
||||
|
||||
void operator=(const sPkiRootKey& other)
|
||||
{
|
||||
name = other.name;
|
||||
key_type = other.key_type;
|
||||
rsa4096_key = other.rsa4096_key;
|
||||
rsa2048_key = other.rsa2048_key;
|
||||
ecdsa240_key = other.ecdsa240_key;
|
||||
}
|
||||
|
||||
bool operator==(const sPkiRootKey& other) const
|
||||
{
|
||||
return (name == other.name) \
|
||||
&& (key_type == other.key_type) \
|
||||
&& (rsa4096_key == other.rsa4096_key) \
|
||||
&& (rsa2048_key == other.rsa2048_key) \
|
||||
&& (ecdsa240_key == other.ecdsa240_key);
|
||||
}
|
||||
|
||||
bool operator!=(const sPkiRootKey& other) const
|
||||
{
|
||||
return !(operator==(other));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* general key config */
|
||||
// acid
|
||||
fnd::rsa::sRsa2048Key mAcidSignKey;
|
||||
|
||||
// pkg1 and pkg2
|
||||
fnd::aes::sAes128Key mPkg1Key[kMasterKeyNum];
|
||||
fnd::rsa::sRsa2048Key mPkg2SignKey;
|
||||
fnd::aes::sAes128Key mPkg2Key[kMasterKeyNum];
|
||||
|
||||
// nca
|
||||
fnd::rsa::sRsa2048Key mNcaHeader0SignKey;
|
||||
fnd::aes::sAesXts128Key mNcaHeaderKey;
|
||||
fnd::aes::sAes128Key mNcaKeyAreaEncryptionKey[kNcaKeakNum][kMasterKeyNum];
|
||||
fnd::aes::sAes128Key mNcaKeyAreaEncryptionKeyHw[kNcaKeakNum][kMasterKeyNum];
|
||||
|
||||
// xci
|
||||
fnd::rsa::sRsa2048Key mXciHeaderSignKey;
|
||||
fnd::aes::sAes128Key mXciHeaderKey;
|
||||
|
||||
// ticket
|
||||
fnd::aes::sAes128Key mETicketCommonKey[kMasterKeyNum];
|
||||
|
||||
// pki
|
||||
fnd::List<sPkiRootKey> mPkiRootKeyList;
|
||||
|
||||
/* Nca External Keys */
|
||||
fnd::List<sNcaExternalContentKey> mNcaExternalContentKeyList;
|
||||
|
||||
template <class T>
|
||||
bool copyOutKeyResourceIfExists(const T& src, T& dst, const T& null_sample) const
|
||||
{
|
||||
bool resource_exists = false;
|
||||
|
||||
if (src != null_sample)
|
||||
{
|
||||
resource_exists = true;
|
||||
dst = src;
|
||||
}
|
||||
|
||||
return resource_exists;
|
||||
}
|
||||
};
|
|
@ -15,7 +15,6 @@
|
|||
NcaProcess::NcaProcess() :
|
||||
mFile(nullptr),
|
||||
mOwnIFile(false),
|
||||
mKeyset(nullptr),
|
||||
mCliOutputMode(_BIT(OUTPUT_BASIC)),
|
||||
mVerify(false),
|
||||
mListFs(false)
|
||||
|
@ -72,9 +71,9 @@ void NcaProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
|||
mOwnIFile = ownIFile;
|
||||
}
|
||||
|
||||
void NcaProcess::setKeyset(const sKeyset* keyset)
|
||||
void NcaProcess::setKeyCfg(const KeyConfiguration& keycfg)
|
||||
{
|
||||
mKeyset = keyset;
|
||||
mKeyCfg = keycfg;
|
||||
}
|
||||
|
||||
void NcaProcess::setCliOutputMode(CliOutputMode type)
|
||||
|
@ -127,7 +126,9 @@ void NcaProcess::importHeader()
|
|||
mFile->read((byte_t*)&mHdrBlock, 0, sizeof(nn::hac::sNcaHeaderBlock));
|
||||
|
||||
// decrypt header block
|
||||
nn::hac::NcaUtils::decryptNcaHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, mKeyset->nca.header_key);
|
||||
fnd::aes::sAesXts128Key header_key;
|
||||
mKeyCfg.getNcaHeaderKey(header_key);
|
||||
nn::hac::NcaUtils::decryptNcaHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, header_key);
|
||||
|
||||
// generate header hash
|
||||
fnd::sha::Sha256((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sNcaHeader), mHdrHash.bytes);
|
||||
|
@ -141,112 +142,93 @@ void NcaProcess::generateNcaBodyEncryptionKeys()
|
|||
// create zeros key
|
||||
fnd::aes::sAes128Key zero_aesctr_key;
|
||||
memset(zero_aesctr_key.key, 0, sizeof(zero_aesctr_key));
|
||||
fnd::aes::sAesXts128Key zero_aesxts_key;
|
||||
memset(zero_aesxts_key.key, 0, sizeof(zero_aesxts_key));
|
||||
|
||||
// get key data from header
|
||||
byte_t masterkey_rev = nn::hac::NcaUtils::getMasterKeyRevisionFromKeyGeneration(mHdr.getKeyGeneration());
|
||||
byte_t keak_index = mHdr.getKaekIndex();
|
||||
|
||||
// process key area
|
||||
sKeys::sKeyAreaKey keak;
|
||||
sKeys::sKeyAreaKey kak;
|
||||
fnd::aes::sAes128Key key_area_enc_key;
|
||||
for (size_t i = 0; i < nn::hac::nca::kAesKeyNum; i++)
|
||||
{
|
||||
if (mHdr.getEncAesKeys()[i] != zero_aesctr_key)
|
||||
{
|
||||
keak.index = (byte_t)i;
|
||||
keak.enc = mHdr.getEncAesKeys()[i];
|
||||
if (i < 4 && mKeyset->nca.key_area_key[keak_index][masterkey_rev] != zero_aesctr_key)
|
||||
kak.index = (byte_t)i;
|
||||
kak.enc = mHdr.getEncAesKeys()[i];
|
||||
// key[0-3]
|
||||
if (i < 4 && mKeyCfg.getNcaKeyAreaEncryptionKey(masterkey_rev, keak_index, key_area_enc_key) == true)
|
||||
{
|
||||
keak.decrypted = true;
|
||||
nn::hac::AesKeygen::generateKey(keak.dec.key, keak.enc.key, mKeyset->nca.key_area_key[keak_index][masterkey_rev].key);
|
||||
kak.decrypted = true;
|
||||
nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key);
|
||||
}
|
||||
// key[4]
|
||||
else if (i == 4 && mKeyCfg.getNcaKeyAreaEncryptionKeyHw(masterkey_rev, keak_index, key_area_enc_key) == true)
|
||||
{
|
||||
kak.decrypted = true;
|
||||
nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key);
|
||||
}
|
||||
else
|
||||
{
|
||||
keak.decrypted = false;
|
||||
kak.decrypted = false;
|
||||
}
|
||||
mBodyKeys.keak_list.addElement(keak);
|
||||
mContentKey.kak_list.addElement(kak);
|
||||
}
|
||||
}
|
||||
|
||||
// set flag to indicate that the keys are not available
|
||||
mBodyKeys.aes_ctr.isSet = false;
|
||||
mBodyKeys.aes_xts.isSet = false;
|
||||
mContentKey.aes_ctr.isSet = false;
|
||||
|
||||
// if this has a rights id, the key needs to be sourced from a ticket
|
||||
if (mHdr.hasRightsId() == true)
|
||||
{
|
||||
// if the titlekey_kek is available
|
||||
if (mKeyset->ticket.titlekey_kek[masterkey_rev] != zero_aesctr_key)
|
||||
fnd::aes::sAes128Key tmp_key;
|
||||
if (mKeyCfg.getNcaExternalContentKey(mHdr.getRightsId(), tmp_key) == true)
|
||||
{
|
||||
// the title key is provided (sourced from ticket)
|
||||
if (mKeyset->nca.manual_title_key_aesctr != zero_aesctr_key)
|
||||
mContentKey.aes_ctr = tmp_key;
|
||||
}
|
||||
else if (mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserTitleKey, tmp_key) == true)
|
||||
{
|
||||
fnd::aes::sAes128Key common_key;
|
||||
if (mKeyCfg.getETicketCommonKey(masterkey_rev, common_key) == true)
|
||||
{
|
||||
nn::hac::AesKeygen::generateKey(mBodyKeys.aes_ctr.var.key, mKeyset->nca.manual_title_key_aesctr.key, mKeyset->ticket.titlekey_kek[masterkey_rev].key);
|
||||
mBodyKeys.aes_ctr.isSet = true;
|
||||
}
|
||||
if (mKeyset->nca.manual_title_key_aesxts != zero_aesxts_key)
|
||||
{
|
||||
nn::hac::AesKeygen::generateKey(mBodyKeys.aes_xts.var.key[0], mKeyset->nca.manual_title_key_aesxts.key[0], mKeyset->ticket.titlekey_kek[masterkey_rev].key);
|
||||
nn::hac::AesKeygen::generateKey(mBodyKeys.aes_xts.var.key[1], mKeyset->nca.manual_title_key_aesxts.key[1], mKeyset->ticket.titlekey_kek[masterkey_rev].key);
|
||||
mBodyKeys.aes_xts.isSet = true;
|
||||
nn::hac::AesKeygen::generateKey(tmp_key.key, tmp_key.key, common_key.key);
|
||||
}
|
||||
mContentKey.aes_ctr = tmp_key;
|
||||
}
|
||||
}
|
||||
// otherwise decrypt key area
|
||||
else
|
||||
{
|
||||
fnd::aes::sAes128Key keak_aesctr_key = zero_aesctr_key;
|
||||
fnd::aes::sAesXts128Key keak_aesxts_key = zero_aesxts_key;
|
||||
for (size_t i = 0; i < mBodyKeys.keak_list.size(); i++)
|
||||
fnd::aes::sAes128Key kak_aes_ctr = zero_aesctr_key;
|
||||
for (size_t i = 0; i < mContentKey.kak_list.size(); i++)
|
||||
{
|
||||
if (mBodyKeys.keak_list[i].index == nn::hac::nca::KEY_AESCTR && mBodyKeys.keak_list[i].decrypted)
|
||||
if (mContentKey.kak_list[i].index == nn::hac::nca::KEY_AESCTR && mContentKey.kak_list[i].decrypted)
|
||||
{
|
||||
keak_aesctr_key = mBodyKeys.keak_list[i].dec;
|
||||
}
|
||||
else if (mBodyKeys.keak_list[i].index == nn::hac::nca::KEY_AESXTS_0 && mBodyKeys.keak_list[i].decrypted)
|
||||
{
|
||||
memcpy(keak_aesxts_key.key[0], mBodyKeys.keak_list[i].dec.key, sizeof(fnd::aes::sAes128Key));
|
||||
}
|
||||
else if (mBodyKeys.keak_list[i].index == nn::hac::nca::KEY_AESXTS_1 && mBodyKeys.keak_list[i].decrypted)
|
||||
{
|
||||
memcpy(keak_aesxts_key.key[1], mBodyKeys.keak_list[i].dec.key, sizeof(fnd::aes::sAes128Key));
|
||||
kak_aes_ctr = mContentKey.kak_list[i].dec;
|
||||
}
|
||||
}
|
||||
|
||||
if (keak_aesctr_key != zero_aesctr_key)
|
||||
if (kak_aes_ctr != zero_aesctr_key)
|
||||
{
|
||||
mBodyKeys.aes_ctr = keak_aesctr_key;
|
||||
}
|
||||
if (keak_aesxts_key != zero_aesxts_key)
|
||||
{
|
||||
mBodyKeys.aes_xts = keak_aesxts_key;
|
||||
mContentKey.aes_ctr = kak_aes_ctr;
|
||||
}
|
||||
}
|
||||
|
||||
// if the keys weren't generated, check if the keys were supplied by the user
|
||||
if (mBodyKeys.aes_ctr.isSet == false && mKeyset->nca.manual_body_key_aesctr != zero_aesctr_key)
|
||||
if (mContentKey.aes_ctr.isSet == false)
|
||||
{
|
||||
mBodyKeys.aes_ctr = mKeyset->nca.manual_body_key_aesctr;
|
||||
}
|
||||
if (mBodyKeys.aes_xts.isSet == false && mKeyset->nca.manual_body_key_aesxts != zero_aesxts_key)
|
||||
{
|
||||
mBodyKeys.aes_xts = mKeyset->nca.manual_body_key_aesxts;
|
||||
if (mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserBodyKey, mContentKey.aes_ctr.var) == true)
|
||||
mContentKey.aes_ctr.isSet = true;
|
||||
}
|
||||
|
||||
|
||||
if (_HAS_BIT(mCliOutputMode, OUTPUT_KEY_DATA))
|
||||
{
|
||||
if (mBodyKeys.aes_ctr.isSet)
|
||||
if (mContentKey.aes_ctr.isSet)
|
||||
{
|
||||
std::cout << "[NCA Body Key]" << std::endl;
|
||||
std::cout << " AES-CTR Key: " << fnd::SimpleTextOutput::arrayToString(mBodyKeys.aes_ctr.var.key, sizeof(mBodyKeys.aes_ctr.var), true, "") << std::endl;
|
||||
}
|
||||
|
||||
if (mBodyKeys.aes_xts.isSet)
|
||||
{
|
||||
std::cout << "[NCA Body Key]" << std::endl;
|
||||
std::cout << " AES-XTS Key0: " << fnd::SimpleTextOutput::arrayToString(mBodyKeys.aes_xts.var.key[0], sizeof(mBodyKeys.aes_ctr.var), true, "") << std::endl;
|
||||
std::cout << " AES-XTS Key1: " << fnd::SimpleTextOutput::arrayToString(mBodyKeys.aes_xts.var.key[1], sizeof(mBodyKeys.aes_ctr.var), true, "") << std::endl;
|
||||
std::cout << "[NCA Content Key]" << std::endl;
|
||||
std::cout << " AES-CTR Key: " << fnd::SimpleTextOutput::arrayToString(mContentKey.aes_ctr.var.key, sizeof(mContentKey.aes_ctr.var), true, "") << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,9 +303,9 @@ void NcaProcess::generatePartitionConfiguration()
|
|||
}
|
||||
else if (info.enc_type == nn::hac::nca::CRYPT_AESCTR)
|
||||
{
|
||||
if (mBodyKeys.aes_ctr.isSet == false)
|
||||
if (mContentKey.aes_ctr.isSet == false)
|
||||
throw fnd::Exception(kModuleName, "AES-CTR Key was not determined");
|
||||
info.reader = new OffsetAdjustedIFile(new AesCtrWrappedIFile(mFile, SHARED_IFILE, mBodyKeys.aes_ctr.var, info.aes_ctr), OWN_IFILE, info.offset, info.size);
|
||||
info.reader = new OffsetAdjustedIFile(new AesCtrWrappedIFile(mFile, SHARED_IFILE, mContentKey.aes_ctr.var, info.aes_ctr), OWN_IFILE, info.offset, info.size);
|
||||
}
|
||||
else if (info.enc_type == nn::hac::nca::CRYPT_AESXTS || info.enc_type == nn::hac::nca::CRYPT_AESCTREX)
|
||||
{
|
||||
|
@ -365,7 +347,9 @@ void NcaProcess::generatePartitionConfiguration()
|
|||
void NcaProcess::validateNcaSignatures()
|
||||
{
|
||||
// validate signature[0]
|
||||
if (fnd::rsa::pss::rsaVerify(mKeyset->nca.header_sign_key, fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_main) != 0)
|
||||
fnd::rsa::sRsa2048Key sign0_key;
|
||||
mKeyCfg.getNcaHeader0SignKey(sign0_key);
|
||||
if (fnd::rsa::pss::rsaVerify(sign0_key, fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_main) != 0)
|
||||
{
|
||||
std::cout << "[WARNING] NCA Header Main Signature: FAIL" << std::endl;
|
||||
}
|
||||
|
@ -434,21 +418,21 @@ void NcaProcess::displayHeader()
|
|||
std::cout << " RightsId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getRightsId(), nn::hac::nca::kRightsIdLen, true, "") << std::endl;
|
||||
}
|
||||
|
||||
if (mBodyKeys.keak_list.size() > 0 && _HAS_BIT(mCliOutputMode, OUTPUT_KEY_DATA))
|
||||
if (mContentKey.kak_list.size() > 0 && _HAS_BIT(mCliOutputMode, OUTPUT_KEY_DATA))
|
||||
{
|
||||
std::cout << " Key Area:" << std::endl;
|
||||
std::cout << " <--------------------------------------------------------------------------->" << std::endl;
|
||||
std::cout << " | IDX | ENCRYPTED KEY | DECRYPTED KEY |" << std::endl;
|
||||
std::cout << " |-----|----------------------------------|----------------------------------|" << std::endl;
|
||||
for (size_t i = 0; i < mBodyKeys.keak_list.size(); i++)
|
||||
for (size_t i = 0; i < mContentKey.kak_list.size(); i++)
|
||||
{
|
||||
std::cout << " | " << std::dec << std::setw(3) << std::setfill(' ') << (uint32_t)mBodyKeys.keak_list[i].index << " | ";
|
||||
std::cout << " | " << std::dec << std::setw(3) << std::setfill(' ') << (uint32_t)mContentKey.kak_list[i].index << " | ";
|
||||
|
||||
std::cout << fnd::SimpleTextOutput::arrayToString(mBodyKeys.keak_list[i].enc.key, 16, false, "") << " | ";
|
||||
std::cout << fnd::SimpleTextOutput::arrayToString(mContentKey.kak_list[i].enc.key, 16, false, "") << " | ";
|
||||
|
||||
|
||||
if (mBodyKeys.keak_list[i].decrypted)
|
||||
std::cout << fnd::SimpleTextOutput::arrayToString(mBodyKeys.keak_list[i].dec.key, 16, false, "");
|
||||
if (mContentKey.kak_list[i].decrypted)
|
||||
std::cout << fnd::SimpleTextOutput::arrayToString(mContentKey.kak_list[i].dec.key, 16, false, "");
|
||||
else
|
||||
std::cout << "<unable to decrypt> ";
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <fnd/SimpleFile.h>
|
||||
#include <nn/hac/NcaHeader.h>
|
||||
#include "HashTreeMeta.h"
|
||||
#include "KeyConfiguration.h"
|
||||
|
||||
|
||||
#include "nstool.h"
|
||||
|
@ -18,7 +19,7 @@ public:
|
|||
|
||||
// generic
|
||||
void setInputFile(fnd::IFile* file, bool ownIFile);
|
||||
void setKeyset(const sKeyset* keyset);
|
||||
void setKeyCfg(const KeyConfiguration& keycfg);
|
||||
void setCliOutputMode(CliOutputMode type);
|
||||
void setVerifyMode(bool verify);
|
||||
|
||||
|
@ -36,7 +37,7 @@ private:
|
|||
// user options
|
||||
fnd::IFile* mFile;
|
||||
bool mOwnIFile;
|
||||
const sKeyset* mKeyset;
|
||||
KeyConfiguration mKeyCfg;
|
||||
CliOutputMode mCliOutputMode;
|
||||
bool mVerify;
|
||||
|
||||
|
@ -84,11 +85,10 @@ private:
|
|||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
fnd::List<sKeyAreaKey> keak_list;
|
||||
fnd::List<sKeyAreaKey> kak_list;
|
||||
|
||||
sOptional<fnd::aes::sAes128Key> aes_ctr;
|
||||
sOptional<fnd::aes::sAesXts128Key> aes_xts;
|
||||
} mBodyKeys;
|
||||
} mContentKey;
|
||||
|
||||
struct sPartitionInfo
|
||||
{
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
NpdmProcess::NpdmProcess() :
|
||||
mFile(nullptr),
|
||||
mOwnIFile(false),
|
||||
mKeyset(nullptr),
|
||||
mCliOutputMode(_BIT(OUTPUT_BASIC)),
|
||||
mVerify(false)
|
||||
{
|
||||
|
@ -57,9 +56,9 @@ void NpdmProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
|||
mOwnIFile = ownIFile;
|
||||
}
|
||||
|
||||
void NpdmProcess::setKeyset(const sKeyset* keyset)
|
||||
void NpdmProcess::setKeyCfg(const KeyConfiguration& keycfg)
|
||||
{
|
||||
mKeyset = keyset;
|
||||
mKeyCfg = keycfg;
|
||||
}
|
||||
|
||||
void NpdmProcess::setCliOutputMode(CliOutputMode type)
|
||||
|
@ -95,7 +94,11 @@ void NpdmProcess::importNpdm()
|
|||
void NpdmProcess::validateAcidSignature(const nn::hac::AccessControlInfoDescBinary& acid)
|
||||
{
|
||||
try {
|
||||
acid.validateSignature(mKeyset->acid_sign_key);
|
||||
fnd::rsa::sRsa2048Key acid_sign_key;
|
||||
if (mKeyCfg.getAcidSignKey(acid_sign_key) != true)
|
||||
throw fnd::Exception();
|
||||
|
||||
acid.validateSignature(acid_sign_key);
|
||||
}
|
||||
catch (...) {
|
||||
std::cout << "[WARNING] ACID Signature: FAIL" << std::endl;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <fnd/types.h>
|
||||
#include <fnd/IFile.h>
|
||||
#include <nn/hac/NpdmBinary.h>
|
||||
#include "KeyConfiguration.h"
|
||||
|
||||
#include "nstool.h"
|
||||
|
||||
|
@ -15,7 +16,7 @@ public:
|
|||
void process();
|
||||
|
||||
void setInputFile(fnd::IFile* file, bool ownIFile);
|
||||
void setKeyset(const sKeyset* keyset);
|
||||
void setKeyCfg(const KeyConfiguration& keycfg);
|
||||
void setCliOutputMode(CliOutputMode type);
|
||||
void setVerifyMode(bool verify);
|
||||
|
||||
|
@ -26,7 +27,7 @@ private:
|
|||
|
||||
fnd::IFile* mFile;
|
||||
bool mOwnIFile;
|
||||
const sKeyset* mKeyset;
|
||||
KeyConfiguration mKeyCfg;
|
||||
CliOutputMode mCliOutputMode;
|
||||
bool mVerify;
|
||||
|
||||
|
|
|
@ -39,9 +39,9 @@ void PkiCertProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
|||
mOwnIFile = ownIFile;
|
||||
}
|
||||
|
||||
void PkiCertProcess::setKeyset(const sKeyset* keyset)
|
||||
void PkiCertProcess::setKeyCfg(const KeyConfiguration& keycfg)
|
||||
{
|
||||
mKeyset = keyset;
|
||||
mKeyCfg = keycfg;
|
||||
}
|
||||
|
||||
void PkiCertProcess::setCliOutputMode(CliOutputMode mode)
|
||||
|
@ -80,7 +80,7 @@ void PkiCertProcess::validateCerts()
|
|||
|
||||
try
|
||||
{
|
||||
pki.setRootKey(mKeyset->pki.root_sign_key);
|
||||
pki.setKeyCfg(mKeyCfg);
|
||||
pki.addCertificates(mCert);
|
||||
}
|
||||
catch (const fnd::Exception& e)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <fnd/Vec.h>
|
||||
#include <nn/pki/SignedData.h>
|
||||
#include <nn/pki/CertificateBody.h>
|
||||
#include "KeyConfiguration.h"
|
||||
#include "nstool.h"
|
||||
|
||||
class PkiCertProcess
|
||||
|
@ -17,7 +18,7 @@ public:
|
|||
void process();
|
||||
|
||||
void setInputFile(fnd::IFile* file, bool ownIFile);
|
||||
void setKeyset(const sKeyset* keyset);
|
||||
void setKeyCfg(const KeyConfiguration& keycfg);
|
||||
void setCliOutputMode(CliOutputMode type);
|
||||
void setVerifyMode(bool verify);
|
||||
|
||||
|
@ -27,7 +28,7 @@ private:
|
|||
|
||||
fnd::IFile* mFile;
|
||||
bool mOwnIFile;
|
||||
const sKeyset* mKeyset;
|
||||
KeyConfiguration mKeyCfg;
|
||||
CliOutputMode mCliOutputMode;
|
||||
bool mVerify;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ PkiValidator::PkiValidator()
|
|||
clearCertificates();
|
||||
}
|
||||
|
||||
void PkiValidator::setRootKey(const fnd::rsa::sRsa4096Key& root_key)
|
||||
void PkiValidator::setKeyCfg(const KeyConfiguration& keycfg)
|
||||
{
|
||||
// save a copy of the certificate bank
|
||||
fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> old_certs = mCertificateBank;
|
||||
|
@ -18,7 +18,7 @@ void PkiValidator::setRootKey(const fnd::rsa::sRsa4096Key& root_key)
|
|||
mCertificateBank.clear();
|
||||
|
||||
// overwrite the root key
|
||||
mRootKey = root_key;
|
||||
mKeyCfg = keycfg;
|
||||
|
||||
// if there were certificates before, reimport them (so they are checked against the new root key)
|
||||
if (old_certs.size() > 0)
|
||||
|
@ -96,13 +96,28 @@ void PkiValidator::validateSignature(const std::string& issuer, nn::pki::sign::S
|
|||
int sig_validate_res = -1;
|
||||
|
||||
// special case if signed by Root
|
||||
if (issuer == nn::pki::sign::kRootIssuerStr)
|
||||
if (issuer.find('-', 0) == std::string::npos)
|
||||
{
|
||||
if (sign_algo != nn::pki::sign::SIGN_ALGO_RSA4096)
|
||||
fnd::rsa::sRsa4096Key rsa4096_pub;
|
||||
fnd::rsa::sRsa2048Key rsa2048_pub;
|
||||
fnd::ecdsa::sEcdsa240Key ecdsa_pub;
|
||||
|
||||
if (mKeyCfg.getPkiRootSignKey(issuer, rsa4096_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Issued by Root, but does not have a RSA4096 signature");
|
||||
sig_validate_res = fnd::rsa::pkcs::rsaVerify(rsa4096_pub, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
|
||||
}
|
||||
else if (mKeyCfg.getPkiRootSignKey(issuer, rsa2048_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048)
|
||||
{
|
||||
sig_validate_res = fnd::rsa::pkcs::rsaVerify(rsa2048_pub, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
|
||||
}
|
||||
else if (mKeyCfg.getPkiRootSignKey(issuer, ecdsa_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "ECDSA signatures are not supported");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Public key for issuer \"" + issuer + "\" does not exist.");
|
||||
}
|
||||
sig_validate_res = fnd::rsa::pkcs::rsaVerify(mRootKey, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -6,13 +6,14 @@
|
|||
#include <nn/pki/SignedData.h>
|
||||
#include <nn/pki/CertificateBody.h>
|
||||
#include <string>
|
||||
#include "KeyConfiguration.h"
|
||||
|
||||
class PkiValidator
|
||||
{
|
||||
public:
|
||||
PkiValidator();
|
||||
|
||||
void setRootKey(const fnd::rsa::sRsa4096Key& root_key);
|
||||
void setKeyCfg(const KeyConfiguration& keycfg);
|
||||
void addCertificates(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs);
|
||||
void addCertificate(const nn::pki::SignedData<nn::pki::CertificateBody>& cert);
|
||||
void clearCertificates();
|
||||
|
@ -22,8 +23,7 @@ public:
|
|||
private:
|
||||
const std::string kModuleName = "NNPkiValidator";
|
||||
|
||||
|
||||
fnd::rsa::sRsa4096Key mRootKey;
|
||||
KeyConfiguration mKeyCfg;
|
||||
fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> mCertificateBank;
|
||||
|
||||
void makeCertIdent(const nn::pki::SignedData<nn::pki::CertificateBody>& cert, std::string& ident) const;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "UserSettings.h"
|
||||
#include "version.h"
|
||||
#include "PkiValidator.h"
|
||||
#include "KeyConfiguration.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
@ -95,9 +96,9 @@ const std::string UserSettings::getInputPath() const
|
|||
return mInputPath;
|
||||
}
|
||||
|
||||
const sKeyset& UserSettings::getKeyset() const
|
||||
const KeyConfiguration& UserSettings::getKeyCfg() const
|
||||
{
|
||||
return mKeyset;
|
||||
return mKeyCfg;
|
||||
}
|
||||
|
||||
FileType UserSettings::getFileType() const
|
||||
|
@ -385,36 +386,23 @@ void UserSettings::populateCmdArgs(const std::vector<std::string>& arg_list, sCm
|
|||
|
||||
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));
|
||||
|
||||
fnd::ResourceFileReader res;
|
||||
if (args.keyset_path.isSet)
|
||||
{
|
||||
res.processFile(*args.keyset_path);
|
||||
mKeyCfg.importHactoolGenericKeyfile(*args.keyset_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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;
|
||||
fnd::io::appendToPath(keyset_path, home);
|
||||
fnd::io::appendToPath(keyset_path, kHomeSwitchDirStr);
|
||||
fnd::io::appendToPath(keyset_path, kKeysetNameStr[args.devkit_keys.isSet ? *args.devkit_keys : 0]);
|
||||
getSwitchPath(keyset_path);
|
||||
if (keyset_path.empty())
|
||||
return;
|
||||
|
||||
fnd::io::appendToPath(keyset_path, kGeneralKeyfileName[args.devkit_keys.isSet]);
|
||||
|
||||
try
|
||||
{
|
||||
res.processFile(keyset_path);
|
||||
mKeyCfg.importHactoolGenericKeyfile(keyset_path);
|
||||
}
|
||||
catch (const fnd::Exception&)
|
||||
{
|
||||
|
@ -422,132 +410,29 @@ void UserSettings::populateKeyset(sCmdArgs& args)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// suffix
|
||||
const std::string kKeyStr = "key";
|
||||
const std::string kKekStr = "kek";
|
||||
const std::string kSourceStr = "source";
|
||||
const std::string kRsaKeySuffix[2] = {"sign_key_private", "sign_key_modulus"};
|
||||
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"};
|
||||
|
||||
// keyname bases
|
||||
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++)
|
||||
{
|
||||
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kMasterBase, kKeyStr, kKeyIndex[i]), master_key[i].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPackage1Base, kKeyStr, kKeyIndex[i]), mKeyset.package1_key[i].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPackage2Base, kKeyStr, kKeyIndex[i]), mKeyset.package2_key[i].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[0], kKeyIndex[i]), mKeyset.ticket.titlekey_kek[i].key, 0x10);
|
||||
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[1], kKeyIndex[i]), mKeyset.ticket.titlekey_kek[i].key, 0x10);
|
||||
_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.var.length() == (sizeof(fnd::aes::sAes128Key)*2))
|
||||
{
|
||||
decodeHexStringToBytes("--bodykey", args.nca_bodykey.var, mKeyset.nca.manual_body_key_aesctr.key, sizeof(fnd::aes::sAes128Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
decodeHexStringToBytes("--bodykey", args.nca_bodykey.var, mKeyset.nca.manual_body_key_aesxts.key[0], sizeof(fnd::aes::sAesXts128Key));
|
||||
}
|
||||
fnd::aes::sAes128Key tmp_key;
|
||||
fnd::Vec<byte_t> tmp_raw;
|
||||
fnd::SimpleTextOutput::stringToArray(args.nca_bodykey.var, tmp_raw);
|
||||
if (tmp_raw.size() != sizeof(fnd::aes::sAes128Key))
|
||||
throw fnd::Exception(kModuleName, "Key: \"--bodykey\" has incorrect length");
|
||||
memcpy(tmp_key.key, tmp_raw.data(), 16);
|
||||
mKeyCfg.addNcaExternalContentKey(kDummyRightsIdForUserBodyKey, tmp_key);
|
||||
}
|
||||
|
||||
if (args.nca_titlekey.isSet)
|
||||
{
|
||||
if (args.nca_titlekey.var.length() == (sizeof(fnd::aes::sAes128Key)*2))
|
||||
{
|
||||
decodeHexStringToBytes("--titlekey", args.nca_titlekey.var, mKeyset.nca.manual_title_key_aesctr.key, sizeof(fnd::aes::sAes128Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
decodeHexStringToBytes("--titlekey", args.nca_titlekey.var, mKeyset.nca.manual_title_key_aesxts.key[0], sizeof(fnd::aes::sAesXts128Key));
|
||||
}
|
||||
fnd::aes::sAes128Key tmp_key;
|
||||
fnd::Vec<byte_t> tmp_raw;
|
||||
fnd::SimpleTextOutput::stringToArray(args.nca_bodykey.var, tmp_raw);
|
||||
if (tmp_raw.size() != sizeof(fnd::aes::sAes128Key))
|
||||
throw fnd::Exception(kModuleName, "Key: \"--titlekey\" has incorrect length");
|
||||
memcpy(tmp_key.key, tmp_raw.data(), 16);
|
||||
mKeyCfg.addNcaExternalContentKey(kDummyRightsIdForUserTitleKey, tmp_key);
|
||||
}
|
||||
|
||||
// import certificate chain
|
||||
|
@ -601,7 +486,7 @@ void UserSettings::populateKeyset(sCmdArgs& args)
|
|||
|
||||
try
|
||||
{
|
||||
pki_validator.setRootKey(mKeyset.pki.root_sign_key);
|
||||
pki_validator.setKeyCfg(mKeyCfg);
|
||||
pki_validator.addCertificates(mCertChain);
|
||||
pki_validator.validateSignature(tik.getBody().getIssuer(), tik.getSignature().getSignType(), tik.getSignature().getSignature(), tik_hash);
|
||||
}
|
||||
|
@ -615,75 +500,24 @@ void UserSettings::populateKeyset(sCmdArgs& args)
|
|||
// extract title key
|
||||
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);
|
||||
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
|
||||
{
|
||||
std::cout << "[WARNING] Titlekey not imported from ticket because it is personalised" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#undef _SAVE_KEYDATA
|
||||
#undef _CONCAT_3_STRINGS
|
||||
#undef _CONCAT_2_STRINGS
|
||||
|
||||
// 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)
|
||||
|
@ -747,21 +581,6 @@ void UserSettings::populateUserSettings(sCmdArgs& args)
|
|||
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)
|
||||
{
|
||||
std::string str = type_str;
|
||||
|
@ -880,7 +699,9 @@ bool UserSettings::determineValidNcaFromSample(const fnd::Vec<byte_t>& sample) c
|
|||
if (sample.size() < nn::hac::nca::kHeaderSize)
|
||||
return false;
|
||||
|
||||
nn::hac::NcaUtils::decryptNcaHeader(sample.data(), nca_raw, mKeyset.nca.header_key);
|
||||
fnd::aes::sAesXts128Key header_key;
|
||||
mKeyCfg.getNcaHeaderKey(header_key);
|
||||
nn::hac::NcaUtils::decryptNcaHeader(sample.data(), nca_raw, header_key);
|
||||
|
||||
if (nca_header->st_magic.get() != nn::hac::nca::kNca2StructMagic && nca_header->st_magic.get() != nn::hac::nca::kNca3StructMagic)
|
||||
return false;
|
||||
|
@ -1019,3 +840,25 @@ nn::hac::npdm::InstructionType UserSettings::getInstructionTypeFromString(const
|
|||
|
||||
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/hac/npdm.h>
|
||||
#include "nstool.h"
|
||||
#include "KeyConfiguration.h"
|
||||
|
||||
class UserSettings
|
||||
{
|
||||
|
@ -19,7 +20,7 @@ public:
|
|||
|
||||
// generic options
|
||||
const std::string getInputPath() const;
|
||||
const sKeyset& getKeyset() const;
|
||||
const KeyConfiguration& getKeyCfg() const;
|
||||
FileType getFileType() const;
|
||||
bool isVerifyFile() const;
|
||||
CliOutputMode getCliOutputMode() const;
|
||||
|
@ -44,8 +45,15 @@ public:
|
|||
const sOptional<std::string>& getAssetNacpPath() const;
|
||||
const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& getCertificateChain() const;
|
||||
|
||||
void dumpKeys() const;
|
||||
|
||||
private:
|
||||
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
|
||||
{
|
||||
|
@ -81,7 +89,7 @@ private:
|
|||
|
||||
std::string mInputPath;
|
||||
FileType mFileType;
|
||||
sKeyset mKeyset;
|
||||
KeyConfiguration mKeyCfg;
|
||||
bool mVerifyFile;
|
||||
CliOutputMode mOutputMode;
|
||||
|
||||
|
@ -109,7 +117,6 @@ private:
|
|||
void populateCmdArgs(const std::vector<std::string>& arg_list, sCmdArgs& cmd_args);
|
||||
void populateKeyset(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 determineFileTypeFromFile(const std::string& path);
|
||||
bool determineValidNcaFromSample(const fnd::Vec<byte_t>& sample) const;
|
||||
|
@ -118,4 +125,6 @@ private:
|
|||
bool determineValidEsCertFromSample(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);
|
||||
void getHomePath(std::string& path) const;
|
||||
void getSwitchPath(std::string& path) const;
|
||||
};
|
|
@ -8,7 +8,6 @@
|
|||
XciProcess::XciProcess() :
|
||||
mFile(nullptr),
|
||||
mOwnIFile(false),
|
||||
mKeyset(nullptr),
|
||||
mCliOutputMode(_BIT(OUTPUT_BASIC)),
|
||||
mVerify(false),
|
||||
mListFs(false),
|
||||
|
@ -50,9 +49,9 @@ void XciProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
|||
mOwnIFile = ownIFile;
|
||||
}
|
||||
|
||||
void XciProcess::setKeyset(const sKeyset* keyset)
|
||||
void XciProcess::setKeyCfg(const KeyConfiguration& keycfg)
|
||||
{
|
||||
mKeyset = keyset;
|
||||
mKeyCfg = keycfg;
|
||||
}
|
||||
|
||||
void XciProcess::setCliOutputMode(CliOutputMode type)
|
||||
|
@ -89,7 +88,10 @@ void XciProcess::importHeader()
|
|||
|
||||
// allocate memory for and decrypt sXciHeader
|
||||
scratch.alloc(sizeof(nn::hac::sXciHeader));
|
||||
nn::hac::XciUtils::decryptXciHeader((const byte_t*)&mHdrPage.header, scratch.data(), mKeyset->xci.header_key.key);
|
||||
|
||||
fnd::aes::sAes128Key header_key;
|
||||
mKeyCfg.getXciHeaderKey(header_key);
|
||||
nn::hac::XciUtils::decryptXciHeader((const byte_t*)&mHdrPage.header, scratch.data(), header_key.key);
|
||||
|
||||
// deserialise header
|
||||
mHdr.fromBytes(scratch.data(), scratch.size());
|
||||
|
@ -198,9 +200,11 @@ bool XciProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* t
|
|||
|
||||
void XciProcess::validateXciSignature()
|
||||
{
|
||||
fnd::rsa::sRsa2048Key header_sign_key;
|
||||
fnd::sha::sSha256Hash calc_hash;
|
||||
fnd::sha::Sha256((byte_t*)&mHdrPage.header, sizeof(nn::hac::sXciHeader), calc_hash.bytes);
|
||||
if (fnd::rsa::pkcs::rsaVerify(mKeyset->xci.header_sign_key, fnd::sha::HASH_SHA256, calc_hash.bytes, mHdrPage.signature) != 0)
|
||||
mKeyCfg.getXciHeaderSignKey(header_sign_key);
|
||||
if (fnd::rsa::pkcs::rsaVerify(header_sign_key, fnd::sha::HASH_SHA256, calc_hash.bytes, mHdrPage.signature) != 0)
|
||||
{
|
||||
std::cout << "[WARNING] XCI Header Signature: FAIL" << std::endl;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,10 @@
|
|||
#include <fnd/IFile.h>
|
||||
#include <fnd/List.h>
|
||||
#include <nn/hac/XciHeader.h>
|
||||
|
||||
#include "nstool.h"
|
||||
|
||||
#include "KeyConfiguration.h"
|
||||
#include "PfsProcess.h"
|
||||
|
||||
#include "nstool.h"
|
||||
|
||||
class XciProcess
|
||||
{
|
||||
|
@ -20,7 +19,7 @@ public:
|
|||
|
||||
// generic
|
||||
void setInputFile(fnd::IFile* file, bool ownIFile);
|
||||
void setKeyset(const sKeyset* keyset);
|
||||
void setKeyCfg(const KeyConfiguration& keycfg);
|
||||
void setCliOutputMode(CliOutputMode type);
|
||||
void setVerifyMode(bool verify);
|
||||
|
||||
|
@ -34,7 +33,7 @@ private:
|
|||
|
||||
fnd::IFile* mFile;
|
||||
bool mOwnIFile;
|
||||
const sKeyset* mKeyset;
|
||||
KeyConfiguration mKeyCfg;
|
||||
CliOutputMode mCliOutputMode;
|
||||
bool mVerify;
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ int main(int argc, char** argv)
|
|||
|
||||
xci.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
|
||||
|
||||
xci.setKeyset(&user_set.getKeyset());
|
||||
xci.setKeyCfg(user_set.getKeyCfg());
|
||||
xci.setCliOutputMode(user_set.getCliOutputMode());
|
||||
xci.setVerifyMode(user_set.isVerifyFile());
|
||||
|
||||
|
@ -90,7 +90,7 @@ int main(int argc, char** argv)
|
|||
NcaProcess nca;
|
||||
|
||||
nca.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
|
||||
nca.setKeyset(&user_set.getKeyset());
|
||||
nca.setKeyCfg(user_set.getKeyCfg());
|
||||
nca.setCliOutputMode(user_set.getCliOutputMode());
|
||||
nca.setVerifyMode(user_set.isVerifyFile());
|
||||
|
||||
|
@ -112,7 +112,7 @@ int main(int argc, char** argv)
|
|||
NpdmProcess npdm;
|
||||
|
||||
npdm.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
|
||||
npdm.setKeyset(&user_set.getKeyset());
|
||||
npdm.setKeyCfg(user_set.getKeyCfg());
|
||||
npdm.setCliOutputMode(user_set.getCliOutputMode());
|
||||
npdm.setVerifyMode(user_set.isVerifyFile());
|
||||
|
||||
|
@ -180,7 +180,7 @@ int main(int argc, char** argv)
|
|||
PkiCertProcess cert;
|
||||
|
||||
cert.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
|
||||
cert.setKeyset(&user_set.getKeyset());
|
||||
cert.setKeyCfg(user_set.getKeyCfg());
|
||||
cert.setCliOutputMode(user_set.getCliOutputMode());
|
||||
cert.setVerifyMode(user_set.isVerifyFile());
|
||||
|
||||
|
@ -191,7 +191,7 @@ int main(int argc, char** argv)
|
|||
EsTikProcess tik;
|
||||
|
||||
tik.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
|
||||
tik.setKeyset(&user_set.getKeyset());
|
||||
tik.setKeyCfg(user_set.getKeyCfg());
|
||||
tik.setCertificateChain(user_set.getCertificateChain());
|
||||
tik.setCliOutputMode(user_set.getCliOutputMode());
|
||||
tik.setVerifyMode(user_set.isVerifyFile());
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <fnd/types.h>
|
||||
#include <fnd/aes.h>
|
||||
|
@ -61,50 +60,5 @@ struct sOptional
|
|||
inline T& operator*() { return var; }
|
||||
};
|
||||
|
||||
struct sKeyset
|
||||
{
|
||||
fnd::rsa::sRsa2048Key acid_sign_key;
|
||||
fnd::aes::sAes128Key package1_key[kMasterKeyNum];
|
||||
fnd::rsa::sRsa2048Key package2_sign_key;
|
||||
fnd::aes::sAes128Key package2_key[kMasterKeyNum];
|
||||
|
||||
struct sNcaData
|
||||
{
|
||||
fnd::rsa::sRsa2048Key header_sign_key;
|
||||
fnd::aes::sAesXts128Key header_key;
|
||||
fnd::aes::sAes128Key key_area_key[kNcaKeakNum][kMasterKeyNum];
|
||||
|
||||
fnd::aes::sAes128Key manual_title_key_aesctr;
|
||||
fnd::aes::sAesXts128Key manual_title_key_aesxts;
|
||||
fnd::aes::sAes128Key manual_body_key_aesctr;
|
||||
fnd::aes::sAesXts128Key manual_body_key_aesxts;
|
||||
} nca;
|
||||
|
||||
struct sXciData
|
||||
{
|
||||
fnd::rsa::sRsa2048Key header_sign_key;
|
||||
fnd::aes::sAes128Key header_key;
|
||||
} xci;
|
||||
|
||||
struct sTicketData
|
||||
{
|
||||
fnd::rsa::sRsa2048Key sign_key;
|
||||
fnd::aes::sAes128Key titlekey_kek[kMasterKeyNum];
|
||||
} ticket;
|
||||
|
||||
struct sPkiData
|
||||
{
|
||||
fnd::rsa::sRsa4096Key root_sign_key;
|
||||
} 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;
|
||||
}
|
||||
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};
|
Loading…
Reference in a new issue