2021-09-28 11:15:54 +00:00
# include "KeyBag.h"
# include "util.h"
# include <tc/cli/FormatUtil.h>
# include <nn/hac/define/gc.h>
# include <nn/hac/AesKeygen.h>
# include <nn/pki/SignUtils.h>
# include <nn/pki/SignedData.h>
# include <nn/pki/CertificateBody.h>
# include <nn/es/TicketBody_V2.h>
nstool : : KeyBagInitializer : : KeyBagInitializer ( bool isDev , const tc : : Optional < tc : : io : : Path > & keyfile_path , const tc : : Optional < tc : : io : : Path > & tik_path , const tc : : Optional < tc : : io : : Path > & cert_path )
{
if ( keyfile_path . isSet ( ) )
{
importBaseKeyFile ( keyfile_path . get ( ) , isDev ) ;
}
if ( cert_path . isSet ( ) )
{
importCertificateChain ( cert_path . get ( ) ) ;
}
if ( tik_path . isSet ( ) )
{
importTicket ( tik_path . get ( ) ) ;
}
}
void nstool : : KeyBagInitializer : : importBaseKeyFile ( const tc : : io : : Path & keyfile_path , bool isDev )
{
std : : shared_ptr < tc : : io : : FileStream > keyfile_stream = std : : make_shared < tc : : io : : FileStream > ( tc : : io : : FileStream ( keyfile_path , tc : : io : : FileMode : : Open , tc : : io : : FileAccess : : Read ) ) ;
// import keyfile into a dictionary
std : : map < std : : string , std : : string > keyfile_dict ;
processResFile ( keyfile_stream , keyfile_dict ) ;
// sources for key derivation
std : : map < byte_t , aes128_key_t > master_key ;
tc : : Optional < aes128_key_t > package2_key_source ;
tc : : Optional < aes128_key_t > ticket_titlekek_source ;
std : : array < tc : : Optional < aes128_key_t > , 3 > key_area_key_source ;
tc : : Optional < aes128_key_t > aes_kek_generation_source ;
tc : : Optional < aes128_key_t > aes_key_generation_source ;
tc : : Optional < aes128_key_t > nca_header_kek_source ;
tc : : Optional < aes128_xtskey_t > nca_header_key_source ;
tc : : Optional < rsa_key_t > pki_root_sign_key ;
// macros for importing
# define _SAVE_AES128KEY(key_name, dst) \
{ \
std : : string key , val ; \
tc : : ByteData dec_val ; \
aes128_key_t tmp_aes128_key ; \
key = ( key_name ) ; \
val = keyfile_dict [ key ] ; \
if ( val . empty ( ) = = false ) { \
dec_val = tc : : cli : : FormatUtil : : hexStringToBytes ( val ) ; \
if ( dec_val . size ( ) ! = tmp_aes128_key . size ( ) ) \
throw tc : : ArgumentException ( " nstool::KeyBagInitializer " , " Key: \" " + key_name + " \" has incorrect length " ) ; \
memcpy ( tmp_aes128_key . data ( ) , dec_val . data ( ) , tmp_aes128_key . size ( ) ) ; \
( dst ) = tmp_aes128_key ; \
} \
}
# define _SAVE_AES128XTSKEY(key_name, dst) \
{ \
std : : string key , val ; \
tc : : ByteData dec_val ; \
aes128_xtskey_t tmp_aes128_xtskey ; \
key = ( key_name ) ; \
val = keyfile_dict [ key ] ; \
if ( val . empty ( ) = = false ) { \
dec_val = tc : : cli : : FormatUtil : : hexStringToBytes ( val ) ; \
if ( dec_val . size ( ) ! = sizeof ( tmp_aes128_xtskey ) ) \
throw tc : : ArgumentException ( " nstool::KeyBagInitializer " , " Key: \" " + key_name + " \" has incorrect length " ) ; \
memcpy ( tmp_aes128_xtskey [ 0 ] . data ( ) , dec_val . data ( ) , tmp_aes128_xtskey [ 0 ] . size ( ) ) ; \
memcpy ( tmp_aes128_xtskey [ 1 ] . data ( ) , dec_val . data ( ) + tmp_aes128_xtskey [ 0 ] . size ( ) , tmp_aes128_xtskey [ 1 ] . size ( ) ) ; \
( dst ) = tmp_aes128_xtskey ; \
} \
}
# define _SAVE_RSAKEY(key_name, dst, bitsize) \
{ \
std : : string key_mod , key_prv , val_mod , val_prv ; \
tc : : ByteData dec_val ; \
rsa_key_t tmp_rsa_key ; \
key_mod = fmt : : format ( " {:s}_modulus " , ( key_name ) ) ; \
key_prv = fmt : : format ( " {:s}_private " , ( key_name ) ) ; \
val_mod = keyfile_dict [ key_mod ] ; \
val_prv = keyfile_dict [ key_prv ] ; \
if ( val_mod . empty ( ) = = false ) { \
dec_val = tc : : cli : : FormatUtil : : hexStringToBytes ( val_mod ) ; \
if ( dec_val . size ( ) = = ( bitsize ) > > 3 ) { \
tmp_rsa_key . n = dec_val ; \
if ( val_prv . empty ( ) = = false ) { \
dec_val = tc : : cli : : FormatUtil : : hexStringToBytes ( val_prv ) ; \
if ( dec_val . size ( ) = = ( bitsize ) > > 3 ) { \
tmp_rsa_key . d = dec_val ; \
( dst ) = tc : : crypto : : RsaPrivateKey ( tmp_rsa_key . n . data ( ) , tmp_rsa_key . n . size ( ) , tmp_rsa_key . d . data ( ) , tmp_rsa_key . d . size ( ) ) ; \
} \
else { \
fmt : : print ( " [WARNING] Key: \" {:s} \" has incorrect length (was: {:d}, expected {:d}) \n " , key_prv , val_prv . size ( ) , ( ( bitsize ) > > 3 ) * 2 ) ; \
} \
} \
else { \
( dst ) = tc : : crypto : : RsaPublicKey ( tmp_rsa_key . n . data ( ) , tmp_rsa_key . n . size ( ) ) ; \
} \
} \
else { \
fmt : : print ( " [WARNING] Key: \" {:s} \" has incorrect length (was: {:d}, expected {:d}) \n " , key_mod , val_mod . size ( ) , ( ( bitsize ) > > 3 ) * 2 ) ; \
} \
} \
}
// keynames
enum NameVariantIndex
{
NNTOOLS ,
LEGACY_HACTOOL ,
LEGACY_0
} ;
static const size_t kNameVariantNum = 3 ;
std : : vector < std : : string > kMasterBase = { " master " } ;
std : : vector < std : : string > kPkg1Base = { " package1 " } ;
std : : vector < std : : string > kPkg2Base = { " package2 " } ;
std : : vector < std : : string > kXciHeaderBase = { " xci_header " } ;
std : : vector < std : : string > kContentArchiveHeaderBase = { " nca_header " , " header " } ;
std : : vector < std : : string > kAcidBase = { " acid " } ;
std : : vector < std : : string > kNrrCertBase = { " nrr_certificate " } ;
std : : vector < std : : string > kPkiRootBase = { " pki_root " } ;
std : : vector < std : : string > kTicketCommonKeyBase = { " ticket_commonkey " , " titlekek " } ;
std : : vector < std : : string > kNcaKeyAreaEncKeyBase = { " nca_key_area_key " , " key_area_key " , " nca_body_keak " } ;
std : : vector < std : : string > kNcaKeyAreaEncKeyHwBase = { " nca_key_area_key_hw " , " key_area_hw_key " } ;
std : : vector < std : : string > kKekGenBase = { " aes_kek_generation " } ;
std : : vector < std : : string > kKeyGenBase = { " aes_key_generation " } ;
// misc str
const std : : string kKeyStr = " key " ;
const std : : string kKekStr = " kek " ;
const std : : string kSourceStr = " source " ;
const std : : string kSignKey = " sign_key " ;
const std : : string kModulusStr = " modulus " ;
const std : : string kPrivateStr = " private " ;
std : : vector < std : : string > kNcaKeyAreaKeyIndexStr = { " application " , " ocean " , " system " } ;
static const size_t kMasterKeyMax = 0x20 ;
/**/
// import key data
for ( size_t name_idx = 0 ; name_idx < kNameVariantNum ; name_idx + + )
{
/* internal key sources */
if ( name_idx < kMasterBase . size ( ) )
{
for ( size_t mkey_rev = 0 ; mkey_rev < kMasterKeyMax ; mkey_rev + + )
{
// std::map<byte_t, aes128_key_t> master_key;
//fmt::print("{:s}_key_{:02x}\n", kMasterBase[name_idx], mkey_rev);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s}_{:02x} " , kMasterBase [ name_idx ] , kKeyStr , mkey_rev ) , master_key [ mkey_rev ] ) ;
}
}
if ( name_idx < kPkg2Base . size ( ) )
{
// tc::Optional<aes128_key_t> package2_key_source;
//fmt::print("{:s}_key_source\n", kPkg2Base[name_idx]);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s}_{:s} " , kPkg2Base [ name_idx ] , kKeyStr , kSourceStr ) , package2_key_source ) ;
}
if ( name_idx < kTicketCommonKeyBase . size ( ) )
{
// tc::Optional<aes128_key_t> ticket_titlekek_source;
//fmt::print("{:s}_source\n", kTicketCommonKeyBase[name_idx]);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s} " , kTicketCommonKeyBase [ name_idx ] , kSourceStr ) , ticket_titlekek_source ) ;
}
if ( name_idx < kNcaKeyAreaEncKeyBase . size ( ) )
{
// std::array<tc::Optional<aes128_key_t>, 3> key_area_key_source;
for ( size_t keak_idx = 0 ; keak_idx < kNcaKeyAreaKeyIndexStr . size ( ) ; keak_idx + + )
{
//fmt::print("{:s}_{:s}_source\n", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx]);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s}_{:s} " , kNcaKeyAreaEncKeyBase [ name_idx ] , kNcaKeyAreaKeyIndexStr [ keak_idx ] , kSourceStr ) , key_area_key_source [ keak_idx ] ) ;
}
}
if ( name_idx < kKekGenBase . size ( ) )
{
// tc::Optional<aes128_key_t> aes_kek_generation_source;
//fmt::print("{:s}_source\n", kKekGenBase[name_idx]);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s} " , kKekGenBase [ name_idx ] , kSourceStr ) , aes_kek_generation_source ) ;
}
if ( name_idx < kKeyGenBase . size ( ) )
{
// tc::Optional<aes128_key_t> aes_key_generation_source;
//fmt::print("{:s}_source\n", kKeyGenBase[name_idx]);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s} " , kKeyGenBase [ name_idx ] , kSourceStr ) , aes_key_generation_source ) ;
}
if ( name_idx < kContentArchiveHeaderBase . size ( ) )
{
// tc::Optional<aes128_key_t> nca_header_kek_source;
//fmt::print("{:s}_kek_source\n", kContentArchiveHeaderBase[name_idx]);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s}_{:s} " , kContentArchiveHeaderBase [ name_idx ] , kKekStr , kSourceStr ) , nca_header_kek_source ) ;
}
if ( name_idx < kContentArchiveHeaderBase . size ( ) )
{
// tc::Optional<aes128_xtskey_t> nca_header_key_source;
//fmt::print("{:s}_key_source\n", kContentArchiveHeaderBase[name_idx]);
_SAVE_AES128XTSKEY ( fmt : : format ( " {:s}_{:s}_{:s} " , kContentArchiveHeaderBase [ name_idx ] , kKeyStr , kSourceStr ) , nca_header_key_source ) ;
}
/* package1 */
// package1_key_xx
if ( name_idx < kPkg1Base . size ( ) )
{
for ( size_t mkey_rev = 0 ; mkey_rev < kMasterKeyMax ; mkey_rev + + )
{
//fmt::print("{:s}_key_{:02x}\n", kPkg1Base[name_idx], mkey_rev);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s}_{:02x} " , kPkg1Base [ name_idx ] , kKeyStr , mkey_rev ) , pkg1_key [ mkey_rev ] ) ;
}
}
/* package2 */
if ( name_idx < kPkg2Base . size ( ) )
{
// package2_key_xx
for ( size_t mkey_rev = 0 ; mkey_rev < kMasterKeyMax ; mkey_rev + + )
{
//fmt::print("{:s}_key_{:02x}\n", kPkg2Base[name_idx], mkey_rev);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s}_{:02x} " , kPkg2Base [ name_idx ] , kKeyStr , mkey_rev ) , pkg2_key [ mkey_rev ] ) ;
}
// package2_sign_key
//fmt::print("{:s}_{:s}_{:s}\n", kPkg2Base[name_idx], kSignKey, kPrivateStr);
//fmt::print("{:s}_{:s}_{:s}\n", kPkg2Base[name_idx], kSignKey, kModulusStr);
_SAVE_RSAKEY ( fmt : : format ( " {:s}_{:s} " , kPkg2Base [ name_idx ] , kSignKey ) , pkg2_sign_key , 2048 ) ;
}
/* eticket */
// ticket common key
if ( name_idx < kTicketCommonKeyBase . size ( ) )
{
for ( size_t mkey_rev = 0 ; mkey_rev < kMasterKeyMax ; mkey_rev + + )
{
//fmt::print("{:s}_{:02x}\n", kTicketCommonKeyBase[name_idx], mkey_rev);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:02x} " , kTicketCommonKeyBase [ name_idx ] , mkey_rev ) , etik_common_key [ mkey_rev ] ) ;
}
}
/* NCA keys */
if ( name_idx < kContentArchiveHeaderBase . size ( ) )
{
// nca header key
//fmt::print("{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kKeyStr);
//_SAVE_AES128XTSKEY(fmt::format("{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kKeyStr), nca_header_key);
// nca header sign0 key (generations)
for ( size_t mkey_rev = 0 ; mkey_rev < kMasterKeyMax ; mkey_rev + + )
{
//fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev, kPrivateStr);
//fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev, kModulusStr);
_SAVE_RSAKEY ( fmt : : format ( " {:s}_{:s}_{:02x} " , kContentArchiveHeaderBase [ name_idx ] , kSignKey , mkey_rev ) , nca_header_sign0_key [ mkey_rev ] , 2048 ) ;
}
// nca header sign0 key (generation 0)
//fmt::print("{:s}_{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, kPrivateStr);
//fmt::print("{:s}_{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, kModulusStr);
_SAVE_RSAKEY ( fmt : : format ( " {:s}_{:s} " , kContentArchiveHeaderBase [ name_idx ] , kSignKey ) , nca_header_sign0_key [ 0 ] , 2048 ) ;
}
// nca body key (unused since prototype format)
// nca key area encryption keys
if ( name_idx < kNcaKeyAreaEncKeyBase . size ( ) )
{
for ( size_t mkey_rev = 0 ; mkey_rev < kMasterKeyMax ; mkey_rev + + )
{
for ( size_t keak_idx = 0 ; keak_idx < kNcaKeyAreaKeyIndexStr . size ( ) ; keak_idx + + )
{
//fmt::print("{:s}_{:s}_{:02x}\n", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s}_{:02x} " , kNcaKeyAreaEncKeyBase [ name_idx ] , kNcaKeyAreaKeyIndexStr [ keak_idx ] , mkey_rev ) , nca_key_area_encryption_key [ keak_idx ] [ mkey_rev ] ) ;
}
}
}
// nca key area "hw" encryption keys
if ( name_idx < kNcaKeyAreaEncKeyHwBase . size ( ) )
{
for ( size_t mkey_rev = 0 ; mkey_rev < kMasterKeyMax ; mkey_rev + + )
{
for ( size_t keak_idx = 0 ; keak_idx < kNcaKeyAreaKeyIndexStr . size ( ) ; keak_idx + + )
{
//fmt::print("{:s}_{:s}_{:02x}\n", kNcaKeyAreaEncKeyHwBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s}_{:02x} " , kNcaKeyAreaEncKeyHwBase [ name_idx ] , kNcaKeyAreaKeyIndexStr [ keak_idx ] , mkey_rev ) , nca_key_area_encryption_key_hw [ keak_idx ] [ mkey_rev ] ) ;
}
}
}
/* ACID */
if ( name_idx < kAcidBase . size ( ) )
{
// acid sign key (generations)
for ( size_t mkey_rev = 0 ; mkey_rev < kMasterKeyMax ; mkey_rev + + )
{
//fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kAcidBase[name_idx], kSignKey, mkey_rev, kPrivateStr);
//fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kAcidBase[name_idx], kSignKey, mkey_rev, kModulusStr);
_SAVE_RSAKEY ( fmt : : format ( " {:s}_{:s}_{:02x} " , kAcidBase [ name_idx ] , kSignKey , mkey_rev ) , acid_sign_key [ mkey_rev ] , 2048 ) ;
}
// acid sign key (generation 0)
//fmt::print("{:s}_{:s}_{:s}\n", kAcidBase[name_idx], kSignKey, kPrivateStr);
//fmt::print("{:s}_{:s}_{:s}\n", kAcidBase[name_idx], kSignKey, kModulusStr);
_SAVE_RSAKEY ( fmt : : format ( " {:s}_{:s} " , kAcidBase [ name_idx ] , kSignKey ) , acid_sign_key [ 0 ] , 2048 ) ;
}
/* NRR certificate */
if ( name_idx < kNrrCertBase . size ( ) )
{
// nrr certificate sign key (generations)
for ( size_t mkey_rev = 0 ; mkey_rev < kMasterKeyMax ; mkey_rev + + )
{
//fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kNrrCertBase[name_idx], kSignKey, mkey_rev, kPrivateStr);
//fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kNrrCertBase[name_idx], kSignKey, mkey_rev, kModulusStr);
_SAVE_RSAKEY ( fmt : : format ( " {:s}_{:s}_{:02x} " , kNrrCertBase [ name_idx ] , kSignKey , mkey_rev ) , nrr_certificate_sign_key [ mkey_rev ] , 2048 ) ;
}
// nrr certificate sign key (generation 0)
//fmt::print("{:s}_{:s}_{:s}\n", kNrrCertBase[name_idx], kSignKey, kPrivateStr);
//fmt::print("{:s}_{:s}_{:s}\n", kNrrCertBase[name_idx], kSignKey, kModulusStr);
_SAVE_RSAKEY ( fmt : : format ( " {:s}_{:s} " , kNrrCertBase [ name_idx ] , kSignKey ) , nrr_certificate_sign_key [ 0 ] , 2048 ) ;
}
/* XCI header */
if ( name_idx < kXciHeaderBase . size ( ) )
{
// xci header key (based on index)
for ( size_t kek_index = 0 ; kek_index < 8 ; kek_index + + )
{
//fmt::print("{:s}_{:s}_{:02x}\n", kXciHeaderBase[name_idx], kKeyStr, kek_index);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s}_{:02x} " , kXciHeaderBase [ name_idx ] , kKeyStr , kek_index ) , xci_header_key [ kek_index ] ) ;
}
// xci header key (old lable)
//fmt::print("{:s}_{:s}\n", kXciHeaderBase[name_idx], kKeyStr);
_SAVE_AES128KEY ( fmt : : format ( " {:s}_{:s} " , kXciHeaderBase [ name_idx ] , kKeyStr ) , xci_header_key [ isDev ? nn : : hac : : gc : : KEK_DEV : nn : : hac : : gc : : KEK_PROD ] ) ;
// xci header sign key
//fmt::print("{:s}_{:s}_{:s}\n", kXciHeaderBase[name_idx], kSignKey, kPrivateStr);
//fmt::print("{:s}_{:s}_{:s}\n", kXciHeaderBase[name_idx], kSignKey, kModulusStr);
_SAVE_RSAKEY ( fmt : : format ( " {:s}_{:s} " , kXciHeaderBase [ name_idx ] , kSignKey ) , xci_header_sign_key , 2048 ) ;
}
/* PKI */
if ( name_idx < kPkiRootBase . size ( ) )
{
// tc::Optional<rsa_key_t> pki_root_sign_key;
//fmt::print("{:s}_{:s}_{:s}\n", kPkiRootBase[name_idx], kSignKey, kPrivateStr);
//fmt::print("{:s}_{:s}_{:s}\n", kPkiRootBase[name_idx], kSignKey, kModulusStr);
_SAVE_RSAKEY ( fmt : : format ( " {:s}_{:s} " , kPkiRootBase [ name_idx ] , kSignKey ) , pki_root_sign_key , 4096 ) ;
}
}
# undef _SAVE_RSAKEY
# undef _SAVE_AES128XTSKEY
# undef _SAVE_AES128KEY
// Derive Keys
for ( auto itr = master_key . begin ( ) ; itr ! = master_key . end ( ) ; itr + + )
{
if ( aes_kek_generation_source . isSet ( ) & & aes_key_generation_source . isSet ( ) )
{
if ( itr - > first = = 0 & & nca_header_kek_source . isSet ( ) & & nca_header_key_source . isSet ( ) )
{
if ( nca_header_key . isNull ( ) )
{
aes128_key_t nca_header_kek_tmp ;
nn : : hac : : AesKeygen : : generateKey ( nca_header_kek_tmp . data ( ) , aes_kek_generation_source . get ( ) . data ( ) , nca_header_kek_source . get ( ) . data ( ) , aes_key_generation_source . get ( ) . data ( ) , itr - > second . data ( ) ) ;
aes128_xtskey_t nca_header_key_tmp ;
nn : : hac : : AesKeygen : : generateKey ( nca_header_key_tmp [ 0 ] . data ( ) , nca_header_key_source . get ( ) [ 0 ] . data ( ) , nca_header_kek_tmp . data ( ) ) ;
nn : : hac : : AesKeygen : : generateKey ( nca_header_key_tmp [ 1 ] . data ( ) , nca_header_key_source . get ( ) [ 1 ] . data ( ) , nca_header_kek_tmp . data ( ) ) ;
nca_header_key = nca_header_key_tmp ;
}
}
for ( size_t keak_idx = 0 ; keak_idx < nn : : hac : : nca : : kKeyAreaEncryptionKeyNum ; keak_idx + + )
{
if ( key_area_key_source [ keak_idx ] . isSet ( ) & & nca_key_area_encryption_key [ keak_idx ] . find ( itr - > first ) ! = nca_key_area_encryption_key [ keak_idx ] . end ( ) )
{
aes128_key_t nca_key_area_encryption_key_tmp ;
nn : : hac : : AesKeygen : : generateKey ( nca_key_area_encryption_key_tmp . data ( ) , aes_kek_generation_source . get ( ) . data ( ) , key_area_key_source [ keak_idx ] . get ( ) . data ( ) , aes_key_generation_source . get ( ) . data ( ) , itr - > second . data ( ) ) ;
nca_key_area_encryption_key [ keak_idx ] [ itr - > first ] = nca_key_area_encryption_key_tmp ;
}
}
}
if ( ticket_titlekek_source . isSet ( ) & & etik_common_key . find ( itr - > first ) = = etik_common_key . end ( ) )
{
aes128_key_t etik_common_key_tmp ;
nn : : hac : : AesKeygen : : generateKey ( etik_common_key_tmp . data ( ) , ticket_titlekek_source . get ( ) . data ( ) , itr - > second . data ( ) ) ;
etik_common_key [ itr - > first ] = etik_common_key_tmp ;
}
if ( package2_key_source . isSet ( ) & & pkg2_key . find ( itr - > first ) = = pkg2_key . end ( ) )
{
aes128_key_t pkg2_key_tmp ;
nn : : hac : : AesKeygen : : generateKey ( pkg2_key_tmp . data ( ) , package2_key_source . get ( ) . data ( ) , itr - > second . data ( ) ) ;
pkg2_key [ itr - > first ] = pkg2_key_tmp ;
}
}
// Save PKI Root Key
if ( pki_root_sign_key . isSet ( ) )
{
2021-09-30 11:40:27 +00:00
broadon_signer [ " Root " ] = { tc : : ByteData ( ) , nn : : pki : : sign : : SIGN_ALGO_RSA4096 , pki_root_sign_key . get ( ) } ;
2021-09-28 11:15:54 +00:00
}
}
void nstool : : KeyBagInitializer : : importTitleKeyFile ( const tc : : io : : Path & keyfile_path )
{
}
void nstool : : KeyBagInitializer : : importCertificateChain ( const tc : : io : : Path & cert_path )
{
// save file path string for error messages
std : : string cert_path_str ;
tc : : io : : PathUtil : : pathToUnixUTF8 ( cert_path , cert_path_str ) ;
// open cert file
std : : shared_ptr < tc : : io : : FileStream > certfile_stream ;
try {
certfile_stream = std : : make_shared < tc : : io : : FileStream > ( tc : : io : : FileStream ( cert_path , tc : : io : : FileMode : : Open , tc : : io : : FileAccess : : Read ) ) ;
}
catch ( tc : : io : : FileNotFoundException & e ) {
fmt : : print ( " [WARNING] Failed to open certificate file \" {:s} \" ({:s}). \n " , cert_path_str , e . error ( ) ) ;
return ;
}
// check size
size_t cert_raw_size = tc : : io : : IOUtil : : castInt64ToSize ( certfile_stream - > length ( ) ) ;
if ( cert_raw_size > 0x10000 )
{
fmt : : print ( " [WARNING] Certificate file \" {:s} \" was too large. \n " , cert_path_str ) ;
return ;
}
// import cert data
tc : : ByteData cert_raw = tc : : ByteData ( cert_raw_size ) ;
certfile_stream - > seek ( 0 , tc : : io : : SeekOrigin : : Begin ) ;
certfile_stream - > read ( cert_raw . data ( ) , cert_raw . size ( ) ) ;
nn : : pki : : SignedData < nn : : pki : : CertificateBody > cert ;
try {
for ( size_t f_pos = 0 ; f_pos < cert_raw . size ( ) ; f_pos + = cert . getBytes ( ) . size ( ) )
{
cert . fromBytes ( cert_raw . data ( ) + f_pos , cert_raw . size ( ) - f_pos ) ;
std : : string cert_identity = fmt : : format ( " {:s}-{:s} " , cert . getBody ( ) . getIssuer ( ) , cert . getBody ( ) . getSubject ( ) ) ;
switch ( cert . getBody ( ) . getPublicKeyType ( ) ) {
case nn : : pki : : cert : : PublicKeyType : : RSA2048 :
2021-09-30 11:40:27 +00:00
broadon_signer [ cert_identity ] = { cert . getBytes ( ) , nn : : pki : : sign : : SIGN_ALGO_RSA2048 , cert . getBody ( ) . getRsa2048PublicKey ( ) } ;
2021-09-28 11:15:54 +00:00
break ;
case nn : : pki : : cert : : PublicKeyType : : RSA4096 :
2021-09-30 11:40:27 +00:00
broadon_signer [ cert_identity ] = { cert . getBytes ( ) , nn : : pki : : sign : : SIGN_ALGO_RSA4096 , cert . getBody ( ) . getRsa4096PublicKey ( ) } ;
2021-09-28 11:15:54 +00:00
break ;
case nn : : pki : : cert : : PublicKeyType : : ECDSA240 :
2021-09-30 11:40:27 +00:00
// broadon_signer[cert_identity] = { cert.getBytes(), nn::pki::sign::SIGN_ALGO_ECDSA240, cert.getBody().getRsa4096PublicKey() };
2021-09-28 11:15:54 +00:00
fmt : : print ( " [WARNING] Certificate {:s} will not be imported. ecc233 public keys are not supported yet. \n " , cert_identity ) ;
break ;
default :
fmt : : print ( " [WARNING] Certificate {:s} will not be imported. Unknown public key type. \n " , cert_identity ) ;
}
}
}
catch ( tc : : Exception & e ) {
fmt : : print ( " [WARNING] Certificate file \" {:s} \" is corrupted ({:s}). \n " , cert_path_str , e . error ( ) ) ;
return ;
}
}
void nstool : : KeyBagInitializer : : importTicket ( const tc : : io : : Path & tik_path )
{
// save file path string for error messages
std : : string tik_path_str ;
tc : : io : : PathUtil : : pathToUnixUTF8 ( tik_path , tik_path_str ) ;
// open cert file
std : : shared_ptr < tc : : io : : FileStream > tik_stream ;
try {
tik_stream = std : : make_shared < tc : : io : : FileStream > ( tc : : io : : FileStream ( tik_path , tc : : io : : FileMode : : Open , tc : : io : : FileAccess : : Read ) ) ;
}
catch ( tc : : io : : FileNotFoundException & e ) {
fmt : : print ( " [WARNING] Failed to open ticket \" {:s} \" ({:s}). \n " , tik_path_str , e . error ( ) ) ;
return ;
}
// check size
size_t tik_raw_size = tc : : io : : IOUtil : : castInt64ToSize ( tik_stream - > length ( ) ) ;
if ( tik_raw_size > 0x10000 )
{
fmt : : print ( " [WARNING] Ticket \" {:s} \" was too large. \n " , tik_path_str ) ;
return ;
}
// import cert data
tc : : ByteData tik_raw = tc : : ByteData ( tik_raw_size ) ;
tik_stream - > seek ( 0 , tc : : io : : SeekOrigin : : Begin ) ;
tik_stream - > read ( tik_raw . data ( ) , tik_raw . size ( ) ) ;
nn : : pki : : SignedData < nn : : es : : TicketBody_V2 > tik ;
try {
// de serialise ticket
tik . fromBytes ( tik_raw . data ( ) , tik_raw . size ( ) ) ;
// save rights id
rights_id_t rights_id ;
memcpy ( rights_id . data ( ) , tik . getBody ( ) . getRightsId ( ) , rights_id . size ( ) ) ;
// check ticket is not personalised
if ( tik . getBody ( ) . getTitleKeyEncType ( ) ! = nn : : es : : ticket : : AES128_CBC )
{
fmt : : print ( " [WARNING] Ticket \" {:s} \" will not be imported. Personalised tickets are not supported. \n " , tc : : cli : : FormatUtil : : formatBytesAsString ( rights_id . data ( ) , rights_id . size ( ) , true , " " ) ) ;
return ;
}
// save enc title key
aes128_key_t enc_title_key ;
memcpy ( enc_title_key . data ( ) , tik . getBody ( ) . getEncTitleKey ( ) , enc_title_key . size ( ) ) ;
// save the encrypted title key as the fallback enc content key incase the ticket was malformed and workarounds to decrypt it in isolation fail
if ( fallback_enc_content_key . isNull ( ) )
{
fallback_enc_content_key = enc_title_key ;
}
// determine key to decrypt title key
byte_t common_key_index = tik . getBody ( ) . getCommonKeyId ( ) ;
// work around for bad scene tickets where they don't set the commonkey id field (detect scene ticket with ffff.... signature)
if ( common_key_index ! = rights_id [ 15 ] & & tik . getSignature ( ) . getBytes ( ) [ 0x00 ] = = 0xff & & tik . getSignature ( ) . getBytes ( ) [ 0x01 ] = = 0xff )
{
common_key_index = rights_id [ 15 ] ;
}
if ( etik_common_key . find ( tik . getBody ( ) . getCommonKeyId ( ) ) = = etik_common_key . end ( ) )
{
fmt : : print ( " [WARNING] Ticket \" {:s} \" will not be imported. Could not decrypt title key. \n " , tc : : cli : : FormatUtil : : formatBytesAsString ( rights_id . data ( ) , rights_id . size ( ) , true , " " ) ) ;
return ;
}
// decrypt title key
aes128_key_t dec_title_key ;
tc : : crypto : : DecryptAes128Ecb ( dec_title_key . data ( ) , enc_title_key . data ( ) , sizeof ( aes128_key_t ) , etik_common_key [ common_key_index ] . data ( ) , sizeof ( aes128_key_t ) ) ;
// add to key dict
external_content_keys [ rights_id ] = dec_title_key ;
}
catch ( tc : : Exception & e ) {
fmt : : print ( " [WARNING] Ticket \" {:s} \" is corrupted ({:s}). \n " , tik_path_str , e . error ( ) ) ;
return ;
}
}