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).
*
2021-03-25 19:26:58 +00:00
* nxdumptool is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2020-04-16 01:06:41 +01:00
*
2021-03-25 19:26:58 +00:00
* nxdumptool is distributed in the hope that 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 .
2020-04-16 01:06:41 +01:00
*
* You should have received a copy of the GNU General Public License
2021-03-25 19:26:58 +00:00
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
2020-04-16 01:06:41 +01:00
*/
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
2021-04-06 01:40:57 +01:00
# define GAMECARD_HEAD_MAGIC 0x48454144 /* "HEAD". */
2020-04-15 06:59:12 +01:00
2021-04-06 01:40:57 +01:00
# define GAMECARD_PAGE_SIZE 0x200
# define GAMECARD_PAGE_OFFSET(x) ((u64)(x) * GAMECARD_PAGE_SIZE)
2020-04-15 06:59:12 +01:00
2021-04-06 01:40:57 +01:00
# define GAMECARD_UPDATE_TID SYSTEM_UPDATE_TID
2020-07-13 07:36:17 +01:00
2021-04-06 01:40:57 +01:00
# define GAMECARD_CERTIFICATE_OFFSET 0x7000
2020-08-13 07:01:23 +01:00
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 ) ;
2021-04-05 21:21:46 +01:00
/// Encrypted using AES-128-CTR with the key and IV/counter from the `GameCardTitleKeyAreaEncryption` section. Assumed to be all zeroes in retail gamecards.
2020-07-15 23:50:34 +01:00
typedef struct {
u8 titlekey [ 0x10 ] ; ///< Decrypted titlekey from the `GameCardInitialData` section.
u8 reserved [ 0xCF0 ] ;
2021-04-05 21:21:46 +01:00
} GameCardTitleKeyArea ;
2020-07-15 23:50:34 +01:00
2021-04-05 21:21:46 +01:00
NXDT_ASSERT ( GameCardTitleKeyArea , 0xD00 ) ;
2021-03-24 17:25:19 +00:00
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 {
2021-04-05 21:21:46 +01:00
u8 titlekey_encryption_key [ 0x10 ] ; ///< Used as the AES-128-CTR key for the `GameCardTitleKeyArea` section. Randomly generated during XCI creation by AuthoringTool.
u8 titlekey_encryption_iv [ 0x10 ] ; ///< Used as the AES-128-CTR IV/counter for the `GameCardTitleKeyArea` section. Randomly generated during XCI creation by AuthoringTool.
2020-07-15 23:50:34 +01:00
u8 reserved [ 0xE0 ] ;
2021-04-05 21:21:46 +01:00
} GameCardTitleKeyAreaEncryption ;
2020-07-15 23:50:34 +01:00
2021-04-05 21:21:46 +01:00
NXDT_ASSERT ( GameCardTitleKeyAreaEncryption , 0x100 ) ;
2021-03-24 17:25:19 +00:00
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 ;
2021-04-05 21:21:46 +01:00
GameCardTitleKeyArea titlekey_area ;
GameCardTitleKeyAreaEncryption titlekey_area_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-06-03 15:46:21 +01:00
GameCardFwVersion_ForDev = 0 ,
GameCardFwVersion_Since100NUP = 1 , ///< upp_version >= 0 (0.0.0-0.0) in GameCardInfo.
GameCardFwVersion_Since400NUP = 2 , ///< upp_version >= 268435456 (4.0.0-0.0) in GameCardInfo.
GameCardFwVersion_Since900NUP = 3 , ///< upp_version >= 603979776 (9.0.0-0.0) in GameCardInfo. Seems to be unused.
GameCardFwVersion_Since1100NUP = 4 , ///< upp_version >= 738197504 (11.0.0-0.0) in GameCardInfo.
GameCardFwVersion_Since1200NUP = 5 , ///< upp_version >= 805306368 (12.0.0-0.0) in GameCardInfo.
GameCardFwVersion_Count = 6
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 ,
2021-06-14 01:47:49 +01:00
GameCardCompatibilityType_Terra = 1 ,
GameCardCompatibilityType_Count = 2
2020-04-15 06:59:12 +01:00
} GameCardCompatibilityType ;
2021-05-22 09:45:40 +01:00
/// Encrypted using AES-128-CBC with the XCI header key (found in FS program memory under HOS 9.0.0+) and the IV from `GameCardHeader`.
2020-10-04 10:25:42 +01:00
/// 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.
2021-04-06 01:40:57 +01:00
VersionType2 fw_mode ; ///< Current SdkAddonVersion.
VersionType1 upp_version ; ///< Bundled system update 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 ] ;
2021-04-06 01:40:57 +01:00
u64 upp_hash ; ///< SHA-256 checksum for the update partition.
u64 upp_id ; ///< Must match GAMECARD_UPDATE_TID.
2020-05-01 16:11:18 +01:00
u8 reserved_2 [ 0x38 ] ;
2021-04-06 01:40:57 +01:00
} GameCardInfo ;
2020-04-15 06:59:12 +01:00
2021-04-06 01:40:57 +01:00
NXDT_ASSERT ( GameCardInfo , 0x70 ) ;
2021-03-24 17:25:19 +00:00
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".
2021-04-06 01:40:57 +01:00
u32 rom_area_start_page_address ; ///< Expressed in GAMECARD_PAGE_SIZE units.
u32 backup_area_start_page_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.
2021-07-19 22:09:58 +01:00
u64 package_id ; ///< Used for challenge-response authentication.
2021-04-06 01:40:57 +01:00
u32 valid_data_end_address ; ///< Expressed in GAMECARD_PAGE_SIZE units.
2020-04-15 06:59:12 +01:00
u8 reserved [ 0x4 ] ;
2021-05-22 09:45:40 +01:00
u8 card_info_iv [ AES_128_KEY_SIZE ] ; ///< AES-128-CBC IV for the CardInfo area (reversed).
2021-04-06 01:40:57 +01:00
u64 partition_fs_header_address ; ///< Root Hash File System header offset.
u64 partition_fs_header_size ; ///< Root Hash File System header size.
2020-04-15 06:59:12 +01:00
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.
2021-07-19 22:09:58 +01:00
u32 sel_key ; ///< Always 0.
2021-04-06 01:40:57 +01:00
u32 lim_area ; ///< Expressed in GAMECARD_PAGE_SIZE units.
GameCardInfo card_info ;
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 {
2021-06-01 02:12:15 +01:00
GameCardStatus_NotInserted = 0 , ///< No gamecard is inserted.
2021-06-13 07:18:14 +01:00
GameCardStatus_Processing = 1 , ///< A gamecard has been inserted and it's being processed.
GameCardStatus_NoGameCardPatchEnabled = 2 , ///< A gamecard has been inserted, but the running CFW enabled the "nogc" patch at boot.
2021-06-01 02:12:15 +01:00
///< This triggers an error whenever fsDeviceOperatorGetGameCardHandle is called. Nothing at all can be done with the inserted gamecard.
2021-06-13 07:18:14 +01:00
GameCardStatus_LotusAsicFirmwareUpdateRequired = 3 , ///< A gamecard has been inserted, but a LAFW update is needed before being able to read the secure storage area.
2021-06-01 02:12:15 +01:00
///< Operations on the normal storage area are still possible, though.
2021-06-13 07:18:14 +01:00
GameCardStatus_InsertedAndInfoNotLoaded = 4 , ///< A gamecard has been inserted, but an unexpected error unrelated to both "nogc" patch and LAFW version occurred.
GameCardStatus_InsertedAndInfoLoaded = 5 ///< A gamecard has been inserted and all required information could be successfully retrieved from it.
2020-07-17 06:01:31 +01:00
} GameCardStatus ;
2020-04-17 22:59:05 +01:00
typedef enum {
2021-06-05 22:06:29 +01:00
GameCardHashFileSystemPartitionType_None = 0 , ///< Not a real value.
GameCardHashFileSystemPartitionType_Root = 1 ,
GameCardHashFileSystemPartitionType_Update = 2 ,
GameCardHashFileSystemPartitionType_Logo = 3 , ///< Only available in GameCardFwVersion_Since400NUP or greater gamecards.
GameCardHashFileSystemPartitionType_Normal = 4 ,
GameCardHashFileSystemPartitionType_Secure = 5 ,
GameCardHashFileSystemPartitionType_Boot = 6 ,
GameCardHashFileSystemPartitionType_Count = 7 ///< 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
2021-06-13 20:32:37 +01:00
/// Fills the provided GameCardInfo pointer.
bool gamecardGetDecryptedCardInfoArea ( GameCardInfo * 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__ */