mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-12-23 00:52:10 +00:00
LAFW shenanigans.
* Added custom key sources to derive CardInfo keys at runtime using SPL. * Implemented CardInfo area decryption. * Implemented LAFW blob lookup in FS .data segment to retrieve the current LAFW version. P.S.: still need to move around code to perform the LAFW version check at the places we need. But the current code is good enough for a test.
This commit is contained in:
parent
0ce6d244e5
commit
21d1b001ee
6 changed files with 290 additions and 27 deletions
|
@ -126,7 +126,8 @@ typedef enum {
|
|||
GameCardFwVersion_ForProdSince400NUP = 2, ///< upp_version >= 268435456 (4.0.0-0.0) in GameCardInfo.
|
||||
GameCardFwVersion_ForDevSince1100NUP = 3, ///< upp_version >= 738197504 (11.0.0-0.0) in GameCardInfo.
|
||||
GameCardFwVersion_ForProdSince1100NUP = 4, ///< upp_version >= 738197504 (11.0.0-0.0) in GameCardInfo.
|
||||
GameCardFwVersion_ForProdSince1200NUP = 5 ///< upp_version >= 805306368 (12.0.0-0.0) in GameCardInfo.
|
||||
GameCardFwVersion_ForProdSince1200NUP = 5, ///< upp_version >= 805306368 (12.0.0-0.0) in GameCardInfo.
|
||||
GameCardFwVersion_Count = 6
|
||||
} GameCardFwVersion;
|
||||
|
||||
typedef enum {
|
||||
|
@ -139,7 +140,7 @@ typedef enum {
|
|||
GameCardCompatibilityType_Terra = 1
|
||||
} GameCardCompatibilityType;
|
||||
|
||||
/// 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`.
|
||||
/// 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`.
|
||||
/// Key hashes for documentation purposes:
|
||||
/// Production XCI header key hash: 2E36CC55157A351090A73E7AE77CF581F69B0B6E48FB066C984879A6ED7D2E96
|
||||
/// Development XCI header key hash: 61D5C02244188810E2E3DE69341AC0F3C7653D370C6D3F77CA82B0B7E59F39AD
|
||||
|
@ -174,7 +175,7 @@ typedef struct {
|
|||
u64 package_id; ///< Used for challenge–response authentication.
|
||||
u32 valid_data_end_address; ///< Expressed in GAMECARD_PAGE_SIZE units.
|
||||
u8 reserved[0x4];
|
||||
u8 iv[0x10];
|
||||
u8 card_info_iv[AES_128_KEY_SIZE]; ///< AES-128-CBC IV for the CardInfo area (reversed).
|
||||
u64 partition_fs_header_address; ///< Root Hash File System header offset.
|
||||
u64 partition_fs_header_size; ///< Root Hash File System header size.
|
||||
u8 partition_fs_header_hash[SHA256_HASH_SIZE];
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// Loads (and derives) NCA keydata from sysmodule program memory and the Lockpick_RCM keys file.
|
||||
/// Loads (and derives) keydata from sysmodule program memory, the Lockpick_RCM keys file and hardcoded/obfuscated information.
|
||||
/// Must be called (and succeed) before calling any of the functions below.
|
||||
bool keysLoadNcaKeyset(void);
|
||||
bool keysLoadKeyset(void);
|
||||
|
||||
/// Returns a pointer to the AES-128-XTS NCA header key, or NULL if keydata hasn't been loaded.
|
||||
const u8 *keysGetNcaHeaderKey(void);
|
||||
|
@ -57,6 +57,9 @@ bool keysDecryptRsaOaepWrappedTitleKey(const void *rsa_wrapped_titlekey, void *o
|
|||
/// Returns a pointer to an AES-128-ECB ticket common key using the provided key generation value, or NULL if keydata hasn't been loaded.
|
||||
const u8 *keysGetTicketCommonKey(u8 key_generation);
|
||||
|
||||
/// Returns a pointer to the AES-128-CBC CardInfo area key for gamecard headers, or NULL if keydata hasn't been loaded.
|
||||
const u8 *keysGetGameCardInfoKey(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "nxdt_utils.h"
|
||||
#include "mem.h"
|
||||
#include "gamecard.h"
|
||||
#include "keys.h"
|
||||
|
||||
#define GAMECARD_HFS0_MAGIC 0x48465330 /* "HFS0". */
|
||||
|
||||
|
@ -34,6 +35,8 @@
|
|||
|
||||
#define GAMECARD_STORAGE_AREA_NAME(x) ((x) == GameCardStorageArea_Normal ? "normal" : ((x) == GameCardStorageArea_Secure ? "secure" : "none"))
|
||||
|
||||
#define LAFW_MAGIC 0x4C414657 /* "LAFW". */
|
||||
|
||||
/* Type definitions. */
|
||||
|
||||
typedef enum {
|
||||
|
@ -64,6 +67,39 @@ typedef struct {
|
|||
|
||||
NXDT_ASSERT(GameCardSecurityInformation, 0x600);
|
||||
|
||||
typedef enum {
|
||||
LotusAsicFirmwareType_ReadFw = 0xFF,
|
||||
LotusAsicFirmwareType_ReadDevFw = 0xFFFF,
|
||||
LotusAsicFirmwareType_WriterFw = 0xFFFFFF,
|
||||
LotusAsicFirmwareType_RmaFw = 0xFFFFFFFF
|
||||
} LotusAsicFirmwareType;
|
||||
|
||||
typedef enum {
|
||||
LotusAsicDeviceType_Test = 0,
|
||||
LotusAsicDeviceType_Dev = 1,
|
||||
LotusAsicDeviceType_Prod = 2,
|
||||
LotusAsicDeviceType_Prod2Dev = 3
|
||||
} LotusAsicDeviceType;
|
||||
|
||||
typedef struct {
|
||||
u8 signature[0x100];
|
||||
u32 magic; ///< "LAFW".
|
||||
u32 fw_type; ///< LotusAsicFirmwareType.
|
||||
u8 reserved_1[0x8];
|
||||
struct {
|
||||
u64 fw_version : 62; ///< Stored using a bitmask.
|
||||
u64 device_type : 2; ///< LotusAsicDeviceType.
|
||||
};
|
||||
u32 data_size;
|
||||
u8 reserved_2[0x4];
|
||||
u8 data_iv[AES_128_KEY_SIZE];
|
||||
char placeholder_str[0x10]; ///< "IDIDIDIDIDIDIDID".
|
||||
u8 reserved_3[0x40];
|
||||
u8 data[0x7680];
|
||||
} LotusAsicFirmwareBlob;
|
||||
|
||||
NXDT_ASSERT(LotusAsicFirmwareBlob, 0x7800);
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
static Mutex g_gameCardMutex = 0;
|
||||
|
@ -74,6 +110,8 @@ static FsEventNotifier g_gameCardEventNotifier = {0};
|
|||
static Event g_gameCardKernelEvent = {0};
|
||||
static bool g_openDeviceOperator = false, g_openEventNotifier = false, g_loadKernelEvent = false;
|
||||
|
||||
static u64 g_lafwVersion = 0;
|
||||
|
||||
static Thread g_gameCardDetectionThread = {0};
|
||||
static UEvent g_gameCardDetectionThreadExitEvent = {0}, g_gameCardStatusChangeEvent = {0};
|
||||
static bool g_gameCardDetectionThreadCreated = false, g_gameCardInserted = false, g_gameCardInfoLoaded = false;
|
||||
|
@ -84,6 +122,7 @@ static u8 g_gameCardStorageCurrentArea = GameCardStorageArea_None;
|
|||
static u8 *g_gameCardReadBuf = NULL;
|
||||
|
||||
static GameCardHeader g_gameCardHeader = {0};
|
||||
static GameCardInfo g_gameCardInfoArea = {0};
|
||||
static u64 g_gameCardStorageNormalAreaSize = 0, g_gameCardStorageSecureAreaSize = 0, g_gameCardStorageTotalSize = 0;
|
||||
static u64 g_gameCardCapacity = 0;
|
||||
|
||||
|
@ -108,6 +147,8 @@ static const char *g_gameCardHfsPartitionNames[] = {
|
|||
|
||||
/* Function prototypes. */
|
||||
|
||||
static bool gamecardGetLotusAsicFirmwareVersion(void);
|
||||
|
||||
static bool gamecardCreateDetectionThread(void);
|
||||
static void gamecardDestroyDetectionThread(void);
|
||||
static void gamecardDetectionThreadFunc(void *arg);
|
||||
|
@ -129,6 +170,8 @@ static void gamecardCloseStorageArea(void);
|
|||
static bool gamecardGetStorageAreasSizes(void);
|
||||
NX_INLINE u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size);
|
||||
|
||||
static bool gamecardGetDecryptedCardInfoArea(void);
|
||||
|
||||
static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char *name, u64 offset, u64 size, u8 *hash, u64 hash_target_offset, u32 hash_target_size);
|
||||
static HashFileSystemContext *_gamecardGetHashFileSystemContext(u8 hfs_partition_type);
|
||||
|
||||
|
@ -186,6 +229,9 @@ bool gamecardInitialize(void)
|
|||
/* Create user-mode gamecard status change event. */
|
||||
ueventCreate(&g_gameCardStatusChangeEvent, true);
|
||||
|
||||
/* Retrieve LAFW version. */
|
||||
if (!gamecardGetLotusAsicFirmwareVersion()) break;
|
||||
|
||||
/* Create gamecard detection thread. */
|
||||
if (!(g_gameCardDetectionThreadCreated = gamecardCreateDetectionThread())) break;
|
||||
|
||||
|
@ -459,6 +505,69 @@ bool gamecardGetHashFileSystemEntryInfoByName(u8 hfs_partition_type, const char
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool gamecardGetLotusAsicFirmwareVersion(void)
|
||||
{
|
||||
u64 fw_version = 0;
|
||||
bool ret = false, found = false, dev_unit = utilsIsDevelopmentUnit();
|
||||
|
||||
/* Temporarily set the segment mask to .data. */
|
||||
g_fsProgramMemory.mask = MemoryProgramSegmentType_Data;
|
||||
|
||||
/* Retrieve full FS program memory dump. */
|
||||
ret = memRetrieveProgramMemorySegment(&g_fsProgramMemory);
|
||||
|
||||
/* Clear segment mask. */
|
||||
g_fsProgramMemory.mask = 0;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
LOG_MSG("Failed to retrieve FS .data segment dump!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Look for the LAFW ReadFw blob in the FS .data memory dump. */
|
||||
for(u64 offset = 0; offset < g_fsProgramMemory.data_size; offset++)
|
||||
{
|
||||
if ((g_fsProgramMemory.data_size - offset) < sizeof(LotusAsicFirmwareBlob)) break;
|
||||
|
||||
LotusAsicFirmwareBlob *lafw_blob = (LotusAsicFirmwareBlob*)(g_fsProgramMemory.data + offset);
|
||||
u32 magic = __builtin_bswap32(lafw_blob->magic), fw_type = lafw_blob->fw_type;
|
||||
|
||||
if (magic == LAFW_MAGIC && ((!dev_unit && fw_type == LotusAsicFirmwareType_ReadFw) || (dev_unit && fw_type == LotusAsicFirmwareType_ReadDevFw)))
|
||||
{
|
||||
/* Jackpot. */
|
||||
fw_version = lafw_blob->fw_version;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
LOG_MSG("Unable to locate Lotus ReadFw blob in FS .data segment!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Convert LAFW version bitmask to an integer. */
|
||||
g_lafwVersion = 0;
|
||||
|
||||
while(fw_version)
|
||||
{
|
||||
g_lafwVersion += (fw_version & 1);
|
||||
fw_version >>= 1;
|
||||
}
|
||||
|
||||
LOG_MSG("LAFW version: %lu.", g_lafwVersion);
|
||||
|
||||
/* Update flag. */
|
||||
ret = true;
|
||||
|
||||
end:
|
||||
memFreeMemoryLocation(&g_fsProgramMemory);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool gamecardCreateDetectionThread(void)
|
||||
{
|
||||
if (!utilsCreateThread(&g_gameCardDetectionThread, gamecardDetectionThreadFunc, NULL, 1))
|
||||
|
@ -577,6 +686,51 @@ static void gamecardLoadInfo(void)
|
|||
goto end;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Get decrypted CardInfo area. */
|
||||
if (!gamecardGetDecryptedCardInfoArea()) goto end;
|
||||
|
||||
LOG_DATA(&g_gameCardInfoArea, sizeof(GameCardInfo), "Gamecard CardInfo area dump:");
|
||||
|
||||
/* Check if we meet the Lotus ASIC firmware (LAFW) version requirement. */
|
||||
/* Lotus treats the GameCardFwVersion field as the maximum unsupported LAFW version, instead of treating it as the minimum supported version. */
|
||||
/* TODO: move this check somewhere else. We're supposed to do it only if things go south while reading gamecard data. */
|
||||
if (g_lafwVersion <= g_gameCardInfoArea.fw_version)
|
||||
{
|
||||
LOG_MSG("LAFW version not supported by the inserted gamecard (%lu <= %lu).", g_lafwVersion, g_gameCardInfoArea.fw_version);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Get gamecard capacity. */
|
||||
g_gameCardCapacity = gamecardGetCapacityFromRomSizeValue(g_gameCardHeader.rom_size);
|
||||
if (!g_gameCardCapacity)
|
||||
|
@ -654,6 +808,8 @@ static void gamecardFreeInfo(void)
|
|||
{
|
||||
memset(&g_gameCardHeader, 0, sizeof(GameCardHeader));
|
||||
|
||||
memset(&g_gameCardInfoArea, 0, sizeof(GameCardInfo));
|
||||
|
||||
g_gameCardStorageNormalAreaSize = g_gameCardStorageSecureAreaSize = g_gameCardStorageTotalSize = 0;
|
||||
|
||||
g_gameCardCapacity = 0;
|
||||
|
@ -976,6 +1132,39 @@ NX_INLINE u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size)
|
|||
return capacity;
|
||||
}
|
||||
|
||||
static bool gamecardGetDecryptedCardInfoArea(void)
|
||||
{
|
||||
const u8 *card_info_key = NULL;
|
||||
u8 card_info_iv[AES_128_KEY_SIZE] = {0};
|
||||
Aes128CbcContext aes_ctx = {0};
|
||||
|
||||
/* Retrieve CardInfo area key. */
|
||||
card_info_key = keysGetGameCardInfoKey();
|
||||
if (!card_info_key)
|
||||
{
|
||||
LOG_MSG("Failed to retrieve CardInfo area key!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reverse CardInfo IV. */
|
||||
for(u8 i = 0; i < AES_128_KEY_SIZE; i++) card_info_iv[i] = g_gameCardHeader.card_info_iv[AES_128_KEY_SIZE - i - 1];
|
||||
|
||||
/* Initialize AES-128-CBC context. */
|
||||
aes128CbcContextCreate(&aes_ctx, card_info_key, card_info_iv, false);
|
||||
|
||||
/* Decrypt CardInfo area. */
|
||||
aes128CbcDecrypt(&aes_ctx, &g_gameCardInfoArea, &(g_gameCardHeader.card_info), sizeof(GameCardInfo));
|
||||
|
||||
/* Verify update ID. */
|
||||
if (utilsGetCustomFirmwareType() != UtilsCustomFirmwareType_SXOS && g_gameCardInfoArea.upp_id != GAMECARD_UPDATE_TID)
|
||||
{
|
||||
LOG_MSG("Failed to decrypt CardInfo area!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char *name, u64 offset, u64 size, u8 *hash, u64 hash_target_offset, u32 hash_target_size)
|
||||
{
|
||||
u32 i = 0, magic = 0;
|
||||
|
|
|
@ -73,13 +73,24 @@ typedef struct {
|
|||
u8 ticket_common_keys[NcaKeyGeneration_Max][AES_128_KEY_SIZE]; ///< Retrieved from the Lockpick_RCM keys file.
|
||||
} KeysNcaKeyset;
|
||||
|
||||
typedef struct {
|
||||
/// AES-128-CBC keys needed to decrypt the CardInfo area from gamecard headers.
|
||||
const u8 gc_cardinfo_kek_source[AES_128_KEY_SIZE]; ///< Randomly generated KEK source to decrypt official CardInfo area keys.
|
||||
const u8 gc_cardinfo_key_prod_source[AES_128_KEY_SIZE]; ///< CardInfo area key used in retail units. Obfuscated using the above KEK source and SMC AES engine keydata.
|
||||
const u8 gc_cardinfo_key_dev_source[AES_128_KEY_SIZE]; ///< CardInfo area key used in development units. Obfuscated using the above KEK source and SMC AES engine keydata.
|
||||
|
||||
u8 gc_cardinfo_kek_sealed[AES_128_KEY_SIZE]; ///< Generated from gc_cardinfo_kek_source. Sealed by the SMC AES engine.
|
||||
u8 gc_cardinfo_key_prod[AES_128_KEY_SIZE]; ///< Generated from gc_cardinfo_kek_sealed and gc_cardinfo_key_prod_source.
|
||||
u8 gc_cardinfo_key_dev[AES_128_KEY_SIZE]; ///< Generated from gc_cardinfo_kek_sealed and gc_cardinfo_key_dev_source.
|
||||
} KeysGameCardKeyset;
|
||||
|
||||
/// Used to parse the eTicket RSA device key retrieved from PRODINFO via setcalGetEticketDeviceKey().
|
||||
/// Everything after the AES CTR is encrypted using the eTicket RSA device key encryption key.
|
||||
typedef struct {
|
||||
u8 ctr[AES_128_KEY_SIZE];
|
||||
u8 private_exponent[RSA2048_BYTES];
|
||||
u8 modulus[RSA2048_BYTES];
|
||||
u32 public_exponent; ///< Must match ETICKET_RSA_DEVICE_KEY_PUBLIC_EXPONENT. Stored using big endian byte order.
|
||||
u32 public_exponent; ///< Stored using big endian byte order. Must match ETICKET_RSA_DEVICE_KEY_PUBLIC_EXPONENT.
|
||||
u8 padding[0x14];
|
||||
u64 device_id;
|
||||
u8 ghash[0x10];
|
||||
|
@ -108,11 +119,23 @@ static bool keysReadKeysFromFile(void);
|
|||
static bool keysGetDecryptedEticketRsaDeviceKey(void);
|
||||
static bool keysTestEticketRsaDeviceKey(const void *e, const void *d, const void *n);
|
||||
|
||||
static bool keysDeriveGameCardKeys(void);
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
static KeysNcaKeyset g_ncaKeyset = {0};
|
||||
static bool g_ncaKeysetLoaded = false;
|
||||
static Mutex g_ncaKeysetMutex = 0;
|
||||
|
||||
static KeysGameCardKeyset g_gameCardKeyset = {
|
||||
.gc_cardinfo_kek_source = { 0xDE, 0xC6, 0x3F, 0x6A, 0xBF, 0x37, 0x72, 0x0B, 0x7E, 0x54, 0x67, 0x6A, 0x2D, 0xEF, 0xDD, 0x97 },
|
||||
.gc_cardinfo_key_prod_source = { 0xF4, 0x92, 0x06, 0x52, 0xD6, 0x37, 0x70, 0xAF, 0xB1, 0x9C, 0x6F, 0x63, 0x09, 0x01, 0xF6, 0x29 },
|
||||
.gc_cardinfo_key_dev_source = { 0x0B, 0x7D, 0xBB, 0x2C, 0xCF, 0x64, 0x1A, 0xF4, 0xD7, 0x38, 0x81, 0x3F, 0x0C, 0x33, 0xF4, 0x1C },
|
||||
.gc_cardinfo_kek_sealed = {0},
|
||||
.gc_cardinfo_key_prod = {0},
|
||||
.gc_cardinfo_key_dev = {0}
|
||||
};
|
||||
|
||||
static bool g_keysetLoaded = false;
|
||||
static Mutex g_keysetMutex = 0;
|
||||
|
||||
static SetCalRsa2048DeviceKey g_eTicketRsaDeviceKey = {0};
|
||||
|
||||
|
@ -230,13 +253,13 @@ static KeysMemoryInfo g_fsDataMemoryInfo = {
|
|||
}
|
||||
};
|
||||
|
||||
bool keysLoadNcaKeyset(void)
|
||||
bool keysLoadKeyset(void)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
SCOPED_LOCK(&g_ncaKeysetMutex)
|
||||
SCOPED_LOCK(&g_keysetMutex)
|
||||
{
|
||||
ret = g_ncaKeysetLoaded;
|
||||
ret = g_keysetLoaded;
|
||||
if (ret) break;
|
||||
|
||||
/* Retrieve FS .rodata keys. */
|
||||
|
@ -273,14 +296,18 @@ bool keysLoadNcaKeyset(void)
|
|||
/* Get decrypted eTicket RSA device key. */
|
||||
if (!keysGetDecryptedEticketRsaDeviceKey()) break;
|
||||
|
||||
/* Derive gamecard keys. */
|
||||
if (!keysDeriveGameCardKeys()) break;
|
||||
|
||||
/* Update flags. */
|
||||
ret = g_ncaKeysetLoaded = true;
|
||||
ret = g_keysetLoaded = true;
|
||||
}
|
||||
|
||||
/*if (ret)
|
||||
{
|
||||
LOG_DATA(&g_ncaKeyset, sizeof(KeysNcaKeyset), "NCA keyset dump:");
|
||||
LOG_DATA(&g_eTicketRsaDeviceKey, sizeof(SetCalRsa2048DeviceKey), "eTicket RSA device key dump:");
|
||||
LOG_DATA(&g_gameCardKeyset, sizeof(KeysGameCardKeyset), "Gamecard keyset dump:");
|
||||
}*/
|
||||
|
||||
return ret;
|
||||
|
@ -290,9 +317,9 @@ const u8 *keysGetNcaHeaderKey(void)
|
|||
{
|
||||
const u8 *ret = NULL;
|
||||
|
||||
SCOPED_LOCK(&g_ncaKeysetMutex)
|
||||
SCOPED_LOCK(&g_keysetMutex)
|
||||
{
|
||||
if (g_ncaKeysetLoaded) ret = (const u8*)(g_ncaKeyset.nca_header_key);
|
||||
if (g_keysetLoaded) ret = (const u8*)(g_ncaKeyset.nca_header_key);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -309,9 +336,9 @@ const u8 *keysGetNcaMainSignatureModulus(u8 key_generation)
|
|||
bool dev_unit = utilsIsDevelopmentUnit();
|
||||
const u8 *ret = NULL, null_modulus[RSA2048_PUBKEY_SIZE] = {0};
|
||||
|
||||
SCOPED_LOCK(&g_ncaKeysetMutex)
|
||||
SCOPED_LOCK(&g_keysetMutex)
|
||||
{
|
||||
if (!g_ncaKeysetLoaded) break;
|
||||
if (!g_keysetLoaded) break;
|
||||
|
||||
ret = (const u8*)(dev_unit ? g_ncaKeyset.nca_main_signature_moduli_dev[key_generation] : g_ncaKeyset.nca_main_signature_moduli_prod[key_generation]);
|
||||
|
||||
|
@ -348,9 +375,9 @@ bool keysDecryptNcaKeyAreaEntry(u8 kaek_index, u8 key_generation, void *dst, con
|
|||
goto end;
|
||||
}
|
||||
|
||||
SCOPED_LOCK(&g_ncaKeysetMutex)
|
||||
SCOPED_LOCK(&g_keysetMutex)
|
||||
{
|
||||
if (!g_ncaKeysetLoaded) break;
|
||||
if (!g_keysetLoaded) break;
|
||||
Result rc = splCryptoGenerateAesKey(g_ncaKeyset.nca_kaek_sealed[kaek_index][key_gen_val], src, dst);
|
||||
if (!(ret = R_SUCCEEDED(rc))) LOG_MSG("splCryptoGenerateAesKey failed! (0x%08X).", rc);
|
||||
}
|
||||
|
@ -376,9 +403,9 @@ const u8 *keysGetNcaKeyAreaEncryptionKey(u8 kaek_index, u8 key_generation)
|
|||
goto end;
|
||||
}
|
||||
|
||||
SCOPED_LOCK(&g_ncaKeysetMutex)
|
||||
SCOPED_LOCK(&g_keysetMutex)
|
||||
{
|
||||
if (g_ncaKeysetLoaded) ret = (const u8*)(g_ncaKeyset.nca_kaek[kaek_index][key_gen_val]);
|
||||
if (g_keysetLoaded) ret = (const u8*)(g_ncaKeyset.nca_kaek[kaek_index][key_gen_val]);
|
||||
}
|
||||
|
||||
end:
|
||||
|
@ -395,9 +422,9 @@ bool keysDecryptRsaOaepWrappedTitleKey(const void *rsa_wrapped_titlekey, void *o
|
|||
|
||||
bool ret = false;
|
||||
|
||||
SCOPED_LOCK(&g_ncaKeysetMutex)
|
||||
SCOPED_LOCK(&g_keysetMutex)
|
||||
{
|
||||
if (!g_ncaKeysetLoaded) break;
|
||||
if (!g_keysetLoaded) break;
|
||||
|
||||
size_t out_keydata_size = 0;
|
||||
u8 out_keydata[RSA2048_BYTES] = {0};
|
||||
|
@ -432,15 +459,27 @@ const u8 *keysGetTicketCommonKey(u8 key_generation)
|
|||
goto end;
|
||||
}
|
||||
|
||||
SCOPED_LOCK(&g_ncaKeysetMutex)
|
||||
SCOPED_LOCK(&g_keysetMutex)
|
||||
{
|
||||
if (g_ncaKeysetLoaded) ret = (const u8*)(g_ncaKeyset.ticket_common_keys[key_gen_val]);
|
||||
if (g_keysetLoaded) ret = (const u8*)(g_ncaKeyset.ticket_common_keys[key_gen_val]);
|
||||
}
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
const u8 *keysGetGameCardInfoKey(void)
|
||||
{
|
||||
const u8 *ret = NULL;
|
||||
|
||||
SCOPED_LOCK(&g_keysetMutex)
|
||||
{
|
||||
if (g_keysetLoaded) ret = (const u8*)(utilsIsDevelopmentUnit() ? g_gameCardKeyset.gc_cardinfo_key_dev : g_gameCardKeyset.gc_cardinfo_key_prod);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool keysIsProductionModulus1xMandatory(void)
|
||||
{
|
||||
return !utilsIsDevelopmentUnit();
|
||||
|
@ -941,3 +980,34 @@ static bool keysTestEticketRsaDeviceKey(const void *e, const void *d, const void
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool keysDeriveGameCardKeys(void)
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
/* Derive gc_cardinfo_kek_sealed from gc_cardinfo_kek_source. */
|
||||
rc = splCryptoGenerateAesKek(g_gameCardKeyset.gc_cardinfo_kek_source, 0, 0, g_gameCardKeyset.gc_cardinfo_kek_sealed);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOG_MSG("splCryptoGenerateAesKek failed! (0x%08X) (gc_cardinfo_kek_sealed).", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Derive gc_cardinfo_key_prod from gc_cardinfo_kek_sealed and gc_cardinfo_key_prod_source. */
|
||||
rc = splCryptoGenerateAesKey(g_gameCardKeyset.gc_cardinfo_kek_sealed, g_gameCardKeyset.gc_cardinfo_key_prod_source, g_gameCardKeyset.gc_cardinfo_key_prod);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOG_MSG("splCryptoGenerateAesKey failed! (0x%08X) (gc_cardinfo_key_prod).", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Derive gc_cardinfo_key_dev from gc_cardinfo_kek_sealed and gc_cardinfo_key_dev_source. */
|
||||
rc = splCryptoGenerateAesKey(g_gameCardKeyset.gc_cardinfo_kek_sealed, g_gameCardKeyset.gc_cardinfo_key_dev_source, g_gameCardKeyset.gc_cardinfo_key_dev);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOG_MSG("splCryptoGenerateAesKey failed! (0x%08X) (gc_cardinfo_key_dev).", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -129,8 +129,8 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
|||
/* Initialize USB Mass Storage interface. */
|
||||
if (!umsInitialize()) break;
|
||||
|
||||
/* Load NCA keyset. */
|
||||
if (!keysLoadNcaKeyset()) break;
|
||||
/* Load keyset. */
|
||||
if (!keysLoadKeyset()) break;
|
||||
|
||||
/* Allocate NCA crypto buffer. */
|
||||
if (!ncaAllocateCryptoBuffer())
|
||||
|
|
2
todo.txt
2
todo.txt
|
@ -15,7 +15,7 @@ todo:
|
|||
title: parse the update partition from gamecards (if available) to generate ncmcontentinfo data for all update titles
|
||||
|
||||
gamecard: functions to display filelist
|
||||
gamecard: check cardinfo's lafw version
|
||||
gamecard: move around code to perform the lafw version check at the places we need
|
||||
|
||||
pfs0: functions to display filelist
|
||||
|
||||
|
|
Loading…
Reference in a new issue