From 17dd24bc92497c188c62a9323079cd34d1b8ad44 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Sun, 25 Apr 2021 19:10:34 -0400 Subject: [PATCH] Modified NCA key area handling + restored PoC building. * Avoid performing any crypto operations on null NCA key area entries. * Added commented out code to handle the aes_ctr_ex NCA key area entry, just in case we end up needing it at some point. --- .gitignore | 2 +- build.sh | 46 ++++++++++ code_templates/nsp_dumper_stor.c | 36 ++++++++ code_templates/nsp_dumper_usb.c | 36 ++++++++ code_templates/sd_romfs_dumper.c | 36 ++++++++ code_templates/system_title_dumper.c | 36 ++++++++ code_templates/usb_gc_dumper.c | 36 ++++++++ code_templates/usb_romfs_dumper.c | 36 ++++++++ code_templates/xml_generator.c | 36 ++++++++ include/core/aes.h | 6 +- {source => include}/custom_layout_tab.hpp | 0 {source => include}/sample_installer_page.hpp | 0 {source => include}/sample_loading_page.hpp | 0 source/core/nca.c | 92 ++++++++++++------- 14 files changed, 363 insertions(+), 35 deletions(-) create mode 100644 build.sh rename {source => include}/custom_layout_tab.hpp (100%) rename {source => include}/sample_installer_page.hpp (100%) rename {source => include}/sample_loading_page.hpp (100%) diff --git a/.gitignore b/.gitignore index 46a00b4..36d47d6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,5 @@ host/nxdumptool *.log *.spec *.exe +main.cpp /code_templates/tmp/* - diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..7f67f60 --- /dev/null +++ b/build.sh @@ -0,0 +1,46 @@ +#!/bin/bash +cd "$(dirname "${BASH_SOURCE[0]}")" + +tar_filename="nxdumptool-rewrite_poc_$(git rev-parse --short HEAD).tar.bz2" + +rm -f ./*.tar.bz2 + +rm -rf ./code_templates/tmp +mkdir ./code_templates/tmp + +mv ./source/main.cpp ./main.cpp + +make clean_all + +for f in ./code_templates/*.c; do + basename="$(basename "$f")" + filename="${basename%.*}" + + if [[ $filename == "dump_title_infos" ]]; then + continue + fi + + echo $filename + + rm -f ./source/main.c + cp $f ./source/main.c + + make clean + make -j$(nproc) + + mkdir ./code_templates/tmp/$filename + cp ./nxdumptool.nro ./code_templates/tmp/$filename/nxdumptool.nro + #cp ./nxdumptool.elf ./code_templates/tmp/$filename/nxdumptool.elf +done + +make clean_all + +cd ./code_templates/tmp +tar -cjf ../../$tar_filename * + +cd ../.. +rm -f ./source/main.c +rm -rf ./code_templates/tmp +mv ./main.cpp ./source/main.cpp + +read -p "Press any key to finish ..." diff --git a/code_templates/nsp_dumper_stor.c b/code_templates/nsp_dumper_stor.c index 4339412..404e7e4 100644 --- a/code_templates/nsp_dumper_stor.c +++ b/code_templates/nsp_dumper_stor.c @@ -36,6 +36,8 @@ int g_argc = 0; char **g_argv = NULL; const char *g_appLaunchPath = NULL; +static PadState g_padState = {0}; + static const char *dump_type_strings[] = { "dump base application", "dump update", @@ -65,6 +67,34 @@ static const u32 options_count = MAX_ELEMENTS(options); static UsbHsFsDevice *ums_devices = NULL; static u32 ums_device_count = 0; +static void utilsScanPads(void) +{ + padUpdate(&g_padState); +} + +static u64 utilsGetButtonsDown(void) +{ + return padGetButtonsDown(&g_padState); +} + +static u64 utilsGetButtonsHeld(void) +{ + return padGetButtons(&g_padState); +} + +static void utilsWaitForButtonPress(u64 flag) +{ + /* Don't consider stick movement as button inputs. */ + if (!flag) flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \ + HidNpadButton_StickRUp | HidNpadButton_StickRDown); + + while(appletMainLoop()) + { + utilsScanPads(); + if (utilsGetButtonsDown() & flag) break; + } +} + static void consolePrint(const char *text, ...) { va_list v; @@ -764,6 +794,12 @@ int main(int argc, char *argv[]) goto out; } + /* Configure input. */ + /* Up to 8 different, full controller inputs. */ + /* Individual Joy-Cons not supported. */ + padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl); + padInitializeWithMask(&g_padState, 0x1000000FFUL); + consoleInit(NULL); u32 app_count = 0; diff --git a/code_templates/nsp_dumper_usb.c b/code_templates/nsp_dumper_usb.c index a38a7fb..69937ef 100644 --- a/code_templates/nsp_dumper_usb.c +++ b/code_templates/nsp_dumper_usb.c @@ -35,6 +35,8 @@ int g_argc = 0; char **g_argv = NULL; const char *g_appLaunchPath = NULL; +static PadState g_padState = {0}; + typedef struct { void *data; @@ -71,6 +73,34 @@ static const u32 options_count = MAX_ELEMENTS(options); static Mutex g_conMutex = 0; +static void utilsScanPads(void) +{ + padUpdate(&g_padState); +} + +static u64 utilsGetButtonsDown(void) +{ + return padGetButtonsDown(&g_padState); +} + +static u64 utilsGetButtonsHeld(void) +{ + return padGetButtons(&g_padState); +} + +static void utilsWaitForButtonPress(u64 flag) +{ + /* Don't consider stick movement as button inputs. */ + if (!flag) flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \ + HidNpadButton_StickRUp | HidNpadButton_StickRDown); + + while(appletMainLoop()) + { + utilsScanPads(); + if (utilsGetButtonsDown() & flag) break; + } +} + static void consolePrint(const char *text, ...) { mutexLock(&g_conMutex); @@ -922,6 +952,12 @@ int main(int argc, char *argv[]) goto out; } + /* Configure input. */ + /* Up to 8 different, full controller inputs. */ + /* Individual Joy-Cons not supported. */ + padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl); + padInitializeWithMask(&g_padState, 0x1000000FFUL); + consoleInit(NULL); u32 app_count = 0; diff --git a/code_templates/sd_romfs_dumper.c b/code_templates/sd_romfs_dumper.c index 1eb80de..47cfc93 100644 --- a/code_templates/sd_romfs_dumper.c +++ b/code_templates/sd_romfs_dumper.c @@ -30,6 +30,8 @@ int g_argc = 0; char **g_argv = NULL; const char *g_appLaunchPath = NULL; +static PadState g_padState = {0}; + static Mutex g_fileMutex = 0; static CondVar g_readCondvar = 0, g_writeCondvar = 0; @@ -47,6 +49,34 @@ typedef struct bool transfer_cancelled; } ThreadSharedData; +static void utilsScanPads(void) +{ + padUpdate(&g_padState); +} + +static u64 utilsGetButtonsDown(void) +{ + return padGetButtonsDown(&g_padState); +} + +static u64 utilsGetButtonsHeld(void) +{ + return padGetButtons(&g_padState); +} + +static void utilsWaitForButtonPress(u64 flag) +{ + /* Don't consider stick movement as button inputs. */ + if (!flag) flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \ + HidNpadButton_StickRUp | HidNpadButton_StickRDown); + + while(appletMainLoop()) + { + utilsScanPads(); + if (utilsGetButtonsDown() & flag) break; + } +} + static void consolePrint(const char *text, ...) { va_list v; @@ -339,6 +369,12 @@ int main(int argc, char *argv[]) goto out; } + /* Configure input. */ + /* Up to 8 different, full controller inputs. */ + /* Individual Joy-Cons not supported. */ + padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl); + padInitializeWithMask(&g_padState, 0x1000000FFUL); + consoleInit(NULL); u32 app_count = 0; diff --git a/code_templates/system_title_dumper.c b/code_templates/system_title_dumper.c index d3d4e75..69cd6e9 100644 --- a/code_templates/system_title_dumper.c +++ b/code_templates/system_title_dumper.c @@ -32,10 +32,40 @@ int g_argc = 0; char **g_argv = NULL; const char *g_appLaunchPath = NULL; +static PadState g_padState = {0}; + static u8 *buf = NULL; static FILE *filefd = NULL; static char path[FS_MAX_PATH * 2] = {0}; +static void utilsScanPads(void) +{ + padUpdate(&g_padState); +} + +static u64 utilsGetButtonsDown(void) +{ + return padGetButtonsDown(&g_padState); +} + +static u64 utilsGetButtonsHeld(void) +{ + return padGetButtons(&g_padState); +} + +static void utilsWaitForButtonPress(u64 flag) +{ + /* Don't consider stick movement as button inputs. */ + if (!flag) flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \ + HidNpadButton_StickRUp | HidNpadButton_StickRDown); + + while(appletMainLoop()) + { + utilsScanPads(); + if (utilsGetButtonsDown() & flag) break; + } +} + static void consolePrint(const char *text, ...) { va_list v; @@ -236,6 +266,12 @@ int main(int argc, char *argv[]) goto out; } + /* Configure input. */ + /* Up to 8 different, full controller inputs. */ + /* Individual Joy-Cons not supported. */ + padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl); + padInitializeWithMask(&g_padState, 0x1000000FFUL); + consoleInit(NULL); u32 app_count = 0; diff --git a/code_templates/usb_gc_dumper.c b/code_templates/usb_gc_dumper.c index 55d691f..005b5d9 100644 --- a/code_templates/usb_gc_dumper.c +++ b/code_templates/usb_gc_dumper.c @@ -31,6 +31,8 @@ int g_argc = 0; char **g_argv = NULL; const char *g_appLaunchPath = NULL; +static PadState g_padState = {0}; + /* Type definitions. */ typedef void (*MenuElementOptionFunction)(u32 idx); @@ -183,6 +185,34 @@ static CondVar g_readCondvar = 0, g_writeCondvar = 0; static char path[FS_MAX_PATH] = {0}; +static void utilsScanPads(void) +{ + padUpdate(&g_padState); +} + +static u64 utilsGetButtonsDown(void) +{ + return padGetButtonsDown(&g_padState); +} + +static u64 utilsGetButtonsHeld(void) +{ + return padGetButtons(&g_padState); +} + +static void utilsWaitForButtonPress(u64 flag) +{ + /* Don't consider stick movement as button inputs. */ + if (!flag) flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \ + HidNpadButton_StickRUp | HidNpadButton_StickRDown); + + while(appletMainLoop()) + { + utilsScanPads(); + if (utilsGetButtonsDown() & flag) break; + } +} + int main(int argc, char *argv[]) { g_argc = argc; @@ -199,6 +229,12 @@ int main(int argc, char *argv[]) goto out; } + /* Configure input. */ + /* Up to 8 different, full controller inputs. */ + /* Individual Joy-Cons not supported. */ + padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl); + padInitializeWithMask(&g_padState, 0x1000000FFUL); + consoleInit(NULL); while(appletMainLoop()) diff --git a/code_templates/usb_romfs_dumper.c b/code_templates/usb_romfs_dumper.c index 7b0d8eb..5159d74 100644 --- a/code_templates/usb_romfs_dumper.c +++ b/code_templates/usb_romfs_dumper.c @@ -31,6 +31,8 @@ int g_argc = 0; char **g_argv = NULL; const char *g_appLaunchPath = NULL; +static PadState g_padState = {0}; + static Mutex g_fileMutex = 0; static CondVar g_readCondvar = 0, g_writeCondvar = 0; @@ -48,6 +50,34 @@ typedef struct bool transfer_cancelled; } ThreadSharedData; +static void utilsScanPads(void) +{ + padUpdate(&g_padState); +} + +static u64 utilsGetButtonsDown(void) +{ + return padGetButtonsDown(&g_padState); +} + +static u64 utilsGetButtonsHeld(void) +{ + return padGetButtons(&g_padState); +} + +static void utilsWaitForButtonPress(u64 flag) +{ + /* Don't consider stick movement as button inputs. */ + if (!flag) flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \ + HidNpadButton_StickRUp | HidNpadButton_StickRDown); + + while(appletMainLoop()) + { + utilsScanPads(); + if (utilsGetButtonsDown() & flag) break; + } +} + static void consolePrint(const char *text, ...) { va_list v; @@ -318,6 +348,12 @@ int main(int argc, char *argv[]) goto out; } + /* Configure input. */ + /* Up to 8 different, full controller inputs. */ + /* Individual Joy-Cons not supported. */ + padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl); + padInitializeWithMask(&g_padState, 0x1000000FFUL); + consoleInit(NULL); u32 app_count = 0; diff --git a/code_templates/xml_generator.c b/code_templates/xml_generator.c index 50ba78d..c77b08b 100644 --- a/code_templates/xml_generator.c +++ b/code_templates/xml_generator.c @@ -31,6 +31,36 @@ int g_argc = 0; char **g_argv = NULL; const char *g_appLaunchPath = NULL; +static PadState g_padState = {0}; + +static void utilsScanPads(void) +{ + padUpdate(&g_padState); +} + +static u64 utilsGetButtonsDown(void) +{ + return padGetButtonsDown(&g_padState); +} + +static u64 utilsGetButtonsHeld(void) +{ + return padGetButtons(&g_padState); +} + +static void utilsWaitForButtonPress(u64 flag) +{ + /* Don't consider stick movement as button inputs. */ + if (!flag) flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \ + HidNpadButton_StickRUp | HidNpadButton_StickRDown); + + while(appletMainLoop()) + { + utilsScanPads(); + if (utilsGetButtonsDown() & flag) break; + } +} + static void consolePrint(const char *text, ...) { va_list v; @@ -64,6 +94,12 @@ int main(int argc, char *argv[]) goto out; } + /* Configure input. */ + /* Up to 8 different, full controller inputs. */ + /* Individual Joy-Cons not supported. */ + padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl); + padInitializeWithMask(&g_padState, 0x1000000FFUL); + consoleInit(NULL); u32 app_count = 0; diff --git a/include/core/aes.h b/include/core/aes.h index 488e183..8edc8ff 100644 --- a/include/core/aes.h +++ b/include/core/aes.h @@ -34,7 +34,7 @@ extern "C" { size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt); /// Initializes an output AES partial counter using an initial CTR value and an offset. -/// The size for both 'out' and 'ctr' should be at least AES_BLOCK_SIZE. +/// The sizes for 'out' and 'ctr' should be at least AES_BLOCK_SIZE and 8 bytes, respectively. NX_INLINE void aes128CtrInitializePartialCtr(u8 *out, const u8 *ctr, u64 offset) { if (!out || !ctr) return; @@ -50,7 +50,7 @@ NX_INLINE void aes128CtrInitializePartialCtr(u8 *out, const u8 *ctr, u64 offset) } /// Updates the provided AES partial counter using an offset. -/// 'out' size should be at least AES_BLOCK_SIZE. +/// Size for 'out' should be at least AES_BLOCK_SIZE. NX_INLINE void aes128CtrUpdatePartialCtr(u8 *ctr, u64 offset) { if (!ctr) return; @@ -65,7 +65,7 @@ NX_INLINE void aes128CtrUpdatePartialCtr(u8 *ctr, u64 offset) } /// Updates the provided AES partial counter using an offset and a 32-bit CTR value. -/// 'out' size should be at least AES_BLOCK_SIZE. +/// Size for 'out' should be at least AES_BLOCK_SIZE. NX_INLINE void aes128CtrUpdatePartialCtrEx(u8 *ctr, u32 ctr_val, u64 offset) { if (!ctr) return; diff --git a/source/custom_layout_tab.hpp b/include/custom_layout_tab.hpp similarity index 100% rename from source/custom_layout_tab.hpp rename to include/custom_layout_tab.hpp diff --git a/source/sample_installer_page.hpp b/include/sample_installer_page.hpp similarity index 100% rename from source/sample_installer_page.hpp rename to include/sample_installer_page.hpp diff --git a/source/sample_loading_page.hpp b/include/sample_loading_page.hpp similarity index 100% rename from source/sample_loading_page.hpp rename to include/sample_loading_page.hpp diff --git a/source/core/nca.c b/source/core/nca.c index 1ace1c1..b15bd7e 100644 --- a/source/core/nca.c +++ b/source/core/nca.c @@ -115,10 +115,11 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, if (out->storage_id == NcmStorageId_GameCard) { - /* Retrieve gamecard NCA offset. */ + /* Generate gamecard NCA filename. */ char nca_filename[0x30] = {0}; sprintf(nca_filename, "%s.%s", out->content_id_str, out->content_type == NcmContentType_Meta ? "cnmt.nca" : "nca"); + /* Retrieve gamecard NCA offset. */ if (!gamecardGetHashFileSystemEntryInfoByName(hfs_partition_type, nca_filename, &(out->gamecard_offset), NULL)) { LOG_MSG("Error retrieving offset for \"%s\" entry in secure hash FS partition!", nca_filename); @@ -225,14 +226,21 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, } else { if (fs_ctx->encryption_type == NcaEncryptionType_AesXts) { - /* We need to create two different contexts: one for decryption and another one for encryption. */ + /* We need to create two different contexts with AES-128-XTS: one for decryption and another one for encryption. */ 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) { + /* 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); - } + } /***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); + }***/ } } @@ -350,17 +358,19 @@ bool ncaRemoveTitlekeyCrypto(NcaContext *ctx) /* Don't proceed if we're not dealing with a NCA with a populated rights ID field, or if we couldn't retrieve the titlekey for it. */ if (!ctx->rights_id_available || !ctx->titlekey_retrieved) return true; - /* Copy decrypted titlekey to the decrypted NCA key area. */ - /* This will be reencrypted at a later stage. */ - for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) + /* Copy decrypted titlekey to the decrypted NCA key area. This will be reencrypted at a later stage. */ + /* AES-128-XTS is not used in FS sections from NCAs with titlekey crypto. */ + /* 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++) { - /* AES-128-XTS is not used in FS sections from NCAs with titlekey crypto. */ 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)) @@ -467,9 +477,11 @@ void ncaUpdateContentIdAndHash(NcaContext *ctx, u8 hash[SHA256_HASH_SIZE]) { if (!ctx) return; + /* Update content ID. */ memcpy(ctx->content_id.c, hash, sizeof(ctx->content_id.c)); utilsGenerateHexStringFromData(ctx->content_id_str, sizeof(ctx->content_id_str), ctx->content_id.c, sizeof(ctx->content_id.c)); + /* Update content hash. */ memcpy(ctx->hash, hash, sizeof(ctx->hash)); utilsGenerateHexStringFromData(ctx->hash_str, sizeof(ctx->hash_str), ctx->hash, sizeof(ctx->hash)); } @@ -602,35 +614,46 @@ static bool ncaDecryptKeyArea(NcaContext *ctx) } Result rc = 0; - const u8 *kek_src = NULL; - u8 key_count = 0, tmp_kek[AES_128_KEY_SIZE] = {0}; + const u8 *kaek_src = NULL, null_key[AES_128_KEY_SIZE] = {0}; + u8 key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4), aes_kek[AES_128_KEY_SIZE] = {0}; - /* Check if we're dealing with a NCA0 with a plain text key area. */ + /* 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); return true; } - kek_src = keysGetKeyAreaEncryptionKeySource(ctx->header.kaek_index); - if (!kek_src) + /* Get KAEK source for this KAEK index. */ + kaek_src = keysGetKeyAreaEncryptionKeySource(ctx->header.kaek_index); + if (!kaek_src) { LOG_MSG("Unable to retrieve KAEK source for index 0x%02X!", ctx->header.kaek_index); return false; } - rc = splCryptoGenerateAesKek(kek_src, ctx->key_generation, 0, tmp_kek); + /* Generate AES key encryption key. */ + rc = splCryptoGenerateAesKek(kaek_src, ctx->key_generation, 0, aes_kek); if (R_FAILED(rc)) { LOG_MSG("splCryptoGenerateAesKek failed! (0x%08X).", rc); return false; } - key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4); + /* Clear decrypted key area. */ + memset(&(ctx->decrypted_key_area), 0, NCA_USED_KEY_AREA_SIZE); + /* Process key area. */ for(u8 i = 0; i < key_count; i++) { - rc = splCryptoGenerateAesKey(tmp_kek, (u8*)&(ctx->header.encrypted_key_area) + (i * AES_128_KEY_SIZE), (u8*)&(ctx->decrypted_key_area) + (i * AES_128_KEY_SIZE)); + 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)); + + /* Don't proceed if we're dealing with a null key. */ + if (!memcmp(src_key, null_key, AES_128_KEY_SIZE)) continue; + + /* Decrypt current key area entry. */ + rc = splCryptoGenerateAesKey(aes_kek, src_key, dst_key); if (R_FAILED(rc)) { LOG_MSG("splCryptoGenerateAesKey failed to decrypt NCA key area entry #%u! (0x%08X).", i, rc); @@ -649,8 +672,8 @@ static bool ncaEncryptKeyArea(NcaContext *ctx) return false; } - u8 key_count = 0; - const u8 *kaek = NULL; + u8 key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4); + 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. */ @@ -660,6 +683,7 @@ static bool ncaEncryptKeyArea(NcaContext *ctx) return true; } + /* Get KAEK for these key generation and KAEK index values. */ kaek = keysGetKeyAreaEncryptionKey(ctx->key_generation, ctx->header.kaek_index); if (!kaek) { @@ -667,10 +691,24 @@ static bool ncaEncryptKeyArea(NcaContext *ctx) return false; } - key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4); + /* Clear encrypted key area. */ + memset(&(ctx->header.encrypted_key_area), 0, NCA_USED_KEY_AREA_SIZE); + /* Initialize AES-128-ECB encryption context using the retrieved KAEK. */ aes128ContextCreate(&key_area_ctx, kaek, true); - for(u8 i = 0; i < key_count; i++) aes128EncryptBlock(&key_area_ctx, (u8*)&(ctx->header.encrypted_key_area) + (i * AES_128_KEY_SIZE), (u8*)&(ctx->decrypted_key_area) + (i * AES_128_KEY_SIZE)); + + /* 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)); + + /* Don't proceed if we're dealing with a null key. */ + if (!memcmp(src_key, null_key, AES_128_KEY_SIZE)) continue; + + /* Encrypt current key area entry. */ + aes128EncryptBlock(&key_area_ctx, dst_key, src_key); + } return true; } @@ -681,9 +719,7 @@ NX_INLINE bool ncaIsVersion0KeyAreaEncrypted(NcaContext *ctx) u8 nca0_key_area_hash[SHA256_HASH_SIZE] = {0}; sha256CalculateHash(nca0_key_area_hash, &(ctx->header.encrypted_key_area), NCA_USED_KEY_AREA_SIZE); - if (!memcmp(nca0_key_area_hash, g_nca0KeyAreaHash, SHA256_HASH_SIZE)) return false; - - return true; + return (memcmp(nca0_key_area_hash, g_nca0KeyAreaHash, SHA256_HASH_SIZE) != 0); } NX_INLINE u8 ncaGetKeyGenerationValue(NcaContext *ctx) @@ -696,18 +732,12 @@ NX_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; - } + if (ctx->header.rights_id.c[i]) return true; } - return rights_id_available; + return false; } static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, bool lock)