mirror of
https://github.com/rvtr/TwlIPL.git
synced 2025-10-31 06:01:12 -04:00
git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/TwlIPL/trunk@1064 b08762b0-b915-fc4b-9d8c-17b2551a87ff
394 lines
13 KiB
C
394 lines
13 KiB
C
/*---------------------------------------------------------------------------*
|
||
Project: TwlIPL - libraries - fs
|
||
File: fs_loader.c
|
||
|
||
Copyright 2007 Nintendo. All rights reserved.
|
||
|
||
These coded instructions, statements, and computer programs contain
|
||
proprietary information of Nintendo of America Inc. and/or Nintendo
|
||
Company Ltd., and are protected by Federal copyright law. They may
|
||
not be disclosed to third parties or copied or duplicated in any form,
|
||
in whole or in part, without the prior written consent of Nintendo.
|
||
|
||
$Date:: $
|
||
$Rev$
|
||
$Author$
|
||
*---------------------------------------------------------------------------*/
|
||
|
||
#include <firm.h>
|
||
#include <estypes.h>
|
||
#include <es.h>
|
||
|
||
#define FS_HEADER_AUTH_SIZE 0xe00
|
||
|
||
#define HASH_UNIT 0x1000
|
||
|
||
static ROM_Header* const rh = (ROM_Header*)HW_TWL_ROM_HEADER_BUF;
|
||
|
||
static u8 currentKey[ SVC_SHA1_BLOCK_SIZE ];
|
||
|
||
static const u8 defaultKey[ SVC_SHA1_BLOCK_SIZE ] =
|
||
{
|
||
0x21, 0x06, 0xc0, 0xde,
|
||
0xba, 0x98, 0xce, 0x3f,
|
||
0xa6, 0x92, 0xe3, 0x9d,
|
||
0x46, 0xf2, 0xed, 0x01,
|
||
|
||
0x76, 0xe3, 0xcc, 0x08,
|
||
0x56, 0x23, 0x63, 0xfa,
|
||
0xca, 0xd4, 0xec, 0xdf,
|
||
0x9a, 0x62, 0x78, 0x34,
|
||
|
||
0x8f, 0x6d, 0x63, 0x3c,
|
||
0xfe, 0x22, 0xca, 0x92,
|
||
0x20, 0x88, 0x97, 0x23,
|
||
0xd2, 0xcf, 0xae, 0xc2,
|
||
|
||
0x32, 0x67, 0x8d, 0xfe,
|
||
0xca, 0x83, 0x64, 0x98,
|
||
0xac, 0xfd, 0x3e, 0x37,
|
||
0x87, 0x46, 0x58, 0x24,
|
||
};
|
||
|
||
|
||
/*---------------------------------------------------------------------------*
|
||
Name: FS_SetDigestKey
|
||
|
||
Description: set specified key or default key for HMAC-SHA-1
|
||
|
||
Arguments: digestKey pointer to key
|
||
if NULL, use default key
|
||
|
||
Returns: TRUE if success
|
||
*---------------------------------------------------------------------------*/
|
||
static inline void FS_SetDigestKey( const u8* digestKey )
|
||
{
|
||
if ( digestKey )
|
||
{
|
||
MI_CpuCopy8(digestKey, currentKey, SVC_SHA1_BLOCK_SIZE);
|
||
}
|
||
else
|
||
{
|
||
MI_CpuCopy8(defaultKey, currentKey, SVC_SHA1_BLOCK_SIZE);
|
||
}
|
||
}
|
||
|
||
static inline BOOL CheckDigest( u8* a, u8* b, BOOL aClr, BOOL bClr )
|
||
{
|
||
BOOL result = TRUE;
|
||
int i;
|
||
for ( i = 0; i < SVC_SHA1_DIGEST_SIZE; i++ )
|
||
{
|
||
if ( a[i] != b[i] )
|
||
{
|
||
result = FALSE;
|
||
}
|
||
}
|
||
if ( aClr ) MI_CpuClear8(a, SVC_SHA1_DIGEST_SIZE);
|
||
if ( bClr ) MI_CpuClear8(b, SVC_SHA1_DIGEST_SIZE);
|
||
return result;
|
||
}
|
||
#ifdef SUPPORT_CERTIFICATION
|
||
/*---------------------------------------------------------------------------*
|
||
Name: CheckRomCertificate
|
||
|
||
Description: check the certification in the ROM
|
||
|
||
ROM<4F>w<EFBFBD>b<EFBFBD>_<EFBFBD>ɕt<C995><74><EFBFBD><EFBFBD><EFBFBD>ꂽ<EFBFBD>ؖ<EFBFBD><D896><EFBFBD><EFBFBD>̃`<60>F<EFBFBD>b<EFBFBD>N<EFBFBD><4E><EFBFBD>s<EFBFBD><73><EFBFBD>܂<EFBFBD><DC82>B
|
||
makerom.TWL<57><4C><EFBFBD>̃R<CC83>[<5B>h<EFBFBD>Ɉˑ<C988><CB91><EFBFBD><EFBFBD>܂<EFBFBD><DC82>B
|
||
|
||
Arguments: pool pointer to the SVCSignHeapContext
|
||
pCert pointer to the certification
|
||
pCAPubKey pointer to the public key for the certification
|
||
gameCode initial code
|
||
|
||
Returns: TRUE if success
|
||
*---------------------------------------------------------------------------*/
|
||
static BOOL CheckRomCertificate( SVCSignHeapContext* pool, const RomCertificate *pCert, const void* pCAPubKey, u32 gameCode )
|
||
{
|
||
u8 digest[SVC_SHA1_DIGEST_SIZE];
|
||
u8 md[SVC_SHA1_DIGEST_SIZE];
|
||
|
||
// <20>ؖ<EFBFBD><D896><EFBFBD><EFBFBD>w<EFBFBD>b<EFBFBD>_<EFBFBD>̃}<7D>W<EFBFBD>b<EFBFBD>N<EFBFBD>i<EFBFBD><69><EFBFBD>o<EFBFBD>[<5B>`<60>F<EFBFBD>b<EFBFBD>N
|
||
if( pCert->header.magicNumber != TWL_ROM_CERT_MAGIC_NUMBER ||
|
||
// <20>ؖ<EFBFBD><D896><EFBFBD><EFBFBD>w<EFBFBD>b<EFBFBD>_<EFBFBD><5F>ROM<4F>w<EFBFBD>b<EFBFBD>_<EFBFBD>̃Q<CC83>[<5B><><EFBFBD>R<EFBFBD>[<5B>h<EFBFBD><68><EFBFBD>v<EFBFBD>`<60>F<EFBFBD>b<EFBFBD>N
|
||
pCert->header.gameCode != gameCode )
|
||
{
|
||
return FALSE;
|
||
}
|
||
// <20>ؖ<EFBFBD><D896><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>`<60>F<EFBFBD>b<EFBFBD>N
|
||
SVC_DecryptSign( pool, &digest, pCert->sign, pCAPubKey );
|
||
|
||
// <20>_<EFBFBD>C<EFBFBD>W<EFBFBD>F<EFBFBD>X<EFBFBD>g<EFBFBD>̌v<CC8C>Z
|
||
SVC_CalcSHA1( md, pCert, ROM_CERT_SIGN_OFFSET );
|
||
|
||
// <20><><EFBFBD>r
|
||
return CheckDigest(md, digest, TRUE, TRUE);
|
||
}
|
||
#endif
|
||
/*---------------------------------------------------------------------------*
|
||
Name: FS_LoadBuffer
|
||
|
||
Description: receive data from ARM7 via WRAM-B and store in destination address,
|
||
calculate SHA1 in parallel if ctx is specified
|
||
|
||
Arguments: dest destination address to read
|
||
size total length to read in bytes
|
||
ctx pointer to SHA1 context or NULL
|
||
|
||
Returns: TRUE if success
|
||
*---------------------------------------------------------------------------*/
|
||
BOOL FS_LoadBuffer( u8* dest, u32 size, SVCSHA1Context *ctx )
|
||
{
|
||
static int count = 0;
|
||
|
||
while ( size > 0 )
|
||
{
|
||
u8* src = (u8*)HW_FIRM_LOAD_BUFFER_BASE + count * HW_FIRM_LOAD_BUFFER_UNIT_SIZE;
|
||
u32 unit = size < HW_FIRM_LOAD_BUFFER_UNIT_SIZE ? size : HW_FIRM_LOAD_BUFFER_UNIT_SIZE;
|
||
PXI_AcquireLoadBufferSemaphore(); // wait to be ready
|
||
MIi_SetWramBankMaster_B( count, MI_WRAM_ARM9 );
|
||
if (ctx)
|
||
{
|
||
int done;
|
||
for ( done = 0; done < unit; done += HASH_UNIT )
|
||
{
|
||
u8* s = src + done;
|
||
u8* d = dest + done;
|
||
u32 u = unit - done < HASH_UNIT ? unit - done : HASH_UNIT;
|
||
SVC_SHA1Update( ctx, s, u );
|
||
MI_CpuCopyFast( s, d, u );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MI_CpuCopyFast( src, dest, unit );
|
||
}
|
||
DC_InvalidateRange( src, unit );
|
||
size -= unit;
|
||
dest += unit;
|
||
MIi_SetWramBankMaster_B( count, MI_WRAM_ARM7 );
|
||
PXI_ReleaseLoadBufferSemaphore();
|
||
count = ( count + 1 ) % HW_FIRM_LOAD_BUFFER_UNIT_NUMS;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
/*---------------------------------------------------------------------------*
|
||
Name: GetTransferSize
|
||
|
||
Description: get size to transfer once
|
||
|
||
<20><><EFBFBD>x<EFBFBD>Ɏ<EFBFBD><C98E>M<EFBFBD><4D><EFBFBD><EFBFBD><EFBFBD>T<EFBFBD>C<EFBFBD>Y<EFBFBD><59><EFBFBD>Ԃ<EFBFBD><D482>܂<EFBFBD><DC82>B
|
||
|
||
<20>]<5D><><EFBFBD>͈͂<CD88>AES<45>̈<EFBFBD><CC88><EFBFBD><EFBFBD>܂<EFBFBD><DC82><EFBFBD><EFBFBD>ꍇ<EFBFBD>́A<CD81><41><EFBFBD>E<EFBFBD>܂ł̃T<CC83>C<EFBFBD>Y (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
<20><><EFBFBD><EFBFBD><EFBFBD>ȃT<C883>C<EFBFBD>Y) <20><><EFBFBD>Ԃ<EFBFBD><D482>܂<EFBFBD><DC82>B
|
||
makerom.TWL<57>܂<EFBFBD><DC82><EFBFBD>IPL<50>̎g<CC8E>p<EFBFBD>Ɉˑ<C988><CB91><EFBFBD><EFBFBD>܂<EFBFBD><DC82>B
|
||
|
||
Arguments: offset offset of region from head of ROM_Header
|
||
size size of region
|
||
|
||
Returns: size to transfer once
|
||
*---------------------------------------------------------------------------*/
|
||
static u32 GetTransferSize( u32 offset, u32 size )
|
||
{
|
||
if ( rh->s.enable_aes )
|
||
{
|
||
u32 end = offset + size;
|
||
u32 aes_offset = rh->s.aes_target_rom_offset;
|
||
u32 aes_end = aes_offset + rh->s.aes_target_size;
|
||
u32 aes_offset2 = rh->s.aes_target2_rom_offset;
|
||
u32 aes_end2 = aes_offset2 + rh->s.aes_target2_size;
|
||
|
||
if ( offset >= aes_offset && offset < aes_end )
|
||
{
|
||
if ( end > aes_end )
|
||
{
|
||
size = aes_end - offset;
|
||
}
|
||
}
|
||
else if ( offset >= aes_offset2 && offset < aes_end2 )
|
||
{
|
||
if ( end > aes_end2 )
|
||
{
|
||
size = aes_end2 - offset;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if ( offset < aes_offset && offset + size > aes_offset )
|
||
{
|
||
size = aes_offset - offset;
|
||
}
|
||
}
|
||
}
|
||
return size;
|
||
}
|
||
|
||
/*---------------------------------------------------------------------------*
|
||
Name: FS_LoadModule
|
||
|
||
Description: receive data from ARM7 via WRAM-B and store in destination address
|
||
in view of AES settings in the ROM header at HW_TWL_ROM_HEADER_BUF,
|
||
then verify the digest
|
||
|
||
Arguments: dest destination address to read
|
||
offset file offset to start to read in bytes
|
||
size total length to read in bytes
|
||
digest digest to verify
|
||
|
||
Returns: TRUE if success
|
||
*---------------------------------------------------------------------------*/
|
||
BOOL FS_LoadModule( u8* dest, u32 offset, u32 size, const u8 digest[SVC_SHA1_DIGEST_SIZE] )
|
||
{
|
||
#ifndef NO_SECURITY_CHECK
|
||
SVCHMACSHA1Context ctx;
|
||
u8 md[SVC_SHA1_DIGEST_SIZE];
|
||
|
||
SVC_HMACSHA1Init(&ctx, currentKey, SVC_SHA1_BLOCK_SIZE );
|
||
while ( size > 0 )
|
||
{
|
||
u32 unit = GetTransferSize( offset, size );
|
||
if ( !FS_LoadBuffer( dest, unit, &ctx.sha1_ctx ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
dest += unit;
|
||
offset += unit;
|
||
size -= unit;
|
||
}
|
||
SVC_HMACSHA1GetHash(&ctx, md);
|
||
return CheckDigest(md, (u8*)digest, TRUE, FALSE);
|
||
#else
|
||
(void)digest;
|
||
while ( size > 0 )
|
||
{
|
||
u32 unit = GetTransferSize( offset, size );
|
||
if ( !FS_LoadBuffer( dest, unit, NULL ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
dest += unit;
|
||
offset += unit;
|
||
size -= unit;
|
||
}
|
||
return TRUE;
|
||
#endif
|
||
}
|
||
|
||
/*---------------------------------------------------------------------------*
|
||
Name: FS_LoadHeader
|
||
|
||
Description: receive ROM header, store to HW_TWL_ROM_HEADER_BUF,
|
||
and verify signature
|
||
|
||
Arguments: pool heap context to call SVC_DecryptSign
|
||
rsa_key_user public key to verify the signature for user application
|
||
rsa_key_sys public key to verify the signature for system application
|
||
rsa_key_secure public key to verify the signature for secure application
|
||
|
||
Returns: TRUE if success
|
||
*---------------------------------------------------------------------------*/
|
||
BOOL FS_LoadHeader( SVCSignHeapContext* pool, const void* rsa_key_user, const void* rsa_key_sys, const void* rsa_key_secure )
|
||
{
|
||
#ifndef NO_SECURITY_CHECK
|
||
const void* rsa_key;
|
||
SVCSHA1Context ctx;
|
||
u8 md[SVC_SHA1_DIGEST_SIZE];
|
||
SignatureData sd;
|
||
|
||
SVC_SHA1Init( &ctx );
|
||
if ( !FS_LoadBuffer( (u8*)rh, FS_HEADER_AUTH_SIZE, &ctx ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
SVC_SHA1GetHash( &ctx, md );
|
||
if ( !FS_LoadBuffer( (u8*)rh + FS_HEADER_AUTH_SIZE, HW_TWL_ROM_HEADER_BUF_SIZE - FS_HEADER_AUTH_SIZE, NULL ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
// <20><><EFBFBD>̊m<CC8A><6D>
|
||
rsa_key = (rh->s.titleID_Hi & TITLE_ID_HI_SECURE_FLAG_MASK)
|
||
? rsa_key_secure
|
||
: ( (rh->s.titleID_Hi & TITLE_ID_HI_APP_TYPE_MASK) ? rsa_key_sys : rsa_key_user );
|
||
#ifdef SUPPORT_CERTIFICATION
|
||
// <20>R<EFBFBD><52><EFBFBD>e<EFBFBD><65><EFBFBD>c<EFBFBD>ؖ<EFBFBD><D896><EFBFBD>
|
||
if ( CheckRomCertificate( pool, &rh->certificate, rsa_key, *(u32*)rh->s.game_code ) )
|
||
{
|
||
rsa_key = rh->certificate.pubKeyMod; // <20>w<EFBFBD>b<EFBFBD>_<EFBFBD>p<EFBFBD>̌<EFBFBD><CC8C>̎<EFBFBD><CC8E><EFBFBD><EFBFBD>o<EFBFBD><6F>
|
||
}
|
||
else
|
||
{
|
||
// <20>Ƃ肠<C682><E882A0><EFBFBD><EFBFBD><EFBFBD>R<EFBFBD><52><EFBFBD>e<EFBFBD><65><EFBFBD>c<EFBFBD>ؖ<EFBFBD><D896><EFBFBD><EFBFBD>p<EFBFBD>̌<EFBFBD><CC8C><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̂܂g<DC8E><67><EFBFBD><EFBFBD><EFBFBD>Ɖ<EFBFBD><C689><EFBFBD>
|
||
}
|
||
#endif
|
||
// <20>w<EFBFBD>b<EFBFBD>_<EFBFBD><5F><EFBFBD><EFBFBD><EFBFBD>`<60>F<EFBFBD>b<EFBFBD>N
|
||
SVC_DecryptSign( pool, &sd, rh->signature, rsa_key );
|
||
|
||
if ( !CheckDigest( md, sd.digest, TRUE, FALSE ) )
|
||
{
|
||
MI_CpuClear8( &sd, sizeof(sd) ); // <20>c<EFBFBD><63><EFBFBD>폜 (<28><><EFBFBD>ɕK<C995>v<EFBFBD>Ȃ<EFBFBD><C882>̂͂Ȃ<CD82><C882>H)
|
||
return FALSE;
|
||
}
|
||
|
||
// <20>_<EFBFBD>C<EFBFBD>W<EFBFBD>F<EFBFBD>X<EFBFBD>g<EFBFBD>ȊO<C88A>̃f<CC83>[<5B>^<5E>̃`<60>F<EFBFBD>b<EFBFBD>N<EFBFBD><4E><EFBFBD>K<EFBFBD>v<EFBFBD>I<EFBFBD>I
|
||
|
||
MI_CpuClear8( &sd, sizeof(sd) ); // <20>c<EFBFBD><63><EFBFBD>폜 (<28><><EFBFBD>ɕK<C995>v<EFBFBD>Ȃ<EFBFBD><C882>̂͂Ȃ<CD82><C882>H)
|
||
#else
|
||
(void)pool;
|
||
(void)rsa_key1;
|
||
(void)rsa_key2;
|
||
FS_LoadBuffer( (u8*)rh, FS_HEADER_AUTH_SIZE, NULL );
|
||
FS_LoadBuffer( (u8*)rh + FS_HEADER_AUTH_SIZE, HW_TWL_ROM_HEADER_BUF_SIZE - FS_HEADER_AUTH_SIZE, NULL );
|
||
#endif
|
||
// ROM<4F>w<EFBFBD>b<EFBFBD>_<EFBFBD>̃R<CC83>s<EFBFBD>[
|
||
MI_CpuCopyFast( rh, (void*)HW_ROM_HEADER_BUF, HW_ROM_HEADER_BUF_END-HW_ROM_HEADER_BUF );
|
||
return TRUE;
|
||
}
|
||
|
||
/*---------------------------------------------------------------------------*
|
||
Name: FS_LoadStatic
|
||
|
||
Description: receive static regions from ARM6 via WRAM-B and store them
|
||
specified by ROM header at HW_TWL_ROM_HEADER_BUF
|
||
|
||
Arguments: digestKey pointer to key for HMAC-SHA1
|
||
if NULL, use default key
|
||
|
||
Returns: TRUE if success
|
||
*---------------------------------------------------------------------------*/
|
||
BOOL FS_LoadStatic( const u8* digestKey )
|
||
{
|
||
FS_SetDigestKey( digestKey );
|
||
if ( rh->s.main_size > 0 )
|
||
{
|
||
if ( !FS_LoadModule( rh->s.main_ram_address, rh->s.main_rom_offset, rh->s.main_size, rh->s.main_static_digest ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
if ( rh->s.sub_size > 0 )
|
||
{
|
||
if ( !FS_LoadModule( rh->s.sub_ram_address, rh->s.sub_rom_offset, rh->s.sub_size, rh->s.sub_static_digest ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
if ( rh->s.main_ltd_size > 0 )
|
||
{
|
||
if ( !FS_LoadModule( rh->s.main_ltd_ram_address, rh->s.main_ltd_rom_offset, rh->s.main_ltd_size, rh->s.main_ltd_static_digest ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
if ( rh->s.sub_ltd_size > 0 )
|
||
{
|
||
if ( !FS_LoadModule( rh->s.sub_ltd_ram_address, rh->s.sub_ltd_rom_offset, rh->s.sub_ltd_size, rh->s.sub_ltd_static_digest ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
return TRUE;
|
||
}
|