mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 12:12:02 +00:00
Changes to the gamecard key area handling.
This commit is contained in:
parent
5082a54571
commit
eba26a59a5
7 changed files with 81 additions and 32 deletions
|
@ -48,7 +48,7 @@ typedef struct {
|
|||
u8 card_id_area[0x48];
|
||||
u8 reserved[0x1B0];
|
||||
FsGameCardCertificate certificate;
|
||||
GameCardKeyArea key_area;
|
||||
GameCardInitialData initial_data;
|
||||
} GameCardSecurityInformation;
|
||||
|
||||
typedef struct {
|
||||
|
@ -114,6 +114,7 @@ static MemoryLocation g_fsProgramMemory = {
|
|||
};
|
||||
|
||||
static GameCardSecurityInformation g_gameCardSecurityInfo = {0};
|
||||
static GameCardKeyArea g_gameCardKeyArea = {0};
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
|
@ -278,7 +279,7 @@ bool gamecardGetKeyArea(GameCardKeyArea *out)
|
|||
{
|
||||
mutexLock(&g_gamecardMutex);
|
||||
bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out);
|
||||
if (ret) memcpy(out, &(g_gameCardSecurityInfo.key_area), sizeof(GameCardKeyArea));
|
||||
if (ret) memcpy(out, &g_gameCardKeyArea, sizeof(GameCardKeyArea));
|
||||
mutexUnlock(&g_gamecardMutex);
|
||||
return ret;
|
||||
}
|
||||
|
@ -711,10 +712,10 @@ static void gamecardLoadInfo(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* Read full FS program memory to retrieve the GameCardSecurityInformation data, which holds the gamecard key area. */
|
||||
/* Read full FS program memory to retrieve the GameCardSecurityInformation data, which holds the gamecard initial data area. */
|
||||
/* This must be performed while the gamecard is in secure mode, which is already taken care of in the gamecardReadStorageArea() calls from the last iteration in the previous for() loop. */
|
||||
/* GameCardSecurityInformation data is returned by Lotus command "ChangeToSecureMode" (0xF), and kept in FS program memory only after the gamecard secure area has been both mounted and read from. */
|
||||
/* Under some circumstances, the gamecard key area is located *after* the GameCardSecurityInformation area (offset 0x600), instead of its common location at offset 0x400. */
|
||||
/* Under some circumstances, the gamecard initial data is located *after* the GameCardSecurityInformation area (offset 0x600), instead of its common location at offset 0x400. */
|
||||
if (!gamecardReadSecurityInformation())
|
||||
{
|
||||
LOGFILE("Failed to read gamecard security information area from FS program memory!");
|
||||
|
@ -732,6 +733,7 @@ static void gamecardFreeInfo(void)
|
|||
memset(&g_gameCardHeader, 0, sizeof(GameCardHeader));
|
||||
|
||||
memset(&g_gameCardSecurityInfo, 0, sizeof(GameCardSecurityInformation));
|
||||
memset(&g_gameCardKeyArea, 0, sizeof(GameCardKeyArea));
|
||||
|
||||
g_gameCardStorageNormalAreaSize = 0;
|
||||
g_gameCardStorageSecureAreaSize = 0;
|
||||
|
@ -786,21 +788,26 @@ static bool gamecardReadSecurityInformation(void)
|
|||
memcpy(&g_gameCardSecurityInfo, g_fsProgramMemory.data + offset, sizeof(GameCardSecurityInformation));
|
||||
|
||||
/* Check the key_source / package_id value. */
|
||||
if (g_gameCardSecurityInfo.key_area.package_id == g_gameCardHeader.package_id)
|
||||
if (g_gameCardSecurityInfo.initial_data.package_id == g_gameCardHeader.package_id)
|
||||
{
|
||||
/* Jackpot. */
|
||||
found = true;
|
||||
} else {
|
||||
/* Copy the sector right after the GameCardSecurityInformation element from the memory dump, since it may hold the gamecard key area. */
|
||||
/* Copy the sector right after the GameCardSecurityInformation element from the memory dump, since it may hold the gamecard initial data. */
|
||||
offset += sizeof(GameCardSecurityInformation);
|
||||
memcpy(&(g_gameCardSecurityInfo.key_area), g_fsProgramMemory.data + offset, sizeof(GameCardKeyArea));
|
||||
found = (g_gameCardSecurityInfo.key_area.package_id == g_gameCardHeader.package_id);
|
||||
memcpy(&(g_gameCardSecurityInfo.initial_data), g_fsProgramMemory.data + offset, sizeof(GameCardInitialData));
|
||||
found = (g_gameCardSecurityInfo.initial_data.package_id == g_gameCardHeader.package_id);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) LOGFILE("Failed to locate gamecard key area!");
|
||||
if (found)
|
||||
{
|
||||
memcpy(&(g_gameCardKeyArea.initial_data), &(g_gameCardSecurityInfo.initial_data), sizeof(GameCardInitialData));
|
||||
} else {
|
||||
LOGFILE("Failed to locate gamecard initial data area!");
|
||||
}
|
||||
|
||||
/* Free FS memory dump. */
|
||||
memFreeMemoryLocation(&g_fsProgramMemory);
|
||||
|
|
|
@ -36,18 +36,40 @@
|
|||
((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : ((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : \
|
||||
((x) == GameCardHashFileSystemPartitionType_Secure ? "secure" : "unknown")))))
|
||||
|
||||
/// Plaintext area. Dumped from FS program memory.
|
||||
typedef struct {
|
||||
union {
|
||||
u8 key_source[0x10];
|
||||
u8 key_source[0x10]; ///< Encrypted using AES-128-ECB with the common titlekek generator key (stored in the .rodata segment from the Lotus firmware).
|
||||
struct {
|
||||
u64 package_id; ///< Matches package_id from GameCardHeader.
|
||||
u64 padding; ///< Just zeroes.
|
||||
u64 package_id; ///< Matches package_id from GameCardHeader.
|
||||
u64 padding; ///< Just zeroes.
|
||||
};
|
||||
};
|
||||
u8 encrypted_titlekey[0x10];
|
||||
u8 mac[0x10];
|
||||
u8 nonce[0xC];
|
||||
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.
|
||||
u8 nonce[0xC]; ///< Used as the IV to decrypt the key_source using AES-128-CCM.
|
||||
u8 reserved[0x1C4];
|
||||
} 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;
|
||||
|
||||
/// Encrypted using RSA-2048-OAEP. Assumed to be all zeroes in retail gamecards.
|
||||
typedef struct {
|
||||
u8 titlekey_encryption_key[0x10]; ///< Used as the AES-128-CTR key for the `GameCardTitleKey` section.
|
||||
u8 titlekey_encryption_iv[0x10]; ///< Used as the AES-128-CTR IV/counter for the `GameCardTitleKey` section.
|
||||
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;
|
||||
} GameCardKeyArea;
|
||||
|
||||
typedef enum {
|
||||
|
@ -99,21 +121,38 @@ typedef enum {
|
|||
} GameCardCompatibilityType;
|
||||
|
||||
typedef struct {
|
||||
u64 fw_version; ///< GameCardFwVersion.
|
||||
u32 acc_ctrl; ///< GameCardAccCtrl.
|
||||
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.
|
||||
u32 fw_mode;
|
||||
u32 upp_version;
|
||||
u8 compatibility_type; ///< GameCardCompatibilityType.
|
||||
u32 GameCardFwMode_Relstep : 8;
|
||||
u32 GameCardFwMode_Micro : 8;
|
||||
u32 GameCardFwMode_Minor : 8;
|
||||
u32 GameCardFwMode_Major : 8;
|
||||
} GameCardFwMode;
|
||||
|
||||
typedef struct {
|
||||
u32 GameCardUppVersion_MinorRelstep : 8;
|
||||
u32 GameCardUppVersion_MajorRelstep : 8;
|
||||
u32 GameCardUppVersion_Micro : 4;
|
||||
u32 GameCardUppVersion_Minor : 6;
|
||||
u32 GameCardUppVersion_Major : 6;
|
||||
} GameCardUppVersion;
|
||||
|
||||
/// Encrypted using AES-128-CBC with the `xci_header_key` (which can't dumped through current methods) and the IV from `GameCardHeader`.
|
||||
typedef struct {
|
||||
u64 fw_version; ///< GameCardFwVersion.
|
||||
u32 acc_ctrl; ///< GameCardAccCtrl.
|
||||
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;
|
||||
GameCardUppVersion upp_version;
|
||||
u8 compatibility_type; ///< GameCardCompatibilityType.
|
||||
u8 reserved_1[0x3];
|
||||
u64 upp_hash;
|
||||
u64 upp_id; ///< Must match GAMECARD_UPDATE_TID.
|
||||
u64 upp_id; ///< Must match GAMECARD_UPDATE_TID.
|
||||
u8 reserved_2[0x38];
|
||||
} GameCardExtendedHeader;
|
||||
} GameCardHeaderEncryptedArea;
|
||||
|
||||
/// Placed after the `GameCardKeyArea` section.
|
||||
typedef struct {
|
||||
u8 signature[0x100]; ///< RSA-2048 PKCS #1 signature over the rest of the header.
|
||||
u32 magic; ///< "HEAD".
|
||||
|
@ -135,7 +174,7 @@ typedef struct {
|
|||
u32 sel_t1_key_index;
|
||||
u32 sel_key_index;
|
||||
u32 normal_area_end_address; ///< Expressed in GAMECARD_MEDIA_UNIT_SIZE blocks.
|
||||
GameCardExtendedHeader extended_header; ///< Encrypted using AES-128-CBC with 'xci_header_key', which can't dumped through current methods.
|
||||
GameCardHeaderEncryptedArea encrypted_area;
|
||||
} GameCardHeader;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -75,10 +75,10 @@ typedef enum {
|
|||
} NcaKeyAreaEncryptionKeyIndex;
|
||||
|
||||
typedef struct {
|
||||
u8 relstep;
|
||||
u8 micro;
|
||||
u8 minor;
|
||||
u8 major;
|
||||
u32 NcaSdkAddOnVersion_Relstep : 8;
|
||||
u32 NcaSdkAddOnVersion_Micro : 8;
|
||||
u32 NcaSdkAddOnVersion_Minor : 8;
|
||||
u32 NcaSdkAddOnVersion_Major : 8;
|
||||
} NcaSdkAddOnVersion;
|
||||
|
||||
/// 'NcaKeyGeneration_Current' will always point to the last known key generation value.
|
||||
|
|
|
@ -101,6 +101,7 @@ typedef struct {
|
|||
u64 file_table_size; ///< RomFS file entries table size.
|
||||
RomFileSystemFileEntry *file_table; ///< RomFS file entries table.
|
||||
u64 body_offset; ///< RomFS file data body offset (relative to the start of the RomFS).
|
||||
u32 cur_dir_offset; ///< Current RomFS directory offset (relative to the start of the directory entries table). Used for RomFS browsing.
|
||||
} RomFileSystemContext;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -34,7 +34,7 @@ bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, s
|
|||
/// Suitable to replace the ACID public key in main.npdm files.
|
||||
const u8 *rsa2048GetCustomAcidPublicKey(void);
|
||||
|
||||
/// Performs RSA-2048 OAEP decryption and verification. Used to decrypt the titlekey block from tickets with personalized crypto.
|
||||
/// Performs RSA-2048-OAEP decryption and verification. Used to decrypt the titlekey block from tickets with personalized crypto.
|
||||
bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signature, const void *modulus, const void *exponent, size_t exponent_size, const void *label_hash, size_t *out_size);
|
||||
|
||||
#endif /* __RSA_H__ */
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "utils.h"
|
||||
#include "tik.h"
|
||||
#include "cert.h"
|
||||
#include "save.h"
|
||||
#include "es.h"
|
||||
#include "keys.h"
|
||||
|
|
1
todo.txt
1
todo.txt
|
@ -2,6 +2,7 @@ todo:
|
|||
|
||||
hfs0: filelist generation methods
|
||||
|
||||
tik: make sure the common crypto cert is available when fakesigning a ticket
|
||||
tik: automatically dump tickets to the SD card?
|
||||
tik: use dumped tickets when the original ones can't be found in the ES savefile?
|
||||
|
||||
|
|
Loading…
Reference in a new issue