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 card_id_area[0x48];
u8 reserved[0x1B0]; u8 reserved[0x1B0];
FsGameCardCertificate certificate; FsGameCardCertificate certificate;
GameCardKeyArea key_area; GameCardInitialData initial_data;
} GameCardSecurityInformation; } GameCardSecurityInformation;
typedef struct { typedef struct {
@ -114,6 +114,7 @@ static MemoryLocation g_fsProgramMemory = {
}; };
static GameCardSecurityInformation g_gameCardSecurityInfo = {0}; static GameCardSecurityInformation g_gameCardSecurityInfo = {0};
static GameCardKeyArea g_gameCardKeyArea = {0};
/* Function prototypes. */ /* Function prototypes. */
@ -278,7 +279,7 @@ bool gamecardGetKeyArea(GameCardKeyArea *out)
{ {
mutexLock(&g_gamecardMutex); mutexLock(&g_gamecardMutex);
bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); 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); mutexUnlock(&g_gamecardMutex);
return ret; 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. */ /* 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. */ /* 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()) if (!gamecardReadSecurityInformation())
{ {
LOGFILE("Failed to read gamecard security information area from FS program memory!"); 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_gameCardHeader, 0, sizeof(GameCardHeader));
memset(&g_gameCardSecurityInfo, 0, sizeof(GameCardSecurityInformation)); memset(&g_gameCardSecurityInfo, 0, sizeof(GameCardSecurityInformation));
memset(&g_gameCardKeyArea, 0, sizeof(GameCardKeyArea));
g_gameCardStorageNormalAreaSize = 0; g_gameCardStorageNormalAreaSize = 0;
g_gameCardStorageSecureAreaSize = 0; g_gameCardStorageSecureAreaSize = 0;
@ -786,21 +788,26 @@ static bool gamecardReadSecurityInformation(void)
memcpy(&g_gameCardSecurityInfo, g_fsProgramMemory.data + offset, sizeof(GameCardSecurityInformation)); memcpy(&g_gameCardSecurityInfo, g_fsProgramMemory.data + offset, sizeof(GameCardSecurityInformation));
/* Check the key_source / package_id value. */ /* 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. */ /* Jackpot. */
found = true; found = true;
} else { } 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); offset += sizeof(GameCardSecurityInformation);
memcpy(&(g_gameCardSecurityInfo.key_area), g_fsProgramMemory.data + offset, sizeof(GameCardKeyArea)); memcpy(&(g_gameCardSecurityInfo.initial_data), g_fsProgramMemory.data + offset, sizeof(GameCardInitialData));
found = (g_gameCardSecurityInfo.key_area.package_id == g_gameCardHeader.package_id); found = (g_gameCardSecurityInfo.initial_data.package_id == g_gameCardHeader.package_id);
} }
break; 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. */ /* Free FS memory dump. */
memFreeMemoryLocation(&g_fsProgramMemory); memFreeMemoryLocation(&g_fsProgramMemory);

View file

@ -36,18 +36,40 @@
((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : ((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : \ ((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : ((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : \
((x) == GameCardHashFileSystemPartitionType_Secure ? "secure" : "unknown"))))) ((x) == GameCardHashFileSystemPartitionType_Secure ? "secure" : "unknown")))))
/// Plaintext area. Dumped from FS program memory.
typedef struct { typedef struct {
union { 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 { struct {
u64 package_id; ///< Matches package_id from GameCardHeader. u64 package_id; ///< Matches package_id from GameCardHeader.
u64 padding; ///< Just zeroes. u64 padding; ///< Just zeroes.
}; };
}; };
u8 encrypted_titlekey[0x10]; u8 encrypted_titlekey[0x10]; ///< Encrypted using AES-128-CCM with the decrypted key_source and the nonce from this section.
u8 mac[0x10]; u8 mac[0x10]; ///< Used to verify the validity of the decrypted titlekey.
u8 nonce[0xC]; u8 nonce[0xC]; ///< Used as the IV to decrypt the key_source using AES-128-CCM.
u8 reserved[0x1C4]; 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; } GameCardKeyArea;
typedef enum { typedef enum {
@ -99,21 +121,38 @@ typedef enum {
} GameCardCompatibilityType; } GameCardCompatibilityType;
typedef struct { typedef struct {
u64 fw_version; ///< GameCardFwVersion. u32 GameCardFwMode_Relstep : 8;
u32 acc_ctrl; ///< GameCardAccCtrl. u32 GameCardFwMode_Micro : 8;
u32 wait_1_time_read; ///< Always 0x1388. u32 GameCardFwMode_Minor : 8;
u32 wait_2_time_read; ///< Always 0. u32 GameCardFwMode_Major : 8;
u32 wait_1_time_write; ///< Always 0. } GameCardFwMode;
u32 wait_2_time_write; ///< Always 0.
u32 fw_mode; typedef struct {
u32 upp_version; u32 GameCardUppVersion_MinorRelstep : 8;
u8 compatibility_type; ///< GameCardCompatibilityType. 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]; u8 reserved_1[0x3];
u64 upp_hash; u64 upp_hash;
u64 upp_id; ///< Must match GAMECARD_UPDATE_TID. u64 upp_id; ///< Must match GAMECARD_UPDATE_TID.
u8 reserved_2[0x38]; u8 reserved_2[0x38];
} GameCardExtendedHeader; } GameCardHeaderEncryptedArea;
/// Placed after the `GameCardKeyArea` section.
typedef struct { typedef struct {
u8 signature[0x100]; ///< RSA-2048 PKCS #1 signature over the rest of the header. u8 signature[0x100]; ///< RSA-2048 PKCS #1 signature over the rest of the header.
u32 magic; ///< "HEAD". u32 magic; ///< "HEAD".
@ -135,7 +174,7 @@ typedef struct {
u32 sel_t1_key_index; u32 sel_t1_key_index;
u32 sel_key_index; u32 sel_key_index;
u32 normal_area_end_address; ///< Expressed in GAMECARD_MEDIA_UNIT_SIZE blocks. 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; } GameCardHeader;
typedef enum { typedef enum {

View file

@ -75,10 +75,10 @@ typedef enum {
} NcaKeyAreaEncryptionKeyIndex; } NcaKeyAreaEncryptionKeyIndex;
typedef struct { typedef struct {
u8 relstep; u32 NcaSdkAddOnVersion_Relstep : 8;
u8 micro; u32 NcaSdkAddOnVersion_Micro : 8;
u8 minor; u32 NcaSdkAddOnVersion_Minor : 8;
u8 major; u32 NcaSdkAddOnVersion_Major : 8;
} NcaSdkAddOnVersion; } NcaSdkAddOnVersion;
/// 'NcaKeyGeneration_Current' will always point to the last known key generation value. /// '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. u64 file_table_size; ///< RomFS file entries table size.
RomFileSystemFileEntry *file_table; ///< RomFS file entries table. RomFileSystemFileEntry *file_table; ///< RomFS file entries table.
u64 body_offset; ///< RomFS file data body offset (relative to the start of the RomFS). 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; } RomFileSystemContext;
typedef struct { 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. /// Suitable to replace the ACID public key in main.npdm files.
const u8 *rsa2048GetCustomAcidPublicKey(void); 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); 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__ */ #endif /* __RSA_H__ */

View file

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

View file

@ -2,6 +2,7 @@ todo:
hfs0: filelist generation methods 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: automatically dump tickets to the SD card?
tik: use dumped tickets when the original ones can't be found in the ES savefile? tik: use dumped tickets when the original ones can't be found in the ES savefile?