diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 4d704b5..798b30a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -20,15 +20,15 @@ Steps to reproduce the behavior: **Screenshots** Add screenshots to help explain your problem. -**Please complete the following information:** -- Horizon OS (Switch FW) version: [e.g. 9.0.1] +**Please fill the following information:** +- Horizon OS (Switch FW) version: [e.g. 10.0.0] - CFW: [e.g. Atmosphère, SX OS, etc.] -- CFW version: [e.g. 0.9.4, 2.9.2, etc.] +- CFW version: [e.g. 0.11.1, 2.9.4, etc.] - Atmosphère launch method (if applicable): [e.g. Hekate, fusee-primary] -- NXDumpTool version: [e.g. 1.1.7] -- Homebrew launch method: [e.g. title override, Album applet] +- NXDumpTool version: [e.g. 1.2.0] +- Homebrew launch method: [e.g. title override, applet] - Source storage used with the application (if applicable): [e.g. gamecard, SD/eMMC] -- SD card specs: [e.g. Samsung EVO 256 GB] +- SD card specs: [e.g. Samsung EVO 256 GB, FAT32 partition] **Additional context** Add any other context about the problem here. diff --git a/README.md b/README.md index 0c279a2..0e52816 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ todo: hfs0 methods - tik gamecard + pfs0: full header aligned to 0x20 diff --git a/romfs/browser/dir_highlight.jpg b/romfs/browser/dir_highlight.jpg deleted file mode 100644 index 88d16ed..0000000 Binary files a/romfs/browser/dir_highlight.jpg and /dev/null differ diff --git a/romfs/browser/dir_normal.jpg b/romfs/browser/dir_normal.jpg deleted file mode 100644 index ac9be25..0000000 Binary files a/romfs/browser/dir_normal.jpg and /dev/null differ diff --git a/romfs/browser/disabled_highlight.jpg b/romfs/browser/disabled_highlight.jpg deleted file mode 100644 index df687be..0000000 Binary files a/romfs/browser/disabled_highlight.jpg and /dev/null differ diff --git a/romfs/browser/disabled_normal.jpg b/romfs/browser/disabled_normal.jpg deleted file mode 100644 index edf54d8..0000000 Binary files a/romfs/browser/disabled_normal.jpg and /dev/null differ diff --git a/romfs/browser/enabled_highlight.jpg b/romfs/browser/enabled_highlight.jpg deleted file mode 100644 index b05be78..0000000 Binary files a/romfs/browser/enabled_highlight.jpg and /dev/null differ diff --git a/romfs/browser/enabled_normal.jpg b/romfs/browser/enabled_normal.jpg deleted file mode 100644 index 9d25aa1..0000000 Binary files a/romfs/browser/enabled_normal.jpg and /dev/null differ diff --git a/romfs/browser/file_highlight.jpg b/romfs/browser/file_highlight.jpg deleted file mode 100644 index 0f51d1d..0000000 Binary files a/romfs/browser/file_highlight.jpg and /dev/null differ diff --git a/romfs/browser/file_normal.jpg b/romfs/browser/file_normal.jpg deleted file mode 100644 index 20c58d3..0000000 Binary files a/romfs/browser/file_normal.jpg and /dev/null differ diff --git a/source/cert.c b/source/cert.c index cf28111..86f14c7 100644 --- a/source/cert.c +++ b/source/cert.c @@ -25,7 +25,8 @@ #define CERT_SAVEFILE_PATH BIS_SYSTEM_PARTITION_MOUNT_NAME "/save/80000000000000e0" #define CERT_SAVEFILE_STORAGE_BASE_PATH "/certificate/" -#define CERT_TYPE(sig) (pub_key_type == CertPubKeyType_Rsa4096 ? CertType_Sig##sig##_PubKeyRsa4096 : (pub_key_type == CertPubKeyType_Rsa2048 ? CertType_Sig##sig##_PubKeyRsa2048 : CertType_Sig##sig##_PubKeyEcsda240)) +#define CERT_TYPE(sig) (pub_key_type == CertPubKeyType_Rsa4096 ? CertType_Sig##sig##_PubKeyRsa4096 : \ + (pub_key_type == CertPubKeyType_Rsa2048 ? CertType_Sig##sig##_PubKeyRsa2048 : CertType_Sig##sig##_PubKeyEcsda240)) /* Global variables. */ @@ -88,6 +89,52 @@ void certFreeCertificateChain(CertificateChain *chain) chain->certs = NULL; } +CertCommonBlock *certGetCommonBlockFromCertificate(Certificate *cert) +{ + if (!cert || cert->type == CertType_None || cert->type > CertType_SigEcsda240_PubKeyEcsda240 || cert->size < CERT_MIN_SIZE || cert->size > CERT_MAX_SIZE) + { + LOGFILE("Invalid parameters!"); + return NULL; + } + + CertCommonBlock *cert_common_blk = NULL; + + switch(cert->type) + { + case CertType_SigRsa4096_PubKeyRsa4096: + cert_common_blk = &(((CertSigRsa4096PubKeyRsa4096*)cert->data)->cert_common_blk); + break; + case CertType_SigRsa4096_PubKeyRsa2048: + cert_common_blk = &(((CertSigRsa4096PubKeyRsa2048*)cert->data)->cert_common_blk); + break; + case CertType_SigRsa4096_PubKeyEcsda240: + cert_common_blk = &(((CertSigRsa4096PubKeyEcsda240*)cert->data)->cert_common_blk); + break; + case CertType_SigRsa2048_PubKeyRsa4096: + cert_common_blk = &(((CertSigRsa2048PubKeyRsa4096*)cert->data)->cert_common_blk); + break; + case CertType_SigRsa2048_PubKeyRsa2048: + cert_common_blk = &(((CertSigRsa2048PubKeyRsa2048*)cert->data)->cert_common_blk); + break; + case CertType_SigRsa2048_PubKeyEcsda240: + cert_common_blk = &(((CertSigRsa2048PubKeyEcsda240*)cert->data)->cert_common_blk); + break; + case CertType_SigEcsda240_PubKeyRsa4096: + cert_common_blk = &(((CertSigEcsda240PubKeyRsa4096*)cert->data)->cert_common_blk); + break; + case CertType_SigEcsda240_PubKeyRsa2048: + cert_common_blk = &(((CertSigEcsda240PubKeyRsa2048*)cert->data)->cert_common_blk); + break; + case CertType_SigEcsda240_PubKeyEcsda240: + cert_common_blk = &(((CertSigEcsda240PubKeyEcsda240*)cert->data)->cert_common_blk); + break; + default: + break; + } + + return cert_common_blk; +} + u8 *certGenerateRawCertificateChainBySignatureIssuer(const char *issuer, u64 *out_size) { if (!issuer || !strlen(issuer) || !out_size) @@ -183,7 +230,7 @@ static bool _certRetrieveCertificateByName(Certificate *dst, const char *name) } dst->type = certGetCertificateType(dst->data, dst->size); - if (dst->type == CertType_Invalid) + if (dst->type == CertType_None) { LOGFILE("Invalid certificate type for \"%s\"!", name); return false; @@ -197,13 +244,13 @@ static u8 certGetCertificateType(const void *data, u64 data_size) if (!data || data_size < CERT_MIN_SIZE || data_size > CERT_MAX_SIZE) { LOGFILE("Invalid parameters!"); - return CertType_Invalid; + return CertType_None; } - u8 type = CertType_Invalid; - const u8 *data_u8 = (const u8*)data; - u32 sig_type, pub_key_type; u64 offset = 0; + u8 type = CertType_None; + const u8 *data_u8 = (const u8*)data; + u32 sig_type = 0, pub_key_type = 0; memcpy(&sig_type, data_u8, sizeof(u32)); sig_type = __builtin_bswap32(sig_type); @@ -227,14 +274,14 @@ static u8 certGetCertificateType(const void *data, u64 data_size) return type; } - offset += MEMBER_SIZE(CertSigRsa4096PubKeyRsa4096, issuer); + offset += MEMBER_SIZE(CertCommonBlock, issuer); memcpy(&pub_key_type, data_u8 + offset, sizeof(u32)); pub_key_type = __builtin_bswap32(pub_key_type); - offset += MEMBER_SIZE(CertSigRsa4096PubKeyRsa4096, pub_key_type); - offset += MEMBER_SIZE(CertSigRsa4096PubKeyRsa4096, name); - offset += MEMBER_SIZE(CertSigRsa4096PubKeyRsa4096, cert_id); + offset += MEMBER_SIZE(CertCommonBlock, pub_key_type); + offset += MEMBER_SIZE(CertCommonBlock, name); + offset += MEMBER_SIZE(CertCommonBlock, cert_id); switch(pub_key_type) { @@ -254,7 +301,7 @@ static u8 certGetCertificateType(const void *data, u64 data_size) if (offset != data_size) { - LOGFILE("Calculated end offset doesn't match certificate size! 0x%lX != 0x%lX", offset, data_size); + LOGFILE("Calculated end offset doesn't match certificate size! (0x%lX != 0x%lX)", offset, data_size); return type; } diff --git a/source/cert.h b/source/cert.h index 1e12386..4f2c3b2 100644 --- a/source/cert.h +++ b/source/cert.h @@ -26,16 +26,16 @@ #define CERT_MIN_SIZE 0x180 /* Equivalent to sizeof(CertSigEcsda240PubKeyEcsda240) */ typedef enum { - CertType_SigRsa4096_PubKeyRsa4096 = 0, - CertType_SigRsa4096_PubKeyRsa2048 = 1, - CertType_SigRsa4096_PubKeyEcsda240 = 2, - CertType_SigRsa2048_PubKeyRsa4096 = 3, - CertType_SigRsa2048_PubKeyRsa2048 = 4, - CertType_SigRsa2048_PubKeyEcsda240 = 5, - CertType_SigEcsda240_PubKeyRsa4096 = 6, - CertType_SigEcsda240_PubKeyRsa2048 = 7, - CertType_SigEcsda240_PubKeyEcsda240 = 8, - CertType_Invalid = 255 + CertType_None = 0, + CertType_SigRsa4096_PubKeyRsa4096 = 1, + CertType_SigRsa4096_PubKeyRsa2048 = 2, + CertType_SigRsa4096_PubKeyEcsda240 = 3, + CertType_SigRsa2048_PubKeyRsa4096 = 4, + CertType_SigRsa2048_PubKeyRsa2048 = 5, + CertType_SigRsa2048_PubKeyEcsda240 = 6, + CertType_SigEcsda240_PubKeyRsa4096 = 7, + CertType_SigEcsda240_PubKeyRsa2048 = 8, + CertType_SigEcsda240_PubKeyEcsda240 = 9 } CertType; /// Always stored using big endian byte order. @@ -62,92 +62,73 @@ typedef struct { u8 padding[0x3C]; } CertPublicKeyBlockEcsda240; +/// Placed after the certificate signature block. typedef struct { - SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order. char issuer[0x40]; - u32 pub_key_type; ///< CertPubKeyType_Rsa4096. + u32 pub_key_type; char name[0x40]; u32 cert_id; +} CertCommonBlock; + +typedef struct { + SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order. + CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096. CertPublicKeyBlockRsa4096 pub_key_block; } CertSigRsa4096PubKeyRsa4096; typedef struct { SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order. - char issuer[0x40]; - u32 pub_key_type; ///< CertPubKeyType_Rsa2048. - char name[0x40]; - u32 cert_id; + CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048. CertPublicKeyBlockRsa2048 pub_key_block; } CertSigRsa4096PubKeyRsa2048; typedef struct { SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order. - char issuer[0x40]; - u32 pub_key_type; ///< CertPubKeyType_Ecsda240. - char name[0x40]; - u32 cert_id; + CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecsda240. CertPublicKeyBlockEcsda240 pub_key_block; } CertSigRsa4096PubKeyEcsda240; typedef struct { SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order. - char issuer[0x40]; - u32 pub_key_type; ///< CertPubKeyType_Rsa4096. - char name[0x40]; - u32 cert_id; + CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096. CertPublicKeyBlockRsa4096 pub_key_block; } CertSigRsa2048PubKeyRsa4096; typedef struct { SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order. - char issuer[0x40]; - u32 pub_key_type; ///< CertPubKeyType_Rsa2048. - char name[0x40]; - u32 cert_id; + CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048. CertPublicKeyBlockRsa2048 pub_key_block; } CertSigRsa2048PubKeyRsa2048; typedef struct { SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order. - char issuer[0x40]; - u32 pub_key_type; ///< CertPubKeyType_Ecsda240. - char name[0x40]; - u32 cert_id; + CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecsda240. CertPublicKeyBlockEcsda240 pub_key_block; } CertSigRsa2048PubKeyEcsda240; typedef struct { SignatureBlockEcsda240 sig_block; ///< sig_type field is stored using big endian byte order. - char issuer[0x40]; - u32 pub_key_type; ///< CertPubKeyType_Rsa4096. - char name[0x40]; - u32 cert_id; + CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096. CertPublicKeyBlockRsa4096 pub_key_block; } CertSigEcsda240PubKeyRsa4096; typedef struct { SignatureBlockEcsda240 sig_block; ///< sig_type field is stored using big endian byte order. - char issuer[0x40]; - u32 pub_key_type; ///< CertPubKeyType_Rsa2048. - char name[0x40]; - u32 cert_id; + CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048. CertPublicKeyBlockRsa2048 pub_key_block; } CertSigEcsda240PubKeyRsa2048; typedef struct { SignatureBlockEcsda240 sig_block; ///< sig_type field is stored using big endian byte order. - char issuer[0x40]; - u32 pub_key_type; ///< CertPubKeyType_Ecsda240. - char name[0x40]; - u32 cert_id; + CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecsda240. CertPublicKeyBlockEcsda240 pub_key_block; } CertSigEcsda240PubKeyEcsda240; /// Used to store certificate type, size and raw data. typedef struct { u8 type; ///< CertType. - u64 size; - u8 data[CERT_MAX_SIZE]; + u64 size; ///< Raw certificate size. + u8 data[CERT_MAX_SIZE]; ///< Raw certificate data. } Certificate; /// Used to store two or more certificates. @@ -161,6 +142,9 @@ bool certRetrieveCertificateByName(Certificate *dst, const char *name); bool certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst, const char *issuer); void certFreeCertificateChain(CertificateChain *chain); +/// Retrieves the common block from an input Certificate. +CertCommonBlock *certGetCommonBlockFromCertificate(Certificate *cert); + /// Returns a pointer to a heap allocated buffer that must be freed by the user. u8 *certGenerateRawCertificateChainBySignatureIssuer(const char *issuer, u64 *out_size); diff --git a/source/gamecard.c b/source/gamecard.c index ba22022..aac95ba 100644 --- a/source/gamecard.c +++ b/source/gamecard.c @@ -41,9 +41,6 @@ #define GAMECARD_CAPACITY_16GiB (u64)0x400000000 #define GAMECARD_CAPACITY_32GiB (u64)0x800000000 -#define GAMECARD_HFS_PARTITION_NAME(x) ((x) == GameCardHashFileSystemPartitionType_Update ? "update" : ((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : \ - ((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : ((x) == GameCardHashFileSystemPartitionType_Secure ? "secure" : "unknown")))) - /* Type definitions. */ typedef enum { diff --git a/source/gamecard.h b/source/gamecard.h index 5c8ebfe..5ca4257 100644 --- a/source/gamecard.h +++ b/source/gamecard.h @@ -27,6 +27,9 @@ #define GAMECARD_MEDIA_UNIT_SIZE 0x200 +#define GAMECARD_HFS_PARTITION_NAME(x) ((x) == GameCardHashFileSystemPartitionType_Update ? "update" : ((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : \ + ((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : ((x) == GameCardHashFileSystemPartitionType_Secure ? "secure" : "unknown")))) + typedef enum { GameCardKekIndex_Version0 = 0, GameCardKekIndex_VersionForDev = 1 diff --git a/source/keys.c b/source/keys.c index eb1b03a..cfb616a 100644 --- a/source/keys.c +++ b/source/keys.c @@ -188,12 +188,6 @@ const u8 *keysGetNcaHeaderKey(void) const u8 *keysGetKeyAreaEncryptionKeySource(u8 kaek_index) { - if (kaek_index > NcaKeyAreaEncryptionKeyIndex_System) - { - LOGFILE("Invalid KAEK index! (0x%02X)", kaek_index); - return NULL; - } - const u8 *ptr = NULL; switch(kaek_index) @@ -208,6 +202,7 @@ const u8 *keysGetKeyAreaEncryptionKeySource(u8 kaek_index) ptr = (const u8*)(g_ncaKeyset.key_area_key_system_source); break; default: + LOGFILE("Invalid KAEK index! (0x%02X)", kaek_index); break; } diff --git a/source/main.c b/source/main.c index 7b67a1f..4eb859f 100644 --- a/source/main.c +++ b/source/main.c @@ -209,6 +209,24 @@ int main(int argc, char *argv[]) consoleUpdate(NULL); + tikConvertPersonalizedTicketToCommonTicket(&tik); + + printf("common tik generated\n"); + consoleUpdate(NULL); + + tmp_file = fopen("sdmc:/common_tik.bin", "wb"); + if (tmp_file) + { + fwrite(&tik, 1, sizeof(Ticket), tmp_file); + fclose(tmp_file); + tmp_file = NULL; + printf("common tik saved\n"); + } else { + printf("common tik not saved\n"); + } + + consoleUpdate(NULL); + tik_common_blk = tikGetCommonBlockFromTicket(&tik); if (tik_common_blk) diff --git a/source/nca.c b/source/nca.c index 0070e30..2014296 100644 --- a/source/nca.c +++ b/source/nca.c @@ -21,6 +21,7 @@ #include "nca.h" #include "keys.h" #include "rsa.h" +#include "gamecard.h" #include "utils.h" /* Global variables. */ @@ -32,7 +33,7 @@ static const u8 g_nca0KeyAreaHash[SHA256_HASH_SIZE] = { /* Function prototypes. */ -static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx); +static inline bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx); static void ncaUpdateAesCtrIv(u8 *ctr, u64 offset); static void ncaUpdateAesCtrExIv(u8 *ctr, u32 ctr_val, u64 offset); @@ -68,7 +69,30 @@ size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, return i; } - +bool ncaRead(NcaContext *ctx, void *out, u64 read_size, u64 offset) +{ + if (!ctx || (ctx->storage_id != NcmStorageId_GameCard && !ctx->ncm_storage) || (ctx->storage_id == NcmStorageId_GameCard && !ctx->gamecard_offset) || !out || !read_size || \ + offset >= ctx->size || (offset + read_size) > ctx->size) + { + LOGFILE("Invalid parameters!"); + return false; + } + + Result rc = 0; + bool ret = false; + + if (ctx->storage_id == NcmStorageId_GameCard) + { + ret = gamecardRead(out, read_size, ctx->gamecard_offset + offset); + if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (gamecard)", read_size, offset, ctx->id_str); + } else { + rc = ncmContentStorageReadContentIdFile(ctx->ncm_storage, out, read_size, &(ctx->id), offset); + ret = R_SUCCEEDED(rc); + if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (0x%08X) (ncm)", read_size, offset, ctx->id_str, rc); + } + + return ret; +} @@ -121,7 +145,7 @@ bool ncaDecryptKeyArea(NcaContext *ctx) for(u8 i = 0; i < key_count; i++) { - rc = splCryptoGenerateAesKey(tmp_kek, &(ctx->header.encrypted_keys[i]), &(ctx->decrypted_keys[i])); + rc = splCryptoGenerateAesKey(tmp_kek, ctx->header.encrypted_keys[i].key, ctx->decrypted_keys[i].key); if (R_FAILED(rc)) { LOGFILE("splCryptoGenerateAesKey failed! (0x%08X)", rc); @@ -161,7 +185,7 @@ bool ncaEncryptKeyArea(NcaContext *ctx) key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4); aes128ContextCreate(&key_area_ctx, kaek, true); - for(u8 i = 0; i < key_count; i++) aes128EncryptBlock(&key_area_ctx, &(ctx->header.encrypted_keys[i]), &(ctx->decrypted_keys[i])); + for(u8 i = 0; i < key_count; i++) aes128EncryptBlock(&key_area_ctx, ctx->header.encrypted_keys[i].key, ctx->decrypted_keys[i].key); return true; } @@ -176,6 +200,7 @@ bool ncaDecryptHeader(NcaContext *ctx) u32 i, magic = 0; size_t crypt_res = 0; + u64 fs_header_offset = 0; const u8 *header_key = NULL; Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0}; @@ -231,18 +256,27 @@ bool ncaDecryptHeader(NcaContext *ctx) return false; } - aes128XtsContextCreate(&nca0_fs_header_ctx, &(ctx->decrypted_keys[0]), &(ctx->decrypted_keys[1]), false); + aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_keys[0].key, ctx->decrypted_keys[1].key, false); for(i = 0; i < NCA_FS_HEADER_COUNT; i++) { if (!ctx->header.fs_entries[i].enable_entry) continue; + /* FS headers are not part of NCA0 headers */ + fs_header_offset = NCA_FS_ENTRY_BLOCK_OFFSET(ctx->header.fs_entries[i].start_block_offset); + if (!ncaRead(ctx, &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, fs_header_offset)) + { + LOGFILE("Failed to read NCA0 FS section header #%u at offset 0x%lX!", i, fs_header_offset); + return false; + } - - - - - + crypt_res = aes128XtsNintendoCrypt(&nca0_fs_header_ctx, &(ctx->header.fs_headers[i]), &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, (fs_header_offset - 0x400) >> 9, \ + NCA_AES_XTS_SECTOR_SIZE, false); + if (crypt_res != NCA_FS_HEADER_LENGTH) + { + LOGFILE("Error decrypting NCA0 FS section header #%u!", i); + return false; + } } break; @@ -332,7 +366,7 @@ bool ncaEncryptHeader(NcaContext *ctx) -static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx) +static inline bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx) { if (!ctx || ctx->format_version != NcaVersion_Nca0) return false; diff --git a/source/nca.h b/source/nca.h index b4bae7c..7212874 100644 --- a/source/nca.h +++ b/source/nca.h @@ -35,6 +35,7 @@ #define NCA_BKTR_MAGIC 0x424B5452 /* "BKTR" */ #define NCA_FS_ENTRY_BLOCK_SIZE 0x200 +#define NCA_FS_ENTRY_BLOCK_OFFSET(x) ((x) * NCA_FS_ENTRY_BLOCK_SIZE) #define NCA_AES_XTS_SECTOR_SIZE 0x200 @@ -98,7 +99,7 @@ typedef struct { typedef struct { u8 key[0x10]; -} NcaEncryptedKey; +} NcaKey; typedef enum { NcaFsType_RomFs = 0, @@ -235,7 +236,7 @@ typedef struct { FsRightsId rights_id; ///< Used for titlekey crypto. NcaFsEntry fs_entries[4]; ///< Start and end offsets for each NCA FS section. NcaFsHash fs_hashes[4]; ///< SHA-256 hashes calculated over each NCA FS section header. - NcaEncryptedKey encrypted_keys[4]; ///< Only the encrypted key at index #2 is used. The other three are zero filled before the key area is encrypted. + NcaKey encrypted_keys[4]; ///< Only the encrypted key at index #2 is used. The other three are zero filled before the key area is encrypted. u8 reserved_2[0xC0]; NcaFsHeader fs_headers[4]; /// NCA FS section headers. } NcaHeader; @@ -278,10 +279,22 @@ typedef struct { bool rights_id_available; NcaHeader header; bool dirty_header; - NcaEncryptedKey decrypted_keys[4]; + NcaKey decrypted_keys[4]; NcaFsContext fs_contexts[4]; } NcaContext; +/// Reads raw encrypted data from a NCA using a NCA context with the 'storage_id', 'id', 'id_str' and 'size' elements filled beforehand. +/// If 'storage_id' == NcmStorageId_GameCard, the 'gamecard_offset' element should hold a value greater than zero. +/// If 'storage_id' != NcmStorageId_GameCard, the 'ncm_storage' element should point to a valid NcmContentStorage instance. +bool ncaRead(NcaContext *ctx, void *out, u64 read_size, u64 offset); + + + + + + + + static inline void ncaConvertNcmContentSizeToU64(const u8 *size, u64 *out) { if (!size || !out) return; diff --git a/source/services.c b/source/services.c index dea3d87..80785be 100644 --- a/source/services.c +++ b/source/services.c @@ -120,12 +120,12 @@ bool servicesCheckRunningServiceByName(const char *name) { if (!name || !strlen(name)) return false; - Handle handle; + Handle handle = INVALID_HANDLE; SmServiceName service_name = smEncodeName(name); Result rc = smRegisterService(&handle, service_name, false, 1); bool running = R_FAILED(rc); - svcCloseHandle(handle); + if (handle != INVALID_HANDLE) svcCloseHandle(handle); if (!running) smUnregisterService(service_name); diff --git a/source/tik.c b/source/tik.c index 40d4045..849a28b 100644 --- a/source/tik.c +++ b/source/tik.c @@ -66,7 +66,7 @@ static TikCommonBlock *tikGetCommonBlockFromMemoryBuffer(void *data); static bool tikGetTitleKeyFromTicketCommonBlock(void *dst, const TikCommonBlock *tik_common_blk); static bool tikGetTitleKekDecryptedTitleKey(void *dst, const void *src, u8 key_generation); -static u8 tikGetTitleKeyTypeFromRightsId(const FsRightsId *id); +static bool tikGetTitleKeyTypeFromRightsId(const FsRightsId *id, u8 *out); static bool tikRetrieveRightsIdsByTitleKeyType(FsRightsId **out, u32 *out_count, bool personalized); static bool tikGetTicketTypeAndSize(const void *data, u64 data_size, u8 *out_type, u64 *out_size); @@ -86,7 +86,7 @@ bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gam return false; } - tik_common_blk = tikGetCommonBlockFromMemoryBuffer(dst->data); + tik_common_blk = tikGetCommonBlockFromTicket(dst); if (!tik_common_blk) { LOGFILE("Unable to retrieve common block from ticket!"); @@ -112,23 +112,40 @@ bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gam TikCommonBlock *tikGetCommonBlockFromTicket(Ticket *tik) { - if (!tik || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE) + if (!tik || tik->type == TikType_None || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE || tik->size > TIK_MAX_SIZE) { LOGFILE("Invalid parameters!"); return NULL; } - return tikGetCommonBlockFromMemoryBuffer(tik->data); + TikCommonBlock *tik_common_blk = NULL; + + switch(tik->type) + { + case TikType_SigRsa4096: + tik_common_blk = &(((TikSigRsa4096*)tik->data)->tik_common_blk); + break; + case TikType_SigRsa2048: + tik_common_blk = &(((TikSigRsa2048*)tik->data)->tik_common_blk); + break; + case TikType_SigEcsda240: + tik_common_blk = &(((TikSigEcsda240*)tik->data)->tik_common_blk); + break; + default: + break; + } + + return tik_common_blk; } void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik) { - if (!tik || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE) return; + if (!tik || tik->type == TikType_None || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE || tik->size > TIK_MAX_SIZE) return; bool dev_cert = false; TikCommonBlock *tik_common_blk = NULL; - tik_common_blk = tikGetCommonBlockFromMemoryBuffer(tik->data); + tik_common_blk = tikGetCommonBlockFromTicket(tik); if (!tik_common_blk || tik_common_blk->titlekey_type != TikTitleKeyType_Personalized) return; switch(tik->type) @@ -220,19 +237,19 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight } u32 i; + u8 titlekey_type = 0; save_ctx_t *save_ctx = NULL; allocation_table_storage_ctx_t fat_storage = {0}; u64 ticket_bin_size = 0; - u64 buf_size = (TIK_MAX_SIZE * 0x10); /* 0x4000 */ + u64 buf_size = (TIK_MAX_SIZE * 0x10); u64 br = 0, total_br = 0; u8 *ticket_bin_buf = NULL; bool found_tik = false, success = false; - u8 titlekey_type = tikGetTitleKeyTypeFromRightsId(id); - if (titlekey_type == TikTitleKeyType_Invalid) + if (!tikGetTitleKeyTypeFromRightsId(id, &titlekey_type)) { LOGFILE("Unable to retrieve ticket titlekey type!"); return false; @@ -326,9 +343,9 @@ static TikCommonBlock *tikGetCommonBlockFromMemoryBuffer(void *data) return NULL; } + u32 sig_type = 0; u8 *data_u8 = (u8*)data; TikCommonBlock *tik_common_blk = NULL; - u32 sig_type = 0; memcpy(&sig_type, data_u8, sizeof(u32)); @@ -426,27 +443,27 @@ static bool tikGetTitleKekDecryptedTitleKey(void *dst, const void *src, u8 key_g return true; } -static u8 tikGetTitleKeyTypeFromRightsId(const FsRightsId *id) +static bool tikGetTitleKeyTypeFromRightsId(const FsRightsId *id, u8 *out) { - if (!id) + if (!id || !out) { LOGFILE("Invalid parameters!"); - return TikTitleKeyType_Invalid; + return false; } - u8 type = TikTitleKeyType_Invalid; u32 count; FsRightsId *rights_ids; + bool found = false; for(u8 i = 0; i < 2; i++) { count = 0; rights_ids = NULL; - if (!tikRetrieveRightsIdsByTitleKeyType(&rights_ids, &count, (bool)i)) + if (!tikRetrieveRightsIdsByTitleKeyType(&rights_ids, &count, i == 1)) { LOGFILE("Unable to retrieve %s rights IDs!", i == 0 ? "common" : "personalized"); - break; + continue; } if (!count) continue; @@ -455,17 +472,18 @@ static u8 tikGetTitleKeyTypeFromRightsId(const FsRightsId *id) { if (!memcmp(rights_ids[j].c, id->c, 0x10)) { - type = i; /* TikTitleKeyType_Common or TikTitleKeyType_Personalized */ + *out = i; /* TikTitleKeyType_Common or TikTitleKeyType_Personalized */ + found = true; break; } } free(rights_ids); - if (type != TikTitleKeyType_Invalid) break; + if (found) break; } - return type; + return found; } static bool tikRetrieveRightsIdsByTitleKeyType(FsRightsId **out, u32 *out_count, bool personalized) @@ -523,11 +541,11 @@ static bool tikGetTicketTypeAndSize(const void *data, u64 data_size, u8 *out_typ return false; } - u8 type = TikType_Invalid; - const u8 *data_u8 = (const u8*)data; - const TikCommonBlock *tik_common_blk = NULL; u32 sig_type = 0; u64 offset = 0; + u8 type = TikType_None; + const u8 *data_u8 = (const u8*)data; + const TikCommonBlock *tik_common_blk = NULL; memcpy(&sig_type, data_u8, sizeof(u32)); diff --git a/source/tik.h b/source/tik.h index 3a99b21..2240e95 100644 --- a/source/tik.h +++ b/source/tik.h @@ -22,20 +22,19 @@ #include #include "signature.h" -#define TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES system savefiles */ +#define TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES ticket system savedata file */ #define TIK_MIN_SIZE 0x200 /* Equivalent to sizeof(TikSigEcsda240) - assuming no ESv2 records are available */ typedef enum { - TikType_SigRsa4096 = 0, - TikType_SigRsa2048 = 1, - TikType_SigEcsda240 = 2, - TikType_Invalid = 255 + TikType_None = 0, + TikType_SigRsa4096 = 1, + TikType_SigRsa2048 = 2, + TikType_SigEcsda240 = 3 } TikType; typedef enum { TikTitleKeyType_Common = 0, - TikTitleKeyType_Personalized = 1, - TikTitleKeyType_Invalid = 255 + TikTitleKeyType_Personalized = 1 } TikTitleKeyType; typedef enum { @@ -121,7 +120,7 @@ typedef struct { u8 dec_titlekey[0x10]; ///< Titlekey without titlekek crypto. Ready to use for NCA FS section decryption. } Ticket; -/// Retrieves a ticket from either the secure hash FS partition from the inserted gamecard or ES ticket savedata using a Rights ID value. +/// Retrieves a ticket from either the ES ticket system savedata file (eMMC BIS System partition) or the secure hash FS partition from an inserted gamecard, using a Rights ID value. /// Titlekey is also RSA-OAEP unwrapped (if needed) and titlekek decrypted right away. bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gamecard); diff --git a/source/utils.c b/source/utils.c index 447d5f2..7bbe9ad 100644 --- a/source/utils.c +++ b/source/utils.c @@ -38,7 +38,7 @@ static AppletHookCookie g_systemOverclockCookie = {0}; static Mutex g_logfileMutex = 0; static FsStorage g_emmcBisSystemPartitionStorage = {0}; -static FATFS *g_emmcBisSystemPartitionFs = NULL; +static FATFS *g_emmcBisSystemPartitionFatFsObj = NULL; /* Function prototypes. */ @@ -200,11 +200,11 @@ void utilsGenerateHexStringFromData(char *dst, size_t dst_size, const void *src, for(i = 0, j = 0; i < src_size; i++) { - char nib1 = ((src_u8[i] >> 4) & 0xF); - char nib2 = (src_u8[i] & 0xF); + char h_nib = ((src_u8[i] >> 4) & 0xF); + char l_nib = (src_u8[i] & 0xF); - dst[j++] = (nib1 + (nib1 < 0xA ? 0x30 : 0x57)); - dst[j++] = (nib2 + (nib2 < 0xA ? 0x30 : 0x57)); + dst[j++] = (h_nib + (h_nib < 0xA ? 0x30 : 0x57)); + dst[j++] = (l_nib + (l_nib < 0xA ? 0x30 : 0x57)); } dst[j] = '\0'; @@ -257,14 +257,14 @@ static bool utilsMountEmmcBisSystemPartitionStorage(void) return false; } - g_emmcBisSystemPartitionFs = calloc(1, sizeof(FATFS)); - if (!g_emmcBisSystemPartitionFs) + g_emmcBisSystemPartitionFatFsObj = calloc(1, sizeof(FATFS)); + if (!g_emmcBisSystemPartitionFatFsObj) { LOGFILE("Unable to allocate memory for FatFs object!"); return false; } - fr = f_mount(g_emmcBisSystemPartitionFs, BIS_SYSTEM_PARTITION_MOUNT_NAME, 1); + fr = f_mount(g_emmcBisSystemPartitionFatFsObj, BIS_SYSTEM_PARTITION_MOUNT_NAME, 1); if (fr != FR_OK) { LOGFILE("Failed to mount eMMC BIS System partition! (%u)", fr); @@ -276,11 +276,11 @@ static bool utilsMountEmmcBisSystemPartitionStorage(void) static void utilsUnmountEmmcBisSystemPartitionStorage(void) { - if (g_emmcBisSystemPartitionFs) + if (g_emmcBisSystemPartitionFatFsObj) { f_unmount(BIS_SYSTEM_PARTITION_MOUNT_NAME); - free(g_emmcBisSystemPartitionFs); - g_emmcBisSystemPartitionFs = NULL; + free(g_emmcBisSystemPartitionFatFsObj); + g_emmcBisSystemPartitionFatFsObj = NULL; } if (serviceIsActive(&(g_emmcBisSystemPartitionStorage.s)))