From 8ce253d13d6404a826a7af33fb37cb0535eaab30 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Fri, 24 Jun 2022 19:39:15 +0200 Subject: [PATCH] More NCA changes. --- include/core/nca.h | 59 +++++++++++++++++++++++++++-------------- include/core/npdm.h | 4 +-- source/core/bktr.c | 8 +++--- source/core/nca.c | 64 ++++++++++++++++++++------------------------- 4 files changed, 74 insertions(+), 61 deletions(-) diff --git a/include/core/nca.h b/include/core/nca.h index 4eedf38..7375025 100644 --- a/include/core/nca.h +++ b/include/core/nca.h @@ -37,7 +37,11 @@ extern "C" { #define NCA_NCA2_MAGIC 0x4E434132 /* "NCA2". */ #define NCA_NCA3_MAGIC 0x4E434133 /* "NCA3". */ -#define NCA_USED_KEY_AREA_SIZE sizeof(NcaDecryptedKeyArea) /* Four keys, 0x40 bytes. */ +#define NCA_KEY_AREA_KEY_COUNT 0x10 +#define NCA_KEY_AREA_SIZE (NCA_KEY_AREA_KEY_COUNT * AES_128_KEY_SIZE) + +#define NCA_KEY_AREA_USED_KEY_COUNT 3 +#define NCA_KEY_AREA_USED_SIZE (NCA_KEY_AREA_USED_KEY_COUNT * AES_128_KEY_SIZE) #define NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT 5 @@ -121,18 +125,22 @@ typedef struct { NXDT_ASSERT(NcaFsHeaderHash, 0x20); /// Encrypted NCA key area used to hold NCA FS section encryption keys. Zeroed out if the NCA uses titlekey crypto. -/// Only the first 4 key entries are encrypted. /// If a particular key entry is unused, it is zeroed out before this area is encrypted. typedef struct { - u8 aes_xts_1[AES_128_KEY_SIZE]; ///< AES-128-XTS key 0 used for NCA FS sections with NcaEncryptionType_AesXts crypto. - u8 aes_xts_2[AES_128_KEY_SIZE]; ///< AES-128-XTS key 1 used for NCA FS sections with NcaEncryptionType_AesXts crypto. - u8 aes_ctr[AES_128_KEY_SIZE]; ///< AES-128-CTR key used for NCA FS sections with NcaEncryptionType_AesCtr crypto. - u8 aes_ctr_ex[AES_128_KEY_SIZE]; ///< AES-128-CTR key used for NCA FS sections with NcaEncryptionType_AesCtrEx crypto. - u8 aes_ctr_hw[AES_128_KEY_SIZE]; ///< Unused AES-128-CTR key. - u8 reserved[0xB0]; + union { + u8 keys[NCA_KEY_AREA_KEY_COUNT][AES_128_KEY_SIZE]; + struct { + u8 aes_xts_1[AES_128_KEY_SIZE]; ///< AES-128-XTS key 0 used for NCA FS sections with NcaEncryptionType_AesXts crypto. + u8 aes_xts_2[AES_128_KEY_SIZE]; ///< AES-128-XTS key 1 used for NCA FS sections with NcaEncryptionType_AesXts crypto. + u8 aes_ctr[AES_128_KEY_SIZE]; ///< AES-128-CTR key used for NCA FS sections with NcaEncryptionType_AesCtr* and NcaEncryptionType_AesCtrEx* crypto. + u8 aes_ctr_ex[AES_128_KEY_SIZE]; ///< Unused AES-128-CTR key. + u8 aes_ctr_hw[AES_128_KEY_SIZE]; ///< Unused AES-128-CTR key. + u8 reserved[0xB0]; + }; + }; } NcaEncryptedKeyArea; -NXDT_ASSERT(NcaEncryptedKeyArea, 0x100); +NXDT_ASSERT(NcaEncryptedKeyArea, NCA_KEY_AREA_SIZE); /// First 0x400 bytes from every NCA. typedef struct { @@ -183,6 +191,12 @@ typedef enum { NcaEncryptionType_AesCtrExSkipLayerHash = 6 } NcaEncryptionType; +typedef enum { + NcaMetaDataHashType_None = 0, + NcaMetaDataHashType_HierarchicalIntegrity = 1, + NcaMetaDataHashType_HierarchicalIntegritySha3 = 2 +} NcaMetaDataHashType; + typedef struct { u64 offset; u64 size; @@ -270,7 +284,7 @@ typedef struct { NXDT_ASSERT(NcaBucketInfo, 0x20); -/// Only used for NcaEncryptionType_AesCtrEx (PatchRomFs). +/// Only used for NcaEncryptionType_AesCtrEx and NcaEncryptionType_AesCtrExSkipLayerHash (PatchRomFs). typedef struct { NcaBucketInfo indirect_bucket; NcaBucketInfo aes_ctr_ex_bucket; @@ -320,16 +334,17 @@ NXDT_ASSERT(NcaMetaDataHashDataInfo, 0x30); /// NCA0 place the FS headers at the start sector from the NcaFsInfo entries. typedef struct { u16 version; - u8 fs_type; ///< NcaFsType. - u8 hash_type; ///< NcaHashType. - u8 encryption_type; ///< NcaEncryptionType. - u8 reserved_1[0x3]; + u8 fs_type; ///< NcaFsType. + u8 hash_type; ///< NcaHashType. + u8 encryption_type; ///< NcaEncryptionType. + u8 metadata_hash_type; ///< NcaMetaDataHashType. + u8 reserved_1[0x2]; NcaHashData hash_data; NcaPatchInfo patch_info; NcaAesCtrUpperIv aes_ctr_upper_iv; NcaSparseInfo sparse_info; NcaCompressionInfo compression_info; - NcaMetaDataHashDataInfo hash_data_info; + NcaMetaDataHashDataInfo metadata_hash_info; u8 reserved_2[0x30]; } NcaFsHeader; @@ -378,13 +393,17 @@ typedef enum { } NcaVersion; typedef struct { - u8 aes_xts_1[AES_128_KEY_SIZE]; ///< AES-128-XTS key 0 used for NCA FS sections with NcaEncryptionType_AesXts crypto. - u8 aes_xts_2[AES_128_KEY_SIZE]; ///< AES-128-XTS key 1 used for NCA FS sections with NcaEncryptionType_AesXts crypto. - u8 aes_ctr[AES_128_KEY_SIZE]; ///< AES-128-CTR key used for NCA FS sections with NcaEncryptionType_AesCtr crypto. - u8 aes_ctr_ex[AES_128_KEY_SIZE]; ///< AES-128-CTR key used for NCA FS sections with NcaEncryptionType_AesCtrEx crypto. + union { + u8 keys[NCA_KEY_AREA_USED_KEY_COUNT][AES_128_KEY_SIZE]; + struct { + u8 aes_xts_1[AES_128_KEY_SIZE]; ///< AES-128-XTS key 0 used for NCA FS sections with NcaEncryptionType_AesXts crypto. + u8 aes_xts_2[AES_128_KEY_SIZE]; ///< AES-128-XTS key 1 used for NCA FS sections with NcaEncryptionType_AesXts crypto. + u8 aes_ctr[AES_128_KEY_SIZE]; ///< AES-128-CTR key used for NCA FS sections with NcaEncryptionType_AesCtr and NcaEncryptionType_AesCtrSkipLayerHash crypto. + }; + }; } NcaDecryptedKeyArea; -NXDT_ASSERT(NcaDecryptedKeyArea, 0x40); +NXDT_ASSERT(NcaDecryptedKeyArea, NCA_KEY_AREA_USED_SIZE); typedef struct { u8 storage_id; ///< NcmStorageId. diff --git a/include/core/npdm.h b/include/core/npdm.h index 0396fcc..5227d2b 100644 --- a/include/core/npdm.h +++ b/include/core/npdm.h @@ -103,8 +103,8 @@ typedef enum { typedef struct { u32 production : 1; u32 unqualified_approval : 1; - u32 memory_region : 5; ///< NpdmMemoryRegion. - u32 reserved : 25; + u32 memory_region : 4; ///< NpdmMemoryRegion. + u32 reserved : 26; } NpdmAcidFlags; NXDT_ASSERT(NpdmAcidFlags, 0x4); diff --git a/source/core/bktr.c b/source/core/bktr.c index 4d97da3..10ae7f2 100644 --- a/source/core/bktr.c +++ b/source/core/bktr.c @@ -40,8 +40,9 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct if (!out || !base_nca_fs_ctx || !(base_nca_ctx = (NcaContext*)base_nca_fs_ctx->nca_ctx) || \ !update_nca_fs_ctx || !update_nca_fs_ctx->enabled || !(update_nca_ctx = (NcaContext*)update_nca_fs_ctx->nca_ctx) || \ - update_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || update_nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx || \ - base_nca_ctx->header.program_id != update_nca_ctx->header.program_id || base_nca_ctx->header.content_type != update_nca_ctx->header.content_type || \ + update_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || (update_nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx && \ + update_nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrExSkipLayerHash) || base_nca_ctx->header.program_id != update_nca_ctx->header.program_id || \ + base_nca_ctx->header.content_type != update_nca_ctx->header.content_type || \ __builtin_bswap32(update_nca_fs_ctx->header.patch_info.indirect_bucket.header.magic) != NCA_BKTR_MAGIC || \ update_nca_fs_ctx->header.patch_info.indirect_bucket.header.version != NCA_BKTR_VERSION || \ __builtin_bswap32(update_nca_fs_ctx->header.patch_info.aes_ctr_ex_bucket.header.magic) != NCA_BKTR_MAGIC || \ @@ -58,7 +59,8 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct bktrFreeContext(out); /* Update missing base NCA RomFS status. */ - out->missing_base_romfs = (!base_nca_fs_ctx->enabled || base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs || base_nca_fs_ctx->encryption_type == NcaEncryptionType_AesCtrEx); + out->missing_base_romfs = (!base_nca_fs_ctx->enabled || base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \ + (base_nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtr && base_nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrSkipLayerHash)); /* Initialize base NCA RomFS context. */ if (!out->missing_base_romfs && !romfsInitializeContext(&(out->base_romfs_ctx), base_nca_fs_ctx)) diff --git a/source/core/nca.c b/source/core/nca.c index 5340952..d7ef3e5 100644 --- a/source/core/nca.c +++ b/source/core/nca.c @@ -278,7 +278,7 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, /* Initialize crypto data. */ if ((!out->rights_id_available || (out->rights_id_available && out->titlekey_retrieved)) && fs_ctx->encryption_type > NcaEncryptionType_None && \ - fs_ctx->encryption_type <= NcaEncryptionType_AesCtrEx) + fs_ctx->encryption_type <= NcaEncryptionType_AesCtrExSkipLayerHash) { /* Initialize the partial AES counter for this section. */ aes128CtrInitializePartialCtr(fs_ctx->ctr, fs_ctx->header.aes_ctr_upper_iv.value, fs_ctx->section_offset); @@ -306,18 +306,12 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, aes128XtsContextCreate(&(fs_ctx->xts_decrypt_ctx), out->decrypted_key_area.aes_xts_1, out->decrypted_key_area.aes_xts_2, false); aes128XtsContextCreate(&(fs_ctx->xts_encrypt_ctx), out->decrypted_key_area.aes_xts_1, out->decrypted_key_area.aes_xts_2, true); } else - if (fs_ctx->encryption_type == NcaEncryptionType_AesCtr || fs_ctx->encryption_type == NcaEncryptionType_AesCtrEx) + if (fs_ctx->encryption_type >= NcaEncryptionType_AesCtr && fs_ctx->encryption_type <= NcaEncryptionType_AesCtrExSkipLayerHash) { /* Patch RomFS sections also use the AES-128-CTR key from the decrypted NCA key area, for some reason. */ aes128CtrContextCreate(&(fs_ctx->ctr_ctx), out->decrypted_key_area.aes_ctr, fs_ctx->ctr); if (fs_ctx->has_sparse_layer) aes128CtrContextCreate(&(fs_ctx->sparse_ctr_ctx), out->decrypted_key_area.aes_ctr, fs_ctx->sparse_ctr); - } /***else - if (fs_ctx->encryption_type == NcaEncryptionType_AesCtr) - { - aes128CtrContextCreate(&(fs_ctx->ctr_ctx), out->decrypted_key_area.aes_ctr, fs_ctx->ctr); - } else { - aes128CtrContextCreate(&(fs_ctx->ctr_ctx), out->decrypted_key_area.aes_ctr_ex, fs_ctx->ctr); - }***/ + } } } @@ -455,15 +449,6 @@ bool ncaRemoveTitleKeyCrypto(NcaContext *ctx) /* Patch RomFS sections also use the AES-128-CTR key from the decrypted NCA key area, for some reason. */ memcpy(ctx->decrypted_key_area.aes_ctr, ctx->titlekey, AES_128_KEY_SIZE); - /***for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) - { - NcaFsSectionContext *fs_ctx = &(ctx->fs_ctx[i]); - if (!fs_ctx->enabled || (fs_ctx->encryption_type != NcaEncryptionType_AesCtr && fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx)) continue; - - u8 *key_ptr = (fs_ctx->encryption_type == NcaEncryptionType_AesCtr ? ctx->decrypted_key_area.aes_ctr : ctx->decrypted_key_area.aes_ctr_ex); - memcpy(key_ptr, ctx->titlekey, AES_128_KEY_SIZE); - }***/ - /* Encrypt NCA key area. */ if (!ncaEncryptKeyArea(ctx)) { @@ -719,23 +704,25 @@ static bool ncaDecryptKeyArea(NcaContext *ctx) } const u8 null_key[AES_128_KEY_SIZE] = {0}; - u8 key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4); + + u8 key_count = NCA_KEY_AREA_USED_KEY_COUNT; + if (ctx->format_version == NcaVersion_Nca0) key_count--; /* Check if we're dealing with a NCA0 with a plaintext key area. */ if (ncaIsVersion0KeyAreaEncrypted(ctx)) { - memcpy(&(ctx->decrypted_key_area), &(ctx->header.encrypted_key_area), NCA_USED_KEY_AREA_SIZE); + memcpy(&(ctx->decrypted_key_area), &(ctx->header.encrypted_key_area), sizeof(NcaDecryptedKeyArea)); return true; } /* Clear decrypted key area. */ - memset(&(ctx->decrypted_key_area), 0, NCA_USED_KEY_AREA_SIZE); + memset(&(ctx->decrypted_key_area), 0, sizeof(NcaDecryptedKeyArea)); /* Process key area. */ for(u8 i = 0; i < key_count; i++) { - const u8 *src_key = ((u8*)&(ctx->header.encrypted_key_area) + (i * AES_128_KEY_SIZE)); - u8 *dst_key = ((u8*)&(ctx->decrypted_key_area) + (i * AES_128_KEY_SIZE)); + const u8 *src_key = ctx->header.encrypted_key_area.keys[i]; + u8 *dst_key = ctx->decrypted_key_area.keys[i]; /* Don't proceed if we're dealing with a null key. */ if (!memcmp(src_key, null_key, AES_128_KEY_SIZE)) continue; @@ -759,14 +746,16 @@ static bool ncaEncryptKeyArea(NcaContext *ctx) return false; } - u8 key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4); + u8 key_count = NCA_KEY_AREA_USED_KEY_COUNT; + if (ctx->format_version == NcaVersion_Nca0) key_count--; + const u8 *kaek = NULL, null_key[AES_128_KEY_SIZE] = {0}; Aes128Context key_area_ctx = {0}; /* Check if we're dealing with a NCA0 with a plaintext key area. */ if (ncaIsVersion0KeyAreaEncrypted(ctx)) { - memcpy(&(ctx->header.encrypted_key_area), &(ctx->decrypted_key_area), NCA_USED_KEY_AREA_SIZE); + memcpy(&(ctx->header.encrypted_key_area), &(ctx->decrypted_key_area), sizeof(NcaDecryptedKeyArea)); return true; } @@ -787,8 +776,8 @@ static bool ncaEncryptKeyArea(NcaContext *ctx) /* Process key area. */ for(u8 i = 0; i < key_count; i++) { - const u8 *src_key = ((u8*)&(ctx->decrypted_key_area) + (i * AES_128_KEY_SIZE)); - u8 *dst_key = ((u8*)&(ctx->header.encrypted_key_area) + (i * AES_128_KEY_SIZE)); + const u8 *src_key = ctx->decrypted_key_area.keys[i]; + u8 *dst_key = ctx->header.encrypted_key_area.keys[i]; /* Don't proceed if we're dealing with a null key. */ if (!memcmp(src_key, null_key, AES_128_KEY_SIZE)) continue; @@ -822,7 +811,7 @@ NX_INLINE bool ncaIsVersion0KeyAreaEncrypted(NcaContext *ctx) if (!ctx || ctx->format_version != NcaVersion_Nca0) return false; u8 nca0_key_area_hash[SHA256_HASH_SIZE] = {0}; - sha256CalculateHash(nca0_key_area_hash, &(ctx->header.encrypted_key_area), NCA_USED_KEY_AREA_SIZE); + sha256CalculateHash(nca0_key_area_hash, &(ctx->header.encrypted_key_area), 4 * AES_128_KEY_SIZE); return (memcmp(nca0_key_area_hash, g_nca0KeyAreaHash, SHA256_HASH_SIZE) != 0); } @@ -847,8 +836,8 @@ NX_INLINE bool ncaCheckRightsIdAvailability(NcaContext *ctx) static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset) { if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \ - ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrEx || !out || !read_size || \ - (offset + read_size) > ctx->section_size) + ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrExSkipLayerHash || \ + !out || !read_size || (offset + read_size) > ctx->section_size) { LOG_MSG("Invalid NCA FS section header parameters!"); return false; @@ -875,7 +864,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size /* Optimization for reads from plaintext FS sections or reads that are aligned to the AES-CTR / AES-XTS sector size. */ if (ctx->encryption_type == NcaEncryptionType_None || \ (ctx->encryption_type == NcaEncryptionType_AesXts && !(content_offset % NCA_AES_XTS_SECTOR_SIZE) && !(read_size % NCA_AES_XTS_SECTOR_SIZE)) || \ - ((ctx->encryption_type == NcaEncryptionType_AesCtr || ctx->encryption_type == NcaEncryptionType_AesCtrEx) && !(content_offset % AES_BLOCK_SIZE) && !(read_size % AES_BLOCK_SIZE))) + (ctx->encryption_type >= NcaEncryptionType_AesCtr && ctx->encryption_type <= NcaEncryptionType_AesCtrExSkipLayerHash && !(content_offset % AES_BLOCK_SIZE) && !(read_size % AES_BLOCK_SIZE))) { /* Read data. */ if (!ncaReadContentFile(nca_ctx, out, read_size, content_offset)) @@ -904,7 +893,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size goto end; } } else - if (ctx->encryption_type == NcaEncryptionType_AesCtr || ctx->encryption_type == NcaEncryptionType_AesCtrEx) + if (ctx->encryption_type >= NcaEncryptionType_AesCtr && ctx->encryption_type <= NcaEncryptionType_AesCtrExSkipLayerHash) { aes128CtrUpdatePartialCtr(ctx->ctr, content_offset); aes128CtrContextResetCtr(&(ctx->ctr_ctx), ctx->ctr); @@ -945,7 +934,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size goto end; } } else - if (ctx->encryption_type == NcaEncryptionType_AesCtr || ctx->encryption_type == NcaEncryptionType_AesCtrEx) + if (ctx->encryption_type >= NcaEncryptionType_AesCtr && ctx->encryption_type <= NcaEncryptionType_AesCtrExSkipLayerHash) { aes128CtrUpdatePartialCtr(ctx->ctr, block_start_offset); aes128CtrContextResetCtr(&(ctx->ctr_ctx), ctx->ctr); @@ -964,7 +953,8 @@ end: static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val) { if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \ - ctx->section_type != NcaFsSectionType_PatchRomFs || ctx->encryption_type != NcaEncryptionType_AesCtrEx || !out || !read_size || (offset + read_size) > ctx->section_size) + ctx->section_type != NcaFsSectionType_PatchRomFs || (ctx->encryption_type != NcaEncryptionType_AesCtrEx && ctx->encryption_type != NcaEncryptionType_AesCtrExSkipLayerHash) || \ + !out || !read_size || (offset + read_size) > ctx->section_size) { LOG_MSG("Invalid NCA FS section header parameters!"); return false; @@ -1277,9 +1267,11 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const u8 *out = NULL; bool success = false; + // TODO: add support for Sha3 layers and SkipLayer crypto types. if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || ctx->has_sparse_layer || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \ - ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type >= NcaEncryptionType_AesCtrEx || !data || !data_size || \ - (data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset) + ctx->hash_type < NcaHashType_HierarchicalSha256 || ctx->hash_type > NcaHashType_HierarchicalIntegrity || ctx->encryption_type == NcaEncryptionType_Auto || \ + ctx->encryption_type >= NcaEncryptionType_AesCtrEx || ctx->section_type >= NcaFsSectionType_Invalid || !data || !data_size || (data_offset + data_size) > ctx->section_size || \ + !out_block_size || !out_block_offset) { LOG_MSG("Invalid NCA FS section header parameters!"); goto end;