mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2025-01-24 18:23:14 -03:00
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:
parent
7d66da7308
commit
99429fd7b4
37 changed files with 913 additions and 752 deletions
20
nxdumptool_todo.txt
Normal file
20
nxdumptool_todo.txt
Normal 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
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
251
source/cert.c
251
source/cert.c
|
@ -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)
|
||||
|
|
137
source/cert.h
137
source/cert.h
|
@ -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__ */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
15
source/es.c
15
source/es.c
|
@ -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"
|
||||
|
||||
|
|
12
source/es.h
12
source/es.h
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" : \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
16
source/nca.c
16
source/nca.c
|
@ -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 */
|
||||
|
||||
|
|
17
source/nca.h
17
source/nca.h
|
@ -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().
|
||||
|
|
16
source/pfs.c
16
source/pfs.c
|
@ -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)
|
||||
{
|
||||
|
|
14
source/pfs.h
14
source/pfs.h
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
20
source/rsa.c
20
source/rsa.c
|
@ -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.
|
||||
|
|
14
source/rsa.h
14
source/rsa.h
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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__ */
|
||||
|
|
324
source/tik.c
324
source/tik.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
162
source/tik.h
162
source/tik.h
|
@ -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__ */
|
||||
|
|
72
source/usb.c
72
source/usb.c
|
@ -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;
|
||||
}
|
||||
|
|
16
source/usb.h
16
source/usb.h
|
@ -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().
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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/"
|
||||
|
|
Loading…
Add table
Reference in a new issue