@ -102,145 +102,62 @@ bool IsValidNCA(const NCAHeader& header) {
return header . magic = = Common : : MakeMagic ( ' N ' , ' C ' , ' A ' , ' 3 ' ) ;
}
u8 NCA : : GetCryptoRevision ( ) const {
u8 master_key_id = header . crypto_type ;
if ( header . crypto_type_2 > master_key_id )
master_key_id = header . crypto_type_2 ;
if ( master_key_id > 0 )
- - master_key_id ;
return master_key_id ;
}
boost : : optional < Core : : Crypto : : Key128 > NCA : : GetKeyAreaKey ( NCASectionCryptoType type ) const {
const auto master_key_id = GetCryptoRevision ( ) ;
if ( ! keys . HasKey ( Core : : Crypto : : S128KeyType : : KeyArea , master_key_id , header . key_index ) )
return boost : : none ;
std : : vector < u8 > key_area ( header . key_area . begin ( ) , header . key_area . end ( ) ) ;
Core : : Crypto : : AESCipher < Core : : Crypto : : Key128 > cipher (
keys . GetKey ( Core : : Crypto : : S128KeyType : : KeyArea , master_key_id , header . key_index ) ,
Core : : Crypto : : Mode : : ECB ) ;
cipher . Transcode ( key_area . data ( ) , key_area . size ( ) , key_area . data ( ) , Core : : Crypto : : Op : : Decrypt ) ;
Core : : Crypto : : Key128 out ;
if ( type = = NCASectionCryptoType : : XTS )
std : : copy ( key_area . begin ( ) , key_area . begin ( ) + 0x10 , out . begin ( ) ) ;
else if ( type = = NCASectionCryptoType : : CTR | | type = = NCASectionCryptoType : : BKTR )
std : : copy ( key_area . begin ( ) + 0x20 , key_area . begin ( ) + 0x30 , out . begin ( ) ) ;
else
LOG_CRITICAL ( Crypto , " Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X} " ,
static_cast < u8 > ( type ) ) ;
u128 out_128 { } ;
memcpy ( out_128 . data ( ) , out . data ( ) , 16 ) ;
LOG_TRACE ( Crypto , " called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X} " ,
master_key_id , header . key_index , out_128 [ 1 ] , out_128 [ 0 ] ) ;
return out ;
}
boost : : optional < Core : : Crypto : : Key128 > NCA : : GetTitlekey ( ) {
const auto master_key_id = GetCryptoRevision ( ) ;
u128 rights_id { } ;
memcpy ( rights_id . data ( ) , header . rights_id . data ( ) , 16 ) ;
if ( rights_id = = u128 { } ) {
status = Loader : : ResultStatus : : ErrorInvalidRightsID ;
return boost : : none ;
NCA : : NCA ( VirtualFile file_ , VirtualFile bktr_base_romfs_ , u64 bktr_base_ivfc_offset )
: file ( std : : move ( file_ ) ) ,
bktr_base_romfs ( bktr_base_romfs_ ? std : : move ( bktr_base_romfs_ ) : nullptr ) {
if ( file = = nullptr ) {
status = Loader : : ResultStatus : : ErrorNullFile ;
return ;
}
auto titlekey = keys . GetKey ( Core : : Crypto : : S128KeyType : : Titlekey , rights_id [ 1 ] , rights_id [ 0 ] ) ;
if ( titlekey = = Core : : Crypto : : Key128 { } ) {
status = Loader : : ResultStatus : : Error MissingTitlekey ;
return boost : : none ;
if ( sizeof ( NCAHeader ) ! = file - > ReadObject ( & header ) ) {
LOG_ERROR ( Loader , " File reader errored out during header read. " ) ;
status = Loader : : ResultStatus : : ErrorBadNCAHeader ;
return ;
}
if ( ! keys . HasKey ( Core : : Crypto : : S128KeyType : : Titlekek , master_key_id ) ) {
status = Loader : : ResultStatus : : ErrorMissingTitlekek ;
return boost : : none ;
if ( ! HandlePotentialHeaderDecryption ( ) ) {
return ;
}
Core : : Crypto : : AESCipher < Core : : Crypto : : Key128 > cipher (
keys . GetKey ( Core : : Crypto : : S128KeyType : : Titlekek , master_key_id ) , Core : : Crypto : : Mode : : ECB ) ;
cipher . Transcode ( titlekey . data ( ) , titlekey . size ( ) , titlekey . data ( ) , Core : : Crypto : : Op : : Decrypt ) ;
return titlekey ;
}
has_rights_id = std : : find_if_not ( header . rights_id . begin ( ) , header . rights_id . end ( ) ,
[ ] ( char c ) { return c = = ' \0 ' ; } ) ! = header . rights_id . end ( ) ;
VirtualFile NCA : : Decrypt ( const NCASectionHeader & s_header , VirtualFile in , u64 starting_offset ) {
if ( ! encrypted )
return in ;
const std : : vector < NCASectionHeader > sections = ReadSectionHeaders ( ) ;
is_update = std : : any_of ( sections . begin ( ) , sections . end ( ) , [ ] ( const NCASectionHeader & header ) {
return header . raw . header . crypto_type = = NCASectionCryptoType : : BKTR ;
} ) ;
switch ( s_header . raw . header . crypto_type ) {
case NCASectionCryptoType : : NONE :
LOG_DEBUG ( Crypto , " called with mode=NONE " ) ;
return in ;
case NCASectionCryptoType : : CTR :
// During normal BKTR decryption, this entire function is skipped. This is for the metadata,
// which uses the same CTR as usual.
case NCASectionCryptoType : : BKTR :
LOG_DEBUG ( Crypto , " called with mode=CTR, starting_offset={:016X} " , starting_offset ) ;
{
boost : : optional < Core : : Crypto : : Key128 > key = boost : : none ;
if ( has_rights_id ) {
status = Loader : : ResultStatus : : Success ;
key = GetTitlekey ( ) ;
if ( key = = boost : : none ) {
if ( status = = Loader : : ResultStatus : : Success )
status = Loader : : ResultStatus : : ErrorMissingTitlekey ;
return nullptr ;
}
} else {
key = GetKeyAreaKey ( NCASectionCryptoType : : CTR ) ;
if ( key = = boost : : none ) {
status = Loader : : ResultStatus : : ErrorMissingKeyAreaKey ;
return nullptr ;
}
if ( ! ReadSections ( sections , bktr_base_ivfc_offset ) ) {
return ;
}
auto out = std : : make_shared < Core : : Crypto : : CTREncryptionLayer > (
std : : move ( in ) , key . value ( ) , starting_offset ) ;
std : : vector < u8 > iv ( 16 ) ;
for ( u8 i = 0 ; i < 8 ; + + i )
iv [ i ] = s_header . raw . section_ctr [ 0x8 - i - 1 ] ;
out - > SetIV ( iv ) ;
return std : : static_pointer_cast < VfsFile > ( out ) ;
}
case NCASectionCryptoType : : XTS :
// TODO(DarkLordZach): Find a test case for XTS-encrypted NCAs
default :
LOG_ERROR ( Crypto , " called with unhandled crypto type={:02X} " ,
static_cast < u8 > ( s_header . raw . header . crypto_type ) ) ;
return nullptr ;
}
status = Loader : : ResultStatus : : Success ;
}
NCA : : NCA ( VirtualFile file_ , VirtualFile bktr_base_romfs_ , u64 bktr_base_ivfc_offset )
: file ( std : : move ( file_ ) ) ,
bktr_base_romfs ( bktr_base_romfs_ ? std : : move ( bktr_base_romfs_ ) : nullptr ) {
status = Loader : : ResultStatus : : Success ;
NCA : : ~ NCA ( ) = default ;
if ( file = = nullptr ) {
status = Loader : : ResultStatus : : ErrorNullFile ;
return ;
bool NCA : : CheckSupportedNCA ( const NCAHeader & nca_header ) {
if ( nca_header . magic = = Common : : MakeMagic ( ' N ' , ' C ' , ' A ' , ' 2 ' ) ) {
status = Loader : : ResultStatus : : ErrorNCA2 ;
return false ;
}
if ( sizeof ( NCAHeader ) ! = file - > ReadObject ( & header ) ) {
LOG_ERROR ( Loader , " File reader errored out during header read. " ) ;
status = Loader : : ResultStatus : : ErrorBadNCAHeader ;
return ;
if ( nca_header . magic = = Common : : MakeMagic ( ' N ' , ' C ' , ' A ' , ' 0 ' ) ) {
status = Loader : : ResultStatus : : ErrorNCA0 ;
return false ;
}
encrypted = false ;
return true ;
}
if ( ! IsValidNCA ( header ) ) {
if ( header . magic = = Common : : MakeMagic ( ' N ' , ' C ' , ' A ' , ' 2 ' ) ) {
status = Loader : : ResultStatus : : ErrorNCA2 ;
return ;
bool NCA : : HandlePotentialHeaderDecryption ( ) {
if ( IsValidNCA ( header ) ) {
return true ;
}
if ( header . magic = = Common : : MakeMagic ( ' N ' , ' C ' , ' A ' , ' 0 ' ) ) {
status = Loader : : ResultStatus : : ErrorNCA0 ;
return ;
if ( ! CheckSupportedNCA ( header ) ) {
return false ;
}
NCAHeader dec_header { } ;
@ -252,26 +169,22 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
header = dec_header ;
encrypted = true ;
} else {
if ( dec_header . magic = = Common : : MakeMagic ( ' N ' , ' C ' , ' A ' , ' 2 ' ) ) {
status = Loader : : ResultStatus : : ErrorNCA2 ;
return ;
}
if ( dec_header . magic = = Common : : MakeMagic ( ' N ' , ' C ' , ' A ' , ' 0 ' ) ) {
status = Loader : : ResultStatus : : ErrorNCA0 ;
return ;
if ( ! CheckSupportedNCA ( dec_header ) ) {
return false ;
}
if ( ! keys . HasKey ( Core : : Crypto : : S256KeyType : : Header ) )
status = Loader : : ResultStatus : : ErrorMissingHeaderKey ;
else
if ( keys . HasKey ( Core : : Crypto : : S256KeyType : : Header ) ) {
status = Loader : : ResultStatus : : ErrorIncorrectHeaderKey ;
return ;
} else {
status = Loader : : ResultStatus : : ErrorMissingHeaderKey ;
}
return false ;
}
has_rights_id = std : : find_if_not ( header . rights_id . begin ( ) , header . rights_id . end ( ) ,
[ ] ( char c ) { return c = = ' \0 ' ; } ) ! = header . rights_id . end ( ) ;
return true ;
}
std : : vector < NCASectionHeader > NCA : : ReadSectionHeaders ( ) const {
const std : : ptrdiff_t number_sections =
std : : count_if ( std : : begin ( header . section_tables ) , std : : end ( header . section_tables ) ,
[ ] ( NCASectionTableEntry entry ) { return entry . media_offset > 0 ; } ) ;
@ -289,17 +202,30 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
file - > ReadBytes ( sections . data ( ) , length_sections , SECTION_HEADER_OFFSET ) ;
}
is_update = std : : find_if ( sections . begin ( ) , sections . end ( ) , [ ] ( const NCASectionHeader & header ) {
return header . raw . header . crypto_type = = NCASectionCryptoType : : BKTR ;
} ) ! = sections . end ( ) ;
ivfc_offset = 0 ;
return sections ;
}
for ( std : : ptrdiff_t i = 0 ; i < number_sections ; + + i ) {
bool NCA : : ReadSections ( const std : : vector < NCASectionHeader > & sections , u64 bktr_base_ivfc_offset ) {
for ( std : : size_t i = 0 ; i < sections . size ( ) ; + + i ) {
const auto & section = sections [ i ] ;
if ( section . raw . header . filesystem_type = = NCASectionFilesystemType : : ROMFS ) {
const std : : size_t base_offset =
header . section_tables [ i ] . media_offset * MEDIA_OFFSET_MULTIPLIER ;
if ( ! ReadRomFSSection ( section , header . section_tables [ i ] , bktr_base_ivfc_offset ) ) {
return false ;
}
} else if ( section . raw . header . filesystem_type = = NCASectionFilesystemType : : PFS0 ) {
if ( ! ReadPFS0Section ( section , header . section_tables [ i ] ) ) {
return false ;
}
}
}
return true ;
}
bool NCA : : ReadRomFSSection ( const NCASectionHeader & section , const NCASectionTableEntry & entry ,
u64 bktr_base_ivfc_offset ) {
const std : : size_t base_offset = entry . media_offset * MEDIA_OFFSET_MULTIPLIER ;
ivfc_offset = section . romfs . ivfc . levels [ IVFC_MAX_LEVEL - 1 ] . offset ;
const std : : size_t romfs_offset = base_offset + ivfc_offset ;
const std : : size_t romfs_size = section . romfs . ivfc . levels [ IVFC_MAX_LEVEL - 1 ] . size ;
@ -308,33 +234,31 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
if ( dec = = nullptr ) {
if ( status ! = Loader : : ResultStatus : : Success )
return ;
return false ;
if ( has_rights_id )
status = Loader : : ResultStatus : : ErrorIncorrectTitlekeyOrTitlekek ;
else
status = Loader : : ResultStatus : : ErrorIncorrectKeyAreaKey ;
return ;
return false ;
}
if ( section . raw . header . crypto_type = = NCASectionCryptoType : : BKTR ) {
if ( section . bktr . relocation . magic ! = Common : : MakeMagic ( ' B ' , ' K ' , ' T ' , ' R ' ) | |
section . bktr . subsection . magic ! = Common : : MakeMagic ( ' B ' , ' K ' , ' T ' , ' R ' ) ) {
status = Loader : : ResultStatus : : ErrorBadBKTRHeader ;
return ;
return false ;
}
if ( section . bktr . relocation . offset + section . bktr . relocation . size ! =
section . bktr . subsection . offset ) {
status = Loader : : ResultStatus : : ErrorBKTRSubsectionNotAfterRelocation ;
return ;
return false ;
}
const u64 size =
MEDIA_OFFSET_MULTIPLIER * ( header . section_tables [ i ] . media_end_offset -
header . section_tables [ i ] . media_offset ) ;
const u64 size = MEDIA_OFFSET_MULTIPLIER * ( entry . media_end_offset - entry . media_offset ) ;
if ( section . bktr . subsection . offset + section . bktr . subsection . size ! = size ) {
status = Loader : : ResultStatus : : ErrorBKTRSubsectionNotAtEnd ;
return ;
return false ;
}
const u64 offset = section . romfs . ivfc . levels [ IVFC_MAX_LEVEL - 1 ] . offset ;
@ -342,37 +266,33 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
if ( dec - > ReadObject ( & relocation_block , section . bktr . relocation . offset - offset ) ! =
sizeof ( RelocationBlock ) ) {
status = Loader : : ResultStatus : : ErrorBadRelocationBlock ;
return ;
return false ;
}
SubsectionBlock subsection_block { } ;
if ( dec - > ReadObject ( & subsection_block , section . bktr . subsection . offset - offset ) ! =
sizeof ( RelocationBlock ) ) {
status = Loader : : ResultStatus : : ErrorBadSubsectionBlock ;
return ;
return false ;
}
std : : vector < RelocationBucketRaw > relocation_buckets_raw (
( section . bktr . relocation . size - sizeof ( RelocationBlock ) ) /
sizeof ( RelocationBucketRaw ) ) ;
( section . bktr . relocation . size - sizeof ( RelocationBlock ) ) / sizeof ( RelocationBucketRaw ) ) ;
if ( dec - > ReadBytes ( relocation_buckets_raw . data ( ) ,
section . bktr . relocation . size - sizeof ( RelocationBlock ) ,
section . bktr . relocation . offset + sizeof ( RelocationBlock ) -
offset ) ! =
section . bktr . relocation . offset + sizeof ( RelocationBlock ) - offset ) ! =
section . bktr . relocation . size - sizeof ( RelocationBlock ) ) {
status = Loader : : ResultStatus : : ErrorBadRelocationBuckets ;
return ;
return false ;
}
std : : vector < SubsectionBucketRaw > subsection_buckets_raw (
( section . bktr . subsection . size - sizeof ( SubsectionBlock ) ) /
sizeof ( SubsectionBucketRaw ) ) ;
( section . bktr . subsection . size - sizeof ( SubsectionBlock ) ) / sizeof ( SubsectionBucketRaw ) ) ;
if ( dec - > ReadBytes ( subsection_buckets_raw . data ( ) ,
section . bktr . subsection . size - sizeof ( SubsectionBlock ) ,
section . bktr . subsection . offset + sizeof ( SubsectionBlock ) -
offset ) ! =
section . bktr . subsection . offset + sizeof ( SubsectionBlock ) - offset ) ! =
section . bktr . subsection . size - sizeof ( SubsectionBlock ) ) {
status = Loader : : ResultStatus : : ErrorBadSubsectionBuckets ;
return ;
return false ;
}
std : : vector < RelocationBucket > relocation_buckets ( relocation_buckets_raw . size ( ) ) ;
@ -384,8 +304,7 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
u32 ctr_low ;
std : : memcpy ( & ctr_low , section . raw . section_ctr . data ( ) , sizeof ( ctr_low ) ) ;
subsection_buckets . back ( ) . entries . push_back (
{ section . bktr . relocation . offset , { 0 } , ctr_low } ) ;
subsection_buckets . back ( ) . entries . push_back ( { section . bktr . relocation . offset , { 0 } , ctr_low } ) ;
subsection_buckets . back ( ) . entries . push_back ( { size , { 0 } , 0 } ) ;
boost : : optional < Core : : Crypto : : Key128 > key = boost : : none ;
@ -395,45 +314,45 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
key = GetTitlekey ( ) ;
if ( key = = boost : : none ) {
status = Loader : : ResultStatus : : ErrorMissingTitlekey ;
return ;
return false ;
}
} else {
key = GetKeyAreaKey ( NCASectionCryptoType : : BKTR ) ;
if ( key = = boost : : none ) {
status = Loader : : ResultStatus : : ErrorMissingKeyAreaKey ;
return ;
return false ;
}
}
}
if ( bktr_base_romfs = = nullptr ) {
status = Loader : : ResultStatus : : ErrorMissingBKTRBaseRomFS ;
return ;
return false ;
}
auto bktr = std : : make_shared < BKTR > (
bktr_base_romfs , std : : make_shared < OffsetVfsFile > ( file , romfs_size , base_offset ) ,
relocation_block , relocation_buckets , subsection_block , subsection_buckets ,
encrypted , encrypted ? key . get ( ) : Core : : Crypto : : Key128 { } , base_offset ,
bktr_base_ivfc_offset , section . raw . section_ctr ) ;
relocation_block , relocation_buckets , subsection_block , subsection_buckets , encrypted ,
encrypted ? key . get ( ) : Core : : Crypto : : Key128 { } , base_offset , bktr_base_ivfc_offset ,
section . raw . section_ctr ) ;
// BKTR applies to entire IVFC, so make an offset version to level 6
files . push_back ( std : : make_shared < OffsetVfsFile > (
bktr , romfs_size , section . romfs . ivfc . levels [ IVFC_MAX_LEVEL - 1 ] . offset ) ) ;
romfs = files . back ( ) ;
} else {
files . push_back ( std : : move ( dec ) ) ;
romfs = files . back ( ) ;
}
} else if ( section . raw . header . filesystem_type = = NCASectionFilesystemType : : PFS0 ) {
u64 offset = ( static_cast < u64 > ( header . section_tables [ i ] . media_offset ) *
MEDIA_OFFSET_MULTIPLIER ) +
romfs = files . back ( ) ;
return true ;
}
bool NCA : : ReadPFS0Section ( const NCASectionHeader & section , const NCASectionTableEntry & entry ) {
const u64 offset = ( static_cast < u64 > ( entry . media_offset ) * MEDIA_OFFSET_MULTIPLIER ) +
section . pfs0 . pfs0_header_offset ;
u64 size = MEDIA_OFFSET_MULTIPLIER * ( header . section_tables [ i ] . media_end_offset -
header . section_tables [ i ] . media_offset ) ;
auto dec =
Decrypt ( section , std : : make_shared < OffsetVfsFile > ( file , size , offset ) , offset ) ;
const u64 size = MEDIA_OFFSET_MULTIPLIER * ( entry . media_end_offset - entry . media_offset ) ;
auto dec = Decrypt ( section , std : : make_shared < OffsetVfsFile > ( file , size , offset ) , offset ) ;
if ( dec ! = nullptr ) {
auto npfs = std : : make_shared < PartitionFilesystem > ( std : : move ( dec ) ) ;
@ -446,24 +365,133 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
status = Loader : : ResultStatus : : ErrorIncorrectTitlekeyOrTitlekek ;
else
status = Loader : : ResultStatus : : ErrorIncorrectKeyAreaKey ;
return ;
return false ;
}
} else {
if ( status ! = Loader : : ResultStatus : : Success )
return ;
return false ;
if ( has_rights_id )
status = Loader : : ResultStatus : : ErrorIncorrectTitlekeyOrTitlekek ;
else
status = Loader : : ResultStatus : : ErrorIncorrectKeyAreaKey ;
return ;
return false ;
}
return true ;
}
u8 NCA : : GetCryptoRevision ( ) const {
u8 master_key_id = header . crypto_type ;
if ( header . crypto_type_2 > master_key_id )
master_key_id = header . crypto_type_2 ;
if ( master_key_id > 0 )
- - master_key_id ;
return master_key_id ;
}
boost : : optional < Core : : Crypto : : Key128 > NCA : : GetKeyAreaKey ( NCASectionCryptoType type ) const {
const auto master_key_id = GetCryptoRevision ( ) ;
if ( ! keys . HasKey ( Core : : Crypto : : S128KeyType : : KeyArea , master_key_id , header . key_index ) )
return boost : : none ;
std : : vector < u8 > key_area ( header . key_area . begin ( ) , header . key_area . end ( ) ) ;
Core : : Crypto : : AESCipher < Core : : Crypto : : Key128 > cipher (
keys . GetKey ( Core : : Crypto : : S128KeyType : : KeyArea , master_key_id , header . key_index ) ,
Core : : Crypto : : Mode : : ECB ) ;
cipher . Transcode ( key_area . data ( ) , key_area . size ( ) , key_area . data ( ) , Core : : Crypto : : Op : : Decrypt ) ;
Core : : Crypto : : Key128 out ;
if ( type = = NCASectionCryptoType : : XTS )
std : : copy ( key_area . begin ( ) , key_area . begin ( ) + 0x10 , out . begin ( ) ) ;
else if ( type = = NCASectionCryptoType : : CTR | | type = = NCASectionCryptoType : : BKTR )
std : : copy ( key_area . begin ( ) + 0x20 , key_area . begin ( ) + 0x30 , out . begin ( ) ) ;
else
LOG_CRITICAL ( Crypto , " Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X} " ,
static_cast < u8 > ( type ) ) ;
u128 out_128 { } ;
memcpy ( out_128 . data ( ) , out . data ( ) , 16 ) ;
LOG_TRACE ( Crypto , " called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X} " ,
master_key_id , header . key_index , out_128 [ 1 ] , out_128 [ 0 ] ) ;
return out ;
}
boost : : optional < Core : : Crypto : : Key128 > NCA : : GetTitlekey ( ) {
const auto master_key_id = GetCryptoRevision ( ) ;
u128 rights_id { } ;
memcpy ( rights_id . data ( ) , header . rights_id . data ( ) , 16 ) ;
if ( rights_id = = u128 { } ) {
status = Loader : : ResultStatus : : ErrorInvalidRightsID ;
return boost : : none ;
}
auto titlekey = keys . GetKey ( Core : : Crypto : : S128KeyType : : Titlekey , rights_id [ 1 ] , rights_id [ 0 ] ) ;
if ( titlekey = = Core : : Crypto : : Key128 { } ) {
status = Loader : : ResultStatus : : ErrorMissingTitlekey ;
return boost : : none ;
}
if ( ! keys . HasKey ( Core : : Crypto : : S128KeyType : : Titlekek , master_key_id ) ) {
status = Loader : : ResultStatus : : ErrorMissingTitlekek ;
return boost : : none ;
}
status = Loader : : ResultStatus : : Success ;
Core : : Crypto : : AESCipher < Core : : Crypto : : Key128 > cipher (
keys . GetKey ( Core : : Crypto : : S128KeyType : : Titlekek , master_key_id ) , Core : : Crypto : : Mode : : ECB ) ;
cipher . Transcode ( titlekey . data ( ) , titlekey . size ( ) , titlekey . data ( ) , Core : : Crypto : : Op : : Decrypt ) ;
return titlekey ;
}
NCA : : ~ NCA ( ) = default ;
VirtualFile NCA : : Decrypt ( const NCASectionHeader & s_header , VirtualFile in , u64 starting_offset ) {
if ( ! encrypted )
return in ;
switch ( s_header . raw . header . crypto_type ) {
case NCASectionCryptoType : : NONE :
LOG_DEBUG ( Crypto , " called with mode=NONE " ) ;
return in ;
case NCASectionCryptoType : : CTR :
// During normal BKTR decryption, this entire function is skipped. This is for the metadata,
// which uses the same CTR as usual.
case NCASectionCryptoType : : BKTR :
LOG_DEBUG ( Crypto , " called with mode=CTR, starting_offset={:016X} " , starting_offset ) ;
{
boost : : optional < Core : : Crypto : : Key128 > key = boost : : none ;
if ( has_rights_id ) {
status = Loader : : ResultStatus : : Success ;
key = GetTitlekey ( ) ;
if ( key = = boost : : none ) {
if ( status = = Loader : : ResultStatus : : Success )
status = Loader : : ResultStatus : : ErrorMissingTitlekey ;
return nullptr ;
}
} else {
key = GetKeyAreaKey ( NCASectionCryptoType : : CTR ) ;
if ( key = = boost : : none ) {
status = Loader : : ResultStatus : : ErrorMissingKeyAreaKey ;
return nullptr ;
}
}
auto out = std : : make_shared < Core : : Crypto : : CTREncryptionLayer > (
std : : move ( in ) , key . value ( ) , starting_offset ) ;
std : : vector < u8 > iv ( 16 ) ;
for ( u8 i = 0 ; i < 8 ; + + i )
iv [ i ] = s_header . raw . section_ctr [ 0x8 - i - 1 ] ;
out - > SetIV ( iv ) ;
return std : : static_pointer_cast < VfsFile > ( out ) ;
}
case NCASectionCryptoType : : XTS :
// TODO(DarkLordZach): Find a test case for XTS-encrypted NCAs
default :
LOG_ERROR ( Crypto , " called with unhandled crypto type={:02X} " ,
static_cast < u8 > ( s_header . raw . header . crypto_type ) ) ;
return nullptr ;
}
}
Loader : : ResultStatus NCA : : GetStatus ( ) const {
return status ;