1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-10-30 23:41:45 +00:00
nxdumptool/source/nca.c

362 lines
9.9 KiB
C
Raw Normal View History

2020-04-11 06:28:26 +01:00
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
2020-04-15 21:50:07 +01:00
#include "nca.h"
2020-04-11 06:28:26 +01:00
#include "keys.h"
#include "rsa.h"
2020-04-15 21:50:07 +01:00
#include "utils.h"
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
/* Global variables. */
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
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
};
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
/* Function prototypes. */
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx);
static void ncaUpdateAesCtrIv(u8 *ctr, u64 offset);
static void ncaUpdateAesCtrExIv(u8 *ctr, u32 ctr_val, u64 offset);
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
if (!ctx || !dst || !src || !size || !sector_size || (size % sector_size) != 0)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Invalid parameters!");
return 0;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
size_t i, crypt_res = 0;
u64 cur_sector = sector;
u8 *dst_u8 = (u8*)dst;
const u8 *src_u8 = (const u8*)src;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
for(i = 0; i < size; i += sector_size, cur_sector++)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
/* 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);
}
if (crypt_res != sector_size) break;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
return i;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
bool ncaDecryptKeyArea(NcaContext *ctx)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
if (!ctx)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Invalid NCA context!");
2020-04-11 06:28:26 +01:00
return false;
}
2020-04-15 21:50:07 +01:00
Result rc = 0;
const u8 *kek_src = NULL;
u8 key_count, tmp_kek[0x10] = {0};
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
/* Check if we're dealing with a NCA0 with a plain text key area */
if (ctx->format_version == NcaVersion_Nca0 && !ncaCheckIfVersion0KeyAreaIsEncrypted(ctx))
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
memcpy(ctx->decrypted_keys, ctx->header.encrypted_keys, 0x40);
return true;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
kek_src = keysGetKeyAreaEncryptionKeySource(ctx->header.kaek_index);
if (!kek_src)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Unable to retrieve KAEK source for index 0x%02X!", ctx->header.kaek_index);
return false;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
rc = splCryptoGenerateAesKek(kek_src, ctx->key_generation, 0, tmp_kek);
if (R_FAILED(rc))
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("splCryptoGenerateAesKek failed! (0x%08X)", rc);
return false;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4);
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
for(u8 i = 0; i < key_count; i++)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
rc = splCryptoGenerateAesKey(tmp_kek, &(ctx->header.encrypted_keys[i]), &(ctx->decrypted_keys[i]));
if (R_FAILED(rc))
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("splCryptoGenerateAesKey failed! (0x%08X)", rc);
return false;
2020-04-11 06:28:26 +01:00
}
}
2020-04-15 21:50:07 +01:00
return true;
}
bool ncaEncryptKeyArea(NcaContext *ctx)
{
if (!ctx)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Invalid NCA context!");
return false;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
u8 key_count;
const u8 *kaek = NULL;
Aes128Context key_area_ctx = {0};
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
/* Check if we're dealing with a NCA0 with a plain text key area */
if (ctx->format_version == NcaVersion_Nca0 && !ncaCheckIfVersion0KeyAreaIsEncrypted(ctx))
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
memcpy(ctx->header.encrypted_keys, ctx->decrypted_keys, 0x40);
return true;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
kaek = keysGetKeyAreaEncryptionKey(ctx->key_generation, ctx->header.kaek_index);
if (!kaek)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Unable to retrieve KAEK for key generation 0x%02X and KAEK index 0x%02X!", ctx->key_generation, ctx->header.kaek_index);
return false;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4);
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
aes128ContextCreate(&key_area_ctx, kaek, true);
for(u8 i = 0; i < key_count; i++) aes128EncryptBlock(&key_area_ctx, &(ctx->header.encrypted_keys[i]), &(ctx->decrypted_keys[i]));
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
return true;
}
bool ncaDecryptHeader(NcaContext *ctx)
{
if (!ctx)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Invalid NCA context!");
return false;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
u32 i, magic = 0;
size_t crypt_res = 0;
const u8 *header_key = NULL;
Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0};
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
header_key = keysGetNcaHeaderKey();
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + 0x10, false);
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
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)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Invalid output length for decrypted NCA header! (0x%X != 0x%lX)", NCA_HEADER_LENGTH, crypt_res);
return false;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
magic = __builtin_bswap32(ctx->header.magic);
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
switch(magic)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
case NCA_NCA3_MAGIC:
ctx->format_version = NcaVersion_Nca3;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
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)
{
LOGFILE("Error decrypting full NCA3 header!");
return false;
}
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
break;
case NCA_NCA2_MAGIC:
ctx->format_version = NcaVersion_Nca2;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
for(i = 0; i < NCA_FS_HEADER_COUNT; i++)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
if (!ctx->header.fs_entries[i].enable_entry) continue;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
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)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Error decrypting NCA2 FS section header #%u!", i);
return false;
2020-04-11 06:28:26 +01:00
}
}
2020-04-15 21:50:07 +01:00
break;
case NCA_NCA0_MAGIC:
ctx->format_version = NcaVersion_Nca0;
/* We first need to decrypt the key area from the NCA0 header in order to access its FS section headers */
if (!ncaDecryptKeyArea(ctx))
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Error decrypting key area from NCA0 header!");
return false;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
aes128XtsContextCreate(&nca0_fs_header_ctx, &(ctx->decrypted_keys[0]), &(ctx->decrypted_keys[1]), false);
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
for(i = 0; i < NCA_FS_HEADER_COUNT; i++)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
if (!ctx->header.fs_entries[i].enable_entry) continue;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
break;
default:
LOGFILE("Invalid NCA magic word! Wrong header key? (0x%08X)", magic);
return false;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
/* Fill additional context info */
ctx->key_generation = ncaGetKeyGenerationValue(ctx);
ctx->rights_id_available = ncaCheckRightsIdAvailability(ctx);
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
return true;
}
bool ncaEncryptHeader(NcaContext *ctx)
{
if (!ctx)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Invalid NCA context!");
return false;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
u32 i;
size_t crypt_res = 0;
const u8 *header_key = NULL;
Aes128XtsContext hdr_aes_ctx = {0};
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
header_key = keysGetNcaHeaderKey();
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + 0x10, true);
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
switch(ctx->format_version)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
case NcaVersion_Nca3:
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_FULL_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true);
if (crypt_res != NCA_FULL_HEADER_LENGTH)
{
LOGFILE("Error encrypting full NCA3 header!");
return false;
}
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
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;
}
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
for(i = 0; i < NCA_FS_HEADER_COUNT; i++)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
if (!ctx->header.fs_entries[i].enable_entry) continue;
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)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
LOGFILE("Error encrypting NCA2 FS section header #%u!", i);
return false;
2020-04-11 06:28:26 +01:00
}
}
2020-04-15 21:50:07 +01:00
break;
case NcaVersion_Nca0:
/* There's nothing else to do */
break;
default:
LOGFILE("Invalid NCA format version! (0x%02X)", ctx->format_version);
return false;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
return true;
}
static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx)
{
if (!ctx || ctx->format_version != NcaVersion_Nca0) return false;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
u8 nca0_key_area_hash[SHA256_HASH_SIZE] = {0};
sha256CalculateHash(nca0_key_area_hash, ctx->header.encrypted_keys, 0x40);
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
if (!memcmp(nca0_key_area_hash, g_nca0KeyAreaHash, SHA256_HASH_SIZE)) return false;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
return true;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
static void ncaUpdateAesCtrIv(u8 *ctr, u64 offset)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
if (!ctr) return;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
offset >>= 4;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
for(u8 i = 0; i < 8; i++)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
ctr[0x10 - i - 1] = (u8)(offset & 0xFF);
offset >>= 8;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
}
static void ncaUpdateAesCtrExIv(u8 *ctr, u32 ctr_val, u64 offset)
{
if (!ctr) return;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
offset >>= 4;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
for(u8 i = 0; i < 8; i++)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
ctr[0x10 - i - 1] = (u8)(offset & 0xFF);
offset >>= 8;
2020-04-11 06:28:26 +01:00
}
2020-04-15 21:50:07 +01:00
for(u8 i = 0; i < 4; i++)
2020-04-11 06:28:26 +01:00
{
2020-04-15 21:50:07 +01:00
ctr[0x8 - i - 1] = (u8)(ctr_val & 0xFF);
ctr_val >>= 8;
2020-04-11 06:28:26 +01:00
}
}