2020-04-16 01:06:41 +01:00
/*
2020-07-03 10:31:22 +01:00
* gamecard . h
2020-04-16 01:06:41 +01:00
*
2020-07-03 10:31:22 +01:00
* Copyright ( c ) 2020 , DarkMatterCore < pabloacurielz @ gmail . com > .
*
* This file is part of nxdumptool ( https : //github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software ; you can redistribute it and / or modify it
2020-04-16 01:06:41 +01:00
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
2020-07-03 10:31:22 +01:00
* nxdumptool is distributed in the hope it will be useful , but WITHOUT
2020-04-16 01:06:41 +01:00
* 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/>.
*/
2020-04-15 06:59:12 +01:00
# pragma once
# ifndef __GAMECARD_H__
# define __GAMECARD_H__
# include "fs_ext.h"
2020-07-06 01:10:07 +01:00
# define GAMECARD_HEAD_MAGIC 0x48454144 /* "HEAD". */
2020-04-15 06:59:12 +01:00
2020-07-03 10:31:22 +01:00
# define GAMECARD_MEDIA_UNIT_SIZE 0x200
2020-07-17 19:42:48 +01:00
# define GAMECARD_MEDIA_UNIT_OFFSET(x) ((u64)(x) * GAMECARD_MEDIA_UNIT_SIZE)
2020-04-15 06:59:12 +01:00
2020-07-13 07:36:17 +01:00
# define GAMECARD_UPDATE_TID (u64)0x0100000000000816
2020-08-13 07:01:23 +01:00
# define GAMECARD_CERTIFICATE_OFFSET 0x7000
2020-07-22 09:03:28 +01:00
/// Encrypted using AES-128-ECB with the common titlekek generator key (stored in the .rodata segment from the Lotus firmware).
typedef struct {
u64 package_id ; ///< Matches package_id from GameCardHeader.
u8 reserved [ 0x8 ] ; ///< Just zeroes.
} GameCardKeySource ;
2020-07-15 23:50:34 +01:00
/// Plaintext area. Dumped from FS program memory.
2020-07-13 07:36:17 +01:00
typedef struct {
2020-07-22 09:03:28 +01:00
GameCardKeySource key_source ;
2020-07-15 23:50:34 +01:00
u8 encrypted_titlekey [ 0x10 ] ; ///< Encrypted using AES-128-CCM with the decrypted key_source and the nonce from this section.
u8 mac [ 0x10 ] ; ///< Used to verify the validity of the decrypted titlekey.
2020-07-22 09:03:28 +01:00
u8 nonce [ 0xC ] ; ///< Used as the IV to decrypt encrypted_titlekey using AES-128-CCM.
2020-07-13 07:36:17 +01:00
u8 reserved [ 0x1C4 ] ;
2020-07-15 23:50:34 +01:00
} GameCardInitialData ;
/// Encrypted using AES-128-CTR with the key and IV/counter from the `GameCardTitleKeyEncryption` section. Assumed to be all zeroes in retail gamecards.
typedef struct {
u8 titlekey [ 0x10 ] ; ///< Decrypted titlekey from the `GameCardInitialData` section.
u8 reserved [ 0xCF0 ] ;
} GameCardTitleKey ;
2020-07-17 19:42:48 +01:00
/// Encrypted using RSA-2048-OAEP and a private OAEP key from AuthoringTool. Assumed to be all zeroes in retail gamecards.
2020-07-15 23:50:34 +01:00
typedef struct {
2020-07-17 19:42:48 +01:00
u8 titlekey_encryption_key [ 0x10 ] ; ///< Used as the AES-128-CTR key for the `GameCardTitleKey` section. Randomly generated during XCI creation by AuthoringTool.
u8 titlekey_encryption_iv [ 0x10 ] ; ///< Used as the AES-128-CTR IV/counter for the `GameCardTitleKey` section. Randomly generated during XCI creation by AuthoringTool.
2020-07-15 23:50:34 +01:00
u8 reserved [ 0xE0 ] ;
} GameCardTitleKeyEncryption ;
/// Used to secure communications between the Lotus and the inserted gamecard.
/// Precedes the gamecard header.
typedef struct {
GameCardInitialData initial_data ;
GameCardTitleKey titlekey_block ;
GameCardTitleKeyEncryption titlekey_encryption ;
2020-07-13 07:36:17 +01:00
} GameCardKeyArea ;
2020-04-15 06:59:12 +01:00
typedef enum {
GameCardKekIndex_Version0 = 0 ,
GameCardKekIndex_VersionForDev = 1
} GameCardKekIndex ;
typedef struct {
u8 kek_index : 4 ; ///< GameCardKekIndex.
u8 titlekey_dec_index : 4 ;
2020-07-22 09:03:28 +01:00
} GameCardKeyIndex ;
2020-04-15 06:59:12 +01:00
typedef enum {
2020-04-16 05:37:16 +01:00
GameCardRomSize_1GiB = 0xFA ,
GameCardRomSize_2GiB = 0xF8 ,
GameCardRomSize_4GiB = 0xF0 ,
GameCardRomSize_8GiB = 0xE0 ,
GameCardRomSize_16GiB = 0xE1 ,
GameCardRomSize_32GiB = 0xE2
2020-04-15 06:59:12 +01:00
} GameCardRomSize ;
2020-05-01 16:06:24 +01:00
typedef enum {
GameCardFlags_AutoBoot = BIT ( 0 ) ,
GameCardFlags_HistoryErase = BIT ( 1 ) ,
GameCardFlags_RepairTool = BIT ( 2 ) ,
GameCardFlags_DifferentRegionCupToTerraDevice = BIT ( 3 ) ,
GameCardFlags_DifferentRegionCupToGlobalDevice = BIT ( 4 )
2020-04-15 06:59:12 +01:00
} GameCardFlags ;
typedef enum {
2020-07-22 09:03:28 +01:00
GameCardSelSec_ForT1 = 1 ,
GameCardSelSec_ForT2 = 2
2020-04-15 06:59:12 +01:00
} GameCardSelSec ;
typedef enum {
2020-07-22 09:03:28 +01:00
GameCardFwVersion_ForDev = 0 ,
GameCardFwVersion_Before400NUP = 1 , ///< cup_version < 268435456 (4.0.0-0.0) in GameCardHeaderEncryptedArea.
GameCardFwVersion_Since400NUP = 2 ///< cup_version >= 268435456 (4.0.0-0.0) in GameCardHeaderEncryptedArea.
2020-04-15 06:59:12 +01:00
} GameCardFwVersion ;
typedef enum {
2020-07-22 09:03:28 +01:00
GameCardAccCtrl1_25MHz = 0xA10011 ,
GameCardAccCtrl1_50MHz = 0xA10010 ///< GameCardRomSize_8GiB or greater.
} GameCardAccCtrl1 ;
2020-04-15 06:59:12 +01:00
typedef enum {
GameCardCompatibilityType_Normal = 0 ,
GameCardCompatibilityType_Terra = 1
} GameCardCompatibilityType ;
typedef struct {
2020-07-15 23:50:34 +01:00
u32 GameCardFwMode_Relstep : 8 ;
u32 GameCardFwMode_Micro : 8 ;
u32 GameCardFwMode_Minor : 8 ;
u32 GameCardFwMode_Major : 8 ;
} GameCardFwMode ;
typedef struct {
2020-07-22 09:03:28 +01:00
u32 GameCardCupVersion_MinorRelstep : 8 ;
u32 GameCardCupVersion_MajorRelstep : 8 ;
u32 GameCardCupVersion_Micro : 4 ;
u32 GameCardCupVersion_Minor : 6 ;
u32 GameCardCupVersion_Major : 6 ;
} GameCardCupVersion ;
2020-07-15 23:50:34 +01:00
2020-10-04 10:25:42 +01:00
/// Encrypted using AES-128-CBC with the XCI header key (found in FS program memory under newer versions of HOS) and the IV from `GameCardHeader`.
/// Key hashes for documentation purposes:
/// Production XCI header key hash: 2E36CC55157A351090A73E7AE77CF581F69B0B6E48FB066C984879A6ED7D2E96
/// Development XCI header key hash: 61D5C02244188810E2E3DE69341AC0F3C7653D370C6D3F77CA82B0B7E59F39AD
2020-07-15 23:50:34 +01:00
typedef struct {
u64 fw_version ; ///< GameCardFwVersion.
2020-07-22 09:03:28 +01:00
u32 acc_ctrl_1 ; ///< GameCardAccCtrl1.
2020-07-15 23:50:34 +01:00
u32 wait_1_time_read ; ///< Always 0x1388.
u32 wait_2_time_read ; ///< Always 0.
u32 wait_1_time_write ; ///< Always 0.
u32 wait_2_time_write ; ///< Always 0.
GameCardFwMode fw_mode ;
2020-07-22 09:03:28 +01:00
GameCardCupVersion cup_version ;
2020-07-15 23:50:34 +01:00
u8 compatibility_type ; ///< GameCardCompatibilityType.
2020-05-01 16:11:18 +01:00
u8 reserved_1 [ 0x3 ] ;
2020-07-22 09:03:28 +01:00
u64 cup_hash ;
u64 cup_id ; ///< Must match GAMECARD_UPDATE_TID.
2020-05-01 16:11:18 +01:00
u8 reserved_2 [ 0x38 ] ;
2020-07-15 23:50:34 +01:00
} GameCardHeaderEncryptedArea ;
2020-04-15 06:59:12 +01:00
2020-07-15 23:50:34 +01:00
/// Placed after the `GameCardKeyArea` section.
2020-04-15 06:59:12 +01:00
typedef struct {
u8 signature [ 0x100 ] ; ///< RSA-2048 PKCS #1 signature over the rest of the header.
u32 magic ; ///< "HEAD".
u32 secure_area_start_address ; ///< Expressed in GAMECARD_MEDIA_UNIT_SIZE blocks.
u32 backup_area_start_address ; ///< Always 0xFFFFFFFF.
2020-07-22 09:03:28 +01:00
GameCardKeyIndex key_index ;
2020-04-15 06:59:12 +01:00
u8 rom_size ; ///< GameCardRomSize.
2020-07-22 09:03:28 +01:00
u8 header_version ; ///< Always 0.
2020-05-01 16:06:24 +01:00
u8 flags ; ///< GameCardFlags.
2020-04-15 06:59:12 +01:00
u64 package_id ;
u32 valid_data_end_address ; ///< Expressed in GAMECARD_MEDIA_UNIT_SIZE blocks.
u8 reserved [ 0x4 ] ;
u8 iv [ 0x10 ] ;
u64 partition_fs_header_address ; ///< Root HFS0 header offset.
u64 partition_fs_header_size ; ///< Root HFS0 header size.
u8 partition_fs_header_hash [ SHA256_HASH_SIZE ] ;
u8 initial_data_hash [ SHA256_HASH_SIZE ] ;
u32 sel_sec ; ///< GameCardSelSec.
2020-07-22 09:03:28 +01:00
u32 sel_t1_key ; ///< Always 2.
u32 sel_key ; ///> Always 0.
2020-04-15 06:59:12 +01:00
u32 normal_area_end_address ; ///< Expressed in GAMECARD_MEDIA_UNIT_SIZE blocks.
2020-07-15 23:50:34 +01:00
GameCardHeaderEncryptedArea encrypted_area ;
2020-04-15 06:59:12 +01:00
} GameCardHeader ;
2020-07-17 06:01:31 +01:00
typedef enum {
GameCardStatus_NotInserted = 0 ,
GameCardStatus_InsertedAndInfoNotLoaded = 1 , ///< Most likely related to the "nogc" patch being enabled. Means nothing at all can be done with the inserted gamecard.
GameCardStatus_InsertedAndInfoLoaded = 2
} GameCardStatus ;
2020-04-17 22:59:05 +01:00
typedef enum {
2020-04-24 10:38:13 +01:00
GameCardHashFileSystemPartitionType_Root = 0 ,
GameCardHashFileSystemPartitionType_Update = 1 ,
GameCardHashFileSystemPartitionType_Logo = 2 , ///< Only available in GameCardFwVersion_Since400NUP gamecards.
GameCardHashFileSystemPartitionType_Normal = 3 ,
2020-07-16 00:43:58 +01:00
GameCardHashFileSystemPartitionType_Secure = 4 ,
2020-10-03 18:09:29 +01:00
GameCardHashFileSystemPartitionType_Boot = 5 , ///< Only available in Terra (Tencent) gamecards.
GameCardHashFileSystemPartitionType_Count = 6 ///< Not a real value.
2020-04-17 22:59:05 +01:00
} GameCardHashFileSystemPartitionType ;
2020-04-15 06:59:12 +01:00
/// Initializes data needed to access raw gamecard storage areas.
/// Also spans a background thread to automatically detect gamecard status changes and to cache data from the inserted gamecard.
2020-05-03 00:40:50 +01:00
bool gamecardInitialize ( void ) ;
2020-04-15 06:59:12 +01:00
/// Deinitializes data generated by gamecardInitialize().
/// This includes destroying the background gamecard detection thread and freeing all cached gamecard data.
void gamecardExit ( void ) ;
2020-05-03 09:40:08 +01:00
/// Returns an usermode gamecard status change event that can be used to wait for status changes on other threads.
/// If the gamecard interface hasn't been initialized, this returns NULL.
UEvent * gamecardGetStatusChangeUserEvent ( void ) ;
2020-07-17 06:01:31 +01:00
/// Returns the current GameCardStatus value.
u8 gamecardGetStatus ( void ) ;
2020-04-15 06:59:12 +01:00
/// Used to read data from the inserted gamecard.
2020-04-15 21:50:07 +01:00
/// All required handles, changes between normal <-> secure storage areas and proper offset calculations are managed internally.
2020-04-24 10:38:13 +01:00
/// 'offset' + 'read_size' must not exceed the value returned by gamecardGetTotalSize().
bool gamecardReadStorage ( void * out , u64 read_size , u64 offset ) ;
2020-04-15 06:59:12 +01:00
/// Miscellaneous functions.
2020-04-24 10:38:13 +01:00
2020-07-13 07:36:17 +01:00
bool gamecardGetKeyArea ( GameCardKeyArea * out ) ;
2020-04-15 06:59:12 +01:00
bool gamecardGetHeader ( GameCardHeader * out ) ;
2020-07-13 07:36:17 +01:00
bool gamecardGetCertificate ( FsGameCardCertificate * out ) ;
2020-04-16 05:37:16 +01:00
bool gamecardGetTotalSize ( u64 * out ) ;
bool gamecardGetTrimmedSize ( u64 * out ) ;
bool gamecardGetRomCapacity ( u64 * out ) ; ///< Not the same as gamecardGetTotalSize().
2020-04-15 06:59:12 +01:00
bool gamecardGetBundledFirmwareUpdateVersion ( u32 * out ) ;
2020-10-03 18:09:29 +01:00
/// Returns a pointer to a string holding the name of the provided hash file system partition type. Returns NULL if the provided value is invalid.
2020-07-29 22:02:21 +01:00
const char * gamecardGetHashFileSystemPartitionName ( u8 hfs_partition_type ) ;
2020-04-24 10:38:13 +01:00
/// Retrieves the entry count from a hash FS partition.
bool gamecardGetEntryCountFromHashFileSystemPartition ( u8 hfs_partition_type , u32 * out_count ) ;
/// Retrieves info from a hash FS partition entry using an entry index.
/// 'out_offset', 'out_size' or 'out_name' may be set to NULL, but at least one of them must be a valid pointer.
/// If 'out_name' != NULL and the function call succeeds, a pointer to a heap allocated buffer is returned.
bool gamecardGetEntryInfoFromHashFileSystemPartitionByIndex ( u8 hfs_partition_type , u32 idx , u64 * out_offset , u64 * out_size , char * * out_name ) ;
/// Retrieves info from a hash FS partition entry using an entry name.
/// 'out_offset' or 'out_size' may be set to NULL, but at least one of them must be a valid pointer.
bool gamecardGetEntryInfoFromHashFileSystemPartitionByName ( u8 hfs_partition_type , const char * name , u64 * out_offset , u64 * out_size ) ;
2020-04-16 11:13:11 +01:00
2020-04-15 06:59:12 +01:00
# endif /* __GAMECARD_H__ */