2020-02-26 09:05:21 +00:00
# include "NcaProcess.h"
# include "PfsProcess.h"
# include "RomfsProcess.h"
# include "MetaProcess.h"
2019-01-31 09:10:19 +00:00
# include <iostream>
# include <iomanip>
# include <sstream>
2020-02-26 09:05:21 +00:00
2019-01-31 09:10:19 +00:00
# include <fnd/SimpleTextOutput.h>
# include <fnd/OffsetAdjustedIFile.h>
# include <fnd/AesCtrWrappedIFile.h>
# include <fnd/LayeredIntegrityWrappedIFile.h>
2020-02-26 09:05:21 +00:00
# include <nn/hac/ContentArchiveUtil.h>
2019-01-31 09:10:19 +00:00
# include <nn/hac/AesKeygen.h>
# include <nn/hac/HierarchicalSha256Header.h>
# include <nn/hac/HierarchicalIntegrityHeader.h>
2021-09-28 11:15:54 +00:00
nstool : : NcaProcess : : NcaProcess ( ) :
2019-01-31 09:10:19 +00:00
mFile ( ) ,
2021-09-28 11:15:54 +00:00
mCliOutputMode ( true , false , false , false ) ,
2019-01-31 09:10:19 +00:00
mVerify ( false ) ,
mListFs ( false )
{
for ( size_t i = 0 ; i < nn : : hac : : nca : : kPartitionNum ; i + + )
{
mPartitionPath [ i ] . doExtract = false ;
}
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : process ( )
2019-01-31 09:10:19 +00:00
{
// import header
importHeader ( ) ;
// determine keys
generateNcaBodyEncryptionKeys ( ) ;
// import/generate fs header data
generatePartitionConfiguration ( ) ;
// validate signatures
if ( mVerify )
validateNcaSignatures ( ) ;
// display header
2021-09-28 11:15:54 +00:00
if ( mCliOutputMode . show_basic_info )
2019-01-31 09:10:19 +00:00
displayHeader ( ) ;
// process partition
processPartitions ( ) ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : setInputFile ( const std : : shared_ptr < tc : : io : : IStream > & file )
2019-01-31 09:10:19 +00:00
{
mFile = file ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : setKeyCfg ( const KeyBag & keycfg )
2019-01-31 09:10:19 +00:00
{
mKeyCfg = keycfg ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : setCliOutputMode ( CliOutputMode type )
2019-01-31 09:10:19 +00:00
{
mCliOutputMode = type ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : setVerifyMode ( bool verify )
2019-01-31 09:10:19 +00:00
{
mVerify = verify ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : setPartition0ExtractPath ( const std : : string & path )
2019-01-31 09:10:19 +00:00
{
mPartitionPath [ 0 ] . path = path ;
mPartitionPath [ 0 ] . doExtract = true ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : setPartition1ExtractPath ( const std : : string & path )
2019-01-31 09:10:19 +00:00
{
mPartitionPath [ 1 ] . path = path ;
mPartitionPath [ 1 ] . doExtract = true ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : setPartition2ExtractPath ( const std : : string & path )
2019-01-31 09:10:19 +00:00
{
mPartitionPath [ 2 ] . path = path ;
mPartitionPath [ 2 ] . doExtract = true ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : setPartition3ExtractPath ( const std : : string & path )
2019-01-31 09:10:19 +00:00
{
mPartitionPath [ 3 ] . path = path ;
mPartitionPath [ 3 ] . doExtract = true ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : setListFs ( bool list_fs )
2019-01-31 09:10:19 +00:00
{
mListFs = list_fs ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : importHeader ( )
2019-01-31 09:10:19 +00:00
{
if ( * mFile = = nullptr )
{
2021-09-28 11:15:54 +00:00
throw tc : : Exception ( kModuleName , " No file reader set. " ) ;
2019-01-31 09:10:19 +00:00
}
// read header block
( * mFile ) - > read ( ( byte_t * ) & mHdrBlock , 0 , sizeof ( nn : : hac : : sContentArchiveHeaderBlock ) ) ;
// decrypt header block
fnd : : aes : : sAesXts128Key header_key ;
mKeyCfg . getContentArchiveHeaderKey ( header_key ) ;
2020-02-26 09:05:21 +00:00
nn : : hac : : ContentArchiveUtil : : decryptContentArchiveHeader ( ( byte_t * ) & mHdrBlock , ( byte_t * ) & mHdrBlock , header_key ) ;
2019-01-31 09:10:19 +00:00
// generate header hash
fnd : : sha : : Sha256 ( ( byte_t * ) & mHdrBlock . header , sizeof ( nn : : hac : : sContentArchiveHeader ) , mHdrHash . bytes ) ;
// proccess main header
mHdr . fromBytes ( ( byte_t * ) & mHdrBlock . header , sizeof ( nn : : hac : : sContentArchiveHeader ) ) ;
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : generateNcaBodyEncryptionKeys ( )
2019-01-31 09:10:19 +00:00
{
// create zeros key
2021-09-28 11:15:54 +00:00
KeyBag : : aes128_key_t zero_aesctr_key ;
2019-01-31 09:10:19 +00:00
memset ( zero_aesctr_key . key , 0 , sizeof ( zero_aesctr_key ) ) ;
// get key data from header
2020-02-26 09:05:21 +00:00
byte_t masterkey_rev = nn : : hac : : ContentArchiveUtil : : getMasterKeyRevisionFromKeyGeneration ( mHdr . getKeyGeneration ( ) ) ;
2019-01-31 09:10:19 +00:00
byte_t keak_index = mHdr . getKeyAreaEncryptionKeyIndex ( ) ;
// process key area
sKeys : : sKeyAreaKey kak ;
2021-09-28 11:15:54 +00:00
KeyBag : : aes128_key_t key_area_enc_key ;
const KeyBag : : aes128_key_t * key_area = ( const KeyBag : : aes128_key_t * ) mHdr . getKeyArea ( ) ;
2019-01-31 09:10:19 +00:00
for ( size_t i = 0 ; i < nn : : hac : : nca : : kKeyAreaKeyNum ; i + + )
{
if ( key_area [ i ] ! = zero_aesctr_key )
{
kak . index = ( byte_t ) i ;
kak . enc = key_area [ i ] ;
// key[0-3]
if ( i < 4 & & mKeyCfg . getNcaKeyAreaEncryptionKey ( 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 ) ;
}
// key[KEY_AESCTR_HW]
else if ( i = = nn : : hac : : nca : : KEY_AESCTR_HW & & 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
{
kak . decrypted = false ;
}
2021-09-28 11:15:54 +00:00
mContentKey . kak_list . push_back ( kak ) ;
2019-01-31 09:10:19 +00:00
}
}
// set flag to indicate that the keys are not available
mContentKey . aes_ctr . isSet = false ;
// if this has a rights id, the key needs to be sourced from a ticket
if ( mHdr . hasRightsId ( ) = = true )
{
2021-09-28 11:15:54 +00:00
KeyBag : : aes128_key_t tmp_key ;
2019-01-31 09:10:19 +00:00
if ( mKeyCfg . getNcaExternalContentKey ( mHdr . getRightsId ( ) , tmp_key ) = = true )
{
mContentKey . aes_ctr = tmp_key ;
}
else if ( mKeyCfg . getNcaExternalContentKey ( kDummyRightsIdForUserTitleKey , tmp_key ) = = true )
{
2021-09-28 11:15:54 +00:00
KeyBag : : aes128_key_t common_key ;
2019-01-31 09:10:19 +00:00
if ( mKeyCfg . getETicketCommonKey ( masterkey_rev , common_key ) = = true )
{
nn : : hac : : AesKeygen : : generateKey ( tmp_key . key , tmp_key . key , common_key . key ) ;
}
mContentKey . aes_ctr = tmp_key ;
}
}
// otherwise decrypt key area
else
{
2021-09-28 11:15:54 +00:00
KeyBag : : aes128_key_t kak_aes_ctr = zero_aesctr_key ;
2019-01-31 09:10:19 +00:00
for ( size_t i = 0 ; i < mContentKey . kak_list . size ( ) ; i + + )
{
if ( mContentKey . kak_list [ i ] . index = = nn : : hac : : nca : : KEY_AESCTR & & mContentKey . kak_list [ i ] . decrypted )
{
kak_aes_ctr = mContentKey . kak_list [ i ] . dec ;
}
}
if ( kak_aes_ctr ! = zero_aesctr_key )
{
mContentKey . aes_ctr = kak_aes_ctr ;
}
}
// if the keys weren't generated, check if the keys were supplied by the user
2021-09-28 11:15:54 +00:00
if ( mContentKey . aes_ctr . isNull ( ) )
2019-01-31 09:10:19 +00:00
{
2021-09-28 11:15:54 +00:00
if ( mKeyCfg . getNcaExternalContentKey ( kDummyRightsIdForUserBodyKey , mContentKey . aes_ctr . get ( ) ) = = true )
2019-01-31 09:10:19 +00:00
mContentKey . aes_ctr . isSet = true ;
}
2021-09-28 11:15:54 +00:00
if ( mCliOutputMode . show_keydata )
2019-01-31 09:10:19 +00:00
{
2021-09-28 11:15:54 +00:00
if ( mContentKey . aes_ctr . isSet ( ) )
2019-01-31 09:10:19 +00:00
{
std : : cout < < " [NCA Content Key] " < < std : : endl ;
2021-09-28 11:15:54 +00:00
std : : cout < < " AES-CTR Key: " < < fnd : : SimpleTextOutput : : arrayToString ( mContentKey . aes_ctr . get ( ) . key , sizeof ( mContentKey . aes_ctr . get ( ) ) , true , " : " ) < < std : : endl ;
2019-01-31 09:10:19 +00:00
}
}
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : generatePartitionConfiguration ( )
2019-01-31 09:10:19 +00:00
{
std : : stringstream error ;
for ( size_t i = 0 ; i < mHdr . getPartitionEntryList ( ) . size ( ) ; i + + )
{
// get reference to relevant structures
const nn : : hac : : ContentArchiveHeader : : sPartitionEntry & partition = mHdr . getPartitionEntryList ( ) [ i ] ;
2020-03-19 11:03:52 +00:00
nn : : hac : : sContentArchiveFsHeader & fs_header = mHdrBlock . fs_header [ partition . header_index ] ;
2019-01-31 09:10:19 +00:00
// output structure
sPartitionInfo & info = mPartitions [ partition . header_index ] ;
// validate header hash
fnd : : sha : : sSha256Hash fs_header_hash ;
2020-03-19 11:03:52 +00:00
fnd : : sha : : Sha256 ( ( const byte_t * ) & mHdrBlock . fs_header [ partition . header_index ] , sizeof ( nn : : hac : : sContentArchiveFsHeader ) , fs_header_hash . bytes ) ;
2019-01-31 09:10:19 +00:00
if ( fs_header_hash . compare ( partition . fs_header_hash ) = = false )
{
error . clear ( ) ;
error < < " NCA FS Header [ " < < partition . header_index < < " ] Hash: FAIL \n " ;
2021-09-28 11:15:54 +00:00
throw tc : : Exception ( kModuleName , error . str ( ) ) ;
2019-01-31 09:10:19 +00:00
}
if ( fs_header . version . get ( ) ! = nn : : hac : : nca : : kDefaultFsHeaderVersion )
{
error . clear ( ) ;
error < < " NCA FS Header [ " < < partition . header_index < < " ] Version( " < < fs_header . version . get ( ) < < " ): UNSUPPORTED " ;
2021-09-28 11:15:54 +00:00
throw tc : : Exception ( kModuleName , error . str ( ) ) ;
2019-01-31 09:10:19 +00:00
}
// setup AES-CTR
2020-02-26 09:05:21 +00:00
nn : : hac : : ContentArchiveUtil : : getNcaPartitionAesCtr ( & fs_header , info . aes_ctr . iv ) ;
2019-01-31 09:10:19 +00:00
// save partition config
info . reader = nullptr ;
info . offset = partition . offset ;
info . size = partition . size ;
info . format_type = ( nn : : hac : : nca : : FormatType ) fs_header . format_type ;
info . hash_type = ( nn : : hac : : nca : : HashType ) fs_header . hash_type ;
info . enc_type = ( nn : : hac : : nca : : EncryptionType ) fs_header . encryption_type ;
2020-03-17 11:11:28 +00:00
if ( info . hash_type = = nn : : hac : : nca : : HashType : : HierarchicalSha256 )
2019-01-31 09:10:19 +00:00
{
2020-03-19 11:03:52 +00:00
// info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_SHA256);
2019-01-31 09:10:19 +00:00
nn : : hac : : HierarchicalSha256Header hdr ;
2021-09-28 11:15:54 +00:00
std : : vector < fnd : : LayeredIntegrityMetadata : : sLayer > hash_layers ;
2019-01-31 09:10:19 +00:00
fnd : : LayeredIntegrityMetadata : : sLayer data_layer ;
2021-09-28 11:15:54 +00:00
std : : vector < fnd : : sha : : sSha256Hash > master_hash_list ;
2019-01-31 09:10:19 +00:00
// import raw data
2020-03-19 11:03:52 +00:00
hdr . fromBytes ( fs_header . hash_info , nn : : hac : : nca : : kHashInfoLen ) ;
2019-01-31 09:10:19 +00:00
for ( size_t i = 0 ; i < hdr . getLayerInfo ( ) . size ( ) ; i + + )
{
fnd : : LayeredIntegrityMetadata : : sLayer layer ;
layer . offset = hdr . getLayerInfo ( ) [ i ] . offset ;
layer . size = hdr . getLayerInfo ( ) [ i ] . size ;
layer . block_size = hdr . getHashBlockSize ( ) ;
if ( i + 1 = = hdr . getLayerInfo ( ) . size ( ) )
{
data_layer = layer ;
}
else
{
2021-09-28 11:15:54 +00:00
hash_layers . push_back ( layer ) ;
2019-01-31 09:10:19 +00:00
}
}
2021-09-28 11:15:54 +00:00
master_hash_list . push_back ( hdr . getMasterHash ( ) ) ;
2019-01-31 09:10:19 +00:00
// write data into metadata
info . layered_intergrity_metadata . setAlignHashToBlock ( false ) ;
info . layered_intergrity_metadata . setHashLayerInfo ( hash_layers ) ;
info . layered_intergrity_metadata . setDataLayerInfo ( data_layer ) ;
info . layered_intergrity_metadata . setMasterHashList ( master_hash_list ) ;
}
2020-03-17 11:11:28 +00:00
else if ( info . hash_type = = nn : : hac : : nca : : HashType : : HierarchicalIntegrity )
2019-01-31 09:10:19 +00:00
{
2020-03-19 11:03:52 +00:00
// info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_INTEGRITY);
2019-01-31 09:10:19 +00:00
nn : : hac : : HierarchicalIntegrityHeader hdr ;
2021-09-28 11:15:54 +00:00
std : : vector < fnd : : LayeredIntegrityMetadata : : sLayer > hash_layers ;
2019-01-31 09:10:19 +00:00
fnd : : LayeredIntegrityMetadata : : sLayer data_layer ;
2021-09-28 11:15:54 +00:00
std : : vector < fnd : : sha : : sSha256Hash > master_hash_list ;
2019-01-31 09:10:19 +00:00
2020-03-19 11:03:52 +00:00
hdr . fromBytes ( fs_header . hash_info , nn : : hac : : nca : : kHashInfoLen ) ;
2019-01-31 09:10:19 +00:00
for ( size_t i = 0 ; i < hdr . getLayerInfo ( ) . size ( ) ; i + + )
{
fnd : : LayeredIntegrityMetadata : : sLayer layer ;
layer . offset = hdr . getLayerInfo ( ) [ i ] . offset ;
layer . size = hdr . getLayerInfo ( ) [ i ] . size ;
2021-09-28 11:15:54 +00:00
layer . block_size = ( 1 < < hdr . getLayerInfo ( ) [ i ] . block_size ) ;
2019-01-31 09:10:19 +00:00
if ( i + 1 = = hdr . getLayerInfo ( ) . size ( ) )
{
data_layer = layer ;
}
else
{
2021-09-28 11:15:54 +00:00
hash_layers . push_back ( layer ) ;
2019-01-31 09:10:19 +00:00
}
}
// write data into metadata
info . layered_intergrity_metadata . setAlignHashToBlock ( true ) ;
info . layered_intergrity_metadata . setHashLayerInfo ( hash_layers ) ;
info . layered_intergrity_metadata . setDataLayerInfo ( data_layer ) ;
info . layered_intergrity_metadata . setMasterHashList ( hdr . getMasterHashList ( ) ) ;
}
// create reader
try
{
// filter out unrecognised format types
switch ( info . format_type )
{
2020-03-17 11:11:28 +00:00
case ( nn : : hac : : nca : : FormatType : : PartitionFs ) :
case ( nn : : hac : : nca : : FormatType : : RomFs ) :
2019-01-31 09:10:19 +00:00
break ;
default :
error . clear ( ) ;
2020-03-17 11:11:28 +00:00
error < < " FormatType( " < < nn : : hac : : ContentArchiveUtil : : getFormatTypeAsString ( info . format_type ) < < " ): UNKNOWN " ;
2021-09-28 11:15:54 +00:00
throw tc : : Exception ( kModuleName , error . str ( ) ) ;
2019-01-31 09:10:19 +00:00
}
// create reader based on encryption type0
2020-03-17 11:11:28 +00:00
if ( info . enc_type = = nn : : hac : : nca : : EncryptionType : : None )
2019-01-31 09:10:19 +00:00
{
info . reader = new fnd : : OffsetAdjustedIFile ( mFile , info . offset , info . size ) ;
}
2020-03-17 11:11:28 +00:00
else if ( info . enc_type = = nn : : hac : : nca : : EncryptionType : : AesCtr )
2019-01-31 09:10:19 +00:00
{
2021-09-28 11:15:54 +00:00
if ( mContentKey . aes_ctr . isNull ( ) )
throw tc : : Exception ( kModuleName , " AES-CTR Key was not determined " ) ;
info . reader = new fnd : : OffsetAdjustedIFile ( new fnd : : AesCtrWrappedIFile ( mFile , mContentKey . aes_ctr . get ( ) , info . aes_ctr ) , info . offset , info . size ) ;
2019-01-31 09:10:19 +00:00
}
2020-03-17 11:11:28 +00:00
else if ( info . enc_type = = nn : : hac : : nca : : EncryptionType : : AesXts | | info . enc_type = = nn : : hac : : nca : : EncryptionType : : AesCtrEx )
2019-01-31 09:10:19 +00:00
{
error . clear ( ) ;
2020-02-26 09:05:21 +00:00
error < < " EncryptionType( " < < nn : : hac : : ContentArchiveUtil : : getEncryptionTypeAsString ( info . enc_type ) < < " ): UNSUPPORTED " ;
2021-09-28 11:15:54 +00:00
throw tc : : Exception ( kModuleName , error . str ( ) ) ;
2019-01-31 09:10:19 +00:00
}
else
{
error . clear ( ) ;
2020-03-17 11:11:28 +00:00
error < < " EncryptionType( " < < nn : : hac : : ContentArchiveUtil : : getEncryptionTypeAsString ( info . enc_type ) < < " ): UNKNOWN " ;
2021-09-28 11:15:54 +00:00
throw tc : : Exception ( kModuleName , error . str ( ) ) ;
2019-01-31 09:10:19 +00:00
}
// filter out unrecognised hash types, and hash based readers
2020-03-17 11:11:28 +00:00
if ( info . hash_type = = nn : : hac : : nca : : HashType : : HierarchicalSha256 | | info . hash_type = = nn : : hac : : nca : : HashType : : HierarchicalIntegrity )
2019-01-31 09:10:19 +00:00
{
info . reader = new fnd : : LayeredIntegrityWrappedIFile ( info . reader , info . layered_intergrity_metadata ) ;
}
2020-03-17 11:11:28 +00:00
else if ( info . hash_type ! = nn : : hac : : nca : : HashType : : None )
2019-01-31 09:10:19 +00:00
{
error . clear ( ) ;
2020-03-17 11:11:28 +00:00
error < < " HashType( " < < nn : : hac : : ContentArchiveUtil : : getHashTypeAsString ( info . hash_type ) < < " ): UNKNOWN " ;
2021-09-28 11:15:54 +00:00
throw tc : : Exception ( kModuleName , error . str ( ) ) ;
2019-01-31 09:10:19 +00:00
}
}
2021-09-28 11:15:54 +00:00
catch ( const tc : : Exception & e )
2019-01-31 09:10:19 +00:00
{
info . fail_reason = std : : string ( e . error ( ) ) ;
}
}
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : validateNcaSignatures ( )
2019-01-31 09:10:19 +00:00
{
// validate signature[0]
fnd : : rsa : : sRsa2048Key sign0_key ;
2020-03-10 11:02:56 +00:00
mKeyCfg . getContentArchiveHeader0SignKey ( sign0_key , mHdr . getSignatureKeyGeneration ( ) ) ;
2019-01-31 09:10:19 +00:00
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 ;
}
// validate signature[1]
2020-03-17 11:11:28 +00:00
if ( mHdr . getContentType ( ) = = nn : : hac : : nca : : ContentType : : Program )
2019-01-31 09:10:19 +00:00
{
2020-03-17 11:11:28 +00:00
if ( mPartitions [ nn : : hac : : nca : : PARTITION_CODE ] . format_type = = nn : : hac : : nca : : FormatType : : PartitionFs )
2019-01-31 09:10:19 +00:00
{
if ( * mPartitions [ nn : : hac : : nca : : PARTITION_CODE ] . reader ! = nullptr )
{
PfsProcess exefs ;
exefs . setInputFile ( mPartitions [ nn : : hac : : nca : : PARTITION_CODE ] . reader ) ;
exefs . setCliOutputMode ( 0 ) ;
exefs . process ( ) ;
// open main.npdm
if ( exefs . getPfsHeader ( ) . getFileList ( ) . hasElement ( kNpdmExefsPath ) = = true )
{
const nn : : hac : : PartitionFsHeader : : sFile & file = exefs . getPfsHeader ( ) . getFileList ( ) . getElement ( kNpdmExefsPath ) ;
MetaProcess npdm ;
npdm . setInputFile ( new fnd : : OffsetAdjustedIFile ( mPartitions [ nn : : hac : : nca : : PARTITION_CODE ] . reader , file . offset , file . size ) ) ;
2020-03-10 11:02:56 +00:00
npdm . setKeyCfg ( mKeyCfg ) ;
npdm . setVerifyMode ( true ) ;
2019-01-31 09:10:19 +00:00
npdm . setCliOutputMode ( 0 ) ;
npdm . process ( ) ;
2020-03-10 11:02:56 +00:00
if ( fnd : : rsa : : pss : : rsaVerify ( npdm . getMeta ( ) . getAccessControlInfoDesc ( ) . getContentArchiveHeaderSignature2Key ( ) , fnd : : sha : : HASH_SHA256 , mHdrHash . bytes , mHdrBlock . signature_acid ) ! = 0 )
2019-01-31 09:10:19 +00:00
{
std : : cout < < " [WARNING] NCA Header ACID Signature: FAIL " < < std : : endl ;
}
}
else
{
std : : cout < < " [WARNING] NCA Header ACID Signature: FAIL ( \" " < < kNpdmExefsPath < < " \" not present in ExeFs) " < < std : : endl ;
}
}
else
{
std : : cout < < " [WARNING] NCA Header ACID Signature: FAIL (ExeFs unreadable) " < < std : : endl ;
}
}
else
{
std : : cout < < " [WARNING] NCA Header ACID Signature: FAIL (No ExeFs partition) " < < std : : endl ;
}
}
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : displayHeader ( )
2019-01-31 09:10:19 +00:00
{
std : : cout < < " [NCA Header] " < < std : : endl ;
2020-05-17 04:30:37 +00:00
std : : cout < < " Format Type: " < < nn : : hac : : ContentArchiveUtil : : getFormatHeaderVersionAsString ( ( nn : : hac : : nca : : HeaderFormatVersion ) mHdr . getFormatVersion ( ) ) < < std : : endl ;
2020-02-26 09:05:21 +00:00
std : : cout < < " Dist. Type: " < < nn : : hac : : ContentArchiveUtil : : getDistributionTypeAsString ( mHdr . getDistributionType ( ) ) < < std : : endl ;
std : : cout < < " Content Type: " < < nn : : hac : : ContentArchiveUtil : : getContentTypeAsString ( mHdr . getContentType ( ) ) < < std : : endl ;
2019-01-31 09:10:19 +00:00
std : : cout < < " Key Generation: " < < std : : dec < < ( uint32_t ) mHdr . getKeyGeneration ( ) < < std : : endl ;
2020-03-10 11:02:56 +00:00
std : : cout < < " Sig. Generation: " < < std : : dec < < ( uint32_t ) mHdr . getSignatureKeyGeneration ( ) < < std : : endl ;
2020-02-26 09:05:21 +00:00
std : : cout < < " Kaek Index: " < < nn : : hac : : ContentArchiveUtil : : getKeyAreaEncryptionKeyIndexAsString ( ( nn : : hac : : nca : : KeyAreaEncryptionKeyIndex ) mHdr . getKeyAreaEncryptionKeyIndex ( ) ) < < " ( " < < std : : dec < < ( uint32_t ) mHdr . getKeyAreaEncryptionKeyIndex ( ) < < " ) " < < std : : endl ;
2019-01-31 09:10:19 +00:00
std : : cout < < " Size: 0x " < < std : : hex < < mHdr . getContentSize ( ) < < std : : endl ;
std : : cout < < " ProgID: 0x " < < std : : hex < < std : : setw ( 16 ) < < std : : setfill ( ' 0 ' ) < < mHdr . getProgramId ( ) < < std : : endl ;
std : : cout < < " Content Index: " < < std : : dec < < mHdr . getContentIndex ( ) < < std : : endl ;
2020-05-17 04:30:37 +00:00
std : : cout < < " SdkAddon Ver.: " < < nn : : hac : : ContentArchiveUtil : : getSdkAddonVersionAsString ( mHdr . getSdkAddonVersion ( ) ) < < " (v " < < std : : dec < < mHdr . getSdkAddonVersion ( ) < < " ) " < < std : : endl ;
2019-01-31 09:10:19 +00:00
if ( mHdr . hasRightsId ( ) )
{
std : : cout < < " RightsId: " < < fnd : : SimpleTextOutput : : arrayToString ( mHdr . getRightsId ( ) , nn : : hac : : nca : : kRightsIdLen , true , " " ) < < std : : endl ;
}
2021-09-28 11:15:54 +00:00
if ( mContentKey . kak_list . size ( ) > 0 & & mCliOutputMode . show_keydata )
2019-01-31 09:10:19 +00:00
{
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 < mContentKey . kak_list . size ( ) ; i + + )
{
std : : cout < < " | " < < std : : dec < < std : : setw ( 3 ) < < std : : setfill ( ' ' ) < < ( uint32_t ) mContentKey . kak_list [ i ] . index < < " | " ;
std : : cout < < fnd : : SimpleTextOutput : : arrayToString ( mContentKey . kak_list [ i ] . enc . key , 16 , true , " : " ) < < " | " ;
if ( mContentKey . kak_list [ i ] . decrypted )
std : : cout < < fnd : : SimpleTextOutput : : arrayToString ( mContentKey . kak_list [ i ] . dec . key , 16 , true , " : " ) ;
else
std : : cout < < " <unable to decrypt> " ;
std : : cout < < " | " < < std : : endl ;
}
std : : cout < < " <---------------------------------------------------------------------------------------------------------> " < < std : : endl ;
}
2021-09-28 11:15:54 +00:00
if ( mCliOutputMode . show_layout )
2019-01-31 09:10:19 +00:00
{
std : : cout < < " Partitions: " < < std : : endl ;
for ( size_t i = 0 ; i < mHdr . getPartitionEntryList ( ) . size ( ) ; i + + )
{
uint32_t index = mHdr . getPartitionEntryList ( ) [ i ] . header_index ;
sPartitionInfo & info = mPartitions [ index ] ;
if ( info . size = = 0 ) continue ;
std : : cout < < " " < < std : : dec < < index < < " : " < < std : : endl ;
std : : cout < < " Offset: 0x " < < std : : hex < < ( uint64_t ) info . offset < < std : : endl ;
std : : cout < < " Size: 0x " < < std : : hex < < ( uint64_t ) info . size < < std : : endl ;
2020-02-26 09:05:21 +00:00
std : : cout < < " Format Type: " < < nn : : hac : : ContentArchiveUtil : : getFormatTypeAsString ( info . format_type ) < < std : : endl ;
std : : cout < < " Hash Type: " < < nn : : hac : : ContentArchiveUtil : : getHashTypeAsString ( info . hash_type ) < < std : : endl ;
std : : cout < < " Enc. Type: " < < nn : : hac : : ContentArchiveUtil : : getEncryptionTypeAsString ( info . enc_type ) < < std : : endl ;
2020-03-17 11:11:28 +00:00
if ( info . enc_type = = nn : : hac : : nca : : EncryptionType : : AesCtr )
2019-01-31 09:10:19 +00:00
{
fnd : : aes : : sAesIvCtr ctr ;
fnd : : aes : : AesIncrementCounter ( info . aes_ctr . iv , info . offset > > 4 , ctr . iv ) ;
std : : cout < < " AesCtr Counter: " < < std : : endl ;
std : : cout < < " " < < fnd : : SimpleTextOutput : : arrayToString ( ctr . iv , sizeof ( fnd : : aes : : sAesIvCtr ) , true , " : " ) < < std : : endl ;
}
2020-03-17 11:11:28 +00:00
if ( info . hash_type = = nn : : hac : : nca : : HashType : : HierarchicalIntegrity )
2019-01-31 09:10:19 +00:00
{
fnd : : LayeredIntegrityMetadata & hash_hdr = info . layered_intergrity_metadata ;
std : : cout < < " HierarchicalIntegrity Header: " < < std : : endl ;
for ( size_t j = 0 ; j < hash_hdr . getHashLayerInfo ( ) . size ( ) ; j + + )
{
std : : cout < < " Hash Layer " < < std : : dec < < j < < " : " < < std : : endl ;
std : : cout < < " Offset: 0x " < < std : : hex < < ( uint64_t ) hash_hdr . getHashLayerInfo ( ) [ j ] . offset < < std : : endl ;
std : : cout < < " Size: 0x " < < std : : hex < < ( uint64_t ) hash_hdr . getHashLayerInfo ( ) [ j ] . size < < std : : endl ;
std : : cout < < " BlockSize: 0x " < < std : : hex < < ( uint32_t ) hash_hdr . getHashLayerInfo ( ) [ j ] . block_size < < std : : endl ;
}
std : : cout < < " Data Layer: " < < std : : endl ;
std : : cout < < " Offset: 0x " < < std : : hex < < ( uint64_t ) hash_hdr . getDataLayer ( ) . offset < < std : : endl ;
std : : cout < < " Size: 0x " < < std : : hex < < ( uint64_t ) hash_hdr . getDataLayer ( ) . size < < std : : endl ;
std : : cout < < " BlockSize: 0x " < < std : : hex < < ( uint32_t ) hash_hdr . getDataLayer ( ) . block_size < < std : : endl ;
for ( size_t j = 0 ; j < hash_hdr . getMasterHashList ( ) . size ( ) ; j + + )
{
std : : cout < < " Master Hash " < < std : : dec < < j < < " : " < < std : : endl ;
std : : cout < < " " < < fnd : : SimpleTextOutput : : arrayToString ( hash_hdr . getMasterHashList ( ) [ j ] . bytes , 0x10 , true , " : " ) < < std : : endl ;
std : : cout < < " " < < fnd : : SimpleTextOutput : : arrayToString ( hash_hdr . getMasterHashList ( ) [ j ] . bytes + 0x10 , 0x10 , true , " : " ) < < std : : endl ;
}
}
2020-03-17 11:11:28 +00:00
else if ( info . hash_type = = nn : : hac : : nca : : HashType : : HierarchicalSha256 )
2019-01-31 09:10:19 +00:00
{
fnd : : LayeredIntegrityMetadata & hash_hdr = info . layered_intergrity_metadata ;
std : : cout < < " HierarchicalSha256 Header: " < < std : : endl ;
std : : cout < < " Master Hash: " < < std : : endl ;
std : : cout < < " " < < fnd : : SimpleTextOutput : : arrayToString ( hash_hdr . getMasterHashList ( ) [ 0 ] . bytes , 0x10 , true , " : " ) < < std : : endl ;
std : : cout < < " " < < fnd : : SimpleTextOutput : : arrayToString ( hash_hdr . getMasterHashList ( ) [ 0 ] . bytes + 0x10 , 0x10 , true , " : " ) < < std : : endl ;
std : : cout < < " HashBlockSize: 0x " < < std : : hex < < ( uint32_t ) hash_hdr . getDataLayer ( ) . block_size < < std : : endl ;
std : : cout < < " Hash Layer: " < < std : : endl ;
std : : cout < < " Offset: 0x " < < std : : hex < < ( uint64_t ) hash_hdr . getHashLayerInfo ( ) [ 0 ] . offset < < std : : endl ;
std : : cout < < " Size: 0x " < < std : : hex < < ( uint64_t ) hash_hdr . getHashLayerInfo ( ) [ 0 ] . size < < std : : endl ;
std : : cout < < " Data Layer: " < < std : : endl ;
std : : cout < < " Offset: 0x " < < std : : hex < < ( uint64_t ) hash_hdr . getDataLayer ( ) . offset < < std : : endl ;
std : : cout < < " Size: 0x " < < std : : hex < < ( uint64_t ) hash_hdr . getDataLayer ( ) . size < < std : : endl ;
}
}
}
}
2021-09-28 11:15:54 +00:00
void nstool : : NcaProcess : : processPartitions ( )
2019-01-31 09:10:19 +00:00
{
for ( size_t i = 0 ; i < mHdr . getPartitionEntryList ( ) . size ( ) ; i + + )
{
uint32_t index = mHdr . getPartitionEntryList ( ) [ i ] . header_index ;
struct sPartitionInfo & partition = mPartitions [ index ] ;
// if the reader is null, skip
if ( * partition . reader = = nullptr )
{
std : : cout < < " [WARNING] NCA Partition " < < std : : dec < < index < < " not readable. " ;
if ( partition . fail_reason . empty ( ) = = false )
{
std : : cout < < " ( " < < partition . fail_reason < < " ) " ;
}
std : : cout < < std : : endl ;
continue ;
}
2021-09-24 10:19:22 +00:00
try {
if ( partition . format_type = = nn : : hac : : nca : : FormatType : : PartitionFs )
2019-01-31 09:10:19 +00:00
{
2021-09-24 10:19:22 +00:00
PfsProcess pfs ;
pfs . setInputFile ( partition . reader ) ;
pfs . setCliOutputMode ( mCliOutputMode ) ;
pfs . setListFs ( mListFs ) ;
if ( mHdr . getContentType ( ) = = nn : : hac : : nca : : ContentType : : Program )
{
pfs . setMountPointName ( std : : string ( getContentTypeForMountStr ( mHdr . getContentType ( ) ) ) + " :/ " + nn : : hac : : ContentArchiveUtil : : getProgramContentParititionIndexAsString ( ( nn : : hac : : nca : : ProgramContentPartitionIndex ) index ) ) ;
}
else
{
pfs . setMountPointName ( std : : string ( getContentTypeForMountStr ( mHdr . getContentType ( ) ) ) + " :/ " ) ;
}
if ( mPartitionPath [ index ] . doExtract )
pfs . setExtractPath ( mPartitionPath [ index ] . path ) ;
pfs . process ( ) ;
2019-01-31 09:10:19 +00:00
}
2021-09-24 10:19:22 +00:00
else if ( partition . format_type = = nn : : hac : : nca : : FormatType : : RomFs )
2019-01-31 09:10:19 +00:00
{
2021-09-24 10:19:22 +00:00
RomfsProcess romfs ;
romfs . setInputFile ( partition . reader ) ;
romfs . setCliOutputMode ( mCliOutputMode ) ;
romfs . setListFs ( mListFs ) ;
if ( mHdr . getContentType ( ) = = nn : : hac : : nca : : ContentType : : Program )
{
romfs . setMountPointName ( std : : string ( getContentTypeForMountStr ( mHdr . getContentType ( ) ) ) + " :/ " + nn : : hac : : ContentArchiveUtil : : getProgramContentParititionIndexAsString ( ( nn : : hac : : nca : : ProgramContentPartitionIndex ) index ) ) ;
}
else
{
romfs . setMountPointName ( std : : string ( getContentTypeForMountStr ( mHdr . getContentType ( ) ) ) + " :/ " ) ;
}
2019-01-31 09:10:19 +00:00
2021-09-24 10:19:22 +00:00
if ( mPartitionPath [ index ] . doExtract )
romfs . setExtractPath ( mPartitionPath [ index ] . path ) ;
romfs . process ( ) ;
}
2021-09-28 11:15:54 +00:00
} catch ( const tc : : Exception & e ) {
2021-09-24 10:19:22 +00:00
std : : cout < < " [WARNING] NCA Partition " < < std : : dec < < index < < " not readable ( " < < e . error ( ) < < " ). " < < std : : endl ;
2019-01-31 09:10:19 +00:00
}
}
}
2021-09-28 11:15:54 +00:00
const char * nstool : : NcaProcess : : getContentTypeForMountStr ( nn : : hac : : nca : : ContentType cont_type ) const
2019-01-31 09:10:19 +00:00
{
const char * str = nullptr ;
switch ( cont_type )
{
2020-03-17 11:11:28 +00:00
case ( nn : : hac : : nca : : ContentType : : Program ) :
2019-01-31 09:10:19 +00:00
str = " program " ;
break ;
2020-03-17 11:11:28 +00:00
case ( nn : : hac : : nca : : ContentType : : Meta ) :
2019-01-31 09:10:19 +00:00
str = " meta " ;
break ;
2020-03-17 11:11:28 +00:00
case ( nn : : hac : : nca : : ContentType : : Control ) :
2019-01-31 09:10:19 +00:00
str = " control " ;
break ;
2020-03-17 11:11:28 +00:00
case ( nn : : hac : : nca : : ContentType : : Manual ) :
2019-01-31 09:10:19 +00:00
str = " manual " ;
break ;
2020-03-17 11:11:28 +00:00
case ( nn : : hac : : nca : : ContentType : : Data ) :
2019-01-31 09:10:19 +00:00
str = " data " ;
break ;
2020-03-17 11:11:28 +00:00
case ( nn : : hac : : nca : : ContentType : : PublicData ) :
2019-01-31 09:10:19 +00:00
str = " publicdata " ;
break ;
default :
str = " " ;
break ;
}
return str ;
2018-08-12 05:23:39 +00:00
}