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-12-23 17:48:57 +00:00
* Copyright ( c ) 2020 - 2021 , DarkMatterCore < pabloacurielz @ gmail . com > .
2020-07-03 10:31:22 +01:00
*
* 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"
2021-03-07 23:22:49 +00:00
# include "hfs.h"
2020-04-15 06:59:12 +01:00
2021-03-24 17:25:19 +00:00
# ifdef __cplusplus
extern " C " {
# endif
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-10-13 03:46:59 +01:00
# define GAMECARD_UPDATE_TID SYSTEM_UPDATE_TID
2020-07-13 07:36:17 +01:00
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 ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( GameCardKeySource , 0x10 ) ;
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 ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( GameCardInitialData , 0x200 ) ;
2020-07-15 23:50:34 +01:00
/// 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 ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( GameCardTitleKey , 0xD00 ) ;
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 ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( GameCardTitleKeyEncryption , 0x100 ) ;
2020-07-15 23:50:34 +01:00
/// 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 ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( GameCardKeyArea , 0x1000 ) ;
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
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( GameCardKeyIndex , 0x1 ) ;
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 {
2021-02-12 20:35:23 +00:00
GameCardFwVersion_ForDev = 0 ,
GameCardFwVersion_ForProd = BIT ( 0 ) ,
GameCardFwVersion_ForProdSince400NUP = BIT ( 1 ) , ///< cup_version >= 268435456 (4.0.0-0.0) in GameCardHeaderEncryptedArea.
GameCardFwVersion_ForProdSince1100NUP = BIT ( 2 ) ///< cup_version >= 738197504 (11.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 ;
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.
2020-10-08 19:31:09 +01:00
VersionType2 fw_mode ;
VersionType1 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
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( GameCardHeaderEncryptedArea , 0x70 ) ;
2020-07-15 23:50:34 +01:00
/// Placed after the `GameCardKeyArea` section.
2020-04-15 06:59:12 +01:00
typedef struct {
2020-10-08 19:31:09 +01:00
u8 signature [ 0x100 ] ; ///< RSA-2048-PSS with SHA-256 signature over the rest of the header.
2020-04-15 06:59:12 +01:00
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 ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( GameCardHeader , 0x200 ) ;
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 ,
2021-02-12 20:35:23 +00:00
GameCardHashFileSystemPartitionType_Boot = 5 ,
2020-10-03 18:09:29 +01:00
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-11-28 06:38:53 +00:00
/// Returns a user-mode gamecard status change event that can be used to wait for status changes on other threads.
2020-05-03 09:40:08 +01:00
/// 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
2021-03-07 23:22:49 +00:00
/// Used to read raw data from the inserted gamecard. Supports unaligned reads.
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
2021-02-12 20:35:23 +00:00
/// Fills the provided GameCardKeyArea pointer. Only GameCardInitialData data is retrieved at this moment.
/// This area can't be read using gamecardReadStorage().
2020-07-13 07:36:17 +01:00
bool gamecardGetKeyArea ( GameCardKeyArea * out ) ;
2021-02-12 20:35:23 +00:00
/// Fills the provided GameCardHeader pointer.
/// This area can also be read using gamecardReadStorage(), starting at offset 0.
2020-04-15 06:59:12 +01:00
bool gamecardGetHeader ( GameCardHeader * out ) ;
2021-02-12 20:35:23 +00:00
/// Fills the provided FsGameCardCertificate pointer.
/// This area can also be read using gamecardReadStorage(), starting at GAMECARD_CERTIFICATE_OFFSET.
2020-07-13 07:36:17 +01:00
bool gamecardGetCertificate ( FsGameCardCertificate * out ) ;
2021-02-12 20:35:23 +00:00
/// Fills the provided u64 pointer with the total gamecard size, which is the size taken by both Normal and Secure storage areas.
2020-04-16 05:37:16 +01:00
bool gamecardGetTotalSize ( u64 * out ) ;
2021-02-12 20:35:23 +00:00
/// Fills the provided u64 pointer with the trimmed gamecard size, which is the same as the size returned by gamecardGetTotalSize() but using the trimmed Secure storage area size.
2020-04-16 05:37:16 +01:00
bool gamecardGetTrimmedSize ( u64 * out ) ;
2021-02-12 20:35:23 +00:00
/// Fills the provided u64 pointer with the gamecard ROM capacity, based on the GameCardRomSize value from the header. Not the same as gamecardGetTotalSize().
bool gamecardGetRomCapacity ( u64 * out ) ;
/// Fills the provided VersionType1 pointer with the bundled firmware update version in the inserted gamecard.
bool gamecardGetBundledFirmwareUpdateVersion ( VersionType1 * out ) ;
2020-04-15 06:59:12 +01:00
2021-03-10 01:12:01 +00:00
/// Fills the provided HashFileSystemContext pointer using information from the requested Hash FS partition.
/// Hash FS functions can be used on the retrieved HashFileSystemContext. hfsFreeContext() must be used to free the underlying data from the filled context.
bool gamecardGetHashFileSystemContext ( u8 hfs_partition_type , HashFileSystemContext * out ) ;
2020-07-29 22:02:21 +01:00
2021-03-07 23:22:49 +00:00
/// One-shot function to retrieve meaningful information from a Hash FS entry by name without using gamecardGetHashFileSystemContext() + Hash FS functions.
/// 'out_offset' or 'out_size' may be set to NULL, but at least one of them must be a valid pointer. The returned offset is always relative to the start of the gamecard image.
/// If you need to get entry information by index, just retrieve the Hash FS context for the target partition and use Hash FS functions on it.
bool gamecardGetHashFileSystemEntryInfoByName ( u8 hfs_partition_type , const char * entry_name , u64 * out_offset , u64 * out_size ) ;
2020-04-16 11:13:11 +01:00
2021-03-23 14:06:52 +00:00
# ifdef __cplusplus
}
2021-03-24 17:25:19 +00:00
# endif
# endif /* __GAMECARD_H__ */