1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 18:26:39 +00:00

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.
This commit is contained in:
Pablo Curiel 2021-04-25 19:10:34 -04:00
parent 32a031943f
commit 17dd24bc92
14 changed files with 363 additions and 35 deletions

2
.gitignore vendored
View file

@ -14,5 +14,5 @@ host/nxdumptool
*.log *.log
*.spec *.spec
*.exe *.exe
main.cpp
/code_templates/tmp/* /code_templates/tmp/*

46
build.sh Normal file
View file

@ -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 ..."

View file

@ -36,6 +36,8 @@ int g_argc = 0;
char **g_argv = NULL; char **g_argv = NULL;
const char *g_appLaunchPath = NULL; const char *g_appLaunchPath = NULL;
static PadState g_padState = {0};
static const char *dump_type_strings[] = { static const char *dump_type_strings[] = {
"dump base application", "dump base application",
"dump update", "dump update",
@ -65,6 +67,34 @@ static const u32 options_count = MAX_ELEMENTS(options);
static UsbHsFsDevice *ums_devices = NULL; static UsbHsFsDevice *ums_devices = NULL;
static u32 ums_device_count = 0; 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, ...) static void consolePrint(const char *text, ...)
{ {
va_list v; va_list v;
@ -764,6 +794,12 @@ int main(int argc, char *argv[])
goto out; 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); consoleInit(NULL);
u32 app_count = 0; u32 app_count = 0;

View file

@ -35,6 +35,8 @@ int g_argc = 0;
char **g_argv = NULL; char **g_argv = NULL;
const char *g_appLaunchPath = NULL; const char *g_appLaunchPath = NULL;
static PadState g_padState = {0};
typedef struct typedef struct
{ {
void *data; void *data;
@ -71,6 +73,34 @@ static const u32 options_count = MAX_ELEMENTS(options);
static Mutex g_conMutex = 0; 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, ...) static void consolePrint(const char *text, ...)
{ {
mutexLock(&g_conMutex); mutexLock(&g_conMutex);
@ -922,6 +952,12 @@ int main(int argc, char *argv[])
goto out; 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); consoleInit(NULL);
u32 app_count = 0; u32 app_count = 0;

View file

@ -30,6 +30,8 @@ int g_argc = 0;
char **g_argv = NULL; char **g_argv = NULL;
const char *g_appLaunchPath = NULL; const char *g_appLaunchPath = NULL;
static PadState g_padState = {0};
static Mutex g_fileMutex = 0; static Mutex g_fileMutex = 0;
static CondVar g_readCondvar = 0, g_writeCondvar = 0; static CondVar g_readCondvar = 0, g_writeCondvar = 0;
@ -47,6 +49,34 @@ typedef struct
bool transfer_cancelled; bool transfer_cancelled;
} ThreadSharedData; } 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, ...) static void consolePrint(const char *text, ...)
{ {
va_list v; va_list v;
@ -339,6 +369,12 @@ int main(int argc, char *argv[])
goto out; 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); consoleInit(NULL);
u32 app_count = 0; u32 app_count = 0;

View file

@ -32,10 +32,40 @@ int g_argc = 0;
char **g_argv = NULL; char **g_argv = NULL;
const char *g_appLaunchPath = NULL; const char *g_appLaunchPath = NULL;
static PadState g_padState = {0};
static u8 *buf = NULL; static u8 *buf = NULL;
static FILE *filefd = NULL; static FILE *filefd = NULL;
static char path[FS_MAX_PATH * 2] = {0}; 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, ...) static void consolePrint(const char *text, ...)
{ {
va_list v; va_list v;
@ -236,6 +266,12 @@ int main(int argc, char *argv[])
goto out; 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); consoleInit(NULL);
u32 app_count = 0; u32 app_count = 0;

View file

@ -31,6 +31,8 @@ int g_argc = 0;
char **g_argv = NULL; char **g_argv = NULL;
const char *g_appLaunchPath = NULL; const char *g_appLaunchPath = NULL;
static PadState g_padState = {0};
/* Type definitions. */ /* Type definitions. */
typedef void (*MenuElementOptionFunction)(u32 idx); 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 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[]) int main(int argc, char *argv[])
{ {
g_argc = argc; g_argc = argc;
@ -199,6 +229,12 @@ int main(int argc, char *argv[])
goto out; 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); consoleInit(NULL);
while(appletMainLoop()) while(appletMainLoop())

View file

@ -31,6 +31,8 @@ int g_argc = 0;
char **g_argv = NULL; char **g_argv = NULL;
const char *g_appLaunchPath = NULL; const char *g_appLaunchPath = NULL;
static PadState g_padState = {0};
static Mutex g_fileMutex = 0; static Mutex g_fileMutex = 0;
static CondVar g_readCondvar = 0, g_writeCondvar = 0; static CondVar g_readCondvar = 0, g_writeCondvar = 0;
@ -48,6 +50,34 @@ typedef struct
bool transfer_cancelled; bool transfer_cancelled;
} ThreadSharedData; } 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, ...) static void consolePrint(const char *text, ...)
{ {
va_list v; va_list v;
@ -318,6 +348,12 @@ int main(int argc, char *argv[])
goto out; 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); consoleInit(NULL);
u32 app_count = 0; u32 app_count = 0;

View file

@ -31,6 +31,36 @@ int g_argc = 0;
char **g_argv = NULL; char **g_argv = NULL;
const char *g_appLaunchPath = 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, ...) static void consolePrint(const char *text, ...)
{ {
va_list v; va_list v;
@ -64,6 +94,12 @@ int main(int argc, char *argv[])
goto out; 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); consoleInit(NULL);
u32 app_count = 0; u32 app_count = 0;

View file

@ -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); 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. /// 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) NX_INLINE void aes128CtrInitializePartialCtr(u8 *out, const u8 *ctr, u64 offset)
{ {
if (!out || !ctr) return; 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. /// 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) NX_INLINE void aes128CtrUpdatePartialCtr(u8 *ctr, u64 offset)
{ {
if (!ctr) return; 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. /// 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) NX_INLINE void aes128CtrUpdatePartialCtrEx(u8 *ctr, u32 ctr_val, u64 offset)
{ {
if (!ctr) return; if (!ctr) return;

View file

@ -115,10 +115,11 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type,
if (out->storage_id == NcmStorageId_GameCard) if (out->storage_id == NcmStorageId_GameCard)
{ {
/* Retrieve gamecard NCA offset. */ /* Generate gamecard NCA filename. */
char nca_filename[0x30] = {0}; char nca_filename[0x30] = {0};
sprintf(nca_filename, "%s.%s", out->content_id_str, out->content_type == NcmContentType_Meta ? "cnmt.nca" : "nca"); 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)) 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); 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 { } else {
if (fs_ctx->encryption_type == NcaEncryptionType_AesXts) 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_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); aes128XtsContextCreate(&(fs_ctx->xts_encrypt_ctx), out->decrypted_key_area.aes_xts_1, out->decrypted_key_area.aes_xts_2, true);
} else } 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_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); 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. */ /* 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; if (!ctx->rights_id_available || !ctx->titlekey_retrieved) return true;
/* Copy decrypted titlekey to the decrypted NCA key area. */ /* Copy decrypted titlekey to the decrypted NCA key area. This will be reencrypted at a later stage. */
/* This will be reencrypted at a later stage. */ /* AES-128-XTS is not used in FS sections from NCAs with titlekey crypto. */
for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) /* 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]); NcaFsSectionContext *fs_ctx = &(ctx->fs_ctx[i]);
if (!fs_ctx->enabled || (fs_ctx->encryption_type != NcaEncryptionType_AesCtr && fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx)) continue; 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); 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); memcpy(key_ptr, ctx->titlekey, AES_128_KEY_SIZE);
} }***/
/* Encrypt NCA key area. */ /* Encrypt NCA key area. */
if (!ncaEncryptKeyArea(ctx)) if (!ncaEncryptKeyArea(ctx))
@ -467,9 +477,11 @@ void ncaUpdateContentIdAndHash(NcaContext *ctx, u8 hash[SHA256_HASH_SIZE])
{ {
if (!ctx) return; if (!ctx) return;
/* Update content ID. */
memcpy(ctx->content_id.c, hash, sizeof(ctx->content_id.c)); 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)); 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)); memcpy(ctx->hash, hash, sizeof(ctx->hash));
utilsGenerateHexStringFromData(ctx->hash_str, sizeof(ctx->hash_str), ctx->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; Result rc = 0;
const u8 *kek_src = NULL; const u8 *kaek_src = NULL, null_key[AES_128_KEY_SIZE] = {0};
u8 key_count = 0, tmp_kek[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)) 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), NCA_USED_KEY_AREA_SIZE);
return true; return true;
} }
kek_src = keysGetKeyAreaEncryptionKeySource(ctx->header.kaek_index); /* Get KAEK source for this KAEK index. */
if (!kek_src) 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); LOG_MSG("Unable to retrieve KAEK source for index 0x%02X!", ctx->header.kaek_index);
return false; 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)) if (R_FAILED(rc))
{ {
LOG_MSG("splCryptoGenerateAesKek failed! (0x%08X).", rc); LOG_MSG("splCryptoGenerateAesKek failed! (0x%08X).", rc);
return false; 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++) 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)) if (R_FAILED(rc))
{ {
LOG_MSG("splCryptoGenerateAesKey failed to decrypt NCA key area entry #%u! (0x%08X).", i, 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; return false;
} }
u8 key_count = 0; u8 key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4);
const u8 *kaek = NULL; const u8 *kaek = NULL, null_key[AES_128_KEY_SIZE] = {0};
Aes128Context key_area_ctx = {0}; Aes128Context key_area_ctx = {0};
/* Check if we're dealing with a NCA0 with a plaintext key area. */ /* Check if we're dealing with a NCA0 with a plaintext key area. */
@ -660,6 +683,7 @@ static bool ncaEncryptKeyArea(NcaContext *ctx)
return true; return true;
} }
/* Get KAEK for these key generation and KAEK index values. */
kaek = keysGetKeyAreaEncryptionKey(ctx->key_generation, ctx->header.kaek_index); kaek = keysGetKeyAreaEncryptionKey(ctx->key_generation, ctx->header.kaek_index);
if (!kaek) if (!kaek)
{ {
@ -667,10 +691,24 @@ static bool ncaEncryptKeyArea(NcaContext *ctx)
return false; 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); 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; return true;
} }
@ -681,9 +719,7 @@ NX_INLINE bool ncaIsVersion0KeyAreaEncrypted(NcaContext *ctx)
u8 nca0_key_area_hash[SHA256_HASH_SIZE] = {0}; 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), NCA_USED_KEY_AREA_SIZE);
if (!memcmp(nca0_key_area_hash, g_nca0KeyAreaHash, SHA256_HASH_SIZE)) return false; return (memcmp(nca0_key_area_hash, g_nca0KeyAreaHash, SHA256_HASH_SIZE) != 0);
return true;
} }
NX_INLINE u8 ncaGetKeyGenerationValue(NcaContext *ctx) NX_INLINE u8 ncaGetKeyGenerationValue(NcaContext *ctx)
@ -696,18 +732,12 @@ NX_INLINE bool ncaCheckRightsIdAvailability(NcaContext *ctx)
{ {
if (!ctx) return false; if (!ctx) return false;
bool rights_id_available = false;
for(u8 i = 0; i < 0x10; i++) for(u8 i = 0; i < 0x10; i++)
{ {
if (ctx->header.rights_id.c[i] != 0) if (ctx->header.rights_id.c[i]) return true;
{
rights_id_available = true;
break;
}
} }
return rights_id_available; return false;
} }
static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, bool lock) static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, bool lock)