2018-08-13 17:14:21 +00:00
# include <iostream>
# include <iomanip>
2018-04-24 05:24:20 +00:00
# include <fnd/SimpleTextOutput.h>
2018-10-06 08:45:09 +00:00
# include <fnd/OffsetAdjustedIFile.h>
2020-02-29 01:47:01 +00:00
# include <nn/hac/GameCardUtil.h>
2020-05-23 08:04:05 +00:00
# include <nn/hac/ContentMetaUtil.h>
# include <nn/hac/ContentArchiveUtil.h>
2018-10-27 06:13:53 +00:00
# include "GameCardProcess.h"
2018-04-24 05:24:20 +00:00
2018-10-27 06:13:53 +00:00
GameCardProcess : : GameCardProcess ( ) :
2018-09-23 03:29:22 +00:00
mFile ( ) ,
2018-06-18 15:30:19 +00:00
mCliOutputMode ( _BIT ( OUTPUT_BASIC ) ) ,
2018-06-03 08:48:12 +00:00
mVerify ( false ) ,
mListFs ( false ) ,
2019-11-17 06:26:23 +00:00
mProccessExtendedHeader ( false ) ,
2018-06-03 08:48:12 +00:00
mRootPfs ( ) ,
mExtractInfo ( )
{
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : process ( )
2018-06-03 08:48:12 +00:00
{
2018-08-13 17:14:21 +00:00
importHeader ( ) ;
2018-06-03 08:48:12 +00:00
// validate header signature
if ( mVerify )
validateXciSignature ( ) ;
// display header
2018-06-18 15:30:19 +00:00
if ( _HAS_BIT ( mCliOutputMode , OUTPUT_BASIC ) )
2018-06-03 08:48:12 +00:00
displayHeader ( ) ;
// process root partition
processRootPfs ( ) ;
// process partitions
processPartitionPfs ( ) ;
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : setInputFile ( const fnd : : SharedPtr < fnd : : IFile > & file )
2018-06-03 08:48:12 +00:00
{
mFile = file ;
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : setKeyCfg ( const KeyConfiguration & keycfg )
2018-06-03 08:48:12 +00:00
{
2018-08-21 12:03:19 +00:00
mKeyCfg = keycfg ;
2018-06-03 08:48:12 +00:00
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : setCliOutputMode ( CliOutputMode type )
2018-06-03 08:48:12 +00:00
{
2018-06-18 15:30:19 +00:00
mCliOutputMode = type ;
2018-06-03 08:48:12 +00:00
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : setVerifyMode ( bool verify )
2018-06-03 08:48:12 +00:00
{
mVerify = verify ;
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : setPartitionForExtract ( const std : : string & partition_name , const std : : string & extract_path )
2018-06-03 08:48:12 +00:00
{
2018-06-24 15:01:16 +00:00
mExtractInfo . addElement ( { partition_name , extract_path } ) ;
2018-06-03 08:48:12 +00:00
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : setListFs ( bool list_fs )
2018-06-03 08:48:12 +00:00
{
mListFs = list_fs ;
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : importHeader ( )
2018-08-13 17:14:21 +00:00
{
fnd : : Vec < byte_t > scratch ;
2018-09-23 03:29:22 +00:00
if ( * mFile = = nullptr )
2018-08-13 17:14:21 +00:00
{
throw fnd : : Exception ( kModuleName , " No file reader set. " ) ;
}
2019-11-17 06:26:23 +00:00
// allocate memory for header
scratch . alloc ( sizeof ( nn : : hac : : sSdkGcHeader ) ) ;
2018-08-13 17:14:21 +00:00
2019-11-17 06:26:23 +00:00
// read header region
( * mFile ) - > read ( ( byte_t * ) scratch . data ( ) , 0 , sizeof ( nn : : hac : : sSdkGcHeader ) ) ;
2018-08-21 12:03:19 +00:00
2019-11-17 06:26:23 +00:00
// determine if this is a SDK XCI or a "Community" XCI
if ( ( ( nn : : hac : : sSdkGcHeader * ) scratch . data ( ) ) - > signed_header . header . st_magic . get ( ) = = nn : : hac : : gc : : kGcHeaderStructMagic )
{
mIsTrueSdkXci = true ;
mGcHeaderOffset = sizeof ( nn : : hac : : sGcKeyDataRegion ) ;
}
else if ( ( ( nn : : hac : : sGcHeader_Rsa2048Signed * ) scratch . data ( ) ) - > header . st_magic . get ( ) = = nn : : hac : : gc : : kGcHeaderStructMagic )
{
mIsTrueSdkXci = false ;
mGcHeaderOffset = 0 ;
}
else
{
throw fnd : : Exception ( kModuleName , " GameCard image did not have expected magic bytes " ) ;
}
nn : : hac : : sGcHeader_Rsa2048Signed * hdr_ptr = ( nn : : hac : : sGcHeader_Rsa2048Signed * ) ( scratch . data ( ) + mGcHeaderOffset ) ;
2018-08-13 17:14:21 +00:00
2019-11-17 06:26:23 +00:00
// generate hash of raw header
fnd : : sha : : Sha256 ( ( byte_t * ) & hdr_ptr - > header , sizeof ( nn : : hac : : sGcHeader ) , mHdrHash . bytes ) ;
// save the signature
memcpy ( mHdrSignature , hdr_ptr - > signature , fnd : : rsa : : kRsa2048Size ) ;
// decrypt extended header
fnd : : aes : : sAes128Key header_key ;
if ( mKeyCfg . getXciHeaderKey ( header_key ) )
{
2020-02-29 01:47:01 +00:00
nn : : hac : : GameCardUtil : : decryptXciHeader ( & hdr_ptr - > header , header_key . key ) ;
2019-11-17 06:26:23 +00:00
mProccessExtendedHeader = true ;
}
2018-08-13 17:14:21 +00:00
// deserialise header
2019-11-17 06:26:23 +00:00
mHdr . fromBytes ( ( byte_t * ) & hdr_ptr - > header , sizeof ( nn : : hac : : sGcHeader ) ) ;
2018-08-13 17:14:21 +00:00
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : displayHeader ( )
2018-04-24 05:24:20 +00:00
{
2018-10-27 06:13:53 +00:00
std : : cout < < " [GameCard Header] " < < std : : endl ;
2018-08-13 17:14:21 +00:00
std : : cout < < " CardHeaderVersion: " < < std : : dec < < ( uint32_t ) mHdr . getCardHeaderVersion ( ) < < std : : endl ;
2020-02-29 01:47:01 +00:00
std : : cout < < " RomSize: " < < nn : : hac : : GameCardUtil : : getRomSizeAsString ( ( nn : : hac : : gc : : RomSize ) mHdr . getRomSizeType ( ) ) ;
2018-07-01 11:18:38 +00:00
if ( _HAS_BIT ( mCliOutputMode , OUTPUT_EXTENDED ) )
2018-08-13 17:14:21 +00:00
std : : cout < < " (0x " < < std : : hex < < ( uint32_t ) mHdr . getRomSizeType ( ) < < " ) " ;
std : : cout < < std : : endl ;
std : : cout < < " PackageId: 0x " < < std : : hex < < std : : setw ( 16 ) < < std : : setfill ( ' 0 ' ) < < mHdr . getPackageId ( ) < < std : : endl ;
std : : cout < < " Flags: 0x " < < std : : dec < < ( uint32_t ) mHdr . getFlags ( ) < < std : : endl ;
2018-07-01 11:18:38 +00:00
if ( mHdr . getFlags ( ) ! = 0 )
{
for ( uint32_t i = 0 ; i < 8 ; i + + )
{
if ( _HAS_BIT ( mHdr . getFlags ( ) , i ) )
{
2020-02-29 01:47:01 +00:00
std : : cout < < " " < < nn : : hac : : GameCardUtil : : getHeaderFlagsAsString ( ( nn : : hac : : gc : : HeaderFlags ) i ) < < std : : endl ;
2018-07-01 11:18:38 +00:00
}
}
}
2018-06-18 15:30:19 +00:00
if ( _HAS_BIT ( mCliOutputMode , OUTPUT_EXTENDED ) )
{
2020-05-23 08:04:05 +00:00
std : : cout < < " KekIndex: " < < nn : : hac : : GameCardUtil : : getKekIndexAsString ( ( nn : : hac : : gc : : KekIndex ) mHdr . getKekIndex ( ) ) < < " ( " < < std : : dec < < ( uint32_t ) mHdr . getKekIndex ( ) < < " ) " < < std : : endl ;
std : : cout < < " TitleKeyDecIndex: " < < std : : dec < < ( uint32_t ) mHdr . getTitleKeyDecIndex ( ) < < std : : endl ;
2018-08-13 17:14:21 +00:00
std : : cout < < " InitialData: " < < std : : endl ;
std : : cout < < " Hash: " < < std : : endl ;
2018-08-24 08:10:03 +00:00
std : : cout < < " " < < fnd : : SimpleTextOutput : : arrayToString ( mHdr . getInitialDataHash ( ) . bytes , 0x10 , true , " : " ) < < std : : endl ;
std : : cout < < " " < < fnd : : SimpleTextOutput : : arrayToString ( mHdr . getInitialDataHash ( ) . bytes + 0x10 , 0x10 , true , " : " ) < < std : : endl ;
2018-06-18 15:30:19 +00:00
}
if ( _HAS_BIT ( mCliOutputMode , OUTPUT_EXTENDED ) )
{
2018-08-24 08:10:03 +00:00
std : : cout < < " Extended Header AesCbc IV: " < < std : : endl ;
std : : cout < < " " < < fnd : : SimpleTextOutput : : arrayToString ( mHdr . getAesCbcIv ( ) . iv , sizeof ( mHdr . getAesCbcIv ( ) . iv ) , true , " : " ) < < std : : endl ;
2018-06-18 15:30:19 +00:00
}
2018-08-13 17:14:21 +00:00
std : : cout < < " SelSec: 0x " < < std : : hex < < mHdr . getSelSec ( ) < < std : : endl ;
std : : cout < < " SelT1Key: 0x " < < std : : hex < < mHdr . getSelT1Key ( ) < < std : : endl ;
std : : cout < < " SelKey: 0x " < < std : : hex < < mHdr . getSelKey ( ) < < std : : endl ;
2018-06-18 15:30:19 +00:00
if ( _HAS_BIT ( mCliOutputMode , OUTPUT_LAYOUT ) )
{
2018-08-13 17:14:21 +00:00
std : : cout < < " RomAreaStartPage: 0x " < < std : : hex < < mHdr . getRomAreaStartPage ( ) ;
2018-06-18 15:30:19 +00:00
if ( mHdr . getRomAreaStartPage ( ) ! = ( uint32_t ) ( - 1 ) )
2020-02-29 01:47:01 +00:00
std : : cout < < " (0x " < < std : : hex < < nn : : hac : : GameCardUtil : : blockToAddr ( mHdr . getRomAreaStartPage ( ) ) < < " ) " ;
2018-08-13 17:14:21 +00:00
std : : cout < < std : : endl ;
2018-06-18 15:30:19 +00:00
2018-08-13 17:14:21 +00:00
std : : cout < < " BackupAreaStartPage: 0x " < < std : : hex < < mHdr . getBackupAreaStartPage ( ) ;
2018-06-18 15:30:19 +00:00
if ( mHdr . getBackupAreaStartPage ( ) ! = ( uint32_t ) ( - 1 ) )
2020-02-29 01:47:01 +00:00
std : : cout < < " (0x " < < std : : hex < < nn : : hac : : GameCardUtil : : blockToAddr ( mHdr . getBackupAreaStartPage ( ) ) < < " ) " ;
2018-08-13 17:14:21 +00:00
std : : cout < < std : : endl ;
2018-04-24 05:24:20 +00:00
2018-08-13 17:14:21 +00:00
std : : cout < < " ValidDataEndPage: 0x " < < std : : hex < < mHdr . getValidDataEndPage ( ) ;
2018-06-18 15:30:19 +00:00
if ( mHdr . getValidDataEndPage ( ) ! = ( uint32_t ) ( - 1 ) )
2020-02-29 01:47:01 +00:00
std : : cout < < " (0x " < < std : : hex < < nn : : hac : : GameCardUtil : : blockToAddr ( mHdr . getValidDataEndPage ( ) ) < < " ) " ;
2018-08-13 17:14:21 +00:00
std : : cout < < std : : endl ;
2018-04-24 05:24:20 +00:00
2018-08-13 17:14:21 +00:00
std : : cout < < " LimArea: 0x " < < std : : hex < < mHdr . getLimAreaPage ( ) ;
2018-06-18 15:30:19 +00:00
if ( mHdr . getLimAreaPage ( ) ! = ( uint32_t ) ( - 1 ) )
2020-02-29 01:47:01 +00:00
std : : cout < < " (0x " < < std : : hex < < nn : : hac : : GameCardUtil : : blockToAddr ( mHdr . getLimAreaPage ( ) ) < < " ) " ;
2018-08-13 17:14:21 +00:00
std : : cout < < std : : endl ;
2018-06-18 15:30:19 +00:00
2018-08-13 17:14:21 +00:00
std : : cout < < " PartitionFs Header: " < < std : : endl ;
std : : cout < < " Offset: 0x " < < std : : hex < < mHdr . getPartitionFsAddress ( ) < < std : : endl ;
std : : cout < < " Size: 0x " < < std : : hex < < mHdr . getPartitionFsSize ( ) < < std : : endl ;
2018-06-18 15:30:19 +00:00
if ( _HAS_BIT ( mCliOutputMode , OUTPUT_EXTENDED ) )
{
2018-08-13 17:14:21 +00:00
std : : cout < < " Hash: " < < std : : endl ;
2018-08-24 08:10:03 +00:00
std : : cout < < " " < < fnd : : SimpleTextOutput : : arrayToString ( mHdr . getPartitionFsHash ( ) . bytes , 0x10 , true , " : " ) < < std : : endl ;
std : : cout < < " " < < fnd : : SimpleTextOutput : : arrayToString ( mHdr . getPartitionFsHash ( ) . bytes + 0x10 , 0x10 , true , " : " ) < < std : : endl ;
2018-06-18 15:30:19 +00:00
}
}
2018-07-01 11:18:38 +00:00
2019-11-17 06:26:23 +00:00
if ( mProccessExtendedHeader )
2018-07-01 11:18:38 +00:00
{
2018-10-27 06:13:53 +00:00
std : : cout < < " [GameCard Extended Header] " < < std : : endl ;
2020-05-17 04:30:14 +00:00
std : : cout < < " FwVersion: v " < < std : : dec < < mHdr . getFwVersion ( ) < < " ( " < < nn : : hac : : GameCardUtil : : getCardFwVersionDescriptionAsString ( ( nn : : hac : : gc : : FwVersion ) mHdr . getFwVersion ( ) ) < < " ) " < < std : : endl ;
2018-08-13 17:14:21 +00:00
std : : cout < < " AccCtrl1: 0x " < < std : : hex < < mHdr . getAccCtrl1 ( ) < < std : : endl ;
2020-02-29 01:47:01 +00:00
std : : cout < < " CardClockRate: " < < nn : : hac : : GameCardUtil : : getCardClockRateAsString ( ( nn : : hac : : gc : : CardClockRate ) mHdr . getAccCtrl1 ( ) ) < < std : : endl ;
2018-08-13 17:14:21 +00:00
std : : cout < < " Wait1TimeRead: 0x " < < std : : hex < < mHdr . getWait1TimeRead ( ) < < std : : endl ;
std : : cout < < " Wait2TimeRead: 0x " < < std : : hex < < mHdr . getWait2TimeRead ( ) < < std : : endl ;
std : : cout < < " Wait1TimeWrite: 0x " < < std : : hex < < mHdr . getWait1TimeWrite ( ) < < std : : endl ;
std : : cout < < " Wait2TimeWrite: 0x " < < std : : hex < < mHdr . getWait2TimeWrite ( ) < < std : : endl ;
2020-05-23 08:04:05 +00:00
std : : cout < < " SdkAddon Version: " < < nn : : hac : : ContentArchiveUtil : : getSdkAddonVersionAsString ( mHdr . getFwMode ( ) ) < < " (v " < < std : : dec < < mHdr . getFwMode ( ) < < " ) " < < std : : endl ;
2020-05-17 04:30:14 +00:00
std : : cout < < " CompatibilityType: " < < nn : : hac : : GameCardUtil : : getCompatibilityTypeAsString ( ( nn : : hac : : gc : : CompatibilityType ) mHdr . getCompatibilityType ( ) ) < < " ( " < < std : : dec < < ( uint32_t ) mHdr . getCompatibilityType ( ) < < " ) " < < std : : endl ;
2018-08-13 17:14:21 +00:00
std : : cout < < " Update Partition Info: " < < std : : endl ;
2020-05-23 08:04:05 +00:00
std : : cout < < " CUP Version: " < < nn : : hac : : ContentMetaUtil : : getVersionAsString ( mHdr . getUppVersion ( ) ) < < " (v " < < std : : dec < < mHdr . getUppVersion ( ) < < " ) " < < std : : endl ;
2018-08-13 17:14:21 +00:00
std : : cout < < " CUP TitleId: 0x " < < std : : hex < < std : : setw ( 16 ) < < std : : setfill ( ' 0 ' ) < < mHdr . getUppId ( ) < < std : : endl ;
2020-05-17 04:30:14 +00:00
std : : cout < < " CUP Digest: " < < fnd : : SimpleTextOutput : : arrayToString ( mHdr . getUppHash ( ) , 8 , true , " : " ) < < std : : endl ;
2018-08-13 17:14:21 +00:00
}
2018-04-24 05:24:20 +00:00
}
2020-05-17 04:30:14 +00:00
bool GameCardProcess : : validateRegionOfFile ( size_t offset , size_t len , const byte_t * test_hash , bool use_salt , byte_t salt )
2018-04-24 05:24:20 +00:00
{
2018-06-24 15:01:16 +00:00
fnd : : Vec < byte_t > scratch ;
2018-08-07 08:35:03 +00:00
fnd : : sha : : sSha256Hash calc_hash ;
2020-05-17 04:30:14 +00:00
if ( use_salt )
{
scratch . alloc ( len + 1 ) ;
scratch . data ( ) [ len ] = salt ;
}
else
{
scratch . alloc ( len ) ;
}
( * mFile ) - > read ( scratch . data ( ) , offset , len ) ;
2018-08-07 08:35:03 +00:00
fnd : : sha : : Sha256 ( scratch . data ( ) , scratch . size ( ) , calc_hash . bytes ) ;
2020-05-17 04:30:14 +00:00
2018-04-24 05:24:20 +00:00
return calc_hash . compare ( test_hash ) ;
}
2020-05-17 04:30:14 +00:00
bool GameCardProcess : : validateRegionOfFile ( size_t offset , size_t len , const byte_t * test_hash )
{
return validateRegionOfFile ( offset , len , test_hash , false , 0 ) ;
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : validateXciSignature ( )
2018-04-24 05:24:20 +00:00
{
2018-08-21 12:03:19 +00:00
fnd : : rsa : : sRsa2048Key header_sign_key ;
2019-11-17 06:26:23 +00:00
2018-08-21 12:03:19 +00:00
mKeyCfg . getXciHeaderSignKey ( header_sign_key ) ;
2019-11-17 06:26:23 +00:00
if ( fnd : : rsa : : pkcs : : rsaVerify ( header_sign_key , fnd : : sha : : HASH_SHA256 , mHdrHash . bytes , mHdrSignature ) ! = 0 )
2018-04-24 05:24:20 +00:00
{
2018-10-27 06:13:53 +00:00
std : : cout < < " [WARNING] GameCard Header Signature: FAIL " < < std : : endl ;
2018-04-24 05:24:20 +00:00
}
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : processRootPfs ( )
2018-04-24 05:24:20 +00:00
{
2020-05-17 04:30:14 +00:00
if ( mVerify & & validateRegionOfFile ( mHdr . getPartitionFsAddress ( ) , mHdr . getPartitionFsSize ( ) , mHdr . getPartitionFsHash ( ) . bytes , mHdr . getCompatibilityType ( ) ! = nn : : hac : : gc : : COMPAT_GLOBAL , mHdr . getCompatibilityType ( ) ) = = false )
2018-04-24 05:24:20 +00:00
{
2018-10-27 06:13:53 +00:00
std : : cout < < " [WARNING] GameCard Root HFS0: FAIL (bad hash) " < < std : : endl ;
2018-04-24 05:24:20 +00:00
}
2018-10-06 08:45:09 +00:00
mRootPfs . setInputFile ( new fnd : : OffsetAdjustedIFile ( mFile , mHdr . getPartitionFsAddress ( ) , mHdr . getPartitionFsSize ( ) ) ) ;
2018-04-24 05:24:20 +00:00
mRootPfs . setListFs ( mListFs ) ;
2018-06-06 13:27:46 +00:00
mRootPfs . setVerifyMode ( false ) ;
2018-06-18 15:30:19 +00:00
mRootPfs . setCliOutputMode ( mCliOutputMode ) ;
2018-04-24 05:24:20 +00:00
mRootPfs . setMountPointName ( kXciMountPointName ) ;
mRootPfs . process ( ) ;
}
2018-10-27 06:13:53 +00:00
void GameCardProcess : : processPartitionPfs ( )
2018-04-24 05:24:20 +00:00
{
2018-10-27 05:43:57 +00:00
const fnd : : List < nn : : hac : : PartitionFsHeader : : sFile > & rootPartitions = mRootPfs . getPfsHeader ( ) . getFileList ( ) ;
2018-06-24 15:01:16 +00:00
for ( size_t i = 0 ; i < rootPartitions . size ( ) ; i + + )
2018-06-06 13:27:46 +00:00
{
// this must be validated here because only the size of the root partiton header is known at verification time
if ( mVerify & & validateRegionOfFile ( mHdr . getPartitionFsAddress ( ) + rootPartitions [ i ] . offset , rootPartitions [ i ] . hash_protected_size , rootPartitions [ i ] . hash . bytes ) = = false )
{
2018-10-27 06:13:53 +00:00
std : : cout < < " [WARNING] GameCard " < < rootPartitions [ i ] . name < < " Partition HFS0: FAIL (bad hash) " < < std : : endl ;
2018-06-06 13:27:46 +00:00
}
2018-04-24 05:24:20 +00:00
PfsProcess tmp ;
2018-10-06 08:45:09 +00:00
tmp . setInputFile ( new fnd : : OffsetAdjustedIFile ( mFile , mHdr . getPartitionFsAddress ( ) + rootPartitions [ i ] . offset , rootPartitions [ i ] . size ) ) ;
2018-04-24 05:24:20 +00:00
tmp . setListFs ( mListFs ) ;
tmp . setVerifyMode ( mVerify ) ;
2018-06-18 15:30:19 +00:00
tmp . setCliOutputMode ( mCliOutputMode ) ;
2018-04-24 05:24:20 +00:00
tmp . setMountPointName ( kXciMountPointName + rootPartitions [ i ] . name ) ;
2018-06-24 15:01:16 +00:00
if ( mExtractInfo . hasElement < std : : string > ( rootPartitions [ i ] . name ) )
tmp . setExtractPath ( mExtractInfo . getElement < std : : string > ( rootPartitions [ i ] . name ) . extract_path ) ;
2018-04-24 05:24:20 +00:00
tmp . process ( ) ;
}
2020-02-29 01:47:01 +00:00
}