Bunch of changes.

* Updated disclaimer in all source files.
* Improved signed payload (certificate, ticket) handling.
* Prefer strlen + strcmp over strncmp whenever possible.
* Simplify header file inclusions in source files.
* Simplify background gamecard thread logic.
* Properly close keys file handle if there's a key parse error.
* Update NcaKeyGeneration enum.
* Small changes to save.c/h.

Will probably have to revert some USB changes...
This commit is contained in:
Pablo Curiel 2020-07-03 05:31:22 -04:00
parent 7d66da7308
commit 99429fd7b4
37 changed files with 913 additions and 752 deletions

20
nxdumptool_todo.txt Normal file
View file

@ -0,0 +1,20 @@
char content_info_path[FS_MAX_PATH] = {0};
sprintf(content_info_path, "sdmc:/%016lX.bin", xml_program_info.title_id);
FILE *content_info = fopen(content_info_path, "wb");
if (content_info)
{
fwrite(titleContentInfos, 1, titleContentInfoCnt * sizeof(NcmContentInfo), content_info);
fclose(content_info);
}
improve cert.c/h
improve headers
improve comments and button handling
improve function names

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* bktr.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,12 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "bktr.h"
#include "utils.h"
#include "bktr.h"
/* Function prototypes. */

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* bktr.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,7 +23,6 @@
#ifndef __BKTR_H__
#define __BKTR_H__
#include <switch.h>
#include "romfs.h"
typedef enum {

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* cert.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,19 +18,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "utils.h"
#include "cert.h"
#include "save.h"
#include "utils.h"
#include "gamecard.h"
#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##_PubKeyEcc480))
#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##_PubKeyEcc480))
/* Global variables. */
@ -39,7 +40,7 @@ static bool certOpenEsCertSaveFile(void);
static void certCloseEsCertSaveFile(void);
static bool _certRetrieveCertificateByName(Certificate *dst, const char *name);
static u8 certGetCertificateType(const void *data, u64 data_size);
static u8 certGetCertificateType(void *data, u64 data_size);
static bool _certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst, const char *issuer);
static u32 certGetCertificateCountInSignatureIssuer(const char *issuer);
@ -76,8 +77,9 @@ bool certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst, const
mutexLock(&g_esCertSaveMutex);
bool ret = false;
size_t issuer_len = 0;
if (!dst || !issuer || !strlen(issuer) || strncmp(issuer, "Root-", 5) != 0)
if (!dst || !issuer || !(issuer_len = strlen(issuer)) || issuer_len <= 5 || strcmp(issuer, "Root-") != 0)
{
LOGFILE("Invalid parameters!");
goto exit;
@ -95,70 +97,6 @@ exit:
return ret;
}
void certFreeCertificateChain(CertificateChain *chain)
{
if (!chain || !chain->certs) return;
chain->count = 0;
free(chain->certs);
chain->certs = NULL;
}
CertCommonBlock *certGetCommonBlockFromCertificate(Certificate *cert)
{
if (!cert || cert->type == CertType_None || cert->type > CertType_SigHmac160_PubKeyEcc480 || 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_PubKeyEcc480:
cert_common_blk = &(((CertSigRsa4096PubKeyEcc480*)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_PubKeyEcc480:
cert_common_blk = &(((CertSigRsa2048PubKeyEcc480*)cert->data)->cert_common_blk);
break;
case CertType_SigEcc480_PubKeyRsa4096:
cert_common_blk = &(((CertSigEcc480PubKeyRsa4096*)cert->data)->cert_common_blk);
break;
case CertType_SigEcc480_PubKeyRsa2048:
cert_common_blk = &(((CertSigEcc480PubKeyRsa2048*)cert->data)->cert_common_blk);
break;
case CertType_SigEcc480_PubKeyEcc480:
cert_common_blk = &(((CertSigEcc480PubKeyEcc480*)cert->data)->cert_common_blk);
break;
case CertType_SigHmac160_PubKeyRsa4096:
cert_common_blk = &(((CertSigHmac160PubKeyRsa4096*)cert->data)->cert_common_blk);
break;
case CertType_SigHmac160_PubKeyRsa2048:
cert_common_blk = &(((CertSigHmac160PubKeyRsa2048*)cert->data)->cert_common_blk);
break;
case CertType_SigHmac160_PubKeyEcc480:
cert_common_blk = &(((CertSigHmac160PubKeyEcc480*)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)
@ -182,7 +120,7 @@ u8 *certGenerateRawCertificateChainBySignatureIssuer(const char *issuer, u64 *ou
raw_chain = malloc(raw_chain_size);
if (!raw_chain)
{
LOGFILE("Unable to allocate memory for raw \"%s\" certificate chain! (0x%lX)", issuer, raw_chain_size);
LOGFILE("Unable to allocate memory for raw \"%s\" certificate chain! (0x%lX).", issuer, raw_chain_size);
goto out;
}
@ -195,6 +133,60 @@ out:
return raw_chain;
}
u8 *certRetrieveRawCertificateChainFromGameCardByRightsId(const FsRightsId *id, u64 *out_size)
{
if (!id || !out_size)
{
LOGFILE("Invalid parameters!");
return NULL;
}
char raw_chain_filename[0x30] = {0};
u64 raw_chain_offset = 0, raw_chain_size = 0;
u8 *raw_chain = NULL;
bool success = false;
utilsGenerateHexStringFromData(raw_chain_filename, sizeof(raw_chain_filename), id->c, 0x10);
strcat(raw_chain_filename, ".cert");
if (!gamecardGetEntryInfoFromHashFileSystemPartitionByName(GameCardHashFileSystemPartitionType_Secure, raw_chain_filename, &raw_chain_offset, &raw_chain_size))
{
LOGFILE("Error retrieving offset and size for \"%s\" entry in secure hash FS partition!", raw_chain_filename);
return NULL;
}
if (raw_chain_size < SIGNED_CERT_MIN_SIZE)
{
LOGFILE("Invalid size for \"%s\"! (0x%lX).", raw_chain_filename, raw_chain_size);
return NULL;
}
raw_chain = malloc(raw_chain_size);
if (!raw_chain)
{
LOGFILE("Unable to allocate memory for raw \"%s\" certificate chain! (0x%lX).", raw_chain_size);
return NULL;
}
if (!gamecardReadStorage(raw_chain, raw_chain_size, raw_chain_offset))
{
LOGFILE("Failed to read \"%s\" data from the inserted gamecard!", raw_chain_filename);
goto out;
}
*out_size = raw_chain_size;
success = true;
out:
if (!success && raw_chain)
{
free(raw_chain);
raw_chain = NULL;
}
return raw_chain;
}
static bool certOpenEsCertSaveFile(void)
{
if (g_esCertSaveCtx) return true;
@ -211,18 +203,16 @@ static bool certOpenEsCertSaveFile(void)
static void certCloseEsCertSaveFile(void)
{
if (g_esCertSaveCtx)
{
save_close_savefile(g_esCertSaveCtx);
g_esCertSaveCtx = NULL;
}
if (!g_esCertSaveCtx) return;
save_close_savefile(g_esCertSaveCtx);
g_esCertSaveCtx = NULL;
}
static bool _certRetrieveCertificateByName(Certificate *dst, const char *name)
{
if (!g_esCertSaveCtx || !dst || !name || !strlen(name))
if (!g_esCertSaveCtx)
{
LOGFILE("Invalid parameters!");
LOGFILE("ES certificate savefile not opened!");
return false;
}
@ -230,7 +220,7 @@ static bool _certRetrieveCertificateByName(Certificate *dst, const char *name)
char cert_path[SAVE_FS_LIST_MAX_NAME_LENGTH] = {0};
allocation_table_storage_ctx_t fat_storage = {0};
snprintf(cert_path, SAVE_FS_LIST_MAX_NAME_LENGTH, "%s%s", CERT_SAVEFILE_STORAGE_BASE_PATH, name);
snprintf(cert_path, SAVE_FS_LIST_MAX_NAME_LENGTH, CERT_SAVEFILE_STORAGE_BASE_PATH "%s", name);
if (!save_get_fat_storage_from_file_entry_by_path(g_esCertSaveCtx, cert_path, &fat_storage, &cert_size))
{
@ -238,9 +228,9 @@ static bool _certRetrieveCertificateByName(Certificate *dst, const char *name)
return false;
}
if (cert_size < CERT_MIN_SIZE || cert_size > CERT_MAX_SIZE)
if (cert_size < SIGNED_CERT_MIN_SIZE || cert_size > SIGNED_CERT_MAX_SIZE)
{
LOGFILE("Invalid size for certificate \"%s\"! (0x%lX)", name, cert_size);
LOGFILE("Invalid size for certificate \"%s\"! (0x%lX).", name, cert_size);
return false;
}
@ -263,88 +253,47 @@ static bool _certRetrieveCertificateByName(Certificate *dst, const char *name)
return true;
}
static u8 certGetCertificateType(const void *data, u64 data_size)
static u8 certGetCertificateType(void *data, u64 data_size)
{
if (!data || data_size < CERT_MIN_SIZE || data_size > CERT_MAX_SIZE)
CertCommonBlock *cert_common_block = NULL;
u32 sig_type = 0, pub_key_type = 0;
u64 signed_cert_size = 0;
u8 type = CertType_None;
if (!data || data_size < SIGNED_CERT_MIN_SIZE || data_size > SIGNED_CERT_MAX_SIZE)
{
LOGFILE("Invalid parameters!");
return CertType_None;
return type;
}
u64 offset = 0;
u8 type = CertType_None;
const u8 *data_u8 = (const u8*)data;
u32 sig_type = 0, pub_key_type = 0;
if (!(cert_common_block = certGetCommonBlock(data)) || !(signed_cert_size = certGetSignedCertificateSize(data)) || signed_cert_size > data_size)
{
LOGFILE("Input buffer doesn't hold a valid signed certificate!");
return type;
}
memcpy(&sig_type, data_u8, sizeof(u32));
sig_type = __builtin_bswap32(sig_type);
sig_type = signatureGetSigType(data, true);
pub_key_type = __builtin_bswap32(cert_common_block->pub_key_type);
switch(sig_type)
{
case SignatureType_Rsa4096Sha1:
case SignatureType_Rsa4096Sha256:
offset += sizeof(SignatureBlockRsa4096);
type = CERT_TYPE(Rsa4096);
break;
case SignatureType_Rsa2048Sha1:
case SignatureType_Rsa2048Sha256:
offset += sizeof(SignatureBlockRsa2048);
type = CERT_TYPE(Rsa2048);
break;
case SignatureType_Ecc480Sha1:
case SignatureType_Ecc480Sha256:
offset += sizeof(SignatureBlockEcc480);
type = CERT_TYPE(Ecc480);
break;
case SignatureType_Hmac160Sha1:
offset += sizeof(SignatureBlockHmac160);
type = CERT_TYPE(Hmac160);
break;
default:
LOGFILE("Invalid signature type value! (0x%08X)", sig_type);
return type;
}
memcpy(&pub_key_type, data_u8 + offset, sizeof(u32));
pub_key_type = __builtin_bswap32(pub_key_type);
offset += MEMBER_SIZE(CertCommonBlock, pub_key_type);
offset += MEMBER_SIZE(CertCommonBlock, name);
offset += MEMBER_SIZE(CertCommonBlock, date);
switch(pub_key_type)
{
case CertPubKeyType_Rsa4096:
offset += sizeof(CertPublicKeyBlockRsa4096);
break;
case CertPubKeyType_Rsa2048:
offset += sizeof(CertPublicKeyBlockRsa2048);
break;
case CertPubKeyType_Ecc480:
offset += sizeof(CertPublicKeyBlockEcc480);
break;
default:
LOGFILE("Invalid public key type value! (0x%08X)", pub_key_type);
return type;
}
if (offset != data_size)
{
LOGFILE("Calculated end offset doesn't match certificate size! (0x%lX != 0x%lX)", offset, data_size);
return type;
}
if (sig_type == SignatureType_Rsa4096Sha1 || sig_type == SignatureType_Rsa4096Sha256)
{
type = CERT_TYPE(Rsa4096);
} else
if (sig_type == SignatureType_Rsa2048Sha1 || sig_type == SignatureType_Rsa2048Sha256)
{
type = CERT_TYPE(Rsa2048);
} else
if (sig_type == SignatureType_Ecc480Sha1 || sig_type == SignatureType_Ecc480Sha256)
{
type = CERT_TYPE(Ecc480);
} else
if (sig_type == SignatureType_Hmac160Sha1)
{
type = CERT_TYPE(Hmac160);
}
return type;
@ -352,9 +301,9 @@ static u8 certGetCertificateType(const void *data, u64 data_size)
static bool _certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst, const char *issuer)
{
if (!dst || !issuer || !strlen(issuer) || strncmp(issuer, "Root-", 5) != 0)
if (!g_esCertSaveCtx)
{
LOGFILE("Invalid parameters!");
LOGFILE("ES certificate savefile not opened!");
return false;
}
@ -372,13 +321,13 @@ static bool _certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst
dst->certs = calloc(dst->count, sizeof(Certificate));
if (!dst->certs)
{
LOGFILE("Unable to allocate memory for the certificate chain! (0x%lX)", dst->count * sizeof(Certificate));
LOGFILE("Unable to allocate memory for the certificate chain! (0x%lX).", dst->count * sizeof(Certificate));
return false;
}
/* Copy string to avoid problems with strtok */
/* The "Root-" parent from the issuer string is skipped */
snprintf(issuer_copy, 0x40, issuer + 5);
snprintf(issuer_copy, 0x40, "%s", issuer + 5);
char *pch = strtok(issuer_copy, "-");
while(pch != NULL)

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* cert.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,11 +23,10 @@
#ifndef __CERT_H__
#define __CERT_H__
#include <switch/types.h>
#include "signature.h"
#define CERT_MAX_SIZE 0x500 /* Equivalent to sizeof(CertSigRsa4096PubKeyRsa4096) */
#define CERT_MIN_SIZE 0x140 /* Equivalent to sizeof(CertSigHmac160PubKeyEcc480) */
#define SIGNED_CERT_MAX_SIZE sizeof(CertSigRsa4096PubKeyRsa4096)
#define SIGNED_CERT_MIN_SIZE sizeof(CertSigHmac160PubKeyEcc480)
typedef enum {
CertType_None = 0,
@ -48,6 +51,14 @@ typedef enum {
CertPubKeyType_Ecc480 = 2
} CertPubKeyType;
/// Placed after the certificate signature block.
typedef struct {
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType. Stored using big endian byte order.
char name[0x40];
u32 date; ///< Stored using big endian byte order.
} CertCommonBlock;
typedef struct {
u8 public_key[0x200];
u32 public_exponent;
@ -65,90 +76,83 @@ typedef struct {
u8 padding[0x3C];
} CertPublicKeyBlockEcc480;
/// Placed after the certificate signature block.
typedef struct {
u32 pub_key_type; ///< CertPubKeyType.
char name[0x40];
u32 date;
} 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.
CertCommonBlock cert_common_block; ///< 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.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
CertCommonBlock cert_common_block; ///< 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.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecc480.
CertCommonBlock cert_common_block; ///< pub_key_type field must be CertPubKeyType_Ecc480.
CertPublicKeyBlockEcc480 pub_key_block;
} CertSigRsa4096PubKeyEcc480;
typedef struct {
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
CertCommonBlock cert_common_block; ///< 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.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
CertCommonBlock cert_common_block; ///< 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.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecc480.
CertCommonBlock cert_common_block; ///< pub_key_type field must be CertPubKeyType_Ecc480.
CertPublicKeyBlockEcc480 pub_key_block;
} CertSigRsa2048PubKeyEcc480;
typedef struct {
SignatureBlockEcc480 sig_block; ///< sig_type field is stored using big endian byte order.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
CertCommonBlock cert_common_block; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
CertPublicKeyBlockRsa4096 pub_key_block;
} CertSigEcc480PubKeyRsa4096;
typedef struct {
SignatureBlockEcc480 sig_block; ///< sig_type field is stored using big endian byte order.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
CertCommonBlock cert_common_block; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
CertPublicKeyBlockRsa2048 pub_key_block;
} CertSigEcc480PubKeyRsa2048;
typedef struct {
SignatureBlockEcc480 sig_block; ///< sig_type field is stored using big endian byte order.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecc480.
CertCommonBlock cert_common_block; ///< pub_key_type field must be CertPubKeyType_Ecc480.
CertPublicKeyBlockEcc480 pub_key_block;
} CertSigEcc480PubKeyEcc480;
typedef struct {
SignatureBlockHmac160 sig_block; ///< sig_type field is stored using big endian byte order.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
CertCommonBlock cert_common_block; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
CertPublicKeyBlockRsa4096 pub_key_block;
} CertSigHmac160PubKeyRsa4096;
typedef struct {
SignatureBlockHmac160 sig_block; ///< sig_type field is stored using big endian byte order.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
CertCommonBlock cert_common_block; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
CertPublicKeyBlockRsa2048 pub_key_block;
} CertSigHmac160PubKeyRsa2048;
typedef struct {
SignatureBlockHmac160 sig_block; ///< sig_type field is stored using big endian byte order.
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecc480.
CertCommonBlock cert_common_block; ///< pub_key_type field must be CertPubKeyType_Ecc480.
CertPublicKeyBlockEcc480 pub_key_block;
} CertSigHmac160PubKeyEcc480;
/// Used to store certificate type, size and raw data.
typedef struct {
u8 type; ///< CertType.
u64 size; ///< Raw certificate size.
u8 data[CERT_MAX_SIZE]; ///< Raw certificate data.
u8 type; ///< CertType.
u64 size; ///< Raw certificate size.
u8 data[SIGNED_CERT_MAX_SIZE]; ///< Raw certificate data.
} Certificate;
/// Used to store two or more certificates.
@ -157,15 +161,82 @@ typedef struct {
Certificate *certs;
} CertificateChain;
/// Retrieves a certificate by its name (e.g. "CA00000003", "XS00000020", etc.).
bool certRetrieveCertificateByName(Certificate *dst, const char *name);
/// Retrieves a certificate chain by a full signature issuer string (e.g. "Root-CA00000003-XS00000020").
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.
/// Returns a pointer to a heap allocated buffer that contains the raw contents from the certificate chain matching the input signature issuer. It must be freed by the user.
/// NULL is returned if an error occurs.
u8 *certGenerateRawCertificateChainBySignatureIssuer(const char *issuer, u64 *out_size);
/// Returns a pointer to a heap allocated buffer that contains the raw contents from the certificate chain matching the input Rights ID (stored in the inserted gamecard). It must be freed by the user.
/// NULL is returned if an error occurs.
u8 *certRetrieveRawCertificateChainFromGameCardByRightsId(const FsRightsId *id, u64 *out_size);
/// Helper inline functions.
NX_INLINE void certFreeCertificateChain(CertificateChain *chain)
{
if (!chain) return;
if (chain->certs) free(chain->certs);
memset(chain, 0, sizeof(CertificateChain));
}
NX_INLINE CertCommonBlock *certGetCommonBlock(void *buf)
{
return (CertCommonBlock*)signatureGetPayload(buf, true);
}
NX_INLINE bool certIsValidPublicKeyType(u32 type)
{
return (type == CertPubKeyType_Rsa4096 || type == CertPubKeyType_Rsa2048 || type == CertPubKeyType_Ecc480);
}
NX_INLINE u8 *certGetPublicKey(CertCommonBlock *cert_common_block)
{
return ((cert_common_block != NULL && certIsValidPublicKeyType(__builtin_bswap32(cert_common_block->pub_key_type))) ? ((u8*)cert_common_block + sizeof(CertCommonBlock)) : NULL);
}
NX_INLINE u64 certGetPublicKeySize(u32 type)
{
return (u64)(type == CertPubKeyType_Rsa4096 ? MEMBER_SIZE(CertPublicKeyBlockRsa4096, public_key) : \
(type == CertPubKeyType_Rsa2048 ? MEMBER_SIZE(CertPublicKeyBlockRsa2048, public_key) : \
(type == CertPubKeyType_Ecc480 ? MEMBER_SIZE(CertPublicKeyBlockEcc480, public_key) : 0)));
}
NX_INLINE u64 certGetPublicKeyBlockSize(u32 type)
{
return (u64)(type == CertPubKeyType_Rsa4096 ? sizeof(CertPublicKeyBlockRsa4096) : \
(type == CertPubKeyType_Rsa2048 ? sizeof(CertPublicKeyBlockRsa2048) : \
(type == CertPubKeyType_Ecc480 ? sizeof(CertPublicKeyBlockEcc480) : 0)));
}
NX_INLINE u32 certGetPublicExponent(CertCommonBlock *cert_common_block)
{
u8 *public_key = certGetPublicKey(cert_common_block);
return ((cert_common_block != NULL && public_key != NULL) ? __builtin_bswap32(*((u32*)(public_key + certGetPublicKeySize(__builtin_bswap32(cert_common_block->pub_key_type))))) : 0);
}
NX_INLINE bool certIsValidCertificate(void *buf)
{
CertCommonBlock *cert_common_block = certGetCommonBlock(buf);
return (cert_common_block != NULL && certIsValidPublicKeyType(__builtin_bswap32(cert_common_block->pub_key_type)));
}
NX_INLINE u64 certGetSignedCertificateSize(void *buf)
{
if (!certIsValidCertificate(buf)) return 0;
CertCommonBlock *cert_common_block = certGetCommonBlock(buf);
return (signatureGetBlockSize(signatureGetSigType(buf, true)) + (u64)sizeof(CertCommonBlock) + certGetPublicKeyBlockSize(__builtin_bswap32(cert_common_block->pub_key_type)));
}
NX_INLINE u64 certGetSignedCertificateHashAreaSize(void *buf)
{
if (!certIsValidCertificate(buf)) return 0;
CertCommonBlock *cert_common_block = certGetCommonBlock(buf);
return ((u64)sizeof(CertCommonBlock) + certGetPublicKeyBlockSize(__builtin_bswap32(cert_common_block->pub_key_type)));
}
#endif /* __CERT_H__ */

View file

@ -1,3 +1,26 @@
/*
* crc32_fast.c
*
* Based on the standard CRC32 checksum fast public domain implementation for
* little-endian architecures by Björn Samuelsson (http://home.thep.lu.se/~bjorn/crc).
*
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Standard CRC32 checksum: fast public domain implementation for
* little-endian architectures. Written for compilation with an
* optimizer set to perform loop unwinding. Outputs the checksum for
@ -5,10 +28,7 @@
* files that cause errors are silently skipped. The program reads
* from stdin if it is called with no arguments. */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <switch/types.h>
#include "utils.h"
u32 crc32_for_byte(u32 r)
{

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* es.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,10 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch/services/sm.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
#include "es.h"
#include "service_guard.h"

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* es.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,8 +23,6 @@
#ifndef __ES_H__
#define __ES_H__
#include <switch.h>
Result esInitialize(void);
void esExit(void);

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* fs_ext.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,10 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch/services/fs.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
#include "fs_ext.h"
/* IFileSystemProxy */

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* fs_ext.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,9 +23,6 @@
#ifndef __FS_EXT_H__
#define __FS_EXT_H__
#include <switch/types.h>
#include <switch/services/fs.h>
/// Located at offset 0x7000 in the gamecard image.
typedef struct {
u8 signature[0x100]; ///< RSA-2048 PKCS #1 signature over the rest of the data.

View file

@ -1,12 +1,16 @@
/*
* Copyright (c) 2019-2020 XorTroll
* Copyright (c) 2019-2020 DarkMatterCore
* fspusb.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2019-2020, XorTroll.
* Copyright (c) 2019-2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -16,11 +20,11 @@
*/
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include "utils.h"
#include "fspusb.h"
#include "service_guard.h"
#include <string.h>
static Service g_fspusbSrv;
NX_GENERATE_SERVICE_GUARD(fspusb);

View file

@ -1,12 +1,16 @@
/*
* Copyright (c) 2019-2020 XorTroll
* Copyright (c) 2019-2020 DarkMatterCore
* fspusb.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2019-2020, XorTroll.
* Copyright (c) 2019-2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -20,8 +24,6 @@
#ifndef __FSPUSB_H__
#define __FSPUSB_H__
#include <switch.h>
/// This is basically FATFS' file system types.
typedef enum {
FspUsbFileSystemType_FAT12 = 1,

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* gamecard.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,13 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <threads.h>
#include "gamecard.h"
#include "utils.h"
#include "gamecard.h"
#define GAMECARD_HFS0_MAGIC 0x48465330 /* "HFS0" */
@ -521,14 +520,13 @@ static int gamecardDetectionThreadFunc(void *arg)
Result rc = 0;
int idx = 0;
bool prev_status = false;
Waiter gamecard_event_waiter = waiterForEvent(&g_gameCardKernelEvent);
Waiter exit_event_waiter = waiterForUEvent(&g_gameCardDetectionThreadExitEvent);
/* Retrieve initial gamecard insertion status */
/* Load gamecard info right away if a gamecard is inserted */
g_gameCardInserted = prev_status = gamecardIsInserted();
g_gameCardInserted = gamecardIsInserted();
if (g_gameCardInserted) gamecardLoadInfo();
ueventSignal(&g_gameCardStatusChangeEvent);
@ -541,26 +539,22 @@ static int gamecardDetectionThreadFunc(void *arg)
/* Exit event triggered */
if (idx == 1) break;
/* Retrieve current gamecard insertion status */
/* Only proceed if we're dealing with a status change */
mutexLock(&g_gamecardMutex);
/* Retrieve current gamecard insertion status */
/* Only proceed if we're dealing with a status change */
g_gameCardInserted = gamecardIsInserted();
gamecardFreeInfo();
if (!prev_status && g_gameCardInserted)
if (g_gameCardInserted)
{
/* Don't access the gamecard immediately to avoid conflicts with HOS / sysmodules */
utilsSleep(GAMECARD_ACCESS_WAIT_TIME);
/* Load gamecard info */
gamecardLoadInfo();
} else {
/* Free gamecard info */
gamecardFreeInfo();
}
prev_status = g_gameCardInserted;
mutexUnlock(&g_gamecardMutex);
ueventSignal(&g_gameCardStatusChangeEvent);
@ -1057,13 +1051,14 @@ NX_INLINE bool gamecardGetHashFileSystemEntryIndexByName(void *header, const cha
GameCardHashFileSystemEntry *fs_entry = NULL;
GameCardHashFileSystemHeader *fs_header = (GameCardHashFileSystemHeader*)header;
char *name_table = gamecardGetHashFileSystemNameTable(header);
if (!fs_header || !fs_header->entry_count || !name_table || !name || !(name_len = strlen(name)) || !out_idx) return false;
for(u32 i = 0; i < fs_header->entry_count; i++)
{
if (!(fs_entry = gamecardGetHashFileSystemEntryByIndex(header, i))) return false;
if (!strncmp(name_table + fs_entry->name_offset, name, name_len))
if (strlen(name_table + fs_entry->name_offset) == name_len && !strcmp(name_table + fs_entry->name_offset, name))
{
*out_idx = i;
return true;

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* gamecard.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,13 +23,12 @@
#ifndef __GAMECARD_H__
#define __GAMECARD_H__
#include <switch/kernel/uevent.h>
#include "fs_ext.h"
#define GAMECARD_HEAD_MAGIC 0x48454144 /* "HEAD" */
#define GAMECARD_CERT_MAGIC 0x43455254 /* "CERT" */
#define GAMECARD_HEAD_MAGIC 0x48454144 /* "HEAD" */
#define GAMECARD_CERT_MAGIC 0x43455254 /* "CERT" */
#define GAMECARD_MEDIA_UNIT_SIZE 0x200
#define GAMECARD_MEDIA_UNIT_SIZE 0x200
#define GAMECARD_HFS_PARTITION_NAME(x) ((x) == GameCardHashFileSystemPartitionType_Root ? "root" : ((x) == GameCardHashFileSystemPartitionType_Update ? "update" : \
((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : ((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : \

View file

@ -1,11 +1,17 @@
/*
* Copyright (c) 2020 DarkMatterCore
* keys.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2018-2020, SciresM.
* Copyright (c) 2019, shchmue.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,14 +20,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "utils.h"
#include "keys.h"
#include "nca.h"
#include "utils.h"
#define KEYS_FILE_PATH "sdmc:/switch/prod.keys" /* Location used by Lockpick_RCM */
@ -678,15 +679,16 @@ static char keysConvertHexCharToBinary(char c)
static bool keysParseHexKey(u8 *out, const char *key, const char *value, u32 size)
{
if (!out || !key || !strlen(key) || !value || !strlen(value) || !size)
u32 hex_str_len = (2 * size);
size_t value_len = 0;
if (!out || !key || !strlen(key) || !value || !(value_len = strlen(value)) || !size)
{
LOGFILE("Invalid parameters!");
return false;
}
u32 hex_str_len = (2 * size);
if (strlen(value) != hex_str_len)
if (value_len != hex_str_len)
{
LOGFILE("Key \"%s\" must be %u hex digits long!", key, hex_str_len);
return false;
@ -717,7 +719,7 @@ static bool keysReadKeysFromFile(void)
FILE *keys_file = NULL;
char *key = NULL, *value = NULL;
char test_name[0x40] = {0};
bool common_eticket_rsa_kek = false, personalized_eticket_rsa_kek = false;
bool parse_fail = false, common_eticket_rsa_kek = false, personalized_eticket_rsa_kek = false;
keys_file = fopen(KEYS_FILE_PATH, "rb");
if (!keys_file)
@ -736,7 +738,7 @@ static bool keysReadKeysFromFile(void)
if (!common_eticket_rsa_kek && !personalized_eticket_rsa_kek && !strcasecmp(key, "eticket_rsa_kek"))
{
if (!keysParseHexKey(g_ncaKeyset.eticket_rsa_kek, key, value, sizeof(g_ncaKeyset.eticket_rsa_kek))) return false;
if ((parse_fail = !keysParseHexKey(g_ncaKeyset.eticket_rsa_kek, key, value, sizeof(g_ncaKeyset.eticket_rsa_kek)))) break;
common_eticket_rsa_kek = true;
key_count++;
} else
@ -744,7 +746,7 @@ static bool keysReadKeysFromFile(void)
{
/* Use the personalized eTicket RSA kek if available */
/* This only appears on consoles that use the new PRODINFO key generation scheme */
if (!keysParseHexKey(g_ncaKeyset.eticket_rsa_kek, key, value, sizeof(g_ncaKeyset.eticket_rsa_kek))) return false;
if ((parse_fail = !keysParseHexKey(g_ncaKeyset.eticket_rsa_kek, key, value, sizeof(g_ncaKeyset.eticket_rsa_kek)))) break;
personalized_eticket_rsa_kek = true;
key_count++;
} else {
@ -753,7 +755,7 @@ static bool keysReadKeysFromFile(void)
snprintf(test_name, sizeof(test_name), "titlekek_%02x", i);
if (!strcasecmp(key, test_name))
{
if (!keysParseHexKey(g_ncaKeyset.titlekeks[i], key, value, sizeof(g_ncaKeyset.titlekeks[i]))) return false;
if ((parse_fail = !keysParseHexKey(g_ncaKeyset.titlekeks[i], key, value, sizeof(g_ncaKeyset.titlekeks[i])))) break;
key_count++;
break;
}
@ -761,7 +763,7 @@ static bool keysReadKeysFromFile(void)
snprintf(test_name, sizeof(test_name), "key_area_key_application_%02x", i);
if (!strcasecmp(key, test_name))
{
if (!keysParseHexKey(g_ncaKeyset.key_area_keys[i][0], key, value, sizeof(g_ncaKeyset.key_area_keys[i][0]))) return false;
if ((parse_fail = !keysParseHexKey(g_ncaKeyset.key_area_keys[i][0], key, value, sizeof(g_ncaKeyset.key_area_keys[i][0])))) break;
key_count++;
break;
}
@ -769,7 +771,7 @@ static bool keysReadKeysFromFile(void)
snprintf(test_name, sizeof(test_name), "key_area_key_ocean_%02x", i);
if (!strcasecmp(key, test_name))
{
if (!keysParseHexKey(g_ncaKeyset.key_area_keys[i][1], key, value, sizeof(g_ncaKeyset.key_area_keys[i][1]))) return false;
if ((parse_fail = !keysParseHexKey(g_ncaKeyset.key_area_keys[i][1], key, value, sizeof(g_ncaKeyset.key_area_keys[i][1])))) break;
key_count++;
break;
}
@ -777,19 +779,21 @@ static bool keysReadKeysFromFile(void)
snprintf(test_name, sizeof(test_name), "key_area_key_system_%02x", i);
if (!strcasecmp(key, test_name))
{
if (!keysParseHexKey(g_ncaKeyset.key_area_keys[i][2], key, value, sizeof(g_ncaKeyset.key_area_keys[i][2]))) return false;
if ((parse_fail = !keysParseHexKey(g_ncaKeyset.key_area_keys[i][2], key, value, sizeof(g_ncaKeyset.key_area_keys[i][2])))) break;
key_count++;
break;
}
}
if (parse_fail) break;
}
}
fclose(keys_file);
if (!key_count)
if (parse_fail || !key_count)
{
LOGFILE("Unable to parse necessary keys from \"%s\"! (keys file empty?)", KEYS_FILE_PATH);
if (!key_count) LOGFILE("Unable to parse necessary keys from \"%s\"! (keys file empty?)", KEYS_FILE_PATH);
return false;
}

View file

@ -1,11 +1,17 @@
/*
* Copyright (c) 2020 DarkMatterCore
* keys.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2018-2020, SciresM.
* Copyright (c) 2019, shchmue.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,8 +25,6 @@
#ifndef __KEYS_H__
#define __KEYS_H__
#include <switch/types.h>
bool keysLoadNcaKeyset(void);
const u8 *keysGetNcaHeaderKey(void);

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* main.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,15 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <switch.h>
#include <dirent.h>
#include <threads.h>
#include <stdarg.h>
#include "utils.h"
#include "bktr.h"
#include "gamecard.h"
@ -423,11 +418,27 @@ int main(int argc, char *argv[])
shared_data.data_written = 0;
romfsGetTotalDataSize(&romfs_ctx, &(shared_data.total_size));
consolePrint("waiting for usb connection...\n");
consolePrint("waiting for usb connection... ");
while(appletMainLoop())
time_t start = time(NULL);
bool usb_conn = false;
while(true)
{
if (usbIsReady()) break;
time_t now = time(NULL);
if ((now - start) >= 10) break;
consolePrint("%lu ", now - start);
if ((usb_conn = usbIsReady())) break;
utilsSleep(1);
}
consolePrint("\n");
if (!usb_conn)
{
consolePrint("usb connection failed\n");
goto out2;
}
consolePrint("creating threads\n");
@ -438,13 +449,13 @@ int main(int argc, char *argv[])
u64 prev_size = 0;
u8 percent = 0;
time_t start = time(NULL);
time_t btn_cancel_start_tmr = 0, btn_cancel_end_tmr = 0;
bool btn_cancel_cur_state = false, btn_cancel_prev_state = false;
consolePrint("hold b to cancel\n\n");
start = time(NULL);
while(shared_data.data_written < shared_data.total_size)
{
if (shared_data.read_error || shared_data.write_error) break;

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* nca.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,15 +18,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "utils.h"
#include "nca.h"
#include "keys.h"
#include "rsa.h"
#include "gamecard.h"
#include "utils.h"
#define NCA_CRYPTO_BUFFER_SIZE 0x800000 /* 8 MiB */

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* nca.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,7 +23,6 @@
#ifndef __NCA_H__
#define __NCA_H__
#include <switch.h>
#include "tik.h"
#define NCA_HEADER_LENGTH 0x400
@ -88,8 +91,8 @@ typedef enum {
NcaKeyGeneration_700_801 = 8,
NcaKeyGeneration_810_811 = 9,
NcaKeyGeneration_900_901 = 10,
NcaKeyGeneration_910_1002 = 11,
NcaKeyGeneration_Current = NcaKeyGeneration_910_1002
NcaKeyGeneration_910_1004 = 11,
NcaKeyGeneration_Current = NcaKeyGeneration_910_1004
} NcaKeyGeneration;
typedef struct {
@ -312,7 +315,7 @@ void ncaFreeCryptoBuffer(void);
/// Initializes a NCA context.
/// If 'storage_id' != NcmStorageId_GameCard, the 'ncm_storage' argument must point to a valid NcmContentStorage instance, previously opened using the same NcmStorageId value.
/// If 'storage_id' == NcmStorageId_GameCard, the 'hfs_partition_type' argument must be a valid GameCardHashFileSystemPartitionType value.
/// If the NCA holds a populated Rights ID field, and if the Ticket object pointed to by 'tik' hasn't been filled, ticket data will be retrieved.
/// If the NCA holds a populated Rights ID field, and if the Ticket element pointed to by 'tik' hasn't been filled, ticket data will be retrieved.
bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm_storage, u8 hfs_partition_type, const NcmContentInfo *content_info, Ticket *tik);
/// Reads raw encrypted data from a NCA using an input context, previously initialized by ncaInitializeContext().

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* pfs.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,12 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "pfs.h"
#include "utils.h"
#include "pfs.h"
bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx)
{

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* pfs.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,7 +23,6 @@
#ifndef __PFS_H__
#define __PFS_H__
#include <switch.h>
#include "nca.h"
#define PFS0_MAGIC 0x50465330 /* "PFS0" */
@ -106,13 +109,14 @@ NX_INLINE bool pfsGetEntryIndexByName(PartitionFileSystemContext *ctx, const cha
PartitionFileSystemEntry *fs_entry = NULL;
u32 entry_count = pfsGetEntryCount(ctx);
char *name_table = pfsGetNameTable(ctx);
if (!entry_count || !name_table || !name || !(name_len = strlen(name)) || !out_idx) return false;
for(u32 i = 0; i < entry_count; i++)
{
if (!(fs_entry = pfsGetEntryByIndex(ctx, i))) return false;
if (!strncmp(name_table + fs_entry->name_offset, name, name_len))
if (strlen(name_table + fs_entry->name_offset) == name_len && !strcmp(name_table + fs_entry->name_offset, name))
{
*out_idx = i;
return true;

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* romfs.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,12 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "romfs.h"
#include "utils.h"
#include "romfs.h"
/* Function prototypes. */
@ -518,7 +518,7 @@ static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSys
while(dir_offset != ROMFS_VOID_ENTRY)
{
if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !child_dir_entry->name_length) return NULL;
if (!strncmp(child_dir_entry->name, name, name_len)) return child_dir_entry;
if (child_dir_entry->name_length == name_len && !strcmp(child_dir_entry->name, name)) return child_dir_entry;
dir_offset = child_dir_entry->next_offset;
}
@ -537,7 +537,7 @@ static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext
while(file_offset != ROMFS_VOID_ENTRY)
{
if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset)) || !child_file_entry->name_length) return NULL;
if (!strncmp(child_file_entry->name, name, name_len)) return child_file_entry;
if (child_file_entry->name_length == name_len && !strcmp(child_file_entry->name, name)) return child_file_entry;
file_offset = child_file_entry->next_offset;
}

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* romfs.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,7 +23,6 @@
#ifndef __ROMFS_H__
#define __ROMFS_H__
#include <switch.h>
#include "nca.h"
#define ROMFS_OLD_HEADER_SIZE 0x28

View file

@ -1,11 +1,17 @@
/*
* Copyright (c) 2020 DarkMatterCore
* rsa.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2018-2019, SciresM.
* Copyright (c) 2018-2019, The-4n.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,17 +20,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include "utils.h"
#include "rsa.h"
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/md.h>
#include <mbedtls/rsa.h>
#include <mbedtls/x509.h>
#include "rsa.h"
#include "utils.h"
/* Global variables. */
/// Self-generated private key.

View file

@ -1,11 +1,17 @@
/*
* Copyright (c) 2020 DarkMatterCore
* rsa.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2018-2019, SciresM.
* Copyright (c) 2018-2019, The-4n.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,8 +25,6 @@
#ifndef __RSA_H__
#define __RSA_H__
#include <switch/types.h>
#define RSA2048_SIGNATURE_SIZE 0x100
bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, size_t size);

View file

@ -1,12 +1,16 @@
/*
* Copyright (c) 2019-2020 shchmue
* Copyright (c) 2019-2020 DarkMatterCore
* save.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2019-2020, shchmue.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -15,12 +19,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "save.h"
#include "utils.h"
#include "save.h"
static inline void save_bitmap_set_bit(void *buffer, size_t bit_offset)
{
@ -131,7 +131,7 @@ static remap_segment_ctx_t *save_remap_init_segments(remap_header_t *header, rem
return NULL;
}
unsigned int i, entry_idx = 0;
u32 i, entry_idx = 0;
bool success = false;
for(i = 0; i < header->map_segment_count; i++)
@ -179,7 +179,7 @@ out:
{
entry_idx = 0;
for(unsigned int j = 0; j <= i; j++)
for(u32 j = 0; j <= i; j++)
{
if (!map_entries[entry_idx].segment) break;
@ -226,7 +226,7 @@ static remap_entry_ctx_t *save_remap_get_map_entry(remap_storage_ctx_t *ctx, u64
if (segment_idx < ctx->header->map_segment_count)
{
for(unsigned int i = 0; i < ctx->segments[segment_idx].entry_count; i++)
for(u32 i = 0; i < ctx->segments[segment_idx].entry_count; i++)
{
if (ctx->segments[segment_idx].entries[i]->virtual_offset_end > offset) return ctx->segments[segment_idx].entries[i];
}
@ -352,7 +352,7 @@ static bool save_ivfc_storage_init(hierarchical_integrity_verification_storage_c
levels[0].type = STORAGE_BYTES;
levels[0].hash_offset = master_hash_offset;
for(unsigned int i = 1; i < 4; i++)
for(u32 i = 1; i < 4; i++)
{
ivfc_level_hdr_t *level = &ivfc->level_headers[i - 1];
levels[i].type = STORAGE_REMAP;
@ -387,7 +387,7 @@ static bool save_ivfc_storage_init(hierarchical_integrity_verification_storage_c
init_info[0].data = &levels[0];
init_info[0].block_size = 0;
for(unsigned int i = 1; i < ivfc->num_levels; i++)
for(u32 i = 1; i < ivfc->num_levels; i++)
{
init_info[i].data = &levels[i];
init_info[i].block_size = (1 << ivfc->level_headers[i - 1].block_size);
@ -403,7 +403,7 @@ static bool save_ivfc_storage_init(hierarchical_integrity_verification_storage_c
goto out;
}
for(unsigned int i = 1; i < ivfc->num_levels; i++)
for(u32 i = 1; i < ivfc->num_levels; i++)
{
integrity_verification_storage_ctx_t *level_data = &ctx->integrity_storages[i - 1];
level_data->hash_storage = &levels[i - 1];
@ -435,7 +435,7 @@ out:
free(ctx->level_validities);
ctx->level_validities = NULL;
for(unsigned int i = 1; i < ivfc->num_levels; i++)
for(u32 i = 1; i < ivfc->num_levels; i++)
{
integrity_verification_storage_ctx_t *level_data = &ctx->integrity_storages[i - 1];
@ -640,7 +640,7 @@ static u32 save_allocation_table_get_list_length(allocation_table_ctx_t *ctx, u3
return 0;
}
allocation_table_entry_t entry;
allocation_table_entry_t entry = {0};
entry.next = block_index;
u32 total_length = 0;
u32 table_size = ctx->header->allocation_table_block_count;
@ -680,7 +680,7 @@ static bool save_allocation_table_iterator_begin(allocation_table_iterator_ctx_t
ctx->physical_block = initial_block;
ctx->virtual_block = 0;
allocation_table_entry_t entry;
allocation_table_entry_t entry = {0};
entry.next = initial_block;
ctx->current_segment_size = save_allocation_table_read_entry_with_length(ctx->fat, &entry);
@ -713,7 +713,7 @@ static bool save_allocation_table_iterator_move_next(allocation_table_iterator_c
ctx->virtual_block += ctx->current_segment_size;
ctx->physical_block = ctx->next_block;
allocation_table_entry_t entry;
allocation_table_entry_t entry = {0};
entry.next = ctx->next_block;
ctx->current_segment_size = save_allocation_table_read_entry_with_length(ctx->fat, &entry);
@ -739,7 +739,7 @@ static bool save_allocation_table_iterator_move_prev(allocation_table_iterator_c
ctx->physical_block = ctx->prev_block;
allocation_table_entry_t entry;
allocation_table_entry_t entry = {0};
entry.next = ctx->prev_block;
ctx->current_segment_size = save_allocation_table_read_entry_with_length(ctx->fat, &entry);
@ -811,7 +811,7 @@ u32 save_allocation_table_storage_read(allocation_table_storage_ctx_t *ctx, void
u32 sector_size = ctx->base_storage->integrity_storages[3].sector_size;
u32 chunk_remaining = bytes_to_read;
for(unsigned int i = 0; i < bytes_to_read; i += sector_size)
for(u32 i = 0; i < bytes_to_read; i += sector_size)
{
u32 bytes_to_request = (chunk_remaining < sector_size ? chunk_remaining : sector_size);
@ -900,7 +900,7 @@ bool save_fs_list_get_value(save_filesystem_list_ctx_t *ctx, u32 index, save_fs_
return true;
}
u32 save_fs_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_key_t *key, u32 *prev_index)
u32 save_fs_list_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_key_t *key, u32 *prev_index)
{
u32 prev;
if (!prev_index) prev_index = &prev;
@ -964,13 +964,13 @@ bool save_hierarchical_file_table_find_path_recursive(hierarchical_save_file_tab
}
key->parent = 0;
char *pos = strchr(path, '/');
const char *pos = strchr(path, '/');
while(pos)
{
memset(key->name, 0, SAVE_FS_LIST_MAX_NAME_LENGTH);
char *tmp = strchr(pos, '/');
const char *tmp = strchr(pos, '/');
if (!tmp)
{
memcpy(key->name, pos, strlen(pos));
@ -979,7 +979,7 @@ bool save_hierarchical_file_table_find_path_recursive(hierarchical_save_file_tab
memcpy(key->name, pos, tmp - pos);
key->parent = save_fs_get_index_from_key(&ctx->directory_table, key, NULL);
key->parent = save_fs_list_get_index_from_key(&ctx->directory_table, key, NULL);
if (key->parent == 0xFFFFFFFF) return false;
pos = (tmp + 1);
@ -1003,7 +1003,7 @@ bool save_hierarchical_file_table_get_file_entry_by_path(hierarchical_save_file_
return false;
}
u32 index = save_fs_get_index_from_key(&ctx->file_table, &key, NULL);
u32 index = save_fs_list_get_index_from_key(&ctx->file_table, &key, NULL);
if (index == 0xFFFFFFFF)
{
LOGFILE("Unable to get table index for file \"%s\"!", path);
@ -1092,7 +1092,7 @@ static validity_t save_ivfc_validate(hierarchical_integrity_verification_storage
validity_t result = VALIDITY_VALID;
for(unsigned int i = 0; i < (ivfc->num_levels - 1) && result != VALIDITY_INVALID; i++)
for(u32 i = 0; i < (ivfc->num_levels - 1) && result != VALIDITY_INVALID; i++)
{
integrity_verification_storage_ctx_t *storage = &ctx->integrity_storages[i];
@ -1107,7 +1107,7 @@ static validity_t save_ivfc_validate(hierarchical_integrity_verification_storage
break;
}
for(unsigned int j = 0; j < block_count; j++)
for(u32 j = 0; j < block_count; j++)
{
if (ctx->level_validities[ivfc->num_levels - 2][j] == VALIDITY_UNCHECKED)
{
@ -1145,11 +1145,11 @@ static bool save_ivfc_set_level_validities(hierarchical_integrity_verification_s
bool success = true;
for(unsigned int i = 0; i < (ivfc->num_levels - 1); i++)
for(u32 i = 0; i < (ivfc->num_levels - 1); i++)
{
validity_t level_validity = VALIDITY_VALID;
for(unsigned int j = 0; j < ctx->integrity_storages[i].sector_count; j++)
for(u32 j = 0; j < ctx->integrity_storages[i].sector_count; j++)
{
if (ctx->level_validities[i][j] == VALIDITY_INVALID)
{
@ -1260,8 +1260,7 @@ bool save_process(save_ctx_t *ctx)
}
}
unsigned char cmac[0x10];
memset(cmac, 0, 0x10);
u8 cmac[0x10] = {0};
cmacAes128CalculateMac(cmac, ctx->save_mac_key, &ctx->header.layout, sizeof(ctx->header.layout));
ctx->header_cmac_validity = (!memcmp(cmac, &ctx->header.cmac, 0x10) ? VALIDITY_VALID : VALIDITY_INVALID);
@ -1286,7 +1285,7 @@ bool save_process(save_ctx_t *ctx)
return success;
}
for(unsigned int i = 0; i < ctx->data_remap_storage.header->map_entry_count; i++)
for(u32 i = 0; i < ctx->data_remap_storage.header->map_entry_count; i++)
{
fr = f_read(ctx->file, &ctx->data_remap_storage.map_entries[i], 0x20, &br);
if (fr || br != 0x20)
@ -1423,7 +1422,7 @@ bool save_process(save_ctx_t *ctx)
goto out;
}
for(unsigned int i = 0; i < ctx->meta_remap_storage.header->map_entry_count; i++)
for(u32 i = 0; i < ctx->meta_remap_storage.header->map_entry_count; i++)
{
fr = f_read(ctx->file, &ctx->meta_remap_storage.map_entries[i], 0x20, &br);
if (fr || br != 0x20)
@ -1474,7 +1473,7 @@ bool save_process(save_ctx_t *ctx)
u32 *pos = (u32*)ctx->journal_storage.map.map_storage;
for(unsigned int i = 0; i < ctx->journal_storage.map.header->main_data_block_count; i++)
for(u32 i = 0; i < ctx->journal_storage.map.header->main_data_block_count; i++)
{
ctx->journal_storage.map.entries[i].virtual_index = i;
ctx->journal_storage.map.entries[i].physical_index = (*pos & 0x7FFFFFFF);
@ -1485,7 +1484,7 @@ bool save_process(save_ctx_t *ctx)
ctx->journal_storage._length = (ctx->journal_storage.header->total_size - ctx->journal_storage.header->journal_size);
/* Initialize core IVFC storage. */
for(unsigned int i = 0; i < 5; i++) ctx->core_data_ivfc_storage.levels[i].save_ctx = ctx;
for(u32 i = 0; i < 5; i++) ctx->core_data_ivfc_storage.levels[i].save_ctx = ctx;
if (!save_ivfc_storage_init(&ctx->core_data_ivfc_storage, ctx->header.layout.ivfc_master_hash_offset_a, &ctx->header.data_ivfc_header))
{
@ -1509,7 +1508,7 @@ bool save_process(save_ctx_t *ctx)
goto out;
}
} else {
for(unsigned int i = 0; i < 5; i++) ctx->fat_ivfc_storage.levels[i].save_ctx = ctx;
for(u32 i = 0; i < 5; i++) ctx->fat_ivfc_storage.levels[i].save_ctx = ctx;
if (!save_ivfc_storage_init(&ctx->fat_ivfc_storage, ctx->header.layout.fat_ivfc_master_hash_a, &ctx->header.fat_ivfc_header))
{
@ -1596,7 +1595,7 @@ void save_free_contexts(save_ctx_t *ctx)
{
if (ctx->data_remap_storage.header)
{
for(unsigned int i = 0; i < ctx->data_remap_storage.header->map_segment_count; i++)
for(u32 i = 0; i < ctx->data_remap_storage.header->map_segment_count; i++)
{
if (ctx->data_remap_storage.segments[i].entries) free(ctx->data_remap_storage.segments[i].entries);
}
@ -1616,7 +1615,7 @@ void save_free_contexts(save_ctx_t *ctx)
{
if (ctx->meta_remap_storage.header)
{
for(unsigned int i = 0; i < ctx->meta_remap_storage.header->map_segment_count; i++)
for(u32 i = 0; i < ctx->meta_remap_storage.header->map_segment_count; i++)
{
if (ctx->meta_remap_storage.segments[i].entries) free(ctx->meta_remap_storage.segments[i].entries);
}
@ -1650,7 +1649,7 @@ void save_free_contexts(save_ctx_t *ctx)
ctx->duplex_storage.layers[1].bitmap_storage = NULL;
}
for(unsigned int i = 1; i < 3; i++)
for(u32 i = 1; i < 3; i++)
{
if (ctx->duplex_layers[i].data_a)
{
@ -1677,7 +1676,7 @@ void save_free_contexts(save_ctx_t *ctx)
ctx->journal_storage.map.entries = NULL;
}
for(unsigned int i = 0; i < ctx->header.data_ivfc_header.num_levels - 1; i++)
for(u32 i = 0; i < ctx->header.data_ivfc_header.num_levels - 1; i++)
{
if (ctx->core_data_ivfc_storage.integrity_storages[i].block_validities)
{
@ -1694,7 +1693,7 @@ void save_free_contexts(save_ctx_t *ctx)
if (ctx->header.layout.version >= 0x50000)
{
for(unsigned int i = 0; i < ctx->header.fat_ivfc_header.num_levels - 1; i++)
for(u32 i = 0; i < ctx->header.fat_ivfc_header.num_levels - 1; i++)
{
if (ctx->fat_ivfc_storage.integrity_storages[i].block_validities)
{

View file

@ -1,12 +1,16 @@
/*
* Copyright (c) 2019-2020 shchmue
* Copyright (c) 2019-2020 DarkMatterCore
* save.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2019-2020, shchmue.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -20,10 +24,6 @@
#ifndef __SAVE_H__
#define __SAVE_H__
#include <stddef.h>
#include <stdint.h>
#include <switch.h>
#include "fatfs/ff.h"
#define IVFC_MAX_LEVEL 6
@ -505,7 +505,7 @@ void save_free_contexts(save_ctx_t *ctx);
bool save_open_fat_storage(save_filesystem_ctx_t *ctx, allocation_table_storage_ctx_t *storage_ctx, u32 block_index);
u32 save_allocation_table_storage_read(allocation_table_storage_ctx_t *ctx, void *buffer, u64 offset, size_t count);
bool save_fs_list_get_value(save_filesystem_list_ctx_t *ctx, u32 index, save_fs_list_entry_t *value);
u32 save_fs_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_key_t *key, u32 *prev_index);
u32 save_fs_list_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_key_t *key, u32 *prev_index);
bool save_hierarchical_file_table_find_path_recursive(hierarchical_save_file_table_ctx_t *ctx, save_entry_key_t *key, const char *path);
bool save_hierarchical_file_table_get_file_entry_by_path(hierarchical_save_file_table_ctx_t *ctx, const char *path, save_fs_list_entry_t *entry);

View file

@ -1,4 +1,29 @@
/*
* service_guard.h
*
* Copyright (c) 2019, Switchbrew, libnx contributors.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __SERVICE_GUARD_H__
#define __SERVICE_GUARD_H__
#include <switch.h>
typedef struct ServiceGuard {
@ -50,3 +75,5 @@ void name##Exit(void) \
}
#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ())
#endif /* __SERVICE_GUARD_H__ */

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* services.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -152,7 +156,7 @@ bool servicesCheckInitializedServiceByName(const char *name)
for(u32 i = 0; i < g_serviceInfoCount; i++)
{
if (!strncmp(g_serviceInfo[i].name, name, name_len))
if (strlen(g_serviceInfo[i].name) == name_len && !strcmp(g_serviceInfo[i].name, name))
{
ret = g_serviceInfo[i].initialized;
break;
@ -235,7 +239,7 @@ static bool servicesClkGetServiceType(void *arg)
if (!arg) return false;
ServicesInfoEntry *info = (ServicesInfoEntry*)arg;
if (!strlen(info->name) || strncmp(info->name, "clk", 3) != 0 || info->init_func != NULL || info->close_func != NULL) return false;
if (strlen(info->name) != 3 || strcmp(info->name, "clk") != 0 || info->init_func != NULL || info->close_func != NULL) return false;
/* Determine which service needs to be used to control hardware clock rates, depending on the system version */
/* This may either be pcv (sysver lower than 8.0.0) or clkrst (sysver equal to or greater than 8.0.0) */
@ -254,7 +258,7 @@ static bool servicesSplCryptoCheckAvailability(void *arg)
if (!arg) return false;
ServicesInfoEntry *info = (ServicesInfoEntry*)arg;
if (!strlen(info->name) || strncmp(info->name, "spl:mig", 7) != 0 || info->init_func == NULL || info->close_func == NULL) return false;
if (strlen(info->name) != 7 || strcmp(info->name, "spl:mig") != 0 || info->init_func == NULL || info->close_func == NULL) return false;
/* Check if spl:mig is available (sysver equal to or greater than 4.0.0) */
return !hosversionBefore(4, 0, 0);
@ -265,7 +269,7 @@ static bool servicesFspUsbCheckAvailability(void *arg)
if (!arg) return false;
ServicesInfoEntry *info = (ServicesInfoEntry*)arg;
if (!strlen(info->name) || strncmp(info->name, "fsp-usb", 7) != 0 || info->init_func == NULL || info->close_func == NULL) return false;
if (strlen(info->name) != 7 || strcmp(info->name, "fsp-usb") != 0 || info->init_func == NULL || info->close_func == NULL) return false;
/* Check if fsp-usb is actually running in the background */
return servicesCheckRunningServiceByName("fsp-usb");

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* services.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* signature.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -33,28 +37,67 @@ typedef struct {
u32 sig_type; ///< SignatureType_Rsa4096Sha1, SignatureType_Rsa4096Sha256.
u8 signature[0x200];
u8 padding[0x3C];
char issuer[0x40];
} SignatureBlockRsa4096;
typedef struct {
u32 sig_type; ///< SignatureType_Rsa2048Sha1, SignatureType_Rsa2048Sha256.
u8 signature[0x100];
u8 padding[0x3C];
char issuer[0x40];
} SignatureBlockRsa2048;
typedef struct {
u32 sig_type; ///< SignatureType_Ecc480Sha1, SignatureType_Ecc480Sha256.
u8 signature[0x3C];
u8 padding[0x40];
char issuer[0x40];
} SignatureBlockEcc480;
typedef struct {
u32 sig_type; ///< SignatureType_Hmac160Sha1.
u8 signature[0x14];
u8 padding[0x28];
char issuer[0x40];
} SignatureBlockHmac160;
/// Helper inline functions.
NX_INLINE u32 signatureGetSigType(void *buf, bool byteswap)
{
if (!buf) return 0;
return (byteswap ? __builtin_bswap32(*((u32*)buf)) : *((u32*)buf));
}
NX_INLINE bool signatureIsValidSigType(u32 type)
{
return (type == SignatureType_Rsa4096Sha1 || type == SignatureType_Rsa2048Sha1 || type == SignatureType_Ecc480Sha1 || \
type == SignatureType_Rsa4096Sha256 || type == SignatureType_Rsa2048Sha256 || type == SignatureType_Ecc480Sha256 || \
type == SignatureType_Hmac160Sha1);
}
NX_INLINE u8 *signatureGetSig(void *buf)
{
return (buf ? ((u8*)buf + 4) : NULL);
}
NX_INLINE u64 signatureGetSigSize(u32 type)
{
return (u64)((type == SignatureType_Rsa4096Sha1 || type == SignatureType_Rsa4096Sha256) ? MEMBER_SIZE(SignatureBlockRsa4096, signature) : \
((type == SignatureType_Rsa2048Sha1 || type == SignatureType_Rsa2048Sha256) ? MEMBER_SIZE(SignatureBlockRsa2048, signature) : \
((type == SignatureType_Ecc480Sha1 || type == SignatureType_Ecc480Sha256) ? MEMBER_SIZE(SignatureBlockEcc480, signature) : \
(type == SignatureType_Hmac160Sha1 ? MEMBER_SIZE(SignatureBlockHmac160, signature) : 0))));
}
NX_INLINE u64 signatureGetBlockSize(u32 type)
{
return (u64)((type == SignatureType_Rsa4096Sha1 || type == SignatureType_Rsa4096Sha256) ? sizeof(SignatureBlockRsa4096) : \
((type == SignatureType_Rsa2048Sha1 || type == SignatureType_Rsa2048Sha256) ? sizeof(SignatureBlockRsa2048) : \
((type == SignatureType_Ecc480Sha1 || type == SignatureType_Ecc480Sha256) ? sizeof(SignatureBlockEcc480) : \
(type == SignatureType_Hmac160Sha1 ? sizeof(SignatureBlockHmac160) : 0))));
}
NX_INLINE void *signatureGetPayload(void *buf, bool big_endian_sig_type)
{
if (!buf) return NULL;
u32 sig_type = signatureGetSigType(buf, big_endian_sig_type);
return (signatureIsValidSigType(sig_type) ? (void*)((u8*)buf + signatureGetBlockSize(sig_type)) : NULL);
}
#endif /* __SIGNATURE_H__ */

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* tik.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,17 +18,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "utils.h"
#include "tik.h"
#include "save.h"
#include "es.h"
#include "keys.h"
#include "rsa.h"
#include "gamecard.h"
#include "utils.h"
#define TIK_COMMON_SAVEFILE_PATH BIS_SYSTEM_PARTITION_MOUNT_NAME "/save/80000000000000e1"
#define TIK_PERSONALIZED_SAVEFILE_PATH BIS_SYSTEM_PARTITION_MOUNT_NAME "/save/80000000000000e2"
@ -62,15 +62,13 @@ static const u8 g_nullHash[0x20] = {
static bool tikRetrieveTicketFromGameCardByRightsId(Ticket *dst, const FsRightsId *id);
static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRightsId *id);
static TikCommonBlock *tikGetCommonBlockFromMemoryBuffer(void *data);
static bool tikGetTitleKekEncryptedTitleKeyFromTicket(Ticket *tik);
static bool tikGetTitleKekDecryptedTitleKey(void *dst, const void *src, u8 key_generation);
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);
static bool tikGetTicketTypeAndSize(void *data, u64 data_size, u8 *out_type, u64 *out_size);
static bool tikRetrieveEticketDeviceKey(void);
static bool tikTestKeyPairFromEticketDeviceKey(const void *e, const void *d, const void *n);
@ -83,11 +81,11 @@ bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gam
return false;
}
/* Check if this ticket has already been retrieved */
if (dst->type > TikType_None && dst->type <= TikType_SigHmac160 && dst->size >= TIK_MIN_SIZE && dst->size <= TIK_MAX_SIZE)
/* Check if this ticket has already been retrieved. */
if (dst->type > TikType_None && dst->type <= TikType_SigHmac160 && dst->size >= SIGNED_TIK_MIN_SIZE && dst->size <= SIGNED_TIK_MAX_SIZE)
{
TikCommonBlock *tik_common_blk = tikGetCommonBlockFromTicket(dst);
if (tik_common_blk && !memcmp(tik_common_blk->rights_id.c, id->c, 0x10)) return true;
TikCommonBlock *tik_common_block = tikGetCommonBlock(dst->data);
if (tik_common_block && !memcmp(tik_common_block->rights_id.c, id->c, 0x10)) return true;
}
bool tik_retrieved = (use_gamecard ? tikRetrieveTicketFromGameCardByRightsId(dst, id) : tikRetrieveTicketFromEsSaveDataByRightsId(dst, id));
@ -107,8 +105,8 @@ bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gam
return false;
}
/* Even though tickets do have a proper key_generation field, we'll just retrieve it from the rights_id field */
/* Old custom tools used to wipe the key_generation field or save it to a different offset */
/* Even though tickets do have a proper key_generation field, we'll just retrieve it from the rights_id field. */
/* Old custom tools used to wipe the key_generation field or save its value to a different offset. */
if (!tikGetTitleKekDecryptedTitleKey(dst->dec_titlekey, dst->enc_titlekey, id->c[0xF]))
{
LOGFILE("Unable to perform titlekek decryption!");
@ -118,105 +116,49 @@ bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gam
return true;
}
TikCommonBlock *tikGetCommonBlockFromTicket(Ticket *tik)
{
if (!tik || tik->type == TikType_None || tik->type > TikType_SigHmac160 || tik->size < TIK_MIN_SIZE || tik->size > TIK_MAX_SIZE)
{
LOGFILE("Invalid parameters!");
return NULL;
}
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_SigEcc480:
tik_common_blk = &(((TikSigEcc480*)tik->data)->tik_common_blk);
break;
case TikType_SigHmac160:
tik_common_blk = &(((TikSigHmac160*)tik->data)->tik_common_blk);
break;
default:
break;
}
return tik_common_blk;
}
void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik)
{
if (!tik || tik->type == TikType_None || tik->type > TikType_SigHmac160 || tik->size < TIK_MIN_SIZE || tik->size > TIK_MAX_SIZE) return;
TikCommonBlock *tik_common_block = NULL;
u32 sig_type = 0;
u8 *signature = NULL;
u64 signature_size = 0;
bool dev_cert = false;
TikCommonBlock *tik_common_blk = NULL;
char *sig_issuer = NULL;
tik_common_blk = tikGetCommonBlockFromTicket(tik);
if (!tik_common_blk || tik_common_blk->titlekey_type != TikTitleKeyType_Personalized) return;
if (!tik || tik->type == TikType_None || tik->type > TikType_SigHmac160 || tik->size < SIGNED_TIK_MIN_SIZE || tik->size > SIGNED_TIK_MAX_SIZE || \
!(tik_common_block = tikGetCommonBlock(tik->data)) || tik_common_block->titlekey_type != TikTitleKeyType_Personalized) return;
switch(tik->type)
{
case TikType_SigRsa4096:
{
tik->size = sizeof(TikSigRsa4096);
SignatureBlockRsa4096 *sig_rsa_4096 = (SignatureBlockRsa4096*)tik->data;
memset(sig_rsa_4096->signature, 0xFF, sizeof(sig_rsa_4096->signature));
sig_issuer = sig_rsa_4096->issuer;
break;
}
case TikType_SigRsa2048:
{
tik->size = sizeof(TikSigRsa2048);
SignatureBlockRsa2048 *sig_rsa_2048 = (SignatureBlockRsa2048*)tik->data;
memset(sig_rsa_2048->signature, 0xFF, sizeof(sig_rsa_2048->signature));
sig_issuer = sig_rsa_2048->issuer;
break;
}
case TikType_SigEcc480:
{
tik->size = sizeof(TikSigEcc480);
SignatureBlockEcc480 *sig_ecc_480 = (SignatureBlockEcc480*)tik->data;
memset(sig_ecc_480->signature, 0xFF, sizeof(sig_ecc_480->signature));
sig_issuer = sig_ecc_480->issuer;
break;
}
case TikType_SigHmac160:
{
tik->size = sizeof(TikSigHmac160);
SignatureBlockHmac160 *sig_hmac_160 = (SignatureBlockHmac160*)tik->data;
memset(sig_hmac_160->signature, 0xFF, sizeof(sig_hmac_160->signature));
sig_issuer = sig_hmac_160->issuer;
break;
}
default:
break;
}
/* Wipe signature. */
sig_type = signatureGetSigType(tik->data, false);
signature = signatureGetSig(tik->data);
signature_size = signatureGetSigSize(sig_type);
memset(signature, 0xFF, signature_size);
dev_cert = (strstr(sig_issuer, "CA00000004") != NULL);
/* Change signature issuer. */
dev_cert = (strstr(tik_common_block->issuer, "CA00000004") != NULL);
memset(tik_common_block->issuer, 0, sizeof(tik_common_block->issuer));
sprintf(tik_common_block->issuer, "Root-CA%08X-XS00000020", (dev_cert ? 4 : 3));
memset(sig_issuer, 0, MEMBER_SIZE(SignatureBlockRsa4096, issuer));
sprintf(sig_issuer, "Root-CA%08X-XS00000020", dev_cert ? 4 : 3);
/* Wipe the titlekey block and copy the titlekek-encrypted titlekey to it. */
memset(tik_common_block->titlekey_block, 0, sizeof(tik_common_block->titlekey_block));
memcpy(tik_common_block->titlekey_block, tik->enc_titlekey, 0x10);
memset(tik_common_blk->titlekey_block, 0, sizeof(tik_common_blk->titlekey_block));
memcpy(tik_common_blk->titlekey_block, tik->enc_titlekey, 0x10);
/* Update ticket size. */
tik->size = (signatureGetBlockSize(sig_type) + sizeof(TikCommonBlock));
tik_common_blk->titlekey_type = TikTitleKeyType_Common;
tik_common_blk->ticket_id = 0;
tik_common_blk->device_id = 0;
tik_common_blk->account_id = 0;
/* Update the rest of the ticket fields. */
tik_common_block->titlekey_type = TikTitleKeyType_Common;
tik_common_block->ticket_id = 0;
tik_common_block->device_id = 0;
tik_common_block->account_id = 0;
tik_common_blk->sect_total_size = 0;
tik_common_blk->sect_hdr_offset = (u32)tik->size;
tik_common_blk->sect_hdr_count = 0;
tik_common_blk->sect_hdr_entry_size = 0;
tik_common_block->sect_total_size = 0;
tik_common_block->sect_hdr_offset = (u32)tik->size;
tik_common_block->sect_hdr_count = 0;
tik_common_block->sect_hdr_entry_size = 0;
memset(tik->data + tik->size, 0, TIK_MAX_SIZE - tik->size);
memset(tik->data + tik->size, 0, SIGNED_TIK_MAX_SIZE - tik->size);
}
static bool tikRetrieveTicketFromGameCardByRightsId(Ticket *dst, const FsRightsId *id)
@ -235,11 +177,11 @@ static bool tikRetrieveTicketFromGameCardByRightsId(Ticket *dst, const FsRightsI
if (!gamecardGetEntryInfoFromHashFileSystemPartitionByName(GameCardHashFileSystemPartitionType_Secure, tik_filename, &tik_offset, &tik_size))
{
LOGFILE("Error retrieving offset and size for \"%s\" entry in secure hash FS partition!");
LOGFILE("Error retrieving offset and size for \"%s\" entry in secure hash FS partition!", tik_filename);
return false;
}
if (tik_size < TIK_MIN_SIZE || tik_size > TIK_MAX_SIZE)
if (tik_size < SIGNED_TIK_MIN_SIZE || tik_size > SIGNED_TIK_MAX_SIZE)
{
LOGFILE("Invalid size for \"%s\"! (0x%lX)", tik_filename, tik_size);
return false;
@ -275,7 +217,7 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight
allocation_table_storage_ctx_t fat_storage = {0};
u64 ticket_bin_size = 0;
u64 buf_size = (TIK_MAX_SIZE * 0x10);
u64 buf_size = (SIGNED_TIK_MAX_SIZE * 0x10);
u64 br = 0, total_br = 0;
u8 *ticket_bin_buf = NULL;
@ -300,9 +242,9 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight
goto out;
}
if (ticket_bin_size < TIK_MIN_SIZE || (ticket_bin_size % TIK_MAX_SIZE) != 0)
if (ticket_bin_size < SIGNED_TIK_MIN_SIZE || (ticket_bin_size % SIGNED_TIK_MAX_SIZE) != 0)
{
LOGFILE("Invalid size for \"%s\"! (0x%lX)", TIK_SAVEFILE_STORAGE_PATH, ticket_bin_size);
LOGFILE("Invalid size for \"%s\"! (0x%lX).", TIK_SAVEFILE_STORAGE_PATH, ticket_bin_size);
goto out;
}
@ -327,14 +269,14 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight
total_br += br;
for(i = 0; i < buf_size; i += TIK_MAX_SIZE)
for(i = 0; i < buf_size; i += SIGNED_TIK_MAX_SIZE)
{
if ((buf_size - i) < TIK_MIN_SIZE) break;
if ((buf_size - i) < SIGNED_TIK_MIN_SIZE) break;
TikCommonBlock *tik_common_blk = tikGetCommonBlockFromMemoryBuffer(ticket_bin_buf + i);
if (tik_common_blk && !memcmp(tik_common_blk->rights_id.c, id->c, 0x10))
TikCommonBlock *tik_common_block = tikGetCommonBlock(ticket_bin_buf + i);
if (tik_common_block && !memcmp(tik_common_block->rights_id.c, id->c, 0x10))
{
/* Jackpot */
/* Jackpot. */
found_tik = true;
break;
}
@ -349,7 +291,7 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight
goto out;
}
if (!tikGetTicketTypeAndSize(ticket_bin_buf + i, TIK_MAX_SIZE, &(dst->type), &(dst->size)))
if (!tikGetTicketTypeAndSize(ticket_bin_buf + i, SIGNED_TIK_MAX_SIZE, &(dst->type), &(dst->size)))
{
LOGFILE("Unable to determine ticket type and size!");
goto out;
@ -367,48 +309,11 @@ out:
return success;
}
static TikCommonBlock *tikGetCommonBlockFromMemoryBuffer(void *data)
{
if (!data)
{
LOGFILE("Invalid parameters!");
return NULL;
}
u32 sig_type = 0;
u8 *data_u8 = (u8*)data;
TikCommonBlock *tik_common_blk = NULL;
memcpy(&sig_type, data_u8, sizeof(u32));
switch(sig_type)
{
case SignatureType_Rsa4096Sha1:
case SignatureType_Rsa4096Sha256:
tik_common_blk = (TikCommonBlock*)(data_u8 + sizeof(SignatureBlockRsa4096));
break;
case SignatureType_Rsa2048Sha1:
case SignatureType_Rsa2048Sha256:
tik_common_blk = (TikCommonBlock*)(data_u8 + sizeof(SignatureBlockRsa2048));
break;
case SignatureType_Ecc480Sha1:
case SignatureType_Ecc480Sha256:
tik_common_blk = (TikCommonBlock*)(data_u8 + sizeof(SignatureBlockEcc480));
break;
case SignatureType_Hmac160Sha1:
tik_common_blk = (TikCommonBlock*)(data_u8 + sizeof(SignatureBlockHmac160));
break;
default:
LOGFILE("Invalid signature type value! (0x%08X)", sig_type);
return NULL;
}
return tik_common_blk;
}
static bool tikGetTitleKekEncryptedTitleKeyFromTicket(Ticket *tik)
{
if (!tik)
TikCommonBlock *tik_common_block = NULL;
if (!tik || !(tik_common_block = tikGetCommonBlock(tik->data)))
{
LOGFILE("Invalid parameters!");
return false;
@ -416,24 +321,17 @@ static bool tikGetTitleKekEncryptedTitleKeyFromTicket(Ticket *tik)
size_t out_keydata_size = 0;
u8 out_keydata[0x100] = {0};
TikCommonBlock *tik_common_blk = NULL;
tikEticketDeviceKeyData *eticket_devkey = NULL;
tik_common_blk = tikGetCommonBlockFromTicket(tik);
if (!tik_common_blk)
{
LOGFILE("Unable to retrieve common block from ticket!");
return false;
}
switch(tik_common_blk->titlekey_type)
switch(tik_common_block->titlekey_type)
{
case TikTitleKeyType_Common:
/* No titlekek crypto used */
memcpy(tik->enc_titlekey, tik_common_blk->titlekey_block, 0x10);
/* No titlekek crypto used. */
memcpy(tik->enc_titlekey, tik_common_block->titlekey_block, 0x10);
break;
case TikTitleKeyType_Personalized:
/* Retrieve eTicket device key */
/* Retrieve eTicket device key. */
if (!tikRetrieveEticketDeviceKey())
{
LOGFILE("Unable to retrieve eTicket device key!");
@ -442,20 +340,20 @@ static bool tikGetTitleKekEncryptedTitleKeyFromTicket(Ticket *tik)
eticket_devkey = (tikEticketDeviceKeyData*)g_eTicketDeviceKey.key;
/* Perform a RSA-OAEP decrypt operation to get the titlekey */
if (!rsa2048OaepDecryptAndVerify(out_keydata, 0x100, tik_common_blk->titlekey_block, eticket_devkey->modulus, eticket_devkey->exponent, 0x100, g_nullHash, &out_keydata_size) || \
/* Perform a RSA-OAEP decrypt operation to get the titlekey. */
if (!rsa2048OaepDecryptAndVerify(out_keydata, 0x100, tik_common_block->titlekey_block, eticket_devkey->modulus, eticket_devkey->exponent, 0x100, g_nullHash, &out_keydata_size) || \
out_keydata_size < 0x10)
{
LOGFILE("RSA-OAEP titlekey decryption failed!");
return false;
}
/* Copy decrypted titlekey */
/* Copy decrypted titlekey. */
memcpy(tik->enc_titlekey, out_keydata, 0x10);
break;
default:
LOGFILE("Invalid titlekey type value! (0x%02X)", tik_common_blk->titlekey_type);
LOGFILE("Invalid titlekey type value! (0x%02X).", tik_common_block->titlekey_type);
return false;
}
@ -494,8 +392,8 @@ static bool tikGetTitleKeyTypeFromRightsId(const FsRightsId *id, u8 *out)
return false;
}
u32 count;
FsRightsId *rights_ids;
u32 count = 0;
FsRightsId *rights_ids = NULL;
bool found = false;
for(u8 i = 0; i < 2; i++)
@ -515,7 +413,7 @@ static bool tikGetTitleKeyTypeFromRightsId(const FsRightsId *id, u8 *out)
{
if (!memcmp(rights_ids[j].c, id->c, 0x10))
{
*out = i; /* TikTitleKeyType_Common or TikTitleKeyType_Personalized */
*out = i; /* TikTitleKeyType_Common or TikTitleKeyType_Personalized. */
found = true;
break;
}
@ -541,17 +439,19 @@ static bool tikRetrieveRightsIdsByTitleKeyType(FsRightsId **out, u32 *out_count,
u32 count = 0, ids_written = 0;
FsRightsId *rights_ids = NULL;
*out = NULL;
*out_count = 0;
rc = (personalized ? esCountPersonalizedTicket((s32*)&count) : esCountCommonTicket((s32*)&count));
if (R_FAILED(rc))
{
LOGFILE("esCount%sTicket failed! (0x%08X)", personalized ? "Personalized" : "Common", rc);
LOGFILE("esCount%sTicket failed! (0x%08X).", personalized ? "Personalized" : "Common", rc);
return false;
}
if (!count)
{
LOGFILE("No %s tickets available!", personalized ? "personalized" : "common");
*out_count = 0;
return true;
}
@ -565,7 +465,7 @@ static bool tikRetrieveRightsIdsByTitleKeyType(FsRightsId **out, u32 *out_count,
rc = (personalized ? esListPersonalizedTicket((s32*)&ids_written, rights_ids, (s32)count) : esListCommonTicket((s32*)&ids_written, rights_ids, (s32)count));
if (R_FAILED(rc) || ids_written != count)
{
LOGFILE("esList%sTicket failed! (0x%08X) | Wrote %u entries, expected %u entries", personalized ? "Personalized" : "Common", rc, ids_written, count);
LOGFILE("esList%sTicket failed! (0x%08X). Wrote %u entries, expected %u entries.", personalized ? "Personalized" : "Common", rc, ids_written, count);
free(rights_ids);
return false;
}
@ -576,73 +476,49 @@ static bool tikRetrieveRightsIdsByTitleKeyType(FsRightsId **out, u32 *out_count,
return true;
}
static bool tikGetTicketTypeAndSize(const void *data, u64 data_size, u8 *out_type, u64 *out_size)
static bool tikGetTicketTypeAndSize(void *data, u64 data_size, u8 *out_type, u64 *out_size)
{
if (!data || data_size < TIK_MIN_SIZE || data_size > TIK_MAX_SIZE || !out_type || !out_size)
u32 sig_type = 0;
u64 signed_ticket_size = 0;
u8 type = TikType_None;
if (!data || data_size < SIGNED_TIK_MIN_SIZE || data_size > SIGNED_TIK_MAX_SIZE || !out_type || !out_size)
{
LOGFILE("Invalid parameters!");
return false;
}
u32 sig_type = 0;
u64 offset = 0;
u8 type = TikType_None;
const u8 *data_u8 = (const u8*)data;
const TikCommonBlock *tik_common_blk = NULL;
if (!(signed_ticket_size = tikGetSignedTicketSize(data)) || signed_ticket_size > data_size)
{
LOGFILE("Input buffer doesn't hold a valid signed ticket!");
return false;
}
memcpy(&sig_type, data_u8, sizeof(u32));
sig_type = signatureGetSigType(data, false);
switch(sig_type)
{
case SignatureType_Rsa4096Sha1:
case SignatureType_Rsa4096Sha256:
type = TikType_SigRsa4096;
offset += sizeof(SignatureBlockRsa4096);
break;
case SignatureType_Rsa2048Sha1:
case SignatureType_Rsa2048Sha256:
type = TikType_SigRsa2048;
offset += sizeof(SignatureBlockRsa2048);
break;
case SignatureType_Ecc480Sha1:
case SignatureType_Ecc480Sha256:
type = TikType_SigEcc480;
offset += sizeof(SignatureBlockEcc480);
break;
case SignatureType_Hmac160Sha1:
type = TikType_SigHmac160;
offset += sizeof(SignatureBlockHmac160);
break;
default:
LOGFILE("Invalid signature type value! (0x%08X)", sig_type);
return false;
}
tik_common_blk = (const TikCommonBlock*)(data_u8 + offset);
offset += sizeof(TikCommonBlock);
if ((u32)offset != tik_common_blk->sect_hdr_offset)
{
LOGFILE("Calculated ticket common block end offset doesn't match ESv2 section records header offset! 0x%X != 0x%X", (u32)offset, tik_common_blk->sect_hdr_offset);
return false;
}
for(u32 i = 0; i < tik_common_blk->sect_hdr_count; i++)
{
const TikEsv2SectionRecord *rec = (const TikEsv2SectionRecord*)(data_u8 + offset);
offset += sizeof(TikEsv2SectionRecord);
offset += ((u64)rec->record_count * (u64)rec->record_size);
if (offset > data_size)
{
LOGFILE("Offset calculation exceeded input buffer size while counting ESv2 section records! (0x%lX)", offset);
return false;
}
break;
}
*out_type = type;
*out_size = offset;
*out_size = signed_ticket_size;
return true;
}
@ -659,25 +535,25 @@ static bool tikRetrieveEticketDeviceKey(void)
rc = setcalGetEticketDeviceKey(&g_eTicketDeviceKey);
if (R_FAILED(rc))
{
LOGFILE("setcalGetEticketDeviceKey failed! (0x%08X)", rc);
LOGFILE("setcalGetEticketDeviceKey failed! (0x%08X).", rc);
return false;
}
/* Decrypt eTicket RSA key */
/* Decrypt eTicket RSA key. */
eticket_devkey = (tikEticketDeviceKeyData*)g_eTicketDeviceKey.key;
aes128CtrContextCreate(&eticket_aes_ctx, keysGetEticketRsaKek(), eticket_devkey->ctr);
aes128CtrCrypt(&eticket_aes_ctx, &(eticket_devkey->exponent), &(eticket_devkey->exponent), sizeof(tikEticketDeviceKeyData) - 0x10);
/* Public exponent value must be 0x10001 */
/* It is stored use big endian byte order */
/* Public exponent value must be 0x10001. */
/* It is stored use big endian byte order. */
public_exponent = __builtin_bswap32(eticket_devkey->public_exponent);
if (public_exponent != ETICKET_DEVKEY_PUBLIC_EXPONENT)
{
LOGFILE("Invalid public RSA exponent for eTicket device key! Wrong keys? (0x%08X)", public_exponent);
LOGFILE("Invalid public RSA exponent for eTicket device key! Wrong keys? (0x%08X).", public_exponent);
return false;
}
/* Test RSA key pair */
/* Test RSA key pair. */
if (!tikTestKeyPairFromEticketDeviceKey(&(eticket_devkey->public_exponent), eticket_devkey->exponent, eticket_devkey->modulus))
{
LOGFILE("RSA key pair test failed! Wrong keys?");
@ -700,7 +576,7 @@ static bool tikTestKeyPairFromEticketDeviceKey(const void *e, const void *d, con
Result rc = 0;
u8 x[0x100] = {0}, y[0x100] = {0}, z[0x100] = {0};
/* 0xCAFEBABE */
/* 0xCAFEBABE. */
x[0xFC] = 0xCA;
x[0xFD] = 0xFE;
x[0xFE] = 0xBA;
@ -709,14 +585,14 @@ static bool tikTestKeyPairFromEticketDeviceKey(const void *e, const void *d, con
rc = splUserExpMod(x, n, d, 0x100, y);
if (R_FAILED(rc))
{
LOGFILE("splUserExpMod failed! (#1) (0x%08X)", rc);
LOGFILE("splUserExpMod failed! (#1) (0x%08X).", rc);
return false;
}
rc = splUserExpMod(y, n, e, 4, z);
if (R_FAILED(rc))
{
LOGFILE("splUserExpMod failed! (#2) (0x%08X)", rc);
LOGFILE("splUserExpMod failed! (#2) (0x%08X).", rc);
return false;
}

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* tik.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,11 +23,10 @@
#ifndef __TIK_H__
#define __TIK_H__
#include <switch.h>
#include "signature.h"
#define TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES ticket system savedata file */
#define TIK_MIN_SIZE 0x1C0 /* Equivalent to sizeof(TikSigHmac160) - assuming no ESv2 records are available */
#define SIGNED_TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES ticket system savedata file. */
#define SIGNED_TIK_MIN_SIZE sizeof(TikSigHmac160) /* Assuming no ESV1/ESV2 records are available. */
typedef enum {
TikType_None = 0,
@ -56,25 +59,17 @@ typedef enum {
TikPropertyMask_ELicenseRequired = BIT(5)
} TikPropertyMask;
typedef enum {
TikSectionType_Permanent = 1,
TikSectionType_Subscription = 2,
TikSectionType_Content = 3,
TikSectionType_ContentConsumption = 4,
TikSectionType_AccessTitle = 5,
TikSectionType_LimitedResource = 6
} TikSectionType;
/// Placed after the ticket signature block.
typedef struct {
char issuer[0x40];
u8 titlekey_block[0x100];
u8 format_version;
u8 titlekey_type; ///< TikTitleKeyType.
u16 ticket_version;
u8 license_type; ///< TikLicenseType.
u8 key_generation;
u8 property_mask; ///< TikPropertyMask.
u8 reserved_1[0x9];
u16 property_mask; ///< TikPropertyMask.
u8 reserved_1[0x8];
u64 ticket_id;
u64 device_id;
FsRightsId rights_id;
@ -86,54 +81,145 @@ typedef struct {
} TikCommonBlock;
typedef struct {
SignatureBlockRsa4096 sig_block;
TikCommonBlock tik_common_blk;
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using little endian byte order.
TikCommonBlock tik_common_block;
} TikSigRsa4096;
typedef struct {
SignatureBlockRsa2048 sig_block;
TikCommonBlock tik_common_blk;
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using little endian byte order.
TikCommonBlock tik_common_block;
} TikSigRsa2048;
typedef struct {
SignatureBlockEcc480 sig_block;
TikCommonBlock tik_common_blk;
SignatureBlockEcc480 sig_block; ///< sig_type field is stored using little endian byte order.
TikCommonBlock tik_common_block;
} TikSigEcc480;
typedef struct {
SignatureBlockHmac160 sig_block;
TikCommonBlock tik_common_blk;
SignatureBlockHmac160 sig_block; ///< sig_type field is stored using little endian byte order.
TikCommonBlock tik_common_block;
} TikSigHmac160;
/// Section records are placed right after the ticket data. These aren't available in TikTitleKeyType_Common tickets.
/// These are only used if the sect_* fields are non-zero (other than 'sect_hdr_offset').
/// Each section record is followed by a 'record_count' number of Esv1 records, each one of 'record_size' size.
/// ESV1/ESV2 section records are placed right after the ticket data. These aren't available in TikTitleKeyType_Common tickets.
/// These are only used if the sect_* fields from the common block are non-zero (other than 'sect_hdr_offset').
/// Each ESV2 section record is followed by a 'record_count' number of ESV1 records, each one of 'record_size' size.
typedef enum {
TikSectionType_Permanent = 1,
TikSectionType_Subscription = 2,
TikSectionType_Content = 3,
TikSectionType_ContentConsumption = 4,
TikSectionType_AccessTitle = 5,
TikSectionType_LimitedResource = 6
} TikSectionType;
typedef struct {
u32 sect_offset;
u32 record_size;
u32 section_size;
u16 record_count;
u16 section_type; ///< TikSectionType.
} TikEsv2SectionRecord;
} TikESV2SectionRecord;
/// Used with TikSectionType_Permanent.
typedef struct {
u8 ref_id[0x10];
u32 ref_id_attr;
} TikESV1PermanentRecord;
/// Used with TikSectionType_Subscription.
typedef struct {
u32 limit;
u8 ref_id[0x10];
u32 ref_id_attr;
} TikESV1SubscriptionRecord;
/// Used with TikSectionType_Content.
typedef struct {
u32 offset;
u8 access_mask[0x80];
} TikESV1ContentRecord;
/// Used with TikSectionType_ContentConsumption.
typedef struct {
u16 index;
u16 code;
u32 limit;
} TikESV1ContentConsumptionRecord;
/// Used with TikSectionType_AccessTitle.
typedef struct {
u64 access_title_id;
u64 access_title_mask;
} TikESV1AccessTitleRecord;
/// Used with TikSectionType_LimitedResource.
typedef struct {
u32 limit;
u8 ref_id[0x10];
u32 ref_id_attr;
} TikESV1LimitedResourceRecord;
/// Used to store ticket type, size and raw data, as well as titlekey data.
typedef struct {
u8 type; ///< TikType.
u64 size; ///< Raw ticket size.
u8 data[TIK_MAX_SIZE]; ///< Raw ticket data.
u8 enc_titlekey[0x10]; ///< Titlekey with titlekek crypto (RSA-OAEP unwrapped if dealing with a TikTitleKeyType_Personalized ticket).
u8 dec_titlekey[0x10]; ///< Titlekey without titlekek crypto. Ready to use for NCA FS section decryption.
u8 type; ///< TikType.
u64 size; ///< Raw ticket size.
u8 data[SIGNED_TIK_MAX_SIZE]; ///< Raw ticket data.
u8 enc_titlekey[0x10]; ///< Titlekey with titlekek crypto (RSA-OAEP unwrapped if dealing with a TikTitleKeyType_Personalized ticket).
u8 dec_titlekey[0x10]; ///< Titlekey without titlekek crypto. Ready to use for NCA FS section decryption.
} Ticket;
/// 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);
/// Retrieves the common block from an input Ticket.
TikCommonBlock *tikGetCommonBlockFromTicket(Ticket *tik);
/// This will convert a TikTitleKeyType_Personalized ticket into a TikTitleKeyType_Common ticket.
/// Bear in mind the 'size' member from the Ticket parameter will be updated by this function to remove any possible references to TikEsv2SectionRecord records.
/// Bear in mind the 'size' member from the Ticket parameter will be updated by this function to remove any possible references to ESV1/ESV2 records.
void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik);
/// Helper inline functions.
NX_INLINE TikCommonBlock *tikGetCommonBlock(void *buf)
{
return (TikCommonBlock*)signatureGetPayload(buf, false);
}
NX_INLINE u64 tikGetTicketSectionRecordsBlockSize(TikCommonBlock *tik_common_block)
{
if (!tik_common_block) return 0;
u64 offset = sizeof(TikCommonBlock), out_size = 0;
for(u32 i = 0; i < tik_common_block->sect_hdr_count; i++)
{
TikESV2SectionRecord *rec = (TikESV2SectionRecord*)((u8*)tik_common_block + offset);
offset += (sizeof(TikESV2SectionRecord) + ((u64)rec->record_count * (u64)rec->record_size));
out_size += offset;
}
return out_size;
}
NX_INLINE bool tikIsValidTicket(void *buf)
{
u64 ticket_size = (signatureGetBlockSize(signatureGetSigType(buf, false)) + sizeof(TikCommonBlock));
return (ticket_size > sizeof(TikCommonBlock) && (ticket_size + tikGetTicketSectionRecordsBlockSize(tikGetCommonBlock(buf))) <= SIGNED_TIK_MAX_SIZE);
}
NX_INLINE u64 tikGetSignedTicketSize(void *buf)
{
return (tikIsValidTicket(buf) ? (signatureGetBlockSize(signatureGetSigType(buf, false)) + sizeof(TikCommonBlock) + tikGetTicketSectionRecordsBlockSize(tikGetCommonBlock(buf))) : 0);
}
NX_INLINE u64 tikGetSignedTicketHashAreaSize(void *buf)
{
return (tikIsValidTicket(buf) ? (sizeof(TikCommonBlock) + tikGetTicketSectionRecordsBlockSize(tikGetCommonBlock(buf))) : 0);
}
NX_INLINE bool tikIsPersonalizedTicket(Ticket *tik)
{
TikCommonBlock *tik_common_block = tikGetCommonBlock(tik->data);
return (tik_common_block != NULL && tik_common_block->titlekey_type == TikTitleKeyType_Personalized);
}
#endif /* __TIK_H__ */

View file

@ -1,12 +1,18 @@
/*
* Copyright (c) 2020 DarkMatterCore
* Heavily based in usb_comms.c/h from libnx
* usb.c
*
* This program is free software; you can redistribute it and/or modify it
* Heavily based in usb_comms from libnx.
*
* Copyright (c) 2018-2020, Switchbrew, libnx contributors.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -15,15 +21,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include <threads.h>
#include "usb.h"
#include "utils.h"
#include "usb.h"
#define USB_ABI_VERSION 1
@ -72,15 +71,15 @@ typedef struct {
} UsbCommandSendFileProperties;
typedef enum {
/// Expected response code.
///< Expected response code.
UsbStatusType_Success = 0,
/// Internal usage.
///< Internal usage.
UsbStatusType_InvalidCommandSize = 1,
UsbStatusType_WriteCommandFailed = 2,
UsbStatusType_ReadStatusFailed = 3,
/// These can be returned by the host device.
///< These can be returned by the host device.
UsbStatusType_InvalidMagicWord = 4,
UsbStatusType_UnsupportedCommand = 5,
UsbStatusType_UnsupportedAbiVersion = 6,
@ -102,7 +101,7 @@ static bool g_usbDeviceInterfaceInitialized = false;
static Event *g_usbStateChangeEvent = NULL;
static thrd_t g_usbDetectionThread;
static UEvent g_usbDetectionThreadExitEvent = {0};
static UEvent g_usbDetectionThreadExitEvent = {0}, g_usbTimeoutEvent = {0};
static bool g_usbDetectionThreadCreated = false, g_usbHostAvailable = false, g_usbSessionStarted = false;
static u8 *g_usbTransferBuffer = NULL;
@ -170,6 +169,9 @@ bool usbInitialize(void)
/* Create usermode exit event */
ueventCreate(&g_usbDetectionThreadExitEvent, true);
/* Create usermode USB timeout event */
ueventCreate(&g_usbTimeoutEvent, true);
/* Create USB detection thread */
if (!(g_usbDetectionThreadCreated = usbCreateDetectionThread())) goto exit;
@ -201,10 +203,6 @@ void usbExit(void)
/* Free USB transfer buffer */
usbFreeTransferBuffer();
/* Reset global variables */
g_usbTransferRemainingSize = 0;
g_usbSessionStarted = false;
rwlockWriteUnlock(&g_usbDeviceLock);
}
@ -249,7 +247,7 @@ bool usbSendFileProperties(u64 file_size, const char *filename)
cmd_block->file_size = file_size;
cmd_block->filename_length = filename_length;
sprintf(cmd_block->filename, filename);
sprintf(cmd_block->filename, "%s", filename);
cmd_size = (sizeof(UsbCommandHeader) + sizeof(UsbCommandSendFileProperties));
@ -361,39 +359,32 @@ static int usbDetectionThreadFunc(void *arg)
Result rc = 0;
int idx = 0;
bool prev_status = false;
Waiter usb_event_waiter = waiterForEvent(g_usbStateChangeEvent);
Waiter usb_change_event_waiter = waiterForEvent(g_usbStateChangeEvent);
Waiter usb_timeout_event_waiter = waiterForUEvent(&g_usbTimeoutEvent);
Waiter exit_event_waiter = waiterForUEvent(&g_usbDetectionThreadExitEvent);
while(true)
{
/* Wait until an event is triggered */
rc = waitMulti(&idx, -1, usb_event_waiter, exit_event_waiter);
rc = waitMulti(&idx, -1, usb_change_event_waiter, usb_timeout_event_waiter, exit_event_waiter);
if (R_FAILED(rc)) continue;
/* Exit event triggered */
if (idx == 1) break;
if (idx == 2) break;
/* Retrieve current USB connection status */
/* Only proceed if we're dealing with a status change */
rwlockWriteLock(&g_usbDeviceLock);
rwlockWriteLock(&(g_usbDeviceInterface.lock));
/* Retrieve current USB connection status */
/* Only proceed if we're dealing with a status change */
g_usbHostAvailable = usbIsHostAvailable();
g_usbSessionStarted = false;
g_usbTransferRemainingSize = 0;
if (!prev_status && g_usbHostAvailable)
{
/* Start a USB session */
/* This will essentially hang this thread and all other threads that call USB-related functions until a session is established */
g_usbSessionStarted = usbStartSession();
} else {
/* Update USB session flag */
g_usbSessionStarted = false;
}
prev_status = g_usbHostAvailable;
/* Start a USB session if we're connected to a host device and if the status change event was triggered */
/* This will essentially hang this thread and all other threads that call USB-related functions until a session is established */
if (g_usbHostAvailable && idx == 1) g_usbSessionStarted = usbStartSession();
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
rwlockWriteUnlock(&g_usbDeviceLock);
@ -402,6 +393,7 @@ static int usbDetectionThreadFunc(void *arg)
/* Close USB session if needed */
if (g_usbHostAvailable && g_usbSessionStarted) usbEndSession();
g_usbHostAvailable = g_usbSessionStarted = false;
g_usbTransferRemainingSize = 0;
return 0;
}
@ -1033,6 +1025,10 @@ static bool usbTransferData(void *buf, u64 size, UsbDsEndpoint *endpoint)
eventWait(&(endpoint->CompletionEvent), UINT64_MAX);
eventClear(&(endpoint->CompletionEvent));
/* Signal usermode USB timeout event if needed */
/* This will "reset" the USB connection by making the background thread wait until a new session is established */
if (g_usbSessionStarted) ueventSignal(&g_usbTimeoutEvent);
LOGFILE("eventWait failed! (0x%08X)", rc);
return false;
}

View file

@ -1,11 +1,18 @@
/*
* Copyright (c) 2020 DarkMatterCore
* usb.h
*
* This program is free software; you can redistribute it and/or modify it
* Heavily based in usb_comms from libnx.
*
* Copyright (c) 2018-2020, Switchbrew, libnx contributors.
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,8 +26,6 @@
#ifndef __USB_H__
#define __USB_H__
#include <switch.h>
#define USB_TRANSFER_BUFFER_SIZE 0x800000 /* 8 MiB */
/// Initializes the USB interface, input and output endpoints and allocates an internal transfer buffer.
@ -35,6 +40,7 @@ void *usbAllocatePageAlignedBuffer(size_t size);
/// Used to check if the console has been connected to an USB host device and if a valid USB session has been established.
/// Bear in mind this call will block the calling thread if the console is connected to an USB host device but no USB session has been established.
/// If the console is disconnected during this block, the function will return false.
/// If the console isn't connected to an USB host device when this function is called, false will be returned right away.
bool usbIsReady(void);
/// Sends file properties to the host device before starting a file data transfer. Must be called before usbSendFileData().

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* utils.c
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -14,20 +18,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include <switch.h>
#include "utils.h"
//#include "freetype_helper.h"
//#include "lvgl_helper.h"
#include "keys.h"
#include "gamecard.h"
#include "services.h"
#include "utils.h"
#include "nca.h"
#include "usb.h"
#include "fatfs/ff.h"
@ -418,7 +414,7 @@ static bool utilsMountEmmcBisSystemPartitionStorage(void)
g_emmcBisSystemPartitionFatFsObj = calloc(1, sizeof(FATFS));
if (!g_emmcBisSystemPartitionFatFsObj)
{
LOGFILE("Unable to allocate memory for FatFs object!");
LOGFILE("Unable to allocate memory for FatFs element!");
return false;
}

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2020 DarkMatterCore
* utils.h
*
* This program is free software; you can redistribute it and/or modify it
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
@ -19,6 +23,17 @@
#ifndef __UTILS_H__
#define __UTILS_H__
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdarg.h>
#include <malloc.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <threads.h>
#include <switch.h>
#define APP_BASE_PATH "sdmc:/switch/nxdumptool/"