From 0e5683b8803789b900523e99d46bf7481643439d Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Tue, 21 Apr 2020 06:23:33 -0400 Subject: [PATCH] NCA read (almost) done. Missing BKTR stuff. --- source/gamecard.c | 9 +- source/main.c | 71 +++++++-- source/nca.c | 383 ++++++++++++++++++++++++++++++++-------------- source/nca.h | 163 +++++++++----------- source/utils.c | 15 +- source/utils.h | 3 +- 6 files changed, 416 insertions(+), 228 deletions(-) diff --git a/source/gamecard.c b/source/gamecard.c index 3a34434..78ae005 100644 --- a/source/gamecard.c +++ b/source/gamecard.c @@ -779,7 +779,7 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l if (!(base_offset % GAMECARD_MEDIA_UNIT_SIZE) && !(read_size % GAMECARD_MEDIA_UNIT_SIZE)) { - /* Optimization for reads that are already aligned to GAMECARD_MEDIA_UNIT_SIZE bytes */ + /* Optimization for reads that are already aligned to a GAMECARD_MEDIA_UNIT_SIZE boundary */ rc = fsStorageRead(&g_gameCardStorage, base_offset, out_u8, read_size); if (R_FAILED(rc)) { @@ -790,12 +790,13 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l success = true; } else { /* Fix offset and/or size to avoid unaligned reads */ - u64 block_start_offset = (base_offset - (base_offset % GAMECARD_MEDIA_UNIT_SIZE)); + u64 block_start_offset = ROUND_DOWN(base_offset, GAMECARD_MEDIA_UNIT_SIZE); u64 block_end_offset = ROUND_UP(base_offset + read_size, GAMECARD_MEDIA_UNIT_SIZE); u64 block_size = (block_end_offset - block_start_offset); + u64 data_start_offset = (base_offset - block_start_offset); u64 chunk_size = (block_size > GAMECARD_READ_BUFFER_SIZE ? GAMECARD_READ_BUFFER_SIZE : block_size); - u64 out_chunk_size = (block_size > GAMECARD_READ_BUFFER_SIZE ? (GAMECARD_READ_BUFFER_SIZE - (base_offset - block_start_offset)) : read_size); + u64 out_chunk_size = (block_size > GAMECARD_READ_BUFFER_SIZE ? (GAMECARD_READ_BUFFER_SIZE - data_start_offset) : read_size); rc = fsStorageRead(&g_gameCardStorage, block_start_offset, g_gameCardReadBuf, chunk_size); if (R_FAILED(rc)) @@ -804,7 +805,7 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l goto out; } - memcpy(out_u8, g_gameCardReadBuf + (base_offset - block_start_offset), out_chunk_size); + memcpy(out_u8, g_gameCardReadBuf + data_start_offset, out_chunk_size); success = (block_size > GAMECARD_READ_BUFFER_SIZE ? gamecardReadStorageArea(out_u8 + out_chunk_size, read_size - out_chunk_size, base_offset + out_chunk_size, false) : true); } diff --git a/source/main.c b/source/main.c index 805678c..4d3e4a4 100644 --- a/source/main.c +++ b/source/main.c @@ -165,8 +165,6 @@ int main(int argc, char *argv[]) } else { printf("read failed\n"); } - - free(buf); } else { printf("buf failed\n"); } @@ -271,22 +269,27 @@ int main(int argc, char *argv[]) printf("ncm open storage succeeded\n"); consoleUpdate(NULL); - NcmContentId nca_id = { - .c = { 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8 } // Untitled Goose Game + // Untitled Goose Game + NcmPackagedContentInfo content_info = { + .hash = { + 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8, + 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8 + }, + .info = { + .content_id = { + .c = { 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8 } + }, + .size = { + 0x00, 0x40, 0xAD, 0x31, 0x00, 0x00 + }, + .content_type = NcmContentType_Program, + .id_offset = 0 + } }; - u8 nca_hash[SHA256_HASH_SIZE] = { - 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8, - 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8 - }; - - u8 nca_size[0x6] = { - 0x00, 0x40, 0xAD, 0x31, 0x00, 0x00 - }; - - if (ncaProcessContent(nca_ctx, &tik, NcmStorageId_SdCard, &ncm_storage, &nca_id, nca_hash, NcmContentType_Program, nca_size, 0, 0)) + if (ncaInitializeContext(nca_ctx, &tik, NcmStorageId_SdCard, &ncm_storage, 0, &content_info)) { - printf("nca process succeeded\n"); + printf("nca initialize ctx succeeded\n"); consoleUpdate(NULL); tmp_file = fopen("sdmc:/nca_ctx.bin", "wb"); @@ -299,8 +302,43 @@ int main(int argc, char *argv[]) } else { printf("nca ctx not saved\n"); } + + consoleUpdate(NULL); + + tmp_file = fopen("sdmc:/section0.bin", "wb"); + if (tmp_file) + { + printf("nca section0 created\n"); + consoleUpdate(NULL); + + u64 curpos = 0; + u64 blksize = (u64)0x400000; + u64 total = nca_ctx->fs_contexts[0].section_size; + + for(u64 curpos = 0; curpos < total; curpos += blksize) + { + if (blksize > (total - curpos)) blksize = (total - curpos); + + if (!ncaReadFsSection(&(nca_ctx->fs_contexts[0]), buf, blksize, curpos)) + { + printf("nca read section failed\n"); + break; + } + + fwrite(buf, 1, blksize, tmp_file); + } + + if (curpos >= total) printf("nca read section success\n"); + + consoleUpdate(NULL); + + fclose(tmp_file); + tmp_file = NULL; + } else { + printf("nca section0 not created\n"); + } } else { - printf("nca process failed\n"); + printf("nca initialize ctx failed\n"); } consoleUpdate(NULL); @@ -325,6 +363,7 @@ int main(int argc, char *argv[]) } + if (buf) free(buf); consoleExit(NULL); diff --git a/source/nca.c b/source/nca.c index 5e5f610..37a4a3c 100644 --- a/source/nca.c +++ b/source/nca.c @@ -24,8 +24,12 @@ #include "gamecard.h" #include "utils.h" +#define NCA_CRYPTO_BUFFER_SIZE 0x800000 /* 8 MiB */ + /* Global variables. */ +static u8 *g_ncaCryptoBuffer = NULL; + static const u8 g_nca0KeyAreaHash[SHA256_HASH_SIZE] = { 0x9A, 0xBB, 0xD2, 0x11, 0x86, 0x00, 0x21, 0x9D, 0x7A, 0xDC, 0x5B, 0x43, 0x95, 0xF8, 0x4E, 0xFD, 0xFF, 0x6B, 0x25, 0xEF, 0x9F, 0x96, 0x85, 0x28, 0x18, 0x9E, 0x76, 0xB0, 0x92, 0xF0, 0x6A, 0xCB @@ -35,10 +39,31 @@ static const u8 g_nca0KeyAreaHash[SHA256_HASH_SIZE] = { static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx); +static inline u8 ncaGetKeyGenerationValue(NcaContext *ctx); +static inline bool ncaCheckRightsIdAvailability(NcaContext *ctx); + static void ncaInitializeAesCtrIv(u8 *out, const u8 *ctr, u64 offset); static void ncaUpdateAesCtrIv(u8 *ctr, u64 offset); static void ncaUpdateAesCtrExIv(u8 *ctr, u32 ctr_val, u64 offset); +bool ncaAllocateCryptoBuffer(void) +{ + if (g_ncaCryptoBuffer) return true; + + g_ncaCryptoBuffer = malloc(NCA_CRYPTO_BUFFER_SIZE); + + return (g_ncaCryptoBuffer != NULL); +} + +void ncaFreeCryptoBuffer(void) +{ + if (g_ncaCryptoBuffer) + { + free(g_ncaCryptoBuffer); + g_ncaCryptoBuffer = NULL; + } +} + size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt) { if (!ctx || !dst || !src || !size || !sector_size || (size % sector_size) != 0) @@ -57,49 +82,13 @@ size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, { /* We have to force a sector reset on each new sector to actually enable Nintendo AES-XTS cipher tweak */ aes128XtsContextResetSector(ctx, cur_sector, true); - - if (encrypt) - { - crypt_res = aes128XtsEncrypt(ctx, dst_u8 + i, src_u8 + i, sector_size); - } else { - crypt_res = aes128XtsDecrypt(ctx, dst_u8 + i, src_u8 + i, sector_size); - } - + crypt_res = (encrypt ? aes128XtsEncrypt(ctx, dst_u8 + i, src_u8 + i, sector_size) : aes128XtsDecrypt(ctx, dst_u8 + i, src_u8 + i, sector_size)); if (crypt_res != sector_size) break; } return i; } -bool ncaReadContent(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) - { - /* Retrieve NCA data normally */ - /* This strips NAX0 crypto from SD card NCAs (not used on eMMC NCAs) */ - 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); - } else { - /* Retrieve NCA data using raw gamecard reads */ - /* Fixes NCA read issues with gamecards under HOS < 4.0.0 when using ncmContentStorageReadContentIdFile() */ - 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); - } - - return ret; -} - bool ncaDecryptKeyArea(NcaContext *ctx) { if (!ctx) @@ -184,7 +173,7 @@ bool ncaEncryptKeyArea(NcaContext *ctx) bool ncaDecryptHeader(NcaContext *ctx) { - if (!ctx) + if (!ctx || !strlen(ctx->content_id_str)) { LOGFILE("Invalid NCA context!"); return false; @@ -194,32 +183,30 @@ bool ncaDecryptHeader(NcaContext *ctx) size_t crypt_res = 0; u64 fs_header_offset = 0; const u8 *header_key = NULL; - u8 tmp_hdr[NCA_HEADER_LENGTH] = {0}; Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0}; header_key = keysGetNcaHeaderKey(); aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + 0x10, false); - crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, tmp_hdr, &(ctx->header), NCA_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, false); + crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, false); if (crypt_res != NCA_HEADER_LENGTH) { - LOGFILE("Invalid output length for decrypted NCA header! (0x%X != 0x%lX)", NCA_HEADER_LENGTH, crypt_res); + LOGFILE("Error decrypting partial NCA \"%s\" header!", ctx->content_id_str); return false; } - memcpy(&magic, tmp_hdr + 0x200, sizeof(u32)); - magic = __builtin_bswap32(magic); + magic = __builtin_bswap32(ctx->header.magic); switch(magic) { case NCA_NCA3_MAGIC: ctx->format_version = NcaVersion_Nca3; - crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_FULL_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, false); - if (crypt_res != NCA_FULL_HEADER_LENGTH) + crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, ctx->header.fs_headers, ctx->header.fs_headers, NCA_FULL_HEADER_LENGTH - NCA_HEADER_LENGTH, 2, NCA_AES_XTS_SECTOR_SIZE, false); + if (crypt_res != (NCA_FULL_HEADER_LENGTH - NCA_HEADER_LENGTH)) { - LOGFILE("Error decrypting full NCA3 header!"); + LOGFILE("Error decrypting NCA3 \"%s\" FS section headers!", ctx->content_id_str); return false; } @@ -234,7 +221,7 @@ bool ncaDecryptHeader(NcaContext *ctx) crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header.fs_headers[i]), &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, false); if (crypt_res != NCA_FS_HEADER_LENGTH) { - LOGFILE("Error decrypting NCA2 FS section header #%u!", i); + LOGFILE("Error decrypting NCA2 \"%s\" FS section header #%u!", ctx->content_id_str, i); return false; } } @@ -246,7 +233,7 @@ bool ncaDecryptHeader(NcaContext *ctx) /* We first need to decrypt the key area from the NCA0 header in order to access its FS section headers */ if (!ncaDecryptKeyArea(ctx)) { - LOGFILE("Error decrypting NCA0 key area!"); + LOGFILE("Error decrypting NCA0 \"%s\" key area!", ctx->content_id_str); return false; } @@ -260,22 +247,22 @@ bool ncaDecryptHeader(NcaContext *ctx) fs_header_offset = NCA_FS_ENTRY_BLOCK_OFFSET(ctx->header.fs_entries[i].start_block_offset); if (!ncaReadContent(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); + LOGFILE("Failed to read NCA0 \"%s\" FS section header #%u at offset 0x%lX!", ctx->content_id_str, 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); + crypt_res = aes128XtsNintendoCrypt(&nca0_fs_header_ctx, &(ctx->header.fs_headers[i]), &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, \ + NCA_NCA0_FS_HEADER_AES_XTS_SECTOR(fs_header_offset), NCA_AES_XTS_SECTOR_SIZE, false); if (crypt_res != NCA_FS_HEADER_LENGTH) { - LOGFILE("Error decrypting NCA0 FS section header #%u!", i); + LOGFILE("Error decrypting NCA0 \"%s\" FS section header #%u!", ctx->content_id_str, i); return false; } } break; default: - LOGFILE("Invalid NCA magic word! Wrong header key? (0x%08X)", magic); + LOGFILE("Invalid NCA \"%s\" magic word! Wrong header key? (0x%08X)", ctx->content_id_str, magic); return false; } @@ -284,7 +271,7 @@ bool ncaDecryptHeader(NcaContext *ctx) bool ncaEncryptHeader(NcaContext *ctx) { - if (!ctx) + if (!ctx || !strlen(ctx->content_id_str)) { LOGFILE("Invalid NCA context!"); return false; @@ -300,25 +287,25 @@ bool ncaEncryptHeader(NcaContext *ctx) aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + 0x10, true); + crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true); + if (crypt_res != NCA_HEADER_LENGTH) + { + LOGFILE("Error encrypting partial NCA \"%s\" header!", ctx->content_id_str); + return false; + } + switch(ctx->format_version) { case NcaVersion_Nca3: - crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_FULL_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true); + crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, ctx->header.fs_headers, ctx->header.fs_headers, NCA_FULL_HEADER_LENGTH - NCA_HEADER_LENGTH, 2, NCA_AES_XTS_SECTOR_SIZE, true); if (crypt_res != NCA_FULL_HEADER_LENGTH) { - LOGFILE("Error encrypting full NCA3 header!"); + LOGFILE("Error encrypting NCA3 \"%s\" FS section headers!", ctx->content_id_str); return false; } break; case NcaVersion_Nca2: - crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true); - if (crypt_res != NCA_HEADER_LENGTH) - { - LOGFILE("Error encrypting partial NCA2 header!"); - return false; - } - for(i = 0; i < NCA_FS_HEADER_COUNT; i++) { if (!ctx->header.fs_entries[i].enable_entry) continue; @@ -326,20 +313,13 @@ bool ncaEncryptHeader(NcaContext *ctx) crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header.fs_headers[i]), &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true); if (crypt_res != NCA_FS_HEADER_LENGTH) { - LOGFILE("Error encrypting NCA2 FS section header #%u!", i); + LOGFILE("Error encrypting NCA2 \"%s\" FS section header #%u!", ctx->content_id_str, i); return false; } } break; case NcaVersion_Nca0: - crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true); - if (crypt_res != NCA_HEADER_LENGTH) - { - LOGFILE("Error encrypting NCA0 header!"); - return false; - } - /* NCA0 FS section headers will be encrypted in-place, but they need to be written to their proper offsets */ aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_keys[0].key, ctx->decrypted_keys[1].key, true); @@ -349,28 +329,28 @@ bool ncaEncryptHeader(NcaContext *ctx) fs_header_offset = NCA_FS_ENTRY_BLOCK_OFFSET(ctx->header.fs_entries[i].start_block_offset); - 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, true); + crypt_res = aes128XtsNintendoCrypt(&nca0_fs_header_ctx, &(ctx->header.fs_headers[i]), &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, \ + NCA_NCA0_FS_HEADER_AES_XTS_SECTOR(fs_header_offset), NCA_AES_XTS_SECTOR_SIZE, true); if (crypt_res != NCA_FS_HEADER_LENGTH) { - LOGFILE("Error decrypting NCA0 FS section header #%u!", i); + LOGFILE("Error decrypting NCA0 \"%s\" FS section header #%u!", ctx->content_id_str, i); return false; } } break; default: - LOGFILE("Invalid NCA format version! (0x%02X)", ctx->format_version); + LOGFILE("Invalid NCA \"%s\" format version! (0x%02X)", ctx->content_id_str, ctx->format_version); return false; } return true; } -bool ncaProcessContent(NcaContext *out, Ticket *tik, u8 storage_id, NcmContentStorage *ncm_storage, const NcmContentId *id, const u8 *hash, u8 type, const u8 *size, u8 id_offset, u8 hfs_partition_type) +bool ncaInitializeContext(NcaContext *out, Ticket *tik, u8 storage_id, NcmContentStorage *ncm_storage, u8 hfs_partition_type, const NcmPackagedContentInfo *content_info) { - if (!out || !tik || (storage_id != NcmStorageId_GameCard && !ncm_storage) || !id || !hash || type > NcmContentType_DeltaFragment || !size || \ - (storage_id == NcmStorageId_GameCard && hfs_partition_type > GameCardHashFileSystemPartitionType_Secure)) + if (!out || !tik || (storage_id != NcmStorageId_GameCard && !ncm_storage) || (storage_id == NcmStorageId_GameCard && hfs_partition_type > GameCardHashFileSystemPartitionType_Secure) || \ + !content_info || content_info->info.content_type > NcmContentType_DeltaFragment) { LOGFILE("Invalid parameters!"); return false; @@ -380,15 +360,21 @@ bool ncaProcessContent(NcaContext *out, Ticket *tik, u8 storage_id, NcmContentSt out->storage_id = storage_id; out->ncm_storage = (out->storage_id != NcmStorageId_GameCard ? ncm_storage : NULL); - memcpy(&(out->id), id, sizeof(NcmContentId)); - utilsGenerateHexStringFromData(out->id_str, sizeof(out->id_str), out->id.c, sizeof(out->id.c)); + memcpy(&(out->content_id), &(content_info->info.content_id), sizeof(NcmContentId)); + utilsGenerateHexStringFromData(out->content_id_str, sizeof(out->content_id_str), out->content_id.c, sizeof(out->content_id.c)); - memcpy(out->hash, hash, SHA256_HASH_SIZE); + memcpy(out->hash, content_info->hash, SHA256_HASH_SIZE); utilsGenerateHexStringFromData(out->hash_str, sizeof(out->hash_str), out->hash, sizeof(out->hash)); - out->type = type; - ncaConvertNcmContentSizeToU64(size, &(out->size)); - out->id_offset = id_offset; + out->content_type = content_info->info.content_type; + out->id_offset = content_info->info.id_offset; + + ncaConvertNcmContentSizeToU64(content_info->info.size, &(out->content_size)); + if (out->content_size < NCA_FULL_HEADER_LENGTH) + { + LOGFILE("Invalid size for NCA \"%s\"!", out->content_id_str); + return false; + } out->rights_id_available = out->dirty_header = false; @@ -396,7 +382,7 @@ bool ncaProcessContent(NcaContext *out, Ticket *tik, u8 storage_id, NcmContentSt { /* Retrieve gamecard NCA offset */ char nca_filename[0x30] = {0}; - sprintf(nca_filename, "%s.%s", out->id_str, out->type == NcmContentType_Meta ? "cnmt.nca" : "nca"); + sprintf(nca_filename, "%s.%s", out->content_id_str, out->content_type == NcmContentType_Meta ? "cnmt.nca" : "nca"); if (!gamecardGetOffsetAndSizeFromHashFileSystemPartitionEntryByName(hfs_partition_type, nca_filename, &(out->gamecard_offset), NULL)) { @@ -408,20 +394,20 @@ bool ncaProcessContent(NcaContext *out, Ticket *tik, u8 storage_id, NcmContentSt /* Read NCA header */ if (!ncaReadContent(out, &(out->header), sizeof(NcaHeader), 0)) { - LOGFILE("Failed to read NCA \"%s\" header!", out->id_str); + LOGFILE("Failed to read NCA \"%s\" header!", out->content_id_str); return false; } - /* Decrypt header */ + /* Decrypt NCA header */ if (!ncaDecryptHeader(out)) { - LOGFILE("Failed to decrypt NCA \"%s\" header!", out->id_str); + LOGFILE("Failed to decrypt NCA \"%s\" header!", out->content_id_str); return false; } - if (out->header.content_size != out->size) + if (out->header.content_size != out->content_size) { - LOGFILE("Content size mismatch for NCA \"%s\"! (0x%lX != 0x%lX)", out->header.content_size, out->size); + LOGFILE("Content size mismatch for NCA \"%s\"! (0x%lX != 0x%lX)", out->content_id_str, out->header.content_size, out->content_size); return false; } @@ -435,7 +421,7 @@ bool ncaProcessContent(NcaContext *out, Ticket *tik, u8 storage_id, NcmContentSt /* This will return true if it has already been retrieved */ if (!tikRetrieveTicketByRightsId(tik, &(out->header.rights_id), out->storage_id == NcmStorageId_GameCard)) { - LOGFILE("Error retrieving ticket for NCA \"%s\"!", out->id_str); + LOGFILE("Error retrieving ticket for NCA \"%s\"!", out->content_id_str); return false; } @@ -458,12 +444,31 @@ bool ncaProcessContent(NcaContext *out, Ticket *tik, u8 storage_id, NcmContentSt /* Fill section context */ out->fs_contexts[i].nca_ctx = out; out->fs_contexts[i].section_num = i; - out->fs_contexts[i].offset = NCA_FS_ENTRY_BLOCK_OFFSET(out->header.fs_entries[i].start_block_offset); - out->fs_contexts[i].size = (NCA_FS_ENTRY_BLOCK_OFFSET(out->header.fs_entries[i].end_block_offset) - out->fs_contexts[i].offset); + out->fs_contexts[i].section_offset = NCA_FS_ENTRY_BLOCK_OFFSET(out->header.fs_entries[i].start_block_offset); + out->fs_contexts[i].section_size = (NCA_FS_ENTRY_BLOCK_OFFSET(out->header.fs_entries[i].end_block_offset) - out->fs_contexts[i].section_offset); out->fs_contexts[i].section_type = NcaSectionType_Invalid; /* Placeholder */ - out->fs_contexts[i].encryption_type = (out->format_version == NcaVersion_Nca0 ? NcaEncryptionType_Nca0 : out->header.fs_headers[i].encryption_type); out->fs_contexts[i].header = &(out->header.fs_headers[i]); - out->fs_contexts[i].use_xts = false; + + /* Determine encryption type */ + out->fs_contexts[i].encryption_type = (out->format_version == NcaVersion_Nca0 ? NcaEncryptionType_Nca0 : out->header.fs_headers[i].encryption_type); + if (out->fs_contexts[i].encryption_type == NcaEncryptionType_Auto) + { + switch(out->fs_contexts[i].section_num) + { + case 0: /* ExeFS PFS0 */ + case 1: /* RomFS */ + out->fs_contexts[i].encryption_type = NcaEncryptionType_AesCtr; + break; + case 2: /* Logo PFS0 */ + out->fs_contexts[i].encryption_type = NcaEncryptionType_None; + break; + default: + break; + } + } + + /* Check if we're dealing with an invalid encryption type value */ + if (out->fs_contexts[i].encryption_type == NcaEncryptionType_Auto || out->fs_contexts[i].encryption_type > NcaEncryptionType_Nca0) continue; /* Determine FS section type */ if (out->fs_contexts[i].header->fs_type == NcaFsType_PartitionFs && out->fs_contexts[i].header->hash_type == NcaHashType_HierarchicalSha256) @@ -479,34 +484,162 @@ bool ncaProcessContent(NcaContext *out, Ticket *tik, u8 storage_id, NcmContentSt out->fs_contexts[i].section_type = NcaSectionType_Nca0RomFs; } - if (out->fs_contexts[i].section_type == NcaSectionType_Invalid || out->fs_contexts[i].encryption_type <= NcaEncryptionType_None) continue; + /* Check if we're dealing with an invalid section type value */ + if (out->fs_contexts[i].section_type >= NcaSectionType_Invalid) continue; - /* Initialize section CTR */ - ncaInitializeAesCtrIv(out->fs_contexts[i].ctr, out->fs_contexts[i].header->section_ctr, out->fs_contexts[i].offset); - - /* Initialize AES context */ - if (out->rights_id_available) + /* Initialize crypto related fields */ + if (out->fs_contexts[i].encryption_type > NcaEncryptionType_None && out->fs_contexts[i].encryption_type <= NcaEncryptionType_Nca0) { - /* AES-128-CTR */ - aes128CtrContextCreate(&(out->fs_contexts[i].ctr_ctx), out->titlekey, out->fs_contexts[i].ctr); - } else { - if (out->fs_contexts[i].encryption_type == NcaEncryptionType_AesCtr || out->fs_contexts[i].encryption_type == NcaEncryptionType_AesCtrEx) + /* Initialize section CTR */ + ncaInitializeAesCtrIv(out->fs_contexts[i].ctr, out->fs_contexts[i].header->section_ctr, out->fs_contexts[i].section_offset); + + /* Initialize AES context */ + if (out->rights_id_available) { - /* AES-128-CTR */ - aes128CtrContextCreate(&(out->fs_contexts[i].ctr_ctx), out->decrypted_keys[2].key, out->fs_contexts[i].ctr); - } else - if (out->fs_contexts[i].encryption_type == NcaEncryptionType_AesXts || out->fs_contexts[i].encryption_type == NcaEncryptionType_Nca0) - { - /* AES-128-XTS */ - aes128XtsContextCreate(&(out->fs_contexts[i].xts_ctx), out->decrypted_keys[0].key, out->decrypted_keys[1].key, false); - out->fs_contexts[i].use_xts = true; + aes128CtrContextCreate(&(out->fs_contexts[i].ctr_ctx), out->titlekey, out->fs_contexts[i].ctr); + } else { + if (out->fs_contexts[i].encryption_type == NcaEncryptionType_AesCtr || out->fs_contexts[i].encryption_type == NcaEncryptionType_AesCtrEx) + { + aes128CtrContextCreate(&(out->fs_contexts[i].ctr_ctx), out->decrypted_keys[2].key, out->fs_contexts[i].ctr); + } else + if (out->fs_contexts[i].encryption_type == NcaEncryptionType_AesXts || out->fs_contexts[i].encryption_type == NcaEncryptionType_Nca0) + { + aes128XtsContextCreate(&(out->fs_contexts[i].xts_ctx), out->decrypted_keys[0].key, out->decrypted_keys[1].key, false); + } } + } else { + memset(out->fs_contexts[i].ctr, 0, sizeof(out->fs_contexts[i].ctr)); + memset(&(out->fs_contexts[i].ctr_ctx), 0, sizeof(Aes128CtrContext)); + memset(&(out->fs_contexts[i].xts_ctx), 0, sizeof(Aes128XtsContext)); } } return true; } +bool ncaReadContent(NcaContext *ctx, void *out, u64 read_size, u64 offset) +{ + if (!ctx || !strlen(ctx->content_id_str) || (ctx->storage_id != NcmStorageId_GameCard && !ctx->ncm_storage) || (ctx->storage_id == NcmStorageId_GameCard && !ctx->gamecard_offset) || !out || \ + !read_size || offset >= ctx->content_size || (offset + read_size) > ctx->content_size) + { + LOGFILE("Invalid parameters!"); + return false; + } + + Result rc = 0; + bool ret = false; + + if (ctx->storage_id != NcmStorageId_GameCard) + { + /* Retrieve NCA data normally */ + /* This strips NAX0 crypto from SD card NCAs (not used on eMMC NCAs) */ + rc = ncmContentStorageReadContentIdFile(ctx->ncm_storage, out, read_size, &(ctx->content_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->content_id_str, rc); + } else { + /* Retrieve NCA data using raw gamecard reads */ + /* Fixes NCA read issues with gamecards under HOS < 4.0.0 when using ncmContentStorageReadContentIdFile() */ + 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->content_id_str); + } + + return ret; +} + +bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset) +{ + if (!g_ncaCryptoBuffer || !ctx || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || ctx->section_type >= NcaSectionType_Invalid || \ + ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_Nca0 || !ctx->header || !out || !read_size || offset >= ctx->section_size || \ + (offset + read_size) > ctx->section_size) + { + LOGFILE("Invalid NCA FS section header parameters!"); + return false; + } + + NcaContext *nca_ctx = (NcaContext*)ctx->nca_ctx; + u64 content_offset = (ctx->section_offset + offset); + + if (!strlen(nca_ctx->content_id_str) || (nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \ + content_offset >= nca_ctx->content_size || (content_offset + read_size) > nca_ctx->content_size) + { + LOGFILE("Invalid NCA header parameters!"); + return false; + } + + /* Read data right away if we're dealing with a FS section with no crypto */ + if (ctx->encryption_type == NcaEncryptionType_None) return ncaReadContent(nca_ctx, out, read_size, content_offset); + + /* Calculate offsets and block sizes */ + size_t crypt_res = 0; + u64 block_start_offset = 0, block_end_offset = 0, block_size = 0; + u64 chunk_size = 0, out_chunk_size = 0; + u64 data_start_offset = 0, sector_num = 0; + + switch(ctx->encryption_type) + { + case NcaEncryptionType_AesXts: + case NcaEncryptionType_Nca0: + block_start_offset = ROUND_DOWN(content_offset, NCA_AES_XTS_SECTOR_SIZE); + block_end_offset = ROUND_UP(content_offset + read_size, NCA_AES_XTS_SECTOR_SIZE); + sector_num = (ctx->encryption_type == NcaEncryptionType_AesXts ? offset : (content_offset - NCA_HEADER_LENGTH)); + sector_num /= NCA_AES_XTS_SECTOR_SIZE; + break; + case NcaEncryptionType_AesCtr: + case NcaEncryptionType_AesCtrEx: /* This function is only supposed to be used on Patch RomFS sections when *not* reading BKTR subsections */ + block_start_offset = ROUND_DOWN(content_offset, AES_BLOCK_SIZE); + block_end_offset = ROUND_UP(content_offset + read_size, AES_BLOCK_SIZE); + ncaUpdateAesCtrIv(ctx->ctr, block_start_offset); + break; + default: + break; + } + + block_size = (block_end_offset - block_start_offset); + chunk_size = (block_size > NCA_CRYPTO_BUFFER_SIZE ? NCA_CRYPTO_BUFFER_SIZE : block_size); + + data_start_offset = (content_offset - block_start_offset); + out_chunk_size = (block_size > NCA_CRYPTO_BUFFER_SIZE ? (NCA_CRYPTO_BUFFER_SIZE - data_start_offset) : read_size); + + /* Read data */ + if (!ncaReadContent(nca_ctx, g_ncaCryptoBuffer, chunk_size, block_start_offset)) + { + LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u!", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num); + return false; + } + + /* Decrypt data */ + switch(ctx->encryption_type) + { + case NcaEncryptionType_AesXts: + case NcaEncryptionType_Nca0: + crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_ctx), g_ncaCryptoBuffer, g_ncaCryptoBuffer, chunk_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, false); + if (crypt_res != chunk_size) + { + LOGFILE("Failed to decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u!", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num); + return false; + } + break; + case NcaEncryptionType_AesCtr: + case NcaEncryptionType_AesCtrEx: /* This function is only supposed to be used on Patch RomFS sections when *not* reading BKTR subsections */ + aes128CtrContextResetCtr(&(ctx->ctr_ctx), ctx->ctr); + aes128CtrCrypt(&(ctx->ctr_ctx), g_ncaCryptoBuffer, g_ncaCryptoBuffer, chunk_size); + break; + default: + break; + } + + /* Copy decrypted data */ + memcpy(out, g_ncaCryptoBuffer + data_start_offset, out_chunk_size); + + if (block_size > NCA_CRYPTO_BUFFER_SIZE) return ncaReadFsSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size); + + return true; +} + + + + + @@ -533,6 +666,30 @@ static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx) return true; } +static inline u8 ncaGetKeyGenerationValue(NcaContext *ctx) +{ + if (!ctx) return 0; + return (ctx->header.key_generation > ctx->header.key_generation_old ? ctx->header.key_generation : ctx->header.key_generation_old); +} + +static inline bool ncaCheckRightsIdAvailability(NcaContext *ctx) +{ + if (!ctx) return false; + + bool rights_id_available = false; + + for(u8 i = 0; i < 0x10; i++) + { + if (ctx->header.rights_id.c[i] != 0) + { + rights_id_available = true; + break; + } + } + + return rights_id_available; +} + static void ncaInitializeAesCtrIv(u8 *out, const u8 *ctr, u64 offset) { if (!out || !ctr) return; diff --git a/source/nca.h b/source/nca.h index 7e1a964..37ba9a1 100644 --- a/source/nca.h +++ b/source/nca.h @@ -22,25 +22,26 @@ #include #include "tik.h" -#define NCA_HEADER_LENGTH 0x400 -#define NCA_FS_HEADER_LENGTH 0x200 -#define NCA_FS_HEADER_COUNT 4 -#define NCA_FULL_HEADER_LENGTH (NCA_HEADER_LENGTH + (NCA_FS_HEADER_LENGTH * NCA_FS_HEADER_COUNT)) +#define NCA_HEADER_LENGTH 0x400 +#define NCA_FS_HEADER_LENGTH 0x200 +#define NCA_FS_HEADER_COUNT 4 +#define NCA_FULL_HEADER_LENGTH (NCA_HEADER_LENGTH + (NCA_FS_HEADER_LENGTH * NCA_FS_HEADER_COUNT)) -#define NCA_NCA0_MAGIC 0x4E434130 /* "NCA0" */ -#define NCA_NCA2_MAGIC 0x4E434132 /* "NCA2" */ -#define NCA_NCA3_MAGIC 0x4E434133 /* "NCA3" */ +#define NCA_NCA0_MAGIC 0x4E434130 /* "NCA0" */ +#define NCA_NCA2_MAGIC 0x4E434132 /* "NCA2" */ +#define NCA_NCA3_MAGIC 0x4E434133 /* "NCA3" */ -#define NCA_IVFC_MAGIC 0x49564643 /* "IVFC" */ +#define NCA_IVFC_MAGIC 0x49564643 /* "IVFC" */ -#define NCA_BKTR_MAGIC 0x424B5452 /* "BKTR" */ +#define NCA_BKTR_MAGIC 0x424B5452 /* "BKTR" */ -#define NCA_FS_ENTRY_BLOCK_SIZE 0x200 -#define NCA_FS_ENTRY_BLOCK_OFFSET(x) ((u64)(x) * NCA_FS_ENTRY_BLOCK_SIZE) +#define NCA_FS_ENTRY_BLOCK_SIZE 0x200 +#define NCA_FS_ENTRY_BLOCK_OFFSET(x) ((u64)(x) * NCA_FS_ENTRY_BLOCK_SIZE) -#define NCA_AES_XTS_SECTOR_SIZE 0x200 +#define NCA_AES_XTS_SECTOR_SIZE 0x200 +#define NCA_NCA0_FS_HEADER_AES_XTS_SECTOR(x) (((x) - NCA_HEADER_LENGTH) >> 9) -#define NCA_IVFC_BLOCK_SIZE(x) (1 << (x)) +#define NCA_IVFC_BLOCK_SIZE(x) (1 << (x)) typedef enum { NcaDistributionType_Download = 0, @@ -114,7 +115,7 @@ typedef enum { NcaEncryptionType_AesXts = 2, NcaEncryptionType_AesCtr = 3, NcaEncryptionType_AesCtrEx = 4, - NcaEncryptionType_Nca0 = 5 ///< Only used to represent NCA0 FS section crypto - not actually used as a possible value for this field. + NcaEncryptionType_Nca0 = 5 ///< Only used to represent NCA0 AES XTS FS section crypto - not actually used as a possible value for this field. } NcaEncryptionType; typedef struct { @@ -134,7 +135,7 @@ typedef struct { typedef struct { u64 offset; u64 size; - u32 block_size; ///< Use NCA_IVFC_CALC_BLOCK_SIZE to calculate the actual block size using this value. + u32 block_size; ///< Use NCA_IVFC_BLOCK_SIZE to calculate the actual block size using this value. u8 reserved[0x4]; } NcaHierarchicalIntegrityLayerInfo; @@ -254,111 +255,60 @@ typedef enum { typedef struct { void *nca_ctx; ///< NcaContext. Used to perform NCA reads. u8 section_num; - u64 offset; - u64 size; + u64 section_offset; + u64 section_size; u8 section_type; ///< NcaSectionType. u8 encryption_type; ///< NcaEncryptionType. NcaFsHeader *header; - bool use_xts; + u8 ctr[0x10]; ///< Used to update the AES CTR context IV based on the desired offset. Aes128CtrContext ctr_ctx; Aes128XtsContext xts_ctx; - u8 ctr[0x10]; ///< Used to update the AES context IV based on the desired offset. -} NcaFsContext; +} NcaFsSectionContext; typedef struct { u8 storage_id; ///< NcmStorageId. NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data. u64 gamecard_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard. - NcmContentId id; ///< Also used to read NCA data. - char id_str[0x21]; - u8 hash[0x20]; + NcmContentId content_id; ///< Also used to read NCA data. + char content_id_str[0x21]; + u8 hash[0x20]; ///< Retrieved from NcmPackagedContentInfo. char hash_str[0x41]; u8 format_version; ///< NcaVersion. - u8 type; ///< NcmContentType. Retrieved from NcmContentInfo. - u64 size; ///< Retrieved from NcmContentInfo. + u8 content_type; ///< NcmContentType. Retrieved from NcmPackagedContentInfo. + u64 content_size; ///< Retrieved from NcmPackagedContentInfo. u8 key_generation; ///< NcaKeyGenerationOld / NcaKeyGeneration. Retrieved from the decrypted header. - u8 id_offset; ///< Retrieved from NcmContentInfo. + u8 id_offset; ///< Retrieved from NcmPackagedContentInfo. bool rights_id_available; + u8 titlekey[0x10]; bool dirty_header; NcaHeader header; - NcaFsContext fs_contexts[4]; + NcaFsSectionContext fs_contexts[4]; NcaKey decrypted_keys[4]; - u8 titlekey[0x10]; } NcaContext; -/// Reads raw encrypted data from a NCA using an input NCA context. -/// 'storage_id', 'id', 'id_str' and 'size' elements must have been filled beforehand (e.g. using ncaProcessContent()). -/// If 'storage_id' != NcmStorageId_GameCard, the 'ncm_storage' element should point to a valid NcmContentStorage instance. -/// If 'storage_id' == NcmStorageId_GameCard, the 'gamecard_offset' element should hold a value greater than zero. +/// Functions to control the internal heap buffer used by NCA FS section crypto code. +/// Must be called at startup. +bool ncaAllocateCryptoBuffer(void); +void ncaFreeCryptoBuffer(void); + +/// Initializes a valid NCA context. +/// 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 '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. +bool ncaInitializeContext(NcaContext *out, Ticket *tik, u8 storage_id, NcmContentStorage *ncm_storage, u8 hfs_partition_type, const NcmPackagedContentInfo *content_info); + +/// Reads raw encrypted data from a NCA using an input NCA context, previously initialized by ncaInitializeContext(). bool ncaReadContent(NcaContext *ctx, void *out, u64 read_size, u64 offset); -/// Generates a valid NCA context. -/// 'hash', 'type', 'size' and 'id_offset' elements can be retrieved from a NcmContentInfo object. -/// If the NCA holds a populated Rights ID field, and if the Ticket object pointed to by 'tik' hasn't been filled, the ticket and titlekey will be retrieved. -/// 'hfs_partition_type' is only necessary if 'storage_id' == NcmStorageId_GameCard. -bool ncaProcessContent(NcaContext *out, Ticket *tik, u8 storage_id, NcmContentStorage *ncm_storage, const NcmContentId *id, const u8 *hash, u8 type, const u8 *size, u8 id_offset, u8 hfs_partition_type); +/// Reads decrypted data from a NCA FS section using an input NCA FS section context. +/// Input offset must be relative to the start of the NCA FS section. +bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset); -static inline void ncaConvertNcmContentSizeToU64(const u8 *size, u64 *out) -{ - if (!size || !out) return; - *out = 0; - memcpy(out, size, 6); -} - -static inline void ncaConvertU64ToNcmContentSize(const u64 *size, u8 *out) -{ - if (!size || !out) return; - memcpy(out, size, 6); -} - - - -static inline u8 ncaGetKeyGenerationValue(NcaContext *ctx) -{ - if (!ctx) return 0; - return (ctx->header.key_generation > ctx->header.key_generation_old ? ctx->header.key_generation : ctx->header.key_generation_old); -} - -static inline void ncaSetDownloadDistributionType(NcaContext *ctx) -{ - if (!ctx || ctx->header.distribution_type == NcaDistributionType_Download) return; - ctx->header.distribution_type = NcaDistributionType_Download; - ctx->dirty_header = true; -} - -static inline bool ncaCheckRightsIdAvailability(NcaContext *ctx) -{ - if (!ctx) return false; - - bool rights_id_available = false; - - for(u8 i = 0; i < 0x10; i++) - { - if (ctx->header.rights_id.c[i] != 0) - { - rights_id_available = true; - break; - } - } - - return rights_id_available; -} - -static inline void ncaWipeRightsId(NcaContext *ctx) -{ - if (!ctx) return; - memset(&(ctx->header.rights_id), 0, sizeof(FsRightsId)); - ctx->dirty_header = true; -} - - - - bool ncaDecryptKeyArea(NcaContext *nca_ctx); bool ncaEncryptKeyArea(NcaContext *nca_ctx); @@ -369,6 +319,33 @@ bool ncaEncryptHeader(NcaContext *ctx); +/// Miscellanous functions. +static inline void ncaConvertNcmContentSizeToU64(const u8 *size, u64 *out) +{ + if (!size || !out) return; + *out = 0; + memcpy(out, size, 6); +} + +static inline void ncaConvertU64ToNcmContentSize(const u64 *size, u8 *out) +{ + if (!size || !out) return; + memcpy(out, size, 6); +} + +static inline void ncaSetDownloadDistributionType(NcaContext *ctx) +{ + if (!ctx || ctx->header.distribution_type == NcaDistributionType_Download) return; + ctx->header.distribution_type = NcaDistributionType_Download; + ctx->dirty_header = true; +} + +static inline void ncaWipeRightsId(NcaContext *ctx) +{ + if (!ctx) return; + memset(&(ctx->header.rights_id), 0, sizeof(FsRightsId)); + ctx->dirty_header = true; +} #endif /* __NCA_H__ */ diff --git a/source/utils.c b/source/utils.c index 7bbe9ad..7d68b31 100644 --- a/source/utils.c +++ b/source/utils.c @@ -27,6 +27,7 @@ #include "gamecard.h" #include "services.h" #include "utils.h" +#include "nca.h" #include "fatfs/ff.h" /* Global variables. */ @@ -119,6 +120,8 @@ void utilsOverclockSystem(bool restore) bool utilsInitializeResources(void) { + Result rc = 0; + /* Initialize needed services */ if (!servicesInitialize()) { @@ -133,8 +136,15 @@ bool utilsInitializeResources(void) return false; } + /* Allocate NCA crypto buffer */ + if (!ncaAllocateCryptoBuffer()) + { + LOGFILE("Unable to allocate memory for NCA crypto buffer!"); + return false; + } + /* Initialize gamecard interface */ - Result rc = gamecardInitialize(); + rc = gamecardInitialize(); if (R_FAILED(rc)) { LOGFILE("Failed to initialize gamecard interface!"); @@ -182,6 +192,9 @@ void utilsCloseResources(void) /* Deinitialize gamecard interface */ gamecardExit(); + /* Free NCA crypto buffer */ + ncaFreeCryptoBuffer(); + /* Close initialized services */ servicesClose(); } diff --git a/source/utils.h b/source/utils.h index 525c1ea..235b946 100644 --- a/source/utils.h +++ b/source/utils.h @@ -29,7 +29,8 @@ #define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0]))) -#define ROUND_UP(x, y) ((x) + (((y) - ((x) % (y))) % (y))) /* Aligns 'x' bytes to a 'y' bytes boundary. */ +#define ROUND_DOWN(x, y) ((x) & ~((y) - 1)) +#define ROUND_UP(x, y) ((x) + (((y) - ((x) % (y))) % (y))) #define BIS_SYSTEM_PARTITION_MOUNT_NAME "sys:"