2019-03-04 23:05:42 +00:00
/*
* Copyright ( c ) 2019 shchmue
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2019-10-06 20:28:54 +01:00
# include "incognito.h"
2019-09-17 05:18:41 +01:00
# include "../config/config.h"
2019-03-04 23:05:42 +00:00
# include "../gfx/di.h"
# include "../gfx/gfx.h"
2019-09-17 16:51:30 +01:00
# include "../gfx/tui.h"
2020-05-30 16:24:47 +01:00
# include "../hos/hos.h"
2019-03-04 23:05:42 +00:00
# include "../hos/pkg1.h"
# include "../hos/pkg2.h"
# include "../hos/sept.h"
2019-09-24 23:49:22 +01:00
# include "../libs/fatfs/ff.h"
2019-03-04 23:05:42 +00:00
# include "../mem/heap.h"
2019-04-19 18:00:10 +01:00
# include "../mem/mc.h"
2019-04-18 16:02:15 +01:00
# include "../mem/sdram.h"
2019-03-04 23:05:42 +00:00
# include "../sec/se.h"
# include "../sec/se_t210.h"
# include "../sec/tsec.h"
# include "../soc/fuse.h"
# include "../soc/smmu.h"
# include "../soc/t210.h"
2019-09-17 16:51:30 +01:00
# include "../storage/emummc.h"
2019-03-04 23:05:42 +00:00
# include "../storage/nx_emmc.h"
# include "../storage/sdmmc.h"
2020-06-18 22:31:37 +01:00
# include "../utils/btn.h"
2019-03-04 23:05:42 +00:00
# include "../utils/list.h"
2019-05-12 18:15:23 +01:00
# include "../utils/sprintf.h"
2019-03-04 23:05:42 +00:00
# include "../utils/util.h"
2019-05-12 18:15:23 +01:00
# include "key_sources.inl"
2019-09-24 14:18:28 +01:00
2019-10-06 20:28:54 +01:00
# include "io/io.h"
2019-03-04 23:05:42 +00:00
# include <string.h>
2019-10-07 18:26:40 +01:00
# define RETRY_COUNT 5
# define RETRY(exp) \
( { \
u8 _attemptc_ = RETRY_COUNT ; \
2019-10-07 19:11:26 +01:00
bool _resultb_ = false ; \
2019-10-07 18:26:40 +01:00
while ( _attemptc_ - - ) \
{ \
if ( ( _resultb_ = exp ) ) \
break ; \
gfx_printf ( " %kretry %d/%d... \n " , COLOR_RED , RETRY_COUNT - _attemptc_ , RETRY_COUNT ) ; \
} \
_resultb_ ; \
} )
2019-03-04 23:05:42 +00:00
extern bool sd_mount ( ) ;
extern void sd_unmount ( ) ;
2019-09-28 23:18:17 +01:00
extern int sd_save_to_file ( void * buf , u32 size , const char * filename ) ;
2019-03-04 23:05:42 +00:00
2019-09-17 05:18:41 +01:00
extern hekate_config h_cfg ;
2019-03-04 23:05:42 +00:00
u32 _key_count = 0 ;
sdmmc_storage_t storage ;
2019-10-03 17:16:41 +01:00
sdmmc_t sdmmc ;
2019-03-04 23:05:42 +00:00
emmc_part_t * system_part ;
2019-09-23 20:40:31 +01:00
emmc_part_t * prodinfo_part ;
2019-03-04 23:05:42 +00:00
2019-10-04 10:26:27 +01:00
# define SECTORS_IN_CLUSTER 32
2019-10-04 15:22:24 +01:00
# define PRODINFO_SIZE 0x3FBC00
2019-10-03 17:16:41 +01:00
2019-10-04 19:48:24 +01:00
# define BACKUP_NAME_EMUNAND "sd: / prodinfo_emunand.bin"
# define BACKUP_NAME_SYSNAND "sd: / prodinfo_sysnand.bin"
2019-03-04 23:05:42 +00:00
static u8 temp_key [ 0x10 ] ,
2019-09-28 23:18:17 +01:00
bis_key [ 4 ] [ 0x20 ] = { 0 } ,
device_key [ 0x10 ] = { 0 } ,
2020-06-18 22:31:37 +01:00
device_key_4x [ 0x10 ] = { 0 } ,
2019-09-28 23:18:17 +01:00
keyblob [ KB_FIRMWARE_VERSION_600 + 1 ] [ 0x90 ] = { 0 } ,
keyblob_key [ KB_FIRMWARE_VERSION_600 + 1 ] [ 0x10 ] = { 0 } ,
keyblob_mac_key [ KB_FIRMWARE_VERSION_600 + 1 ] [ 0x10 ] = { 0 } ,
package1_key [ KB_FIRMWARE_VERSION_600 + 1 ] [ 0x10 ] = { 0 } ,
// master key-derived families
master_kek [ KB_FIRMWARE_VERSION_MAX + 1 ] [ 0x10 ] = { 0 } ,
master_key [ KB_FIRMWARE_VERSION_MAX + 1 ] [ 0x10 ] = { 0 } ;
2019-03-04 23:05:42 +00:00
2019-10-03 17:16:41 +01:00
LIST_INIT ( gpt ) ;
2019-03-04 23:05:42 +00:00
// key functions
2020-06-18 22:31:37 +01:00
static int _key_exists ( const void * data ) { return memcmp ( data , zeros , 0x10 ) ! = 0 ; } ;
2019-09-28 23:18:17 +01:00
static void _generate_kek ( u32 ks , const void * key_source , void * master_key , const void * kek_seed , const void * key_seed ) ;
2020-06-18 22:31:37 +01:00
static void _get_device_key ( u32 ks , void * out_device_key , u32 revision , const void * device_key , const void * master_key ) ;
2019-09-28 23:18:17 +01:00
2020-06-03 14:00:41 +01:00
unsigned int crc_16_table [ 16 ] = {
0x0000 , 0xCC01 , 0xD801 , 0x1400 , 0xF001 , 0x3C00 , 0x2800 , 0xE401 ,
0xA001 , 0x6C00 , 0x7800 , 0xB401 , 0x5000 , 0x9C01 , 0x8801 , 0x4400 } ;
unsigned short int get_crc_16 ( const char * p , int n ) {
unsigned short int crc = 0x55AA ;
int r ;
while ( n - - > 0 ) {
r = crc_16_table [ crc & 0xF ] ;
crc = ( crc > > 4 ) & 0x0FFF ;
crc = crc ^ r ^ crc_16_table [ * p & 0xF ] ;
r = crc_16_table [ crc & 0xF ] ;
crc = ( crc > > 4 ) & 0x0FFF ;
crc = crc ^ r ^ crc_16_table [ ( * p > > 4 ) & 0xF ] ;
p + + ;
}
return ( crc ) ;
}
2020-06-06 18:29:44 +01:00
u16 calculateCrc ( u32 offset , u32 size , u8 * blob )
2020-06-06 14:46:20 +01:00
{
const char buffer [ size + 1 ] ;
2020-06-06 18:29:44 +01:00
if ( blob = = NULL )
readData ( ( u8 * ) buffer , offset , size , NULL ) ;
else
memcpy ( ( u8 * ) buffer , blob + offset , size ) ;
2020-06-06 14:46:20 +01:00
return get_crc_16 ( buffer , size ) ;
}
2020-06-06 18:29:44 +01:00
u16 readCrc ( u32 offset , u8 * blob )
2020-06-06 14:46:20 +01:00
{
u16 buffer ;
2020-06-06 18:29:44 +01:00
if ( blob = = NULL )
readData ( ( u8 * ) & buffer , offset , sizeof ( u16 ) , NULL ) ;
else
memcpy ( ( u8 * ) & buffer , blob + offset , sizeof ( u16 ) ) ;
2020-06-06 14:46:20 +01:00
return buffer ;
}
2020-06-06 18:29:44 +01:00
bool validateCrc ( u32 offset , u32 size , u8 * blob )
2020-06-06 14:46:20 +01:00
{
2020-06-06 18:29:44 +01:00
return calculateCrc ( offset , size , blob ) = = readCrc ( offset + size , blob ) ;
2020-06-06 14:46:20 +01:00
}
bool calculateAndWriteCrc ( u32 offset , u32 size )
{
2020-06-06 18:29:44 +01:00
u16 crcValue = calculateCrc ( offset , size , NULL ) ;
2020-06-06 14:46:20 +01:00
u8 crc [ 2 ] = { crcValue & 0xff , crcValue > > 8 } ; // bytes of u16
return writeData ( crc , offset + size , sizeof ( u16 ) , NULL ) ;
}
2020-06-06 18:29:44 +01:00
void validateChecksums ( u8 * blob )
{
if ( ! validateCrc ( 0x0250 , 0x1E , blob ) )
2020-06-06 19:23:28 +01:00
gfx_printf ( " %kWarning - invalid serial crc \n " , COLOR_RED ) ;
2020-06-06 18:29:44 +01:00
if ( ! validateCrc ( 0x0480 , 0x18E , blob ) )
2020-06-06 19:23:28 +01:00
gfx_printf ( " %kWarning - invalid ECC-B233 crc... \n " , COLOR_RED ) ;
2020-06-06 18:29:44 +01:00
if ( ! validateCrc ( 0x3AE0 , 0x13E , blob ) )
2020-06-06 19:23:28 +01:00
gfx_printf ( " %kWarning - invalid ext SSL key crc... \n " , COLOR_RED ) ;
2020-06-06 18:29:44 +01:00
if ( ! validateCrc ( 0x35A0 , 0x07E , blob ) )
2020-06-06 19:23:28 +01:00
gfx_printf ( " %kWarning - invalid ECDSA cert crc... \n " , COLOR_RED ) ;
2020-06-06 18:29:44 +01:00
if ( ! validateCrc ( 0x36A0 , 0x09E , blob ) )
2020-06-06 19:23:28 +01:00
gfx_printf ( " %kWarning - invalid ECQV-BLS cert crc... \n " , COLOR_RED ) ;
2020-06-06 18:29:44 +01:00
}
2019-09-28 23:18:17 +01:00
bool dump_keys ( )
{
2019-03-04 23:05:42 +00:00
display_backlight_brightness ( 100 , 1000 ) ;
2019-09-17 16:51:30 +01:00
gfx_clear_partial_grey ( 0x1B , 0 , 1256 ) ;
2019-04-18 17:47:34 +01:00
gfx_con_setpos ( 0 , 0 ) ;
2019-03-04 23:05:42 +00:00
2019-09-28 23:18:17 +01:00
gfx_print_header ( ) ;
2019-10-03 17:16:41 +01:00
gfx_printf ( " %kGetting bis_keys... \n " , COLOR_YELLOW ) ;
2019-09-17 05:18:41 +01:00
u32 retries = 0 ;
2019-03-04 23:05:42 +00:00
tsec_ctxt_t tsec_ctxt ;
2020-05-30 17:43:06 +01:00
if ( emummc_storage_init_mmc ( & storage , & sdmmc ) = = 2 )
2019-10-26 11:25:49 +01:00
{
EPRINTF ( " Unable to init MMC. " ) ;
return false ;
}
2019-03-04 23:05:42 +00:00
// Read package1.
u8 * pkg1 = ( u8 * ) malloc ( 0x40000 ) ;
2020-06-18 22:31:37 +01:00
emummc_storage_set_mmc_partition ( & storage , EMMC_BOOT0 ) ;
2019-09-17 16:51:30 +01:00
emummc_storage_read ( & storage , 0x100000 / NX_EMMC_BLOCKSIZE , 0x40000 / NX_EMMC_BLOCKSIZE , pkg1 ) ;
2019-03-04 23:05:42 +00:00
const pkg1_id_t * pkg1_id = pkg1_identify ( pkg1 ) ;
2019-09-28 23:18:17 +01:00
if ( ! pkg1_id )
{
2020-05-30 17:43:06 +01:00
EPRINTF ( " Unknown pkg1 version. \n Make sure you have the latest Incognito_RCM. \n If a new firmware version just came out, \n Incognito_RCM must be updated. \n Check Github for new release. " ) ;
2019-10-26 11:25:49 +01:00
free ( pkg1 ) ;
2019-09-28 23:18:17 +01:00
return false ;
2019-03-04 23:05:42 +00:00
}
2019-05-12 02:03:24 +01:00
bool found_tsec_fw = false ;
2019-09-28 23:18:17 +01:00
for ( const u32 * pos = ( const u32 * ) pkg1 ; ( u8 * ) pos < pkg1 + 0x40000 ; pos + = 0x100 / sizeof ( u32 ) )
{
if ( * pos = = 0xCF42004D )
{
2019-05-12 02:03:24 +01:00
tsec_ctxt . fw = ( u8 * ) pos ;
found_tsec_fw = true ;
break ;
}
}
2019-09-28 23:18:17 +01:00
if ( ! found_tsec_fw )
{
2019-05-12 02:03:24 +01:00
EPRINTF ( " Failed to locate TSEC firmware. " ) ;
2019-10-26 11:25:49 +01:00
free ( pkg1 ) ;
2019-09-28 23:18:17 +01:00
return false ;
2019-05-12 02:03:24 +01:00
}
2019-06-19 23:16:02 +01:00
tsec_key_data_t * key_data = ( tsec_key_data_t * ) ( tsec_ctxt . fw + TSEC_KEY_DATA_ADDR ) ;
tsec_ctxt . pkg1 = pkg1 ;
tsec_ctxt . size = 0x100 + key_data - > blob0_size + key_data - > blob1_size + key_data - > blob2_size + key_data - > blob3_size + key_data - > blob4_size ;
2019-09-28 23:18:17 +01:00
// u32 MAX_KEY = 6;
// if (pkg1_id->kb >= KB_FIRMWARE_VERSION_620)
// {
// MAX_KEY = pkg1_id->kb + 1;
// }
2019-09-24 23:49:22 +01:00
2019-09-28 23:18:17 +01:00
if ( pkg1_id - > kb > = KB_FIRMWARE_VERSION_700 )
{
2020-06-18 22:31:37 +01:00
se_aes_key_read ( se_key_acc_ctrl_get ( 12 ) = = 0x6A ? 13 : 12 , master_key [ KB_FIRMWARE_VERSION_MAX ] , 0x10 ) ;
2019-03-04 23:05:42 +00:00
}
2019-09-28 23:18:17 +01:00
//get_tsec: ;
2019-03-04 23:05:42 +00:00
u8 tsec_keys [ 0x10 * 2 ] = { 0 } ;
2019-09-28 23:18:17 +01:00
if ( pkg1_id - > kb = = KB_FIRMWARE_VERSION_620 )
{
2019-03-04 23:05:42 +00:00
u8 * tsec_paged = ( u8 * ) page_alloc ( 3 ) ;
memcpy ( tsec_paged , ( void * ) tsec_ctxt . fw , tsec_ctxt . size ) ;
tsec_ctxt . fw = tsec_paged ;
}
int res = 0 ;
2019-04-19 18:00:10 +01:00
mc_disable_ahb_redirect ( ) ;
2019-09-28 23:18:17 +01:00
while ( tsec_query ( tsec_keys , pkg1_id - > kb , & tsec_ctxt ) < 0 )
{
2019-03-04 23:05:42 +00:00
memset ( tsec_keys , 0x00 , 0x20 ) ;
retries + + ;
2019-09-28 23:18:17 +01:00
if ( retries > 15 )
{
2019-03-04 23:05:42 +00:00
res = - 1 ;
break ;
}
}
free ( pkg1 ) ;
2019-04-19 18:00:10 +01:00
mc_enable_ahb_redirect ( ) ;
2019-09-28 23:18:17 +01:00
if ( res < 0 )
{
2019-03-04 23:05:42 +00:00
EPRINTFARGS ( " ERROR %x dumping TSEC. \n " , res ) ;
2019-10-03 17:16:41 +01:00
return false ;
2019-03-04 23:05:42 +00:00
}
// Master key derivation
2019-09-28 23:18:17 +01:00
if ( pkg1_id - > kb = = KB_FIRMWARE_VERSION_620 & & _key_exists ( tsec_keys + 0x10 ) )
{
2019-03-04 23:05:42 +00:00
se_aes_key_set ( 8 , tsec_keys + 0x10 , 0x10 ) ; // mkek6 = unwrap(mkeks6, tsecroot)
se_aes_crypt_block_ecb ( 8 , 0 , master_kek [ 6 ] , master_kek_sources [ 0 ] ) ;
se_aes_key_set ( 8 , master_kek [ 6 ] , 0x10 ) ; // mkey = unwrap(mkek, mks)
se_aes_crypt_block_ecb ( 8 , 0 , master_key [ 6 ] , master_key_source ) ;
}
u8 * keyblob_block = ( u8 * ) calloc ( NX_EMMC_BLOCKSIZE , 1 ) ;
u8 keyblob_mac [ 0x10 ] = { 0 } ;
u32 sbk [ 4 ] = { FUSE ( FUSE_PRIVATE_KEY0 ) , FUSE ( FUSE_PRIVATE_KEY1 ) ,
FUSE ( FUSE_PRIVATE_KEY2 ) , FUSE ( FUSE_PRIVATE_KEY3 ) } ;
se_aes_key_set ( 8 , tsec_keys , 0x10 ) ;
se_aes_key_set ( 9 , sbk , 0x10 ) ;
2019-09-28 23:18:17 +01:00
for ( u32 i = 0 ; i < = KB_FIRMWARE_VERSION_600 ; i + + )
{
2019-03-04 23:05:42 +00:00
se_aes_crypt_block_ecb ( 8 , 0 , keyblob_key [ i ] , keyblob_key_source [ i ] ) ; // temp = unwrap(kbks, tsec)
2019-09-28 23:18:17 +01:00
se_aes_crypt_block_ecb ( 9 , 0 , keyblob_key [ i ] , keyblob_key [ i ] ) ; // kbk = unwrap(temp, sbk)
2019-03-04 23:05:42 +00:00
se_aes_key_set ( 7 , keyblob_key [ i ] , 0x10 ) ;
se_aes_crypt_block_ecb ( 7 , 0 , keyblob_mac_key [ i ] , keyblob_mac_key_source ) ; // kbm = unwrap(kbms, kbk)
2019-09-28 23:18:17 +01:00
if ( i = = 0 )
{
2019-03-04 23:05:42 +00:00
se_aes_crypt_block_ecb ( 7 , 0 , device_key , per_console_key_source ) ; // devkey = unwrap(pcks, kbk0)
2020-06-18 22:31:37 +01:00
se_aes_crypt_block_ecb ( 7 , 0 , device_key_4x , per_console_key_source_4x ) ;
2019-09-16 02:30:42 +01:00
}
2019-03-04 23:05:42 +00:00
// verify keyblob is not corrupt
2019-09-17 16:51:30 +01:00
emummc_storage_read ( & storage , 0x180000 / NX_EMMC_BLOCKSIZE + i , 1 , keyblob_block ) ;
2019-03-04 23:05:42 +00:00
se_aes_key_set ( 3 , keyblob_mac_key [ i ] , 0x10 ) ;
se_aes_cmac ( 3 , keyblob_mac , 0x10 , keyblob_block + 0x10 , 0xa0 ) ;
2020-06-18 22:31:37 +01:00
if ( memcmp ( keyblob_block , keyblob_mac , 0x10 ) ! = 0 )
2019-09-28 23:18:17 +01:00
{
2019-03-04 23:05:42 +00:00
EPRINTFARGS ( " Keyblob %x corrupt. " , i ) ;
2019-10-07 18:26:40 +01:00
// gfx_hexdump(i, keyblob_block, 0x10);
// gfx_hexdump(i, keyblob_mac, 0x10);
2019-03-04 23:05:42 +00:00
continue ;
}
// decrypt keyblobs
se_aes_key_set ( 2 , keyblob_key [ i ] , 0x10 ) ;
se_aes_crypt_ctr ( 2 , keyblob [ i ] , 0x90 , keyblob_block + 0x20 , 0x90 , keyblob_block + 0x10 ) ;
memcpy ( package1_key [ i ] , keyblob [ i ] + 0x80 , 0x10 ) ;
memcpy ( master_kek [ i ] , keyblob [ i ] , 0x10 ) ;
se_aes_key_set ( 7 , master_kek [ i ] , 0x10 ) ;
se_aes_crypt_block_ecb ( 7 , 0 , master_key [ i ] , master_key_source ) ;
}
free ( keyblob_block ) ;
2019-09-16 02:30:42 +01:00
u32 key_generation = 0 ;
2019-09-28 23:18:17 +01:00
if ( pkg1_id - > kb > = KB_FIRMWARE_VERSION_500 )
{
if ( ( fuse_read_odm ( 4 ) & 0x800 ) & & fuse_read_odm ( 0 ) = = 0x8E61ECAE & & fuse_read_odm ( 1 ) = = 0xF2BA3BB2 )
{
2019-09-16 02:30:42 +01:00
key_generation = fuse_read_odm ( 2 ) & 0x1F ;
2020-06-18 22:31:37 +01:00
if ( key_generation )
key_generation - - ;
2019-09-16 02:30:42 +01:00
}
}
2019-09-28 23:18:17 +01:00
if ( _key_exists ( device_key ) )
{
if ( key_generation )
{
2020-06-18 22:31:37 +01:00
_get_device_key ( 8 , temp_key , key_generation , device_key_4x , master_key [ 0 ] ) ;
2019-09-28 23:18:17 +01:00
}
else
2019-09-16 02:30:42 +01:00
memcpy ( temp_key , device_key , 0x10 ) ;
se_aes_key_set ( 8 , temp_key , 0x10 ) ;
2019-09-28 23:18:17 +01:00
se_aes_unwrap_key ( 8 , 8 , retail_specific_aes_key_source ) ; // kek = unwrap(rsaks, devkey)
2019-03-04 23:05:42 +00:00
se_aes_crypt_block_ecb ( 8 , 0 , bis_key [ 0 ] + 0x00 , bis_key_source [ 0 ] + 0x00 ) ; // bkey = unwrap(bkeys, kek)
se_aes_crypt_block_ecb ( 8 , 0 , bis_key [ 0 ] + 0x10 , bis_key_source [ 0 ] + 0x10 ) ;
// kek = generate_kek(bkeks, devkey, aeskek, aeskey)
2019-09-16 02:30:42 +01:00
_generate_kek ( 8 , bis_kek_source , temp_key , aes_kek_generation_source , aes_key_generation_source ) ;
2019-03-04 23:05:42 +00:00
se_aes_crypt_block_ecb ( 8 , 0 , bis_key [ 1 ] + 0x00 , bis_key_source [ 1 ] + 0x00 ) ; // bkey = unwrap(bkeys, kek)
se_aes_crypt_block_ecb ( 8 , 0 , bis_key [ 1 ] + 0x10 , bis_key_source [ 1 ] + 0x10 ) ;
se_aes_crypt_block_ecb ( 8 , 0 , bis_key [ 2 ] + 0x00 , bis_key_source [ 2 ] + 0x00 ) ;
se_aes_crypt_block_ecb ( 8 , 0 , bis_key [ 2 ] + 0x10 , bis_key_source [ 2 ] + 0x10 ) ;
memcpy ( bis_key [ 3 ] , bis_key [ 2 ] , 0x20 ) ;
}
2020-06-18 22:31:37 +01:00
emummc_storage_set_mmc_partition ( & storage , EMMC_GPP ) ;
2019-03-04 23:05:42 +00:00
// Parse eMMC GPT.
2019-10-03 17:16:41 +01:00
2019-03-04 23:05:42 +00:00
nx_emmc_gpt_parse ( & gpt , & storage ) ;
2019-09-17 17:03:41 +01:00
2019-09-28 23:18:17 +01:00
// Find PRODINFO partition.
2019-09-23 20:40:31 +01:00
prodinfo_part = nx_emmc_part_find ( & gpt , " PRODINFO " ) ;
2019-09-28 23:18:17 +01:00
if ( ! prodinfo_part )
{
2019-09-23 14:25:37 +01:00
EPRINTF ( " Failed to locate PRODINFO. " ) ;
2019-10-03 17:16:41 +01:00
return false ;
2019-03-04 23:05:42 +00:00
}
2019-09-23 20:40:31 +01:00
se_aes_key_set ( 8 , bis_key [ 0 ] + 0x00 , 0x10 ) ;
se_aes_key_set ( 9 , bis_key [ 0 ] + 0x10 , 0x10 ) ;
2019-03-04 23:05:42 +00:00
2019-10-07 19:11:26 +01:00
gfx_printf ( " %kGot keys! \n %kValidate... " , COLOR_GREEN , COLOR_YELLOW ) ;
2019-10-06 20:05:50 +01:00
const char magic [ 4 ] = " CAL0 " ;
char buffer [ 4 ] ;
readData ( ( u8 * ) buffer , 0 , 4 , NULL ) ;
2019-10-07 19:11:26 +01:00
if ( memcmp ( magic , buffer , 4 ) = = 0 )
{
2019-10-06 20:05:50 +01:00
gfx_printf ( " %kOK! \n " , COLOR_GREEN ) ;
2019-10-07 19:11:26 +01:00
}
else
{
2019-10-06 20:05:50 +01:00
gfx_printf ( " %kError! \n " , COLOR_RED ) ;
return false ;
}
2019-10-07 19:11:26 +01:00
2020-06-06 14:46:20 +01:00
char serial [ 15 ] ;
2020-06-06 19:23:28 +01:00
readData ( ( u8 * ) serial , 0x250 , 14 , NULL ) ;
2020-06-03 14:00:41 +01:00
2020-06-06 14:46:20 +01:00
gfx_printf ( " %kCurrent serial: [%s] \n \n " , COLOR_BLUE , serial ) ;
2020-06-03 14:00:41 +01:00
2019-10-03 17:16:41 +01:00
return true ;
}
2019-03-04 23:05:42 +00:00
2019-10-04 19:48:24 +01:00
bool erase ( u32 offset , u32 length )
2019-10-03 17:16:41 +01:00
{
u8 * tmp = ( u8 * ) calloc ( length , sizeof ( u8 ) ) ;
2019-10-04 19:53:08 +01:00
bool result = writeData ( tmp , offset , length , NULL ) ;
2019-10-03 17:16:41 +01:00
free ( tmp ) ;
2019-10-04 19:48:24 +01:00
return result ;
2019-10-03 17:16:41 +01:00
}
2019-03-04 23:05:42 +00:00
2019-10-04 19:48:24 +01:00
bool writeSerial ( )
2019-10-03 17:16:41 +01:00
{
const char * junkSerial ;
2019-10-06 20:05:50 +01:00
if ( isSysNAND ( ) )
2019-10-03 17:16:41 +01:00
{
junkSerial = " XAW00000000000 " ;
}
else
{
junkSerial = " XAW00000000001 " ;
}
2019-03-04 23:05:42 +00:00
2020-06-03 14:00:41 +01:00
const u32 serialOffset = 0x250 ;
if ( ! writeData ( ( u8 * ) junkSerial , serialOffset , 14 , NULL ) )
return false ;
2020-06-06 14:46:20 +01:00
return calculateAndWriteCrc ( serialOffset , 0x1E ) ;
2019-10-03 17:16:41 +01:00
}
2019-09-24 23:49:22 +01:00
2019-10-04 19:48:24 +01:00
bool incognito ( )
2019-10-03 17:16:41 +01:00
{
2019-10-04 19:48:24 +01:00
gfx_printf ( " %kChecking if backup exists... \n " , COLOR_YELLOW ) ;
if ( ! checkBackupExists ( ) )
{
2019-10-06 20:05:50 +01:00
gfx_printf ( " %kI'm sorry Dave, I'm afraid I can't do that.. \n %kWill make a backup first... \n " , COLOR_RED , COLOR_YELLOW ) ;
2019-10-04 19:48:24 +01:00
if ( ! backupProdinfo ( ) )
return false ;
}
2019-03-04 23:05:42 +00:00
2020-06-06 18:29:44 +01:00
validateChecksums ( NULL ) ;
2020-06-06 09:04:24 +01:00
gfx_printf ( " %kWriting fake serial... \n " , COLOR_YELLOW ) ;
2019-10-04 19:48:24 +01:00
if ( ! writeSerial ( ) )
return false ;
2020-06-06 14:46:20 +01:00
/*
2020-06-06 09:04:24 +01:00
gfx_printf ( " %kErasing ECC-B233 device cert... \n " , COLOR_YELLOW ) ;
2020-06-06 18:29:44 +01:00
if ( ! erase ( 0x0480 , 0x180 ) )
2019-10-04 19:48:24 +01:00
return false ;
2020-06-06 14:46:20 +01:00
2020-06-06 18:29:44 +01:00
if ( ! calculateAndWriteCrc ( 0x0480 , 0x18E ) ) // whatever I do here, it crashes Atmos..?
return false ;
*/
2020-06-06 09:04:24 +01:00
gfx_printf ( " %kErasing SSL cert... \n " , COLOR_YELLOW ) ;
if ( ! erase ( 0x0AE0 , 0x800 ) )
2019-10-04 19:48:24 +01:00
return false ;
2019-10-07 19:11:26 +01:00
2020-06-06 09:04:24 +01:00
gfx_printf ( " %kErasing extended SSL key... \n " , COLOR_YELLOW ) ;
if ( ! erase ( 0x3AE0 , 0x130 ) )
2019-10-04 19:48:24 +01:00
return false ;
2019-10-07 19:11:26 +01:00
2020-06-06 18:29:44 +01:00
gfx_printf ( " %kWriting checksum... \n " , COLOR_YELLOW ) ;
if ( ! calculateAndWriteCrc ( 0x3AE0 , 0x13E ) )
return false ;
2020-06-06 09:04:24 +01:00
gfx_printf ( " %kErasing Amiibo ECDSA cert... \n " , COLOR_YELLOW ) ;
if ( ! erase ( 0x35A0 , 0x070 ) )
2019-10-04 19:48:24 +01:00
return false ;
2019-10-07 19:11:26 +01:00
2020-06-06 18:29:44 +01:00
gfx_printf ( " %kWriting checksum... \n " , COLOR_YELLOW ) ;
if ( ! calculateAndWriteCrc ( 0x35A0 , 0x07E ) )
return false ;
2020-06-06 09:04:24 +01:00
gfx_printf ( " %kErasing Amiibo ECQV-BLS root cert... \n " , COLOR_YELLOW ) ;
if ( ! erase ( 0x36A0 , 0x090 ) )
2019-10-04 19:48:24 +01:00
return false ;
2019-10-07 19:11:26 +01:00
2020-06-06 18:29:44 +01:00
gfx_printf ( " %kWriting checksum... \n " , COLOR_YELLOW ) ;
if ( ! calculateAndWriteCrc ( 0x36A0 , 0x09E ) )
return false ;
2020-06-06 09:04:24 +01:00
gfx_printf ( " %kErasing RSA-2048 extended device key... \n " , COLOR_YELLOW ) ;
2020-06-06 19:23:28 +01:00
if ( ! erase ( 0x3D70 , 0x240 ) ) // seems empty & unused!
2019-10-04 19:48:24 +01:00
return false ;
2019-10-07 19:11:26 +01:00
2020-06-06 09:04:24 +01:00
gfx_printf ( " %kErasing RSA-2048 device certificate... \n " , COLOR_YELLOW ) ;
2020-06-06 19:23:28 +01:00
if ( ! erase ( 0x3FC0 , 0x240 ) ) // seems empty & unused!
2019-10-04 19:48:24 +01:00
return false ;
2019-10-07 19:11:26 +01:00
2020-06-06 14:46:20 +01:00
gfx_printf ( " %kWriting SSL cert hash... \n " , COLOR_YELLOW ) ;
2019-10-04 19:48:24 +01:00
if ( ! writeClientCertHash ( ) )
return false ;
2019-10-07 19:11:26 +01:00
2020-06-06 14:46:20 +01:00
gfx_printf ( " %kWriting body hash... \n " , COLOR_YELLOW ) ;
2019-10-04 19:48:24 +01:00
if ( ! writeCal0Hash ( ) )
return false ;
2019-09-17 05:18:41 +01:00
2019-10-06 20:05:50 +01:00
gfx_printf ( " \n %kIncognito done! \n " , COLOR_GREEN ) ;
2019-10-04 19:48:24 +01:00
return true ;
2019-09-28 23:18:17 +01:00
}
2019-04-18 16:02:15 +01:00
2019-10-04 10:26:27 +01:00
u32 divideCeil ( u32 x , u32 y )
{
return 1 + ( ( x - 1 ) / y ) ;
}
2019-10-03 17:16:41 +01:00
void cleanUp ( )
{
2019-03-04 23:05:42 +00:00
2019-10-03 17:16:41 +01:00
h_cfg . emummc_force_disable = emummc_load_cfg ( ) ;
//nx_emmc_gpt_free(&gpt);
2019-10-04 19:48:24 +01:00
//emummc_storage_end(&storage);
2019-03-04 23:05:42 +00:00
}
2019-09-28 23:18:17 +01:00
static void _generate_kek ( u32 ks , const void * key_source , void * master_key , const void * kek_seed , const void * key_seed )
{
2019-03-04 23:05:42 +00:00
if ( ! _key_exists ( key_source ) | | ! _key_exists ( master_key ) | | ! _key_exists ( kek_seed ) )
return ;
se_aes_key_set ( ks , master_key , 0x10 ) ;
se_aes_unwrap_key ( ks , ks , kek_seed ) ;
se_aes_unwrap_key ( ks , ks , key_source ) ;
if ( key_seed & & _key_exists ( key_seed ) )
se_aes_unwrap_key ( ks , ks , key_seed ) ;
}
2020-06-18 22:31:37 +01:00
static void _get_device_key ( u32 ks , void * out_device_key , u32 revision , const void * device_key , const void * master_key ) {
if ( revision < KB_FIRMWARE_VERSION_400 )
memcpy ( out_device_key , device_key , 0x10 ) ;
revision - = KB_FIRMWARE_VERSION_400 ;
u8 temp_key [ 0x10 ] = { 0 } ;
se_aes_key_set ( ks , device_key , 0x10 ) ;
se_aes_crypt_ecb ( ks , 0 , temp_key , 0x10 , new_device_key_sources [ revision ] , 0x10 ) ;
se_aes_key_set ( ks , master_key , 0x10 ) ;
se_aes_unwrap_key ( ks , ks , new_device_keygen_sources [ revision ] ) ;
se_aes_crypt_ecb ( ks , 0 , out_device_key , 0x10 , temp_key , 0x10 ) ;
}
2019-09-28 23:18:17 +01:00
static inline u32 _read_le_u32 ( const void * buffer , u32 offset )
{
return ( * ( u8 * ) ( buffer + offset + 0 ) ) |
( * ( u8 * ) ( buffer + offset + 1 ) < < 0x08 ) |
( * ( u8 * ) ( buffer + offset + 2 ) < < 0x10 ) |
( * ( u8 * ) ( buffer + offset + 3 ) < < 0x18 ) ;
2019-03-04 23:05:42 +00:00
}
2019-10-04 19:53:08 +01:00
bool readData ( u8 * buffer , u32 offset , u32 length , void ( * progress_callback ) ( u32 , u32 ) )
2019-09-23 20:40:31 +01:00
{
2019-10-04 19:48:24 +01:00
if ( progress_callback ! = NULL )
{
( * progress_callback ) ( 0 , length ) ;
}
2019-10-04 13:08:33 +01:00
bool result = false ;
2019-09-28 23:18:17 +01:00
u32 sector = ( offset / NX_EMMC_BLOCKSIZE ) ;
u32 newOffset = ( offset % NX_EMMC_BLOCKSIZE ) ;
2019-09-26 07:56:27 +01:00
2019-10-04 19:48:24 +01:00
u32 sectorCount = divideCeil ( newOffset + length , NX_EMMC_BLOCKSIZE ) ;
2019-09-26 07:56:27 +01:00
2019-09-28 23:18:17 +01:00
u8 * tmp = ( u8 * ) malloc ( sectorCount * NX_EMMC_BLOCKSIZE ) ;
2019-09-26 07:56:27 +01:00
2019-10-04 10:26:27 +01:00
u32 clusterOffset = sector % SECTORS_IN_CLUSTER ;
2019-10-04 00:27:43 +01:00
u32 sectorOffset = 0 ;
2019-10-04 10:26:27 +01:00
while ( clusterOffset + sectorCount > SECTORS_IN_CLUSTER )
2019-10-04 00:27:43 +01:00
{
2019-10-04 13:08:33 +01:00
u32 sectorsToRead = SECTORS_IN_CLUSTER - clusterOffset ;
2019-10-07 18:26:40 +01:00
if ( ! RETRY ( prodinfo_read ( tmp + ( sectorOffset * NX_EMMC_BLOCKSIZE ) , sector , sectorsToRead ) ) )
2019-10-04 13:08:33 +01:00
goto out ;
2019-10-04 19:48:24 +01:00
2019-10-04 13:08:33 +01:00
sector + = sectorsToRead ;
sectorCount - = sectorsToRead ;
2019-10-04 00:27:43 +01:00
clusterOffset = 0 ;
2019-10-04 13:08:33 +01:00
sectorOffset + = sectorsToRead ;
2019-10-04 19:48:24 +01:00
if ( progress_callback ! = NULL )
{
( * progress_callback ) ( sectorOffset * NX_EMMC_BLOCKSIZE , length ) ;
}
2019-10-04 00:27:43 +01:00
}
2019-10-04 13:15:40 +01:00
if ( sectorCount = = 0 )
goto done ;
2019-10-04 00:27:43 +01:00
2019-10-07 18:26:40 +01:00
if ( ! RETRY ( prodinfo_read ( tmp + ( sectorOffset * NX_EMMC_BLOCKSIZE ) , sector , sectorCount ) ) )
2019-10-04 13:08:33 +01:00
goto out ;
2019-09-24 08:41:22 +01:00
2019-09-24 14:15:18 +01:00
memcpy ( buffer , tmp + newOffset , length ) ;
2019-10-04 13:08:33 +01:00
done :
result = true ;
2019-10-04 19:48:24 +01:00
if ( progress_callback ! = NULL )
{
( * progress_callback ) ( length , length ) ;
}
2019-10-04 13:08:33 +01:00
out :
2019-09-23 20:40:31 +01:00
free ( tmp ) ;
2019-10-04 13:08:33 +01:00
return result ;
2019-09-24 08:41:22 +01:00
}
2019-10-04 19:53:08 +01:00
bool writeData ( u8 * buffer , u32 offset , u32 length , void ( * progress_callback ) ( u32 , u32 ) )
2019-09-24 14:15:18 +01:00
{
2019-10-04 19:48:24 +01:00
if ( progress_callback ! = NULL )
{
( * progress_callback ) ( 0 , length ) ;
}
2019-10-04 13:08:33 +01:00
bool result = false ;
2019-09-24 23:49:22 +01:00
2019-10-04 19:48:24 +01:00
u32 initialLength = length ;
2019-10-04 13:08:33 +01:00
u8 * tmp_sec = ( u8 * ) malloc ( NX_EMMC_BLOCKSIZE ) ;
u8 * tmp = NULL ;
2019-09-24 23:49:22 +01:00
2019-10-04 13:08:33 +01:00
u32 sector = ( offset / NX_EMMC_BLOCKSIZE ) ;
u32 newOffset = ( offset % NX_EMMC_BLOCKSIZE ) ;
2019-09-28 23:18:17 +01:00
2019-10-04 13:08:33 +01:00
// if there is a sector offset, read involved sector, write data to it with offset and write back whole sector to be sector aligned
2019-10-04 13:15:40 +01:00
if ( newOffset > 0 )
{
2019-10-04 19:48:24 +01:00
u32 bytesToRead = NX_EMMC_BLOCKSIZE - newOffset ;
u32 bytesToWrite ;
if ( length > = bytesToRead )
2019-10-04 13:15:40 +01:00
{
2019-10-04 19:48:24 +01:00
bytesToWrite = bytesToRead ;
2019-10-04 13:08:33 +01:00
}
2019-10-04 13:15:40 +01:00
else
{
2019-10-04 19:48:24 +01:00
bytesToWrite = length ;
2019-10-04 13:08:33 +01:00
}
2019-10-07 18:26:40 +01:00
if ( ! RETRY ( prodinfo_read ( tmp_sec , sector , 1 ) ) )
2019-10-04 13:15:40 +01:00
goto out ;
2019-10-04 19:48:24 +01:00
memcpy ( tmp_sec + newOffset , buffer , bytesToWrite ) ;
2019-10-07 18:26:40 +01:00
if ( ! RETRY ( prodinfo_write ( tmp_sec , sector , 1 ) ) )
2019-10-04 13:08:33 +01:00
goto out ;
2019-10-04 19:48:24 +01:00
2019-10-04 13:08:33 +01:00
sector + + ;
2019-10-04 19:48:24 +01:00
length - = bytesToWrite ;
newOffset = bytesToWrite ;
2019-09-28 23:18:17 +01:00
2019-10-04 19:48:24 +01:00
if ( progress_callback ! = NULL )
{
( * progress_callback ) ( initialLength - length , initialLength ) ;
}
2019-10-04 13:08:33 +01:00
// are we done?
2019-10-04 13:15:40 +01:00
if ( length = = 0 )
goto done ;
2019-10-04 13:08:33 +01:00
}
2019-10-04 13:15:40 +01:00
2019-10-04 13:08:33 +01:00
// write whole sectors in chunks while being cluster aligned
2019-10-04 22:43:10 +01:00
u32 sectorCount = length / NX_EMMC_BLOCKSIZE ;
2019-10-04 13:08:33 +01:00
tmp = ( u8 * ) malloc ( sectorCount * NX_EMMC_BLOCKSIZE ) ;
2019-09-24 23:49:22 +01:00
2019-10-04 13:08:33 +01:00
u32 clusterOffset = sector % SECTORS_IN_CLUSTER ;
u32 sectorOffset = 0 ;
2019-10-04 19:48:24 +01:00
while ( clusterOffset + sectorCount > = SECTORS_IN_CLUSTER )
2019-10-04 13:08:33 +01:00
{
u32 sectorsToRead = SECTORS_IN_CLUSTER - clusterOffset ;
2019-10-07 18:26:40 +01:00
if ( ! RETRY ( prodinfo_write ( buffer + newOffset + ( sectorOffset * NX_EMMC_BLOCKSIZE ) , sector , sectorsToRead ) ) )
2019-10-04 13:08:33 +01:00
goto out ;
2019-10-04 19:48:24 +01:00
2019-10-04 13:08:33 +01:00
sector + = sectorsToRead ;
sectorOffset + = sectorsToRead ;
sectorCount - = sectorsToRead ;
clusterOffset = 0 ;
length - = sectorsToRead * NX_EMMC_BLOCKSIZE ;
2019-10-04 19:48:24 +01:00
if ( progress_callback ! = NULL )
{
( * progress_callback ) ( initialLength - length , initialLength ) ;
}
2019-10-04 13:08:33 +01:00
}
2019-09-24 23:49:22 +01:00
2019-10-04 13:08:33 +01:00
// write remaining sectors
2019-10-04 13:15:40 +01:00
if ( sectorCount > 0 )
{
2019-10-07 18:26:40 +01:00
if ( ! RETRY ( prodinfo_write ( buffer + newOffset + ( sectorOffset * NX_EMMC_BLOCKSIZE ) , sector , sectorCount ) ) )
2019-10-04 13:08:33 +01:00
goto out ;
2019-10-04 19:48:24 +01:00
2019-10-04 13:08:33 +01:00
length - = sectorCount * NX_EMMC_BLOCKSIZE ;
sector + = sectorCount ;
sectorOffset + = sectorCount ;
2019-10-04 19:48:24 +01:00
if ( progress_callback ! = NULL )
{
( * progress_callback ) ( initialLength - length , initialLength ) ;
}
2019-10-04 13:08:33 +01:00
}
2019-10-04 13:15:40 +01:00
// if there is data remaining that is smaller than a sector, read that sector, write remaining data to it and write back whole sector
if ( length = = 0 )
goto done ;
2019-10-04 19:48:24 +01:00
if ( length > NX_EMMC_BLOCKSIZE )
2019-10-04 13:15:40 +01:00
{
2019-10-04 19:48:24 +01:00
gfx_printf ( " %kERROR, ERRO! Length is %d! \n " , COLOR_RED , length ) ;
2019-10-04 13:08:33 +01:00
goto out ;
}
2019-10-04 19:48:24 +01:00
2019-10-07 18:26:40 +01:00
if ( ! RETRY ( prodinfo_read ( tmp_sec , sector , 1 ) ) )
2019-10-04 13:08:33 +01:00
goto out ;
2019-10-04 19:48:24 +01:00
memcpy ( tmp_sec , buffer + newOffset + ( sectorOffset * NX_EMMC_BLOCKSIZE ) , length ) ;
2019-10-07 18:26:40 +01:00
if ( ! RETRY ( prodinfo_write ( tmp_sec , sector , 1 ) ) )
2019-10-04 13:08:33 +01:00
goto out ;
2019-09-26 07:56:27 +01:00
2019-10-04 13:15:40 +01:00
done :
2019-10-04 13:08:33 +01:00
result = true ;
2019-10-04 19:48:24 +01:00
if ( progress_callback ! = NULL )
{
( * progress_callback ) ( initialLength , initialLength ) ;
}
2019-10-04 13:08:33 +01:00
out :
free ( tmp_sec ) ;
free ( tmp ) ;
return result ;
2019-09-24 23:49:22 +01:00
}
bool writeHash ( u32 hashOffset , u32 offset , u32 sz )
2019-09-28 23:18:17 +01:00
{
2019-10-04 19:48:24 +01:00
bool result = false ;
2019-10-04 00:27:43 +01:00
u8 * buffer = ( u8 * ) malloc ( sz ) ;
2019-10-04 19:53:08 +01:00
if ( ! readData ( buffer , offset , sz , NULL ) )
2019-10-04 19:48:24 +01:00
{
goto out ;
}
2019-09-28 23:18:17 +01:00
u8 hash [ 0x20 ] ;
2019-10-04 00:27:43 +01:00
se_calc_sha256 ( hash , buffer , sz ) ;
2019-09-24 14:15:18 +01:00
2019-10-04 19:53:08 +01:00
if ( ! writeData ( hash , hashOffset , 0x20 , NULL ) )
2019-10-04 19:48:24 +01:00
{
goto out ;
}
result = true ;
out :
2019-09-28 23:18:17 +01:00
free ( buffer ) ;
2019-10-04 19:48:24 +01:00
return result ;
2019-09-28 23:18:17 +01:00
}
2019-09-24 23:49:22 +01:00
2019-10-07 18:26:40 +01:00
# ifdef DEBUG
2019-10-06 20:05:50 +01:00
void screenshot ( const char * filename )
2019-10-04 19:48:24 +01:00
{
2019-10-06 20:05:50 +01:00
sd_mount ( ) ;
FIL fp ;
if ( f_open ( & fp , filename , FA_CREATE_ALWAYS | FA_WRITE ) ! = FR_OK )
{
gfx_printf ( " \n %kCannot write image! \n " , COLOR_RED ) ;
return ;
}
u32 size ;
u8 * buffer = gfx_bmp_screenshot ( & size ) ;
f_write ( & fp , buffer , size , NULL ) ;
f_close ( & fp ) ;
free ( buffer ) ;
2019-10-04 15:22:24 +01:00
}
2019-10-07 18:26:40 +01:00
# endif
2019-10-04 00:27:43 +01:00
2019-10-07 19:02:42 +01:00
bool verifyHash ( u32 hashOffset , u32 offset , u32 sz , u8 * blob )
2019-09-28 23:18:17 +01:00
{
bool result = false ;
2019-10-04 00:27:43 +01:00
u8 * buffer = ( u8 * ) malloc ( sz ) ;
2019-10-07 19:11:26 +01:00
if ( blob = = NULL )
{
if ( ! readData ( buffer , offset , sz , NULL ) )
2019-10-07 19:02:42 +01:00
goto out ;
2019-10-07 19:11:26 +01:00
}
else
{
2019-10-07 19:02:42 +01:00
memcpy ( buffer , blob + offset , sz ) ;
}
2019-09-28 23:18:17 +01:00
u8 hash1 [ 0x20 ] ;
2019-10-04 00:27:43 +01:00
se_calc_sha256 ( hash1 , buffer , sz ) ;
2019-09-24 14:15:18 +01:00
2019-09-28 23:18:17 +01:00
u8 hash2 [ 0x20 ] ;
2019-09-24 14:15:18 +01:00
2019-10-07 19:11:26 +01:00
if ( blob = = NULL )
{
if ( ! readData ( hash2 , hashOffset , 0x20 , NULL ) )
2019-10-07 19:02:42 +01:00
goto out ;
2019-10-07 19:11:26 +01:00
}
else
{
2019-10-07 19:02:42 +01:00
memcpy ( hash2 , blob + hashOffset , 0x20 ) ;
}
2019-09-24 14:15:18 +01:00
2020-06-18 22:31:37 +01:00
if ( memcmp ( hash1 , hash2 , 0x20 ) ! = 0 )
2019-09-28 23:18:17 +01:00
{
EPRINTF ( " error: hash verification failed \n " ) ;
2019-10-07 18:26:40 +01:00
// gfx_hexdump(0, hash1, 0x20);
// gfx_hexdump(0, hash2, 0x20);
2019-10-04 22:43:10 +01:00
goto out ;
2019-09-28 23:18:17 +01:00
}
2019-09-25 09:12:15 +01:00
2019-10-04 22:43:10 +01:00
result = true ;
out :
2019-09-28 23:18:17 +01:00
free ( buffer ) ;
return result ;
}
2019-09-25 09:12:15 +01:00
2019-10-07 19:40:41 +01:00
s32 getClientCertSize ( u8 * blob )
2019-09-28 23:18:17 +01:00
{
2019-10-07 19:11:18 +01:00
s32 buffer ;
2019-10-07 19:40:41 +01:00
if ( blob = = NULL )
2019-10-07 19:11:26 +01:00
{
2019-10-07 19:40:41 +01:00
if ( ! RETRY ( readData ( ( u8 * ) & buffer , 0x0AD0 , sizeof ( buffer ) , NULL ) ) )
{
return - 1 ;
}
}
else
{
memcpy ( & buffer , blob + 0x0AD0 , sizeof ( buffer ) ) ;
2019-10-07 19:11:18 +01:00
}
2019-09-28 23:18:17 +01:00
return buffer ;
2019-09-24 14:15:18 +01:00
}
2019-10-07 19:40:41 +01:00
s32 getCalibrationDataSize ( u8 * blob )
2019-09-24 14:15:18 +01:00
{
2019-10-07 19:11:18 +01:00
s32 buffer ;
2019-10-07 19:40:41 +01:00
if ( blob = = NULL )
2019-10-07 19:11:26 +01:00
{
2019-10-07 19:40:41 +01:00
if ( ! RETRY ( readData ( ( u8 * ) & buffer , 0x08 , sizeof ( buffer ) , NULL ) ) )
{
return - 1 ;
}
}
else
{
memcpy ( & buffer , blob + 0x08 , sizeof ( buffer ) ) ;
2019-10-07 19:11:18 +01:00
}
2019-10-07 19:40:41 +01:00
2019-09-24 14:15:18 +01:00
return buffer ;
}
2019-09-24 23:49:22 +01:00
bool writeCal0Hash ( )
2019-10-07 19:11:26 +01:00
{
2019-10-07 19:40:41 +01:00
s32 calibrationSize = getCalibrationDataSize ( NULL ) ;
2019-10-07 19:11:26 +01:00
if ( calibrationSize = = - 1 )
2019-10-07 19:11:18 +01:00
return false ;
2019-10-07 19:11:26 +01:00
2019-10-07 19:11:18 +01:00
return writeHash ( 0x20 , 0x40 , calibrationSize ) ;
2019-09-24 23:49:22 +01:00
}
bool writeClientCertHash ( )
{
2019-10-07 19:40:41 +01:00
s32 certSize = getClientCertSize ( NULL ) ;
2019-10-07 19:11:26 +01:00
if ( certSize = = - 1 )
2019-10-07 19:11:18 +01:00
return false ;
return writeHash ( 0x12E0 , 0xAE0 , certSize ) ;
2019-09-24 23:49:22 +01:00
}
2019-10-07 19:02:42 +01:00
bool verifyCal0Hash ( u8 * blob )
2019-09-28 23:18:17 +01:00
{
2019-10-07 19:40:41 +01:00
s32 calibrationSize = getCalibrationDataSize ( blob ) ;
2019-10-07 19:11:26 +01:00
if ( calibrationSize = = - 1 )
2019-10-07 19:11:18 +01:00
return false ;
return verifyHash ( 0x20 , 0x40 , calibrationSize , blob ) ;
2019-09-24 23:49:22 +01:00
}
2019-10-07 19:02:42 +01:00
bool verifyClientCertHash ( u8 * blob )
2019-09-24 23:49:22 +01:00
{
2019-10-07 19:40:41 +01:00
s32 certSize = getClientCertSize ( blob ) ;
2019-10-07 19:11:26 +01:00
if ( certSize = = - 1 )
2019-10-07 19:11:18 +01:00
return false ;
return verifyHash ( 0x12E0 , 0xAE0 , certSize , blob ) ;
2019-09-24 23:49:22 +01:00
}
2019-10-07 19:02:42 +01:00
bool verifyProdinfo ( u8 * blob )
2019-09-24 14:15:18 +01:00
{
2019-10-07 19:02:42 +01:00
gfx_printf ( " %kVerifying client cert hash and CAL0 hash%s... \n " , COLOR_YELLOW , blob ! = NULL ? " \n from backup " : " " ) ;
2019-10-03 17:16:41 +01:00
2019-10-07 19:02:42 +01:00
if ( verifyClientCertHash ( blob ) & & verifyCal0Hash ( blob ) )
2019-10-03 17:16:41 +01:00
{
2020-06-06 18:29:44 +01:00
validateChecksums ( blob ) ;
2019-10-07 19:02:42 +01:00
char serial [ 15 ] = " " ;
2019-10-07 19:11:26 +01:00
if ( blob = = NULL )
{
2019-10-07 19:02:42 +01:00
readData ( ( u8 * ) serial , 0x250 , 14 , NULL ) ;
2019-10-07 19:11:26 +01:00
}
else
{
2019-10-07 19:02:42 +01:00
memcpy ( serial , blob + 0x250 , 14 ) ;
}
2019-10-07 19:11:26 +01:00
2019-10-07 19:02:42 +01:00
gfx_printf ( " %kVerification successful! \n %kSerial:%s \n " , COLOR_GREEN , COLOR_BLUE , serial ) ;
2019-10-03 17:16:41 +01:00
return true ;
}
2019-10-07 19:02:42 +01:00
gfx_printf ( " %kVerification not successful! \n " , COLOR_RED ) ;
2019-10-03 17:16:41 +01:00
return false ;
}
2019-10-04 19:48:24 +01:00
void print_progress ( u32 count , u32 max )
2019-10-03 17:16:41 +01:00
{
2019-10-08 09:36:50 +01:00
tui_pbar ( 0 , gfx_con . y + 1 , ( int ) ( count * 100 / ( float ) max ) , COLOR_BLUE , COLOR_ORANGE ) ;
2019-09-24 14:15:18 +01:00
}
2019-09-24 23:49:22 +01:00
2019-10-06 20:05:50 +01:00
// bool getLastBackup()
// {
// DIR dir;
// //char* path = "sd:/incognito";
// char path[255];
// strcpy(path, "sd:/incognito");
// FILINFO fno;
// FRESULT res;
// res = f_opendir(&dir, path); /* Open the directory */
// if (res == FR_OK)
// {
// for (;;)
// {
// res = f_readdir(&dir, &fno); /* Read a directory item */
// if (res != FR_OK || fno.fname[0] == 0)
// break; /* Break on error or end of dir */
// if ((fno.fattrib & AM_DIR) == 0)
// { /* It is not a directory */
// gfx_printf("%s/%s\n", path, fno.fname);
// }
// }
// f_closedir(&dir);
// }
// return res;
// }
bool isSysNAND ( )
2019-10-04 19:48:24 +01:00
{
2019-10-06 20:05:50 +01:00
return ( ! emu_cfg . enabled | | h_cfg . emummc_force_disable ) ;
2019-10-04 19:48:24 +01:00
}
2019-10-04 15:22:24 +01:00
2019-10-04 19:48:24 +01:00
bool checkBackupExists ( )
{
char * name ;
2019-10-06 20:05:50 +01:00
if ( isSysNAND ( ) )
2019-10-04 19:48:24 +01:00
{
name = BACKUP_NAME_SYSNAND ;
}
else
{
name = BACKUP_NAME_EMUNAND ;
}
return f_stat ( name , NULL ) = = FR_OK ;
2019-10-04 15:22:24 +01:00
}
2019-09-28 23:18:17 +01:00
bool backupProdinfo ( )
{
2019-10-04 19:48:24 +01:00
bool result = false ;
2019-10-03 17:16:41 +01:00
char * name ;
2019-10-06 20:05:50 +01:00
if ( isSysNAND ( ) )
2019-10-03 17:16:41 +01:00
{
2019-10-04 19:48:24 +01:00
name = BACKUP_NAME_SYSNAND ;
2019-09-28 23:18:17 +01:00
}
2019-10-03 17:16:41 +01:00
else
{
2019-10-04 19:48:24 +01:00
name = BACKUP_NAME_EMUNAND ;
2019-10-03 17:16:41 +01:00
}
gfx_printf ( " %kBacking up %s... \n " , COLOR_YELLOW , name ) ;
2019-10-04 19:48:24 +01:00
if ( checkBackupExists ( ) )
2019-09-28 23:18:17 +01:00
{
2019-10-07 19:02:42 +01:00
gfx_printf ( " %kBackup already exists! \n Will rename old backup. \n " , COLOR_ORANGE ) ;
2019-10-04 19:48:24 +01:00
u32 filenameSuffix = 0 ;
char newName [ 255 ] ;
do
{
sprintf ( newName , " %s.%d " , name , filenameSuffix ) ;
filenameSuffix + + ;
} while ( f_stat ( newName , NULL ) = = FR_OK ) ;
f_rename ( name , newName ) ;
2019-10-06 20:05:50 +01:00
gfx_printf ( " %kOld backup renamed to: \n %s \n " , COLOR_YELLOW , newName ) ;
2019-09-24 23:49:22 +01:00
}
2019-10-03 17:16:41 +01:00
2019-10-04 19:48:24 +01:00
FIL fp ;
if ( f_open ( & fp , name , FA_CREATE_ALWAYS | FA_WRITE ) ! = FR_OK )
2019-09-28 23:18:17 +01:00
{
2019-10-04 19:48:24 +01:00
gfx_printf ( " \n %kCannot write to %s! \n " , COLOR_RED , name ) ;
return false ;
}
2019-10-03 17:16:41 +01:00
2019-10-04 19:48:24 +01:00
u8 * bufferNX = ( u8 * ) malloc ( PRODINFO_SIZE ) ;
gfx_printf ( " %kReading from NAND... \n " , COLOR_YELLOW ) ;
2019-10-04 19:53:08 +01:00
if ( ! readData ( bufferNX , 0 , PRODINFO_SIZE , print_progress ) )
2019-10-04 19:48:24 +01:00
{
gfx_printf ( " \n %kError reading from NAND! \n " , COLOR_RED ) ;
goto out ;
2019-09-24 23:49:22 +01:00
}
2019-10-07 19:40:41 +01:00
gfx_putc ( ' \n ' ) ;
2019-10-07 19:11:26 +01:00
if ( ! verifyProdinfo ( bufferNX ) )
{
2019-10-07 19:02:42 +01:00
goto out ;
}
2019-10-04 19:48:24 +01:00
gfx_printf ( " %k \n Writing to file... \n " , COLOR_YELLOW ) ;
u32 bytesWritten ;
if ( f_write ( & fp , bufferNX , PRODINFO_SIZE , & bytesWritten ) ! = FR_OK | | bytesWritten ! = PRODINFO_SIZE )
2019-09-28 23:18:17 +01:00
{
2019-10-04 19:48:24 +01:00
gfx_printf ( " \n %kError writing to file! \n Please try again. If this doesn't work, you don't have a working backup! \n " , COLOR_RED ) ;
goto out ;
2019-09-28 23:18:17 +01:00
}
2019-10-04 19:48:24 +01:00
f_sync ( & fp ) ;
2019-09-28 23:18:17 +01:00
2019-10-04 19:48:24 +01:00
result = true ;
2019-10-06 20:05:50 +01:00
gfx_printf ( " \n %kBackup to %s done! \n " , COLOR_GREEN , name ) ;
2019-09-28 23:18:17 +01:00
2019-10-04 19:48:24 +01:00
out :
2019-10-03 17:16:41 +01:00
f_close ( & fp ) ;
free ( bufferNX ) ;
2019-09-25 09:12:15 +01:00
2019-10-04 19:48:24 +01:00
return result ;
2019-09-25 09:12:15 +01:00
}
2019-09-28 23:18:17 +01:00
bool restoreProdinfo ( )
{
2019-10-04 19:48:24 +01:00
bool result = false ;
2019-09-28 23:18:17 +01:00
sd_mount ( ) ;
2019-10-07 19:02:42 +01:00
const char * name ;
2019-10-06 20:05:50 +01:00
if ( isSysNAND ( ) )
2019-10-03 17:16:41 +01:00
{
2019-10-04 19:48:24 +01:00
name = BACKUP_NAME_SYSNAND ;
2019-09-28 23:18:17 +01:00
}
2019-10-03 17:16:41 +01:00
else
{
2019-10-04 19:48:24 +01:00
name = BACKUP_NAME_EMUNAND ;
2019-10-03 17:16:41 +01:00
}
2019-10-07 19:11:26 +01:00
2019-10-04 19:48:24 +01:00
gfx_printf ( " %kRestoring from %s... \n " , COLOR_YELLOW , name ) ;
2019-09-28 23:18:17 +01:00
FIL fp ;
if ( f_open ( & fp , name , FA_READ ) ! = FR_OK )
{
2019-10-04 19:48:24 +01:00
gfx_printf ( " \n %kCannot open %s! \n " , COLOR_RED , name ) ;
2019-09-28 23:18:17 +01:00
return false ;
}
2019-10-03 17:16:41 +01:00
2019-10-04 19:48:24 +01:00
u8 * bufferNX = ( u8 * ) malloc ( PRODINFO_SIZE ) ;
u32 bytesRead ;
gfx_printf ( " %kReading from file... \n " , COLOR_YELLOW ) ;
if ( f_read ( & fp , bufferNX , PRODINFO_SIZE , & bytesRead ) ! = FR_OK | | bytesRead ! = PRODINFO_SIZE )
2019-09-28 23:18:17 +01:00
{
2019-10-04 19:48:24 +01:00
gfx_printf ( " \n %kError reading from file! \n " , COLOR_RED ) ;
goto out ;
2019-09-28 23:18:17 +01:00
}
2019-10-07 19:11:26 +01:00
if ( ! verifyProdinfo ( bufferNX ) )
{
2019-10-07 19:02:42 +01:00
goto out ;
}
2019-10-04 19:48:24 +01:00
gfx_printf ( " %kWriting to NAND... \n " , COLOR_YELLOW ) ;
2019-10-04 19:53:08 +01:00
if ( ! writeData ( bufferNX , 0 , PRODINFO_SIZE , print_progress ) )
2019-10-03 17:16:41 +01:00
{
2019-10-04 19:48:24 +01:00
gfx_printf ( " \n %kError writing to NAND! \n This is bad. Try again, because your switch probably won't boot. \n "
" If you see this error again, you should restore via NAND backup in hekate. \n " ,
COLOR_RED ) ;
goto out ;
2019-10-03 17:16:41 +01:00
}
2019-09-25 09:12:15 +01:00
2019-10-04 19:48:24 +01:00
result = true ;
gfx_printf ( " \n %kRestore from %s done! \n \n " , COLOR_GREEN , name ) ;
out :
2019-09-28 23:18:17 +01:00
f_close ( & fp ) ;
2019-10-04 19:48:24 +01:00
free ( bufferNX ) ;
2019-09-28 23:18:17 +01:00
2019-10-04 19:48:24 +01:00
return result ;
2019-09-24 23:49:22 +01:00
}