diff --git a/code_templates/nxdt_rw_poc.c b/code_templates/nxdt_rw_poc.c index cc2d2e9..18109de 100644 --- a/code_templates/nxdt_rw_poc.c +++ b/code_templates/nxdt_rw_poc.c @@ -934,6 +934,7 @@ int main(int argc, char *argv[]) } consoleClear(); + consolePrint(APP_TITLE " v" APP_VERSION " (" GIT_REV "). Built on " BUILD_TIMESTAMP ".\n"); consolePrint("______________________________\n\n"); if (cur_menu->parent) consolePrint("press b to go back\n"); if (g_umsDeviceCount) consolePrint("press x to safely remove all ums devices\n"); diff --git a/include/core/key_sources.h b/include/core/key_sources.h index 43f6948..8b56cb9 100644 --- a/include/core/key_sources.h +++ b/include/core/key_sources.h @@ -20,8 +20,8 @@ * along with this program. If not, see . */ -/* Last updated on: 2023-04-08. */ -/* Current key generation: NcaKeyGeneration_Since1600NUP (16 / 0F). */ +/* Last updated on: 2023-10-11. */ +/* Current key generation: NcaKeyGeneration_Since1700NUP (17 / 10). */ #pragma once @@ -54,6 +54,7 @@ static const u8 g_masterKeyVectorsProd[NcaKeyGeneration_Current][AES_128_KEY_SIZ { 0x83, 0x67, 0xAF, 0x01, 0xCF, 0x93, 0xA1, 0xAB, 0x80, 0x45, 0xF7, 0x3F, 0x72, 0xFD, 0x3B, 0x38 }, ///< Master key 0C encrypted with master key 0D. { 0xB1, 0x81, 0xA6, 0x0D, 0x72, 0xC7, 0xEE, 0x15, 0x21, 0xF3, 0xC0, 0xB5, 0x6B, 0x61, 0x6D, 0xE7 }, ///< Master key 0D encrypted with master key 0E. { 0xAF, 0x11, 0x4C, 0x67, 0x17, 0x7A, 0x52, 0x43, 0xF7, 0x70, 0x2F, 0xC7, 0xEF, 0x81, 0x72, 0x16 }, ///< Master key 0E encrypted with master key 0F. + { 0x25, 0x12, 0x8B, 0xCB, 0xB5, 0x46, 0xA1, 0xF8, 0xE0, 0x52, 0x15, 0xB7, 0x0B, 0x57, 0x00, 0xBD }, ///< Master key 0F encrypted with master key 10. }; /* Used to derive all previous master keys using the latest master key on development units. */ @@ -75,24 +76,25 @@ static const u8 g_masterKeyVectorsDev[NcaKeyGeneration_Current][AES_128_KEY_SIZE { 0x08, 0xE0, 0xF4, 0xBE, 0xAA, 0x6E, 0x5A, 0xC3, 0xA6, 0xBC, 0xFE, 0xB9, 0xE2, 0xA3, 0x24, 0x12 }, ///< Master key 0C encrypted with master key 0D. { 0xD6, 0x80, 0x98, 0xC0, 0xFA, 0xC7, 0x13, 0xCB, 0x93, 0xD2, 0x0B, 0x82, 0x4C, 0xA1, 0x7B, 0x8D }, ///< Master key 0D encrypted with master key 0E. { 0x78, 0x66, 0x19, 0xBD, 0x86, 0xE7, 0xC1, 0x09, 0x9B, 0x6F, 0x92, 0xB2, 0x58, 0x7D, 0xCF, 0x26 }, ///< Master key 0E encrypted with master key 0F. + { 0x39, 0x1E, 0x7E, 0xF8, 0x7E, 0x73, 0xEA, 0x6F, 0xAF, 0x00, 0x3A, 0xB4, 0xAA, 0xB8, 0xB7, 0x59 }, ///< Master key 0F encrypted with master key 10. }; /* Used to derive a master KEK using the TSEC root key on Erista units. */ /* TODO: update on master key changes. */ static const u8 g_eristaMasterKekSource[AES_128_KEY_SIZE] = { - 0x99, 0x22, 0x09, 0x57, 0xA7, 0xF9, 0x5E, 0x94, 0xFE, 0x78, 0x7F, 0x41, 0xD6, 0xE7, 0x56, 0xE6 + 0x71, 0xB9, 0xA6, 0xC0, 0xFF, 0x97, 0x6B, 0x0C, 0xB4, 0x40, 0xB9, 0xD5, 0x81, 0x5D, 0x81, 0x90 }; /* Used to derive a master KEK on retail Mariko units. */ /* TODO: update on master key changes. */ static const u8 g_marikoMasterKekSourceProd[AES_128_KEY_SIZE] = { - 0xA5, 0xEC, 0x16, 0x39, 0x1A, 0x30, 0x16, 0x08, 0x2E, 0xCF, 0x09, 0x6F, 0x5E, 0x7C, 0xEE, 0xA9 + 0x8D, 0xEE, 0x9E, 0x11, 0x36, 0x3A, 0x9B, 0x0A, 0x6A, 0xC7, 0xBB, 0xE9, 0xD1, 0x03, 0xF7, 0x80 }; /* Used to derive a master KEK on development Mariko units. */ /* TODO: update on master key changes. */ static const u8 g_marikoMasterKekSourceDev[AES_128_KEY_SIZE] = { - 0x3A, 0x9C, 0xF0, 0x39, 0x70, 0x23, 0xF6, 0xAF, 0x71, 0x44, 0x60, 0xF4, 0x6D, 0xED, 0xA1, 0xD6 + 0x43, 0xDB, 0x9D, 0x88, 0xDB, 0x38, 0xE9, 0xBF, 0x3D, 0xD7, 0x83, 0x39, 0xEF, 0xB1, 0x4F, 0xA7 }; /* Used to derive master keys from master KEKs. Found in TrustZone / Secure Monitor. */ diff --git a/include/core/nca.h b/include/core/nca.h index 4d070dc..eb02434 100644 --- a/include/core/nca.h +++ b/include/core/nca.h @@ -94,8 +94,9 @@ typedef enum { NcaKeyGeneration_Since1300NUP = 13, ///< 13.0.0 - 13.2.1. NcaKeyGeneration_Since1400NUP = 14, ///< 14.0.0 - 14.1.2. NcaKeyGeneration_Since1500NUP = 15, ///< 15.0.0 - 15.0.1. - NcaKeyGeneration_Since1600NUP = 16, ///< 16.0.0 - 16.0.3. - NcaKeyGeneration_Current = NcaKeyGeneration_Since1600NUP, + NcaKeyGeneration_Since1600NUP = 16, ///< 16.0.0 - 16.1.0. + NcaKeyGeneration_Since1700NUP = 17, ///< 17.0.0. + NcaKeyGeneration_Current = NcaKeyGeneration_Since1700NUP, NcaKeyGeneration_Max = 32 } NcaKeyGeneration; @@ -110,7 +111,7 @@ typedef enum { /// TODO: update on signature keygen changes. typedef enum { NcaSignatureKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 8.1.1. - NcaSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0 - 16.0.3. + NcaSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0 - 17.0.0. NcaSignatureKeyGeneration_Current = NcaSignatureKeyGeneration_Since900NUP, NcaSignatureKeyGeneration_Max = (NcaSignatureKeyGeneration_Current + 1) } NcaSignatureKeyGeneration; diff --git a/include/core/npdm.h b/include/core/npdm.h index 7668560..2ea0bb6 100644 --- a/include/core/npdm.h +++ b/include/core/npdm.h @@ -43,7 +43,7 @@ extern "C" { /// TODO: update on signature keygen changes. typedef enum { NpdmSignatureKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 8.1.1. - NpdmSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0 - 16.0.3. + NpdmSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0 - 17.0.0. NpdmSignatureKeyGeneration_Current = NpdmSignatureKeyGeneration_Since900NUP, NpdmSignatureKeyGeneration_Max = (NpdmSignatureKeyGeneration_Current + 1) } NpdmSignatureKeyGeneration; diff --git a/source/core/nca_storage.c b/source/core/nca_storage.c index b0b0075..1369a9c 100644 --- a/source/core/nca_storage.c +++ b/source/core/nca_storage.c @@ -25,8 +25,8 @@ /* Function prototypes. */ static bool ncaStorageInitializeBucketTreeContext(BucketTreeContext **out, NcaFsSectionContext *nca_fs_ctx, u8 storage_type); -static bool ncaStorageInitializeCompressedStorageBucketTreeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx); static bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaFsSectionContext *patch_nca_fs_ctx, NcaStorageContext *base_ctx); +static bool ncaStorageInitializeCompressedStorageBucketTreeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx); bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx, NcaStorageContext *base_ctx) { @@ -261,6 +261,52 @@ end: return success; } +static bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaFsSectionContext *patch_nca_fs_ctx, NcaStorageContext *base_ctx) +{ + NcaContext *patch_nca_ctx = NULL, *base_nca_ctx = NULL; + + if (!patch_ctx || !patch_ctx->indirect_storage || !patch_ctx->aes_ctr_ex_storage || !patch_nca_fs_ctx || !ncaStorageIsValidContext(base_ctx) || \ + !(patch_nca_ctx = patch_nca_fs_ctx->nca_ctx) || !(base_nca_ctx = base_ctx->nca_fs_ctx->nca_ctx) || \ + patch_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || base_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \ + patch_nca_ctx->header.program_id != base_nca_ctx->header.program_id || patch_nca_ctx->header.content_type != base_nca_ctx->header.content_type || \ + patch_nca_ctx->id_offset != base_nca_ctx->id_offset || patch_nca_ctx->title_version.value < base_nca_ctx->title_version.value) + { + LOG_MSG_ERROR("Invalid parameters!"); + return false; + } + + bool success = false; + + /* Set original substorage. */ + switch(base_ctx->base_storage_type) + { + case NcaStorageBaseStorageType_Regular: + case NcaStorageBaseStorageType_Compressed: + /* Regular: we just make the Patch's Indirect Storage's SubStorage #0 point to the Base NCA FS section as-is and call it a day. */ + + /* Compressed: if a Compressed Storage is available in the Base NCA FS section, the corresponding Patch NCA FS section *must* also have one. */ + /* This is because the Patch's Compressed Storage also takes care of LZ4-compressed chunks within Base NCA FS section areas. */ + /* Furthermore, the Patch's Indirect Storage already provides section-relative physical offsets for the Base NCA FS section. */ + /* In other words, we don't need to parse the Base NCA's Compressed Storage on every read. */ + success = bktrSetRegularSubStorage(patch_ctx->indirect_storage, base_ctx->nca_fs_ctx); + break; + case NcaStorageBaseStorageType_Sparse: + /* Sparse: we should *always* arrive here if a Sparse Storage is available in the Base NCA FS section, regardless if a Compressed Storage is available or not. */ + /* This is because compression bucket trees are non-existent in Base NCA FS sections that have both Sparse and Compressed Storages. */ + /* Furthermore, in these cases, the compression BucketInfo from the NCA FS section header references the full, patched FS section, so we can't really use it. */ + /* We just completely ignore the Base's Compressed Storage and let the Patch's Compressed Storage take care of LZ4-compressed chunks. */ + /* Anyway, we just make the Patch's Indirect Storage's SubStorage #0 point to the Base's Sparse Storage and call it a day. */ + success = bktrSetBucketTreeSubStorage(patch_ctx->indirect_storage, base_ctx->sparse_storage, 0); + break; + default: + break; + } + + if (!success) LOG_MSG_ERROR("Failed to set base storage to patch storage! (0x%02X, 0x%02X).", base_ctx->base_storage_type, patch_ctx->base_storage_type); + + return success; +} + static bool ncaStorageInitializeCompressedStorageBucketTreeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx) { if (!out || out->base_storage_type < NcaStorageBaseStorageType_Regular || out->base_storage_type > NcaStorageBaseStorageType_Indirect || !nca_fs_ctx || \ @@ -322,49 +368,3 @@ end: return success; } - -static bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaFsSectionContext *patch_nca_fs_ctx, NcaStorageContext *base_ctx) -{ - NcaContext *patch_nca_ctx = NULL, *base_nca_ctx = NULL; - - if (!patch_ctx || !patch_ctx->indirect_storage || !patch_ctx->aes_ctr_ex_storage || !patch_nca_fs_ctx || !ncaStorageIsValidContext(base_ctx) || \ - !(patch_nca_ctx = patch_nca_fs_ctx->nca_ctx) || !(base_nca_ctx = base_ctx->nca_fs_ctx->nca_ctx) || \ - patch_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || base_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \ - patch_nca_ctx->header.program_id != base_nca_ctx->header.program_id || patch_nca_ctx->header.content_type != base_nca_ctx->header.content_type || \ - patch_nca_ctx->id_offset != base_nca_ctx->id_offset || patch_nca_ctx->title_version.value < base_nca_ctx->title_version.value) - { - LOG_MSG_ERROR("Invalid parameters!"); - return false; - } - - bool success = false; - - /* Set original substorage. */ - switch(base_ctx->base_storage_type) - { - case NcaStorageBaseStorageType_Regular: - case NcaStorageBaseStorageType_Compressed: - /* Regular: we just make the Patch's Indirect Storage's SubStorage #0 point to the Base NCA FS section as-is and call it a day. */ - - /* Compressed: if a Compressed Storage is available in the Base NCA FS section, the corresponding Patch NCA FS section *must* also have one. */ - /* This is because the Patch's Compressed Storage also takes care of LZ4-compressed chunks within Base NCA FS section areas. */ - /* Furthermore, the Patch's Indirect Storage already provides section-relative physical offsets for the Base NCA FS section. */ - /* In other words, we don't need to parse the Base NCA's Compressed Storage on every read. */ - success = bktrSetRegularSubStorage(patch_ctx->indirect_storage, base_ctx->nca_fs_ctx); - break; - case NcaStorageBaseStorageType_Sparse: - /* Sparse: we should *always* arrive here if a Sparse Storage is available in the Base NCA FS section, regardless if a Compressed Storage is available or not. */ - /* This is because compression bucket trees are non-existent in Base NCA FS sections that have both Sparse and Compressed Storages. */ - /* Furthermore, in these cases, the compression BucketInfo from the NCA FS section header references the full, patched FS section, so we can't really use it. */ - /* We just completely ignore the Base's Compressed Storage and let the Patch's Compressed Storage take care of LZ4-compressed chunks. */ - /* Anyway, we just make the Patch's Indirect Storage's SubStorage #0 point to the Base's Sparse Storage and call it a day. */ - success = bktrSetBucketTreeSubStorage(patch_ctx->indirect_storage, base_ctx->sparse_storage, 0); - break; - default: - break; - } - - if (!success) LOG_MSG_ERROR("Failed to set base storage to patch storage! (0x%02X, 0x%02X).", base_ctx->base_storage_type, patch_ctx->base_storage_type); - - return success; -}