1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 10:16:39 +00:00

More improvements.

This commit is contained in:
Pablo Curiel 2020-04-19 18:44:22 -04:00
parent 1b45cdf05a
commit cf8ab4d4ac
22 changed files with 238 additions and 130 deletions

View file

@ -20,15 +20,15 @@ Steps to reproduce the behavior:
**Screenshots**
Add screenshots to help explain your problem.
**Please complete the following information:**
- Horizon OS (Switch FW) version: [e.g. 9.0.1]
**Please fill the following information:**
- Horizon OS (Switch FW) version: [e.g. 10.0.0]
- CFW: [e.g. Atmosphère, SX OS, etc.]
- CFW version: [e.g. 0.9.4, 2.9.2, etc.]
- CFW version: [e.g. 0.11.1, 2.9.4, etc.]
- Atmosphère launch method (if applicable): [e.g. Hekate, fusee-primary]
- NXDumpTool version: [e.g. 1.1.7]
- Homebrew launch method: [e.g. title override, Album applet]
- NXDumpTool version: [e.g. 1.2.0]
- Homebrew launch method: [e.g. title override, applet]
- Source storage used with the application (if applicable): [e.g. gamecard, SD/eMMC]
- SD card specs: [e.g. Samsung EVO 256 GB]
- SD card specs: [e.g. Samsung EVO 256 GB, FAT32 partition]
**Additional context**
Add any other context about the problem here.

View file

@ -1,7 +1,7 @@
todo:
hfs0 methods
tik gamecard
pfs0: full header aligned to 0x20

Binary file not shown.

Before

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 B

View file

@ -25,7 +25,8 @@
#define CERT_SAVEFILE_PATH BIS_SYSTEM_PARTITION_MOUNT_NAME "/save/80000000000000e0"
#define CERT_SAVEFILE_STORAGE_BASE_PATH "/certificate/"
#define CERT_TYPE(sig) (pub_key_type == CertPubKeyType_Rsa4096 ? CertType_Sig##sig##_PubKeyRsa4096 : (pub_key_type == CertPubKeyType_Rsa2048 ? CertType_Sig##sig##_PubKeyRsa2048 : CertType_Sig##sig##_PubKeyEcsda240))
#define CERT_TYPE(sig) (pub_key_type == CertPubKeyType_Rsa4096 ? CertType_Sig##sig##_PubKeyRsa4096 : \
(pub_key_type == CertPubKeyType_Rsa2048 ? CertType_Sig##sig##_PubKeyRsa2048 : CertType_Sig##sig##_PubKeyEcsda240))
/* Global variables. */
@ -88,6 +89,52 @@ void certFreeCertificateChain(CertificateChain *chain)
chain->certs = NULL;
}
CertCommonBlock *certGetCommonBlockFromCertificate(Certificate *cert)
{
if (!cert || cert->type == CertType_None || cert->type > CertType_SigEcsda240_PubKeyEcsda240 || cert->size < CERT_MIN_SIZE || cert->size > CERT_MAX_SIZE)
{
LOGFILE("Invalid parameters!");
return NULL;
}
CertCommonBlock *cert_common_blk = NULL;
switch(cert->type)
{
case CertType_SigRsa4096_PubKeyRsa4096:
cert_common_blk = &(((CertSigRsa4096PubKeyRsa4096*)cert->data)->cert_common_blk);
break;
case CertType_SigRsa4096_PubKeyRsa2048:
cert_common_blk = &(((CertSigRsa4096PubKeyRsa2048*)cert->data)->cert_common_blk);
break;
case CertType_SigRsa4096_PubKeyEcsda240:
cert_common_blk = &(((CertSigRsa4096PubKeyEcsda240*)cert->data)->cert_common_blk);
break;
case CertType_SigRsa2048_PubKeyRsa4096:
cert_common_blk = &(((CertSigRsa2048PubKeyRsa4096*)cert->data)->cert_common_blk);
break;
case CertType_SigRsa2048_PubKeyRsa2048:
cert_common_blk = &(((CertSigRsa2048PubKeyRsa2048*)cert->data)->cert_common_blk);
break;
case CertType_SigRsa2048_PubKeyEcsda240:
cert_common_blk = &(((CertSigRsa2048PubKeyEcsda240*)cert->data)->cert_common_blk);
break;
case CertType_SigEcsda240_PubKeyRsa4096:
cert_common_blk = &(((CertSigEcsda240PubKeyRsa4096*)cert->data)->cert_common_blk);
break;
case CertType_SigEcsda240_PubKeyRsa2048:
cert_common_blk = &(((CertSigEcsda240PubKeyRsa2048*)cert->data)->cert_common_blk);
break;
case CertType_SigEcsda240_PubKeyEcsda240:
cert_common_blk = &(((CertSigEcsda240PubKeyEcsda240*)cert->data)->cert_common_blk);
break;
default:
break;
}
return cert_common_blk;
}
u8 *certGenerateRawCertificateChainBySignatureIssuer(const char *issuer, u64 *out_size)
{
if (!issuer || !strlen(issuer) || !out_size)
@ -183,7 +230,7 @@ static bool _certRetrieveCertificateByName(Certificate *dst, const char *name)
}
dst->type = certGetCertificateType(dst->data, dst->size);
if (dst->type == CertType_Invalid)
if (dst->type == CertType_None)
{
LOGFILE("Invalid certificate type for \"%s\"!", name);
return false;
@ -197,13 +244,13 @@ static u8 certGetCertificateType(const void *data, u64 data_size)
if (!data || data_size < CERT_MIN_SIZE || data_size > CERT_MAX_SIZE)
{
LOGFILE("Invalid parameters!");
return CertType_Invalid;
return CertType_None;
}
u8 type = CertType_Invalid;
const u8 *data_u8 = (const u8*)data;
u32 sig_type, pub_key_type;
u64 offset = 0;
u8 type = CertType_None;
const u8 *data_u8 = (const u8*)data;
u32 sig_type = 0, pub_key_type = 0;
memcpy(&sig_type, data_u8, sizeof(u32));
sig_type = __builtin_bswap32(sig_type);
@ -227,14 +274,14 @@ static u8 certGetCertificateType(const void *data, u64 data_size)
return type;
}
offset += MEMBER_SIZE(CertSigRsa4096PubKeyRsa4096, issuer);
offset += MEMBER_SIZE(CertCommonBlock, issuer);
memcpy(&pub_key_type, data_u8 + offset, sizeof(u32));
pub_key_type = __builtin_bswap32(pub_key_type);
offset += MEMBER_SIZE(CertSigRsa4096PubKeyRsa4096, pub_key_type);
offset += MEMBER_SIZE(CertSigRsa4096PubKeyRsa4096, name);
offset += MEMBER_SIZE(CertSigRsa4096PubKeyRsa4096, cert_id);
offset += MEMBER_SIZE(CertCommonBlock, pub_key_type);
offset += MEMBER_SIZE(CertCommonBlock, name);
offset += MEMBER_SIZE(CertCommonBlock, cert_id);
switch(pub_key_type)
{
@ -254,7 +301,7 @@ static u8 certGetCertificateType(const void *data, u64 data_size)
if (offset != data_size)
{
LOGFILE("Calculated end offset doesn't match certificate size! 0x%lX != 0x%lX", offset, data_size);
LOGFILE("Calculated end offset doesn't match certificate size! (0x%lX != 0x%lX)", offset, data_size);
return type;
}

View file

@ -26,16 +26,16 @@
#define CERT_MIN_SIZE 0x180 /* Equivalent to sizeof(CertSigEcsda240PubKeyEcsda240) */
typedef enum {
CertType_SigRsa4096_PubKeyRsa4096 = 0,
CertType_SigRsa4096_PubKeyRsa2048 = 1,
CertType_SigRsa4096_PubKeyEcsda240 = 2,
CertType_SigRsa2048_PubKeyRsa4096 = 3,
CertType_SigRsa2048_PubKeyRsa2048 = 4,
CertType_SigRsa2048_PubKeyEcsda240 = 5,
CertType_SigEcsda240_PubKeyRsa4096 = 6,
CertType_SigEcsda240_PubKeyRsa2048 = 7,
CertType_SigEcsda240_PubKeyEcsda240 = 8,
CertType_Invalid = 255
CertType_None = 0,
CertType_SigRsa4096_PubKeyRsa4096 = 1,
CertType_SigRsa4096_PubKeyRsa2048 = 2,
CertType_SigRsa4096_PubKeyEcsda240 = 3,
CertType_SigRsa2048_PubKeyRsa4096 = 4,
CertType_SigRsa2048_PubKeyRsa2048 = 5,
CertType_SigRsa2048_PubKeyEcsda240 = 6,
CertType_SigEcsda240_PubKeyRsa4096 = 7,
CertType_SigEcsda240_PubKeyRsa2048 = 8,
CertType_SigEcsda240_PubKeyEcsda240 = 9
} CertType;
/// Always stored using big endian byte order.
@ -62,92 +62,73 @@ typedef struct {
u8 padding[0x3C];
} CertPublicKeyBlockEcsda240;
/// Placed after the certificate signature block.
typedef struct {
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order.
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType_Rsa4096.
u32 pub_key_type;
char name[0x40];
u32 cert_id;
} CertCommonBlock;
typedef struct {
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
CertPublicKeyBlockRsa4096 pub_key_block;
} CertSigRsa4096PubKeyRsa4096;
typedef struct {
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order.
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType_Rsa2048.
char name[0x40];
u32 cert_id;
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
CertPublicKeyBlockRsa2048 pub_key_block;
} CertSigRsa4096PubKeyRsa2048;
typedef struct {
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order.
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType_Ecsda240.
char name[0x40];
u32 cert_id;
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecsda240.
CertPublicKeyBlockEcsda240 pub_key_block;
} CertSigRsa4096PubKeyEcsda240;
typedef struct {
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order.
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType_Rsa4096.
char name[0x40];
u32 cert_id;
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
CertPublicKeyBlockRsa4096 pub_key_block;
} CertSigRsa2048PubKeyRsa4096;
typedef struct {
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order.
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType_Rsa2048.
char name[0x40];
u32 cert_id;
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
CertPublicKeyBlockRsa2048 pub_key_block;
} CertSigRsa2048PubKeyRsa2048;
typedef struct {
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order.
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType_Ecsda240.
char name[0x40];
u32 cert_id;
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecsda240.
CertPublicKeyBlockEcsda240 pub_key_block;
} CertSigRsa2048PubKeyEcsda240;
typedef struct {
SignatureBlockEcsda240 sig_block; ///< sig_type field is stored using big endian byte order.
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType_Rsa4096.
char name[0x40];
u32 cert_id;
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
CertPublicKeyBlockRsa4096 pub_key_block;
} CertSigEcsda240PubKeyRsa4096;
typedef struct {
SignatureBlockEcsda240 sig_block; ///< sig_type field is stored using big endian byte order.
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType_Rsa2048.
char name[0x40];
u32 cert_id;
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
CertPublicKeyBlockRsa2048 pub_key_block;
} CertSigEcsda240PubKeyRsa2048;
typedef struct {
SignatureBlockEcsda240 sig_block; ///< sig_type field is stored using big endian byte order.
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType_Ecsda240.
char name[0x40];
u32 cert_id;
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecsda240.
CertPublicKeyBlockEcsda240 pub_key_block;
} CertSigEcsda240PubKeyEcsda240;
/// Used to store certificate type, size and raw data.
typedef struct {
u8 type; ///< CertType.
u64 size;
u8 data[CERT_MAX_SIZE];
u64 size; ///< Raw certificate size.
u8 data[CERT_MAX_SIZE]; ///< Raw certificate data.
} Certificate;
/// Used to store two or more certificates.
@ -161,6 +142,9 @@ bool certRetrieveCertificateByName(Certificate *dst, const char *name);
bool certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst, const char *issuer);
void certFreeCertificateChain(CertificateChain *chain);
/// Retrieves the common block from an input Certificate.
CertCommonBlock *certGetCommonBlockFromCertificate(Certificate *cert);
/// Returns a pointer to a heap allocated buffer that must be freed by the user.
u8 *certGenerateRawCertificateChainBySignatureIssuer(const char *issuer, u64 *out_size);

View file

@ -41,9 +41,6 @@
#define GAMECARD_CAPACITY_16GiB (u64)0x400000000
#define GAMECARD_CAPACITY_32GiB (u64)0x800000000
#define GAMECARD_HFS_PARTITION_NAME(x) ((x) == GameCardHashFileSystemPartitionType_Update ? "update" : ((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : \
((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : ((x) == GameCardHashFileSystemPartitionType_Secure ? "secure" : "unknown"))))
/* Type definitions. */
typedef enum {

View file

@ -27,6 +27,9 @@
#define GAMECARD_MEDIA_UNIT_SIZE 0x200
#define GAMECARD_HFS_PARTITION_NAME(x) ((x) == GameCardHashFileSystemPartitionType_Update ? "update" : ((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : \
((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : ((x) == GameCardHashFileSystemPartitionType_Secure ? "secure" : "unknown"))))
typedef enum {
GameCardKekIndex_Version0 = 0,
GameCardKekIndex_VersionForDev = 1

View file

@ -188,12 +188,6 @@ const u8 *keysGetNcaHeaderKey(void)
const u8 *keysGetKeyAreaEncryptionKeySource(u8 kaek_index)
{
if (kaek_index > NcaKeyAreaEncryptionKeyIndex_System)
{
LOGFILE("Invalid KAEK index! (0x%02X)", kaek_index);
return NULL;
}
const u8 *ptr = NULL;
switch(kaek_index)
@ -208,6 +202,7 @@ const u8 *keysGetKeyAreaEncryptionKeySource(u8 kaek_index)
ptr = (const u8*)(g_ncaKeyset.key_area_key_system_source);
break;
default:
LOGFILE("Invalid KAEK index! (0x%02X)", kaek_index);
break;
}

View file

@ -209,6 +209,24 @@ int main(int argc, char *argv[])
consoleUpdate(NULL);
tikConvertPersonalizedTicketToCommonTicket(&tik);
printf("common tik generated\n");
consoleUpdate(NULL);
tmp_file = fopen("sdmc:/common_tik.bin", "wb");
if (tmp_file)
{
fwrite(&tik, 1, sizeof(Ticket), tmp_file);
fclose(tmp_file);
tmp_file = NULL;
printf("common tik saved\n");
} else {
printf("common tik not saved\n");
}
consoleUpdate(NULL);
tik_common_blk = tikGetCommonBlockFromTicket(&tik);
if (tik_common_blk)

View file

@ -21,6 +21,7 @@
#include "nca.h"
#include "keys.h"
#include "rsa.h"
#include "gamecard.h"
#include "utils.h"
/* Global variables. */
@ -32,7 +33,7 @@ static const u8 g_nca0KeyAreaHash[SHA256_HASH_SIZE] = {
/* Function prototypes. */
static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx);
static inline bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx);
static void ncaUpdateAesCtrIv(u8 *ctr, u64 offset);
static void ncaUpdateAesCtrExIv(u8 *ctr, u32 ctr_val, u64 offset);
@ -68,7 +69,30 @@ size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src,
return i;
}
bool ncaRead(NcaContext *ctx, void *out, u64 read_size, u64 offset)
{
if (!ctx || (ctx->storage_id != NcmStorageId_GameCard && !ctx->ncm_storage) || (ctx->storage_id == NcmStorageId_GameCard && !ctx->gamecard_offset) || !out || !read_size || \
offset >= ctx->size || (offset + read_size) > ctx->size)
{
LOGFILE("Invalid parameters!");
return false;
}
Result rc = 0;
bool ret = false;
if (ctx->storage_id == NcmStorageId_GameCard)
{
ret = gamecardRead(out, read_size, ctx->gamecard_offset + offset);
if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (gamecard)", read_size, offset, ctx->id_str);
} else {
rc = ncmContentStorageReadContentIdFile(ctx->ncm_storage, out, read_size, &(ctx->id), offset);
ret = R_SUCCEEDED(rc);
if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (0x%08X) (ncm)", read_size, offset, ctx->id_str, rc);
}
return ret;
}
@ -121,7 +145,7 @@ bool ncaDecryptKeyArea(NcaContext *ctx)
for(u8 i = 0; i < key_count; i++)
{
rc = splCryptoGenerateAesKey(tmp_kek, &(ctx->header.encrypted_keys[i]), &(ctx->decrypted_keys[i]));
rc = splCryptoGenerateAesKey(tmp_kek, ctx->header.encrypted_keys[i].key, ctx->decrypted_keys[i].key);
if (R_FAILED(rc))
{
LOGFILE("splCryptoGenerateAesKey failed! (0x%08X)", rc);
@ -161,7 +185,7 @@ bool ncaEncryptKeyArea(NcaContext *ctx)
key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4);
aes128ContextCreate(&key_area_ctx, kaek, true);
for(u8 i = 0; i < key_count; i++) aes128EncryptBlock(&key_area_ctx, &(ctx->header.encrypted_keys[i]), &(ctx->decrypted_keys[i]));
for(u8 i = 0; i < key_count; i++) aes128EncryptBlock(&key_area_ctx, ctx->header.encrypted_keys[i].key, ctx->decrypted_keys[i].key);
return true;
}
@ -176,6 +200,7 @@ bool ncaDecryptHeader(NcaContext *ctx)
u32 i, magic = 0;
size_t crypt_res = 0;
u64 fs_header_offset = 0;
const u8 *header_key = NULL;
Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0};
@ -231,18 +256,27 @@ bool ncaDecryptHeader(NcaContext *ctx)
return false;
}
aes128XtsContextCreate(&nca0_fs_header_ctx, &(ctx->decrypted_keys[0]), &(ctx->decrypted_keys[1]), false);
aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_keys[0].key, ctx->decrypted_keys[1].key, false);
for(i = 0; i < NCA_FS_HEADER_COUNT; i++)
{
if (!ctx->header.fs_entries[i].enable_entry) continue;
/* FS headers are not part of NCA0 headers */
fs_header_offset = NCA_FS_ENTRY_BLOCK_OFFSET(ctx->header.fs_entries[i].start_block_offset);
if (!ncaRead(ctx, &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, fs_header_offset))
{
LOGFILE("Failed to read NCA0 FS section header #%u at offset 0x%lX!", i, fs_header_offset);
return false;
}
crypt_res = aes128XtsNintendoCrypt(&nca0_fs_header_ctx, &(ctx->header.fs_headers[i]), &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, (fs_header_offset - 0x400) >> 9, \
NCA_AES_XTS_SECTOR_SIZE, false);
if (crypt_res != NCA_FS_HEADER_LENGTH)
{
LOGFILE("Error decrypting NCA0 FS section header #%u!", i);
return false;
}
}
break;
@ -332,7 +366,7 @@ bool ncaEncryptHeader(NcaContext *ctx)
static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx)
static inline bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx)
{
if (!ctx || ctx->format_version != NcaVersion_Nca0) return false;

View file

@ -35,6 +35,7 @@
#define NCA_BKTR_MAGIC 0x424B5452 /* "BKTR" */
#define NCA_FS_ENTRY_BLOCK_SIZE 0x200
#define NCA_FS_ENTRY_BLOCK_OFFSET(x) ((x) * NCA_FS_ENTRY_BLOCK_SIZE)
#define NCA_AES_XTS_SECTOR_SIZE 0x200
@ -98,7 +99,7 @@ typedef struct {
typedef struct {
u8 key[0x10];
} NcaEncryptedKey;
} NcaKey;
typedef enum {
NcaFsType_RomFs = 0,
@ -235,7 +236,7 @@ typedef struct {
FsRightsId rights_id; ///< Used for titlekey crypto.
NcaFsEntry fs_entries[4]; ///< Start and end offsets for each NCA FS section.
NcaFsHash fs_hashes[4]; ///< SHA-256 hashes calculated over each NCA FS section header.
NcaEncryptedKey encrypted_keys[4]; ///< Only the encrypted key at index #2 is used. The other three are zero filled before the key area is encrypted.
NcaKey encrypted_keys[4]; ///< Only the encrypted key at index #2 is used. The other three are zero filled before the key area is encrypted.
u8 reserved_2[0xC0];
NcaFsHeader fs_headers[4]; /// NCA FS section headers.
} NcaHeader;
@ -278,10 +279,22 @@ typedef struct {
bool rights_id_available;
NcaHeader header;
bool dirty_header;
NcaEncryptedKey decrypted_keys[4];
NcaKey decrypted_keys[4];
NcaFsContext fs_contexts[4];
} NcaContext;
/// Reads raw encrypted data from a NCA using a NCA context with the 'storage_id', 'id', 'id_str' and 'size' elements filled beforehand.
/// If 'storage_id' == NcmStorageId_GameCard, the 'gamecard_offset' element should hold a value greater than zero.
/// If 'storage_id' != NcmStorageId_GameCard, the 'ncm_storage' element should point to a valid NcmContentStorage instance.
bool ncaRead(NcaContext *ctx, void *out, u64 read_size, u64 offset);
static inline void ncaConvertNcmContentSizeToU64(const u8 *size, u64 *out)
{
if (!size || !out) return;

View file

@ -120,12 +120,12 @@ bool servicesCheckRunningServiceByName(const char *name)
{
if (!name || !strlen(name)) return false;
Handle handle;
Handle handle = INVALID_HANDLE;
SmServiceName service_name = smEncodeName(name);
Result rc = smRegisterService(&handle, service_name, false, 1);
bool running = R_FAILED(rc);
svcCloseHandle(handle);
if (handle != INVALID_HANDLE) svcCloseHandle(handle);
if (!running) smUnregisterService(service_name);

View file

@ -66,7 +66,7 @@ static TikCommonBlock *tikGetCommonBlockFromMemoryBuffer(void *data);
static bool tikGetTitleKeyFromTicketCommonBlock(void *dst, const TikCommonBlock *tik_common_blk);
static bool tikGetTitleKekDecryptedTitleKey(void *dst, const void *src, u8 key_generation);
static u8 tikGetTitleKeyTypeFromRightsId(const FsRightsId *id);
static bool tikGetTitleKeyTypeFromRightsId(const FsRightsId *id, u8 *out);
static bool tikRetrieveRightsIdsByTitleKeyType(FsRightsId **out, u32 *out_count, bool personalized);
static bool tikGetTicketTypeAndSize(const void *data, u64 data_size, u8 *out_type, u64 *out_size);
@ -86,7 +86,7 @@ bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gam
return false;
}
tik_common_blk = tikGetCommonBlockFromMemoryBuffer(dst->data);
tik_common_blk = tikGetCommonBlockFromTicket(dst);
if (!tik_common_blk)
{
LOGFILE("Unable to retrieve common block from ticket!");
@ -112,23 +112,40 @@ bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gam
TikCommonBlock *tikGetCommonBlockFromTicket(Ticket *tik)
{
if (!tik || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE)
if (!tik || tik->type == TikType_None || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE || tik->size > TIK_MAX_SIZE)
{
LOGFILE("Invalid parameters!");
return NULL;
}
return tikGetCommonBlockFromMemoryBuffer(tik->data);
TikCommonBlock *tik_common_blk = NULL;
switch(tik->type)
{
case TikType_SigRsa4096:
tik_common_blk = &(((TikSigRsa4096*)tik->data)->tik_common_blk);
break;
case TikType_SigRsa2048:
tik_common_blk = &(((TikSigRsa2048*)tik->data)->tik_common_blk);
break;
case TikType_SigEcsda240:
tik_common_blk = &(((TikSigEcsda240*)tik->data)->tik_common_blk);
break;
default:
break;
}
return tik_common_blk;
}
void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik)
{
if (!tik || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE) return;
if (!tik || tik->type == TikType_None || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE || tik->size > TIK_MAX_SIZE) return;
bool dev_cert = false;
TikCommonBlock *tik_common_blk = NULL;
tik_common_blk = tikGetCommonBlockFromMemoryBuffer(tik->data);
tik_common_blk = tikGetCommonBlockFromTicket(tik);
if (!tik_common_blk || tik_common_blk->titlekey_type != TikTitleKeyType_Personalized) return;
switch(tik->type)
@ -220,19 +237,19 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight
}
u32 i;
u8 titlekey_type = 0;
save_ctx_t *save_ctx = NULL;
allocation_table_storage_ctx_t fat_storage = {0};
u64 ticket_bin_size = 0;
u64 buf_size = (TIK_MAX_SIZE * 0x10); /* 0x4000 */
u64 buf_size = (TIK_MAX_SIZE * 0x10);
u64 br = 0, total_br = 0;
u8 *ticket_bin_buf = NULL;
bool found_tik = false, success = false;
u8 titlekey_type = tikGetTitleKeyTypeFromRightsId(id);
if (titlekey_type == TikTitleKeyType_Invalid)
if (!tikGetTitleKeyTypeFromRightsId(id, &titlekey_type))
{
LOGFILE("Unable to retrieve ticket titlekey type!");
return false;
@ -326,9 +343,9 @@ static TikCommonBlock *tikGetCommonBlockFromMemoryBuffer(void *data)
return NULL;
}
u32 sig_type = 0;
u8 *data_u8 = (u8*)data;
TikCommonBlock *tik_common_blk = NULL;
u32 sig_type = 0;
memcpy(&sig_type, data_u8, sizeof(u32));
@ -426,27 +443,27 @@ static bool tikGetTitleKekDecryptedTitleKey(void *dst, const void *src, u8 key_g
return true;
}
static u8 tikGetTitleKeyTypeFromRightsId(const FsRightsId *id)
static bool tikGetTitleKeyTypeFromRightsId(const FsRightsId *id, u8 *out)
{
if (!id)
if (!id || !out)
{
LOGFILE("Invalid parameters!");
return TikTitleKeyType_Invalid;
return false;
}
u8 type = TikTitleKeyType_Invalid;
u32 count;
FsRightsId *rights_ids;
bool found = false;
for(u8 i = 0; i < 2; i++)
{
count = 0;
rights_ids = NULL;
if (!tikRetrieveRightsIdsByTitleKeyType(&rights_ids, &count, (bool)i))
if (!tikRetrieveRightsIdsByTitleKeyType(&rights_ids, &count, i == 1))
{
LOGFILE("Unable to retrieve %s rights IDs!", i == 0 ? "common" : "personalized");
break;
continue;
}
if (!count) continue;
@ -455,17 +472,18 @@ static u8 tikGetTitleKeyTypeFromRightsId(const FsRightsId *id)
{
if (!memcmp(rights_ids[j].c, id->c, 0x10))
{
type = i; /* TikTitleKeyType_Common or TikTitleKeyType_Personalized */
*out = i; /* TikTitleKeyType_Common or TikTitleKeyType_Personalized */
found = true;
break;
}
}
free(rights_ids);
if (type != TikTitleKeyType_Invalid) break;
if (found) break;
}
return type;
return found;
}
static bool tikRetrieveRightsIdsByTitleKeyType(FsRightsId **out, u32 *out_count, bool personalized)
@ -523,11 +541,11 @@ static bool tikGetTicketTypeAndSize(const void *data, u64 data_size, u8 *out_typ
return false;
}
u8 type = TikType_Invalid;
const u8 *data_u8 = (const u8*)data;
const TikCommonBlock *tik_common_blk = NULL;
u32 sig_type = 0;
u64 offset = 0;
u8 type = TikType_None;
const u8 *data_u8 = (const u8*)data;
const TikCommonBlock *tik_common_blk = NULL;
memcpy(&sig_type, data_u8, sizeof(u32));

View file

@ -22,20 +22,19 @@
#include <switch.h>
#include "signature.h"
#define TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES system savefiles */
#define TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES ticket system savedata file */
#define TIK_MIN_SIZE 0x200 /* Equivalent to sizeof(TikSigEcsda240) - assuming no ESv2 records are available */
typedef enum {
TikType_SigRsa4096 = 0,
TikType_SigRsa2048 = 1,
TikType_SigEcsda240 = 2,
TikType_Invalid = 255
TikType_None = 0,
TikType_SigRsa4096 = 1,
TikType_SigRsa2048 = 2,
TikType_SigEcsda240 = 3
} TikType;
typedef enum {
TikTitleKeyType_Common = 0,
TikTitleKeyType_Personalized = 1,
TikTitleKeyType_Invalid = 255
TikTitleKeyType_Personalized = 1
} TikTitleKeyType;
typedef enum {
@ -121,7 +120,7 @@ typedef struct {
u8 dec_titlekey[0x10]; ///< Titlekey without titlekek crypto. Ready to use for NCA FS section decryption.
} Ticket;
/// Retrieves a ticket from either the secure hash FS partition from the inserted gamecard or ES ticket savedata using a Rights ID value.
/// Retrieves a ticket from either the ES ticket system savedata file (eMMC BIS System partition) or the secure hash FS partition from an inserted gamecard, using a Rights ID value.
/// Titlekey is also RSA-OAEP unwrapped (if needed) and titlekek decrypted right away.
bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gamecard);

View file

@ -38,7 +38,7 @@ static AppletHookCookie g_systemOverclockCookie = {0};
static Mutex g_logfileMutex = 0;
static FsStorage g_emmcBisSystemPartitionStorage = {0};
static FATFS *g_emmcBisSystemPartitionFs = NULL;
static FATFS *g_emmcBisSystemPartitionFatFsObj = NULL;
/* Function prototypes. */
@ -200,11 +200,11 @@ void utilsGenerateHexStringFromData(char *dst, size_t dst_size, const void *src,
for(i = 0, j = 0; i < src_size; i++)
{
char nib1 = ((src_u8[i] >> 4) & 0xF);
char nib2 = (src_u8[i] & 0xF);
char h_nib = ((src_u8[i] >> 4) & 0xF);
char l_nib = (src_u8[i] & 0xF);
dst[j++] = (nib1 + (nib1 < 0xA ? 0x30 : 0x57));
dst[j++] = (nib2 + (nib2 < 0xA ? 0x30 : 0x57));
dst[j++] = (h_nib + (h_nib < 0xA ? 0x30 : 0x57));
dst[j++] = (l_nib + (l_nib < 0xA ? 0x30 : 0x57));
}
dst[j] = '\0';
@ -257,14 +257,14 @@ static bool utilsMountEmmcBisSystemPartitionStorage(void)
return false;
}
g_emmcBisSystemPartitionFs = calloc(1, sizeof(FATFS));
if (!g_emmcBisSystemPartitionFs)
g_emmcBisSystemPartitionFatFsObj = calloc(1, sizeof(FATFS));
if (!g_emmcBisSystemPartitionFatFsObj)
{
LOGFILE("Unable to allocate memory for FatFs object!");
return false;
}
fr = f_mount(g_emmcBisSystemPartitionFs, BIS_SYSTEM_PARTITION_MOUNT_NAME, 1);
fr = f_mount(g_emmcBisSystemPartitionFatFsObj, BIS_SYSTEM_PARTITION_MOUNT_NAME, 1);
if (fr != FR_OK)
{
LOGFILE("Failed to mount eMMC BIS System partition! (%u)", fr);
@ -276,11 +276,11 @@ static bool utilsMountEmmcBisSystemPartitionStorage(void)
static void utilsUnmountEmmcBisSystemPartitionStorage(void)
{
if (g_emmcBisSystemPartitionFs)
if (g_emmcBisSystemPartitionFatFsObj)
{
f_unmount(BIS_SYSTEM_PARTITION_MOUNT_NAME);
free(g_emmcBisSystemPartitionFs);
g_emmcBisSystemPartitionFs = NULL;
free(g_emmcBisSystemPartitionFatFsObj);
g_emmcBisSystemPartitionFatFsObj = NULL;
}
if (serviceIsActive(&(g_emmcBisSystemPartitionStorage.s)))