1
0
Fork 0
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:
Pablo Curiel 2020-07-15 18:50:34 -04:00
parent 5082a54571
commit eba26a59a5
7 changed files with 81 additions and 32 deletions

View file

@ -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);

View file

@ -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 {

View file

@ -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.

View file

@ -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 {

View file

@ -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__ */

View file

@ -21,6 +21,7 @@
#include "utils.h"
#include "tik.h"
#include "cert.h"
#include "save.h"
#include "es.h"
#include "keys.h"

View file

@ -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?