mirror of
https://github.com/SciresM/hactool.git
synced 2024-11-23 04:29:41 +00:00
Add support for NAX0 input files.
This commit is contained in:
parent
5bc21ebe73
commit
41e6695e6e
4
Makefile
4
Makefile
@ -13,7 +13,7 @@ all:
|
|||||||
.c.o:
|
.c.o:
|
||||||
$(CC) $(INCLUDE) -c $(CFLAGS) -o $@ $<
|
$(CC) $(INCLUDE) -c $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
hactool: sha.o aes.o extkeys.o rsa.o npdm.o bktr.o kip.o packages.o pki.o pfs0.o hfs0.o romfs.o utils.o nca.o xci.o main.o filepath.o ConvertUTF.o
|
hactool: sha.o aes.o extkeys.o rsa.o npdm.o bktr.o kip.o packages.o pki.o pfs0.o hfs0.o romfs.o utils.o nax0.o nca.o xci.o main.o filepath.o ConvertUTF.o
|
||||||
$(CC) -o $@ $^ $(LDFLAGS) -L $(LIBDIR)
|
$(CC) -o $@ $^ $(LDFLAGS) -L $(LIBDIR)
|
||||||
|
|
||||||
aes.o: aes.h types.h
|
aes.o: aes.h types.h
|
||||||
@ -36,6 +36,8 @@ pfs0.o: pfs0.h types.h
|
|||||||
|
|
||||||
pki.o: pki.h aes.h types.h
|
pki.o: pki.h aes.h types.h
|
||||||
|
|
||||||
|
nax0.o: nax0.h aes.h sha.h types.h
|
||||||
|
|
||||||
nca.o: nca.h aes.h sha.h rsa.h bktr.h filepath.h types.h
|
nca.o: nca.h aes.h sha.h rsa.h bktr.h filepath.h types.h
|
||||||
|
|
||||||
npdm.o: npdm.c types.h
|
npdm.o: npdm.c types.h
|
||||||
|
@ -212,6 +212,15 @@ void extkeys_initialize_keyset(nca_keyset_t *keyset, FILE *f) {
|
|||||||
} else if (strcmp(key, "package2_key_source") == 0) {
|
} else if (strcmp(key, "package2_key_source") == 0) {
|
||||||
parse_hex_key(keyset->package2_key_source, value, sizeof(keyset->package2_key_source));
|
parse_hex_key(keyset->package2_key_source, value, sizeof(keyset->package2_key_source));
|
||||||
matched_key = 1;
|
matched_key = 1;
|
||||||
|
} else if (strcmp(key, "sd_card_kek_source") == 0) {
|
||||||
|
parse_hex_key(keyset->sd_card_kek_source, value, sizeof(keyset->sd_card_kek_source));
|
||||||
|
matched_key = 1;
|
||||||
|
} else if (strcmp(key, "sd_card_nca_key_source") == 0) {
|
||||||
|
parse_hex_key(keyset->sd_card_key_sources[1], value, sizeof(keyset->sd_card_key_sources[1]));
|
||||||
|
matched_key = 1;
|
||||||
|
} else if (strcmp(key, "sd_card_save_key_source") == 0) {
|
||||||
|
parse_hex_key(keyset->sd_card_key_sources[0], value, sizeof(keyset->sd_card_key_sources[0]));
|
||||||
|
matched_key = 1;
|
||||||
} else {
|
} else {
|
||||||
char test_name[0x100];
|
char test_name[0x100];
|
||||||
memset(test_name, 0, sizeof(100));
|
memset(test_name, 0, sizeof(100));
|
||||||
|
62
main.c
62
main.c
@ -9,6 +9,7 @@
|
|||||||
#include "pki.h"
|
#include "pki.h"
|
||||||
#include "nca.h"
|
#include "nca.h"
|
||||||
#include "xci.h"
|
#include "xci.h"
|
||||||
|
#include "nax0.h"
|
||||||
#include "extkeys.h"
|
#include "extkeys.h"
|
||||||
#include "packages.h"
|
#include "packages.h"
|
||||||
|
|
||||||
@ -80,8 +81,11 @@ static void usage(void) {
|
|||||||
" --extractini1 Enable INI1 extraction to default directory (redundant with --ini1dir set).\n"
|
" --extractini1 Enable INI1 extraction to default directory (redundant with --ini1dir set).\n"
|
||||||
" --ini1dir=dir Specify INI1 directory path. Overrides default path, if present.\n"
|
" --ini1dir=dir Specify INI1 directory path. Overrides default path, if present.\n"
|
||||||
"INI1 options:\n"
|
"INI1 options:\n"
|
||||||
" --ini1dir=dir Specify Package1 directory path.\n"
|
" --ini1dir=dir Specify INI1 directory path.\n"
|
||||||
" --outdir=dir Specify Package1 directory path. Overrides previous path, if present.\n"
|
" --outdir=dir Specify INI1 directory path. Overrides previous path, if present.\n"
|
||||||
|
"NAX0 options:\n"
|
||||||
|
" --sdseed=seed Set console unique seed for SD card NAX0 encryption.\n"
|
||||||
|
" --sdpath=path Set relative path for NAX0 key derivation (ex: /registered/000000FF/cafebabecafebabecafebabecafebabe.nca).\n"
|
||||||
"\n", __TIME__, __DATE__, prog_name);
|
"\n", __TIME__, __DATE__, prog_name);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@ -152,6 +156,8 @@ int main(int argc, char **argv) {
|
|||||||
{"extractini1", 0, NULL, 29},
|
{"extractini1", 0, NULL, 29},
|
||||||
{"basefake", 0, NULL, 30},
|
{"basefake", 0, NULL, 30},
|
||||||
{"onlyupdated", 0, NULL, 31},
|
{"onlyupdated", 0, NULL, 31},
|
||||||
|
{"sdseed", 1, NULL, 32},
|
||||||
|
{"sdpath", 1, NULL, 33},
|
||||||
{NULL, 0, NULL, 0},
|
{NULL, 0, NULL, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -201,6 +207,8 @@ int main(int argc, char **argv) {
|
|||||||
nca_ctx.tool_ctx->file_type = FILETYPE_INI1;
|
nca_ctx.tool_ctx->file_type = FILETYPE_INI1;
|
||||||
} else if (!strcmp(optarg, "kip1") || !strcmp(optarg, "kip")) {
|
} else if (!strcmp(optarg, "kip1") || !strcmp(optarg, "kip")) {
|
||||||
nca_ctx.tool_ctx->file_type = FILETYPE_KIP1;
|
nca_ctx.tool_ctx->file_type = FILETYPE_KIP1;
|
||||||
|
} else if (!strcmp(optarg, "nax0") || !strcmp(optarg, "nax")) {
|
||||||
|
nca_ctx.tool_ctx->file_type = FILETYPE_NAX0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0: filepath_set(&nca_ctx.tool_ctx->settings.section_paths[0], optarg); break;
|
case 0: filepath_set(&nca_ctx.tool_ctx->settings.section_paths[0], optarg); break;
|
||||||
@ -273,7 +281,7 @@ int main(int argc, char **argv) {
|
|||||||
filepath_set(&tool_ctx.settings.out_dir_path.path, optarg);
|
filepath_set(&tool_ctx.settings.out_dir_path.path, optarg);
|
||||||
break;
|
break;
|
||||||
case 18:
|
case 18:
|
||||||
filepath_set(&nca_ctx.tool_ctx->settings.dec_nca_path, optarg);
|
filepath_set(&nca_ctx.tool_ctx->settings.plaintext_path, optarg);
|
||||||
break;
|
break;
|
||||||
case 19:
|
case 19:
|
||||||
filepath_set(&nca_ctx.tool_ctx->settings.header_path, optarg);
|
filepath_set(&nca_ctx.tool_ctx->settings.header_path, optarg);
|
||||||
@ -319,6 +327,19 @@ int main(int argc, char **argv) {
|
|||||||
case 31:
|
case 31:
|
||||||
tool_ctx.action |= ACTION_ONLYUPDATEDROMFS;
|
tool_ctx.action |= ACTION_ONLYUPDATEDROMFS;
|
||||||
break;
|
break;
|
||||||
|
case 32:
|
||||||
|
parse_hex_key(nca_ctx.tool_ctx->settings.sdseed, optarg, 16);
|
||||||
|
nca_ctx.tool_ctx->settings.has_sdseed = 1;
|
||||||
|
for (unsigned int key = 0; key < 2; key++) {
|
||||||
|
for (unsigned int i = 0; i < 0x20; i++) {
|
||||||
|
tool_ctx.settings.keyset.sd_card_key_sources[key][i] ^= tool_ctx.settings.sdseed[i & 0xF];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pki_derive_keys(&tool_ctx.settings.keyset);
|
||||||
|
break;
|
||||||
|
case 33:
|
||||||
|
filepath_set(&tool_ctx.settings.nax0_sd_path, optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@ -364,6 +385,13 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
if (keyfile != NULL) {
|
if (keyfile != NULL) {
|
||||||
extkeys_initialize_keyset(&tool_ctx.settings.keyset, keyfile);
|
extkeys_initialize_keyset(&tool_ctx.settings.keyset, keyfile);
|
||||||
|
if (tool_ctx.settings.has_sdseed) {
|
||||||
|
for (unsigned int key = 0; key < 2; key++) {
|
||||||
|
for (unsigned int i = 0; i < 0x20; i++) {
|
||||||
|
tool_ctx.settings.keyset.sd_card_key_sources[key][i] ^= tool_ctx.settings.sdseed[i & 0xF];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pki_derive_keys(&tool_ctx.settings.keyset);
|
pki_derive_keys(&tool_ctx.settings.keyset);
|
||||||
fclose(keyfile);
|
fclose(keyfile);
|
||||||
}
|
}
|
||||||
@ -374,11 +402,37 @@ int main(int argc, char **argv) {
|
|||||||
} else if ((optind < argc) || (argc == 1)) {
|
} else if ((optind < argc) || (argc == 1)) {
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Special case NAX0. */
|
||||||
|
if (tool_ctx.file_type == FILETYPE_NAX0) {
|
||||||
|
nax0_ctx_t nax_ctx;
|
||||||
|
memset(&nax_ctx, 0, sizeof(nax_ctx));
|
||||||
|
filepath_set(&nax_ctx.base_path, input_name);
|
||||||
|
nax_ctx.tool_ctx = &tool_ctx;
|
||||||
|
nax0_process(&nax_ctx);
|
||||||
|
|
||||||
|
if (nax_ctx.aes_ctx) {
|
||||||
|
free_aes_ctx(nax_ctx.aes_ctx);
|
||||||
|
}
|
||||||
|
if (nax_ctx.num_files) {
|
||||||
|
for (unsigned int i = 0; i < nax_ctx.num_files; i++) {
|
||||||
|
fclose(nax_ctx.files[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nax_ctx.files) {
|
||||||
|
free(nax_ctx.files);
|
||||||
|
}
|
||||||
|
printf("Done!\n");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if ((tool_ctx.file = fopen(input_name, "rb")) == NULL) {
|
if ((tool_ctx.file = fopen(input_name, "rb")) == NULL) {
|
||||||
fprintf(stderr, "unable to open %s: %s\n", input_name, strerror(errno));
|
fprintf(stderr, "unable to open %s: %s\n", input_name, strerror(errno));
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
switch (tool_ctx.file_type) {
|
switch (tool_ctx.file_type) {
|
||||||
case FILETYPE_NCA: {
|
case FILETYPE_NCA: {
|
||||||
|
168
nax0.c
Normal file
168
nax0.c
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include "aes.h"
|
||||||
|
#include "sha.h"
|
||||||
|
#include "nax0.h"
|
||||||
|
|
||||||
|
size_t nax0_read(nax0_ctx_t *ctx, uint64_t offset, void *dst, size_t size) {
|
||||||
|
if (ctx->num_files == 1) {
|
||||||
|
fseeko64(ctx->files[0], offset, SEEK_SET);
|
||||||
|
return fread(dst, 1, size, ctx->files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *which = ctx->files[offset / 0xFFFF0000ULL];
|
||||||
|
uint64_t offset_in_file = offset % 0xFFFF0000ULL;
|
||||||
|
fseeko64(which, offset_in_file, SEEK_SET);
|
||||||
|
uint64_t left_in_file = 0xFFFF0000ULL - offset_in_file;
|
||||||
|
if (size > left_in_file) {
|
||||||
|
return fread(dst, 1, left_in_file, which) + nax0_read(ctx, offset + left_in_file, (unsigned char *)dst + left_in_file, size - left_in_file);
|
||||||
|
} else {
|
||||||
|
return fread(dst, 1, size, which);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nax0_process(nax0_ctx_t *ctx) {
|
||||||
|
/* First things first... */
|
||||||
|
FILE *f_temp;
|
||||||
|
if ((f_temp = os_fopen(ctx->base_path.os_path, OS_MODE_READ)) != NULL) {
|
||||||
|
ctx->num_files = 1;
|
||||||
|
ctx->files = calloc(1, sizeof(FILE *));
|
||||||
|
if (ctx->files == NULL) {
|
||||||
|
fprintf(stderr, "Failed to allocate NAX0 file holder!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
ctx->files[0] = f_temp;
|
||||||
|
} else {
|
||||||
|
ctx->num_files = 0;
|
||||||
|
filepath_t temp_path;
|
||||||
|
while (1) {
|
||||||
|
filepath_copy(&temp_path, &ctx->base_path);
|
||||||
|
filepath_append(&temp_path, "%02"PRIu32, ctx->num_files);
|
||||||
|
if ((f_temp = os_fopen(temp_path.os_path, OS_MODE_READ)) == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ctx->num_files++;
|
||||||
|
fclose(f_temp);
|
||||||
|
}
|
||||||
|
if (ctx->num_files == 0) {
|
||||||
|
fprintf(stderr, "Input path appears to neither be a NAX0, nor a NAX0 directory!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
ctx->files = calloc(ctx->num_files, sizeof(FILE *));
|
||||||
|
if (ctx->files == NULL) {
|
||||||
|
fprintf(stderr, "Failed to allocate NAX0 file holder!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i < ctx->num_files; i++) {
|
||||||
|
filepath_copy(&temp_path, &ctx->base_path);
|
||||||
|
filepath_append(&temp_path, "%02"PRIu32, i);
|
||||||
|
if ((ctx->files[i] = os_fopen(temp_path.os_path, OS_MODE_READ)) == NULL) {
|
||||||
|
fprintf(stderr, "Failed to open %s!\n", temp_path.char_path);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nax0_read(ctx, 0, &ctx->header, sizeof(ctx->header));
|
||||||
|
if (ctx->header.magic != MAGIC_NAX0) {
|
||||||
|
printf("Error: File has invalid NAX0 magic!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ctx->encrypted_keys, ctx->header.keys, sizeof(ctx->header.keys));
|
||||||
|
|
||||||
|
int found = 0;
|
||||||
|
for (ctx->k = 0; ctx->k < 2; ctx->k++) {
|
||||||
|
unsigned char nax_specific_keys[2][0x10];
|
||||||
|
sha256_get_buffer_hmac(nax_specific_keys, ctx->tool_ctx->settings.keyset.sd_card_keys[ctx->k], 0x10, ctx->tool_ctx->settings.nax0_sd_path.char_path, strlen(ctx->tool_ctx->settings.nax0_sd_path.char_path));
|
||||||
|
for (unsigned int i = 0; i < 2; i++) {
|
||||||
|
aes_ctx_t *nax_k_ctx = new_aes_ctx(nax_specific_keys[i], 0x10, AES_MODE_ECB);
|
||||||
|
aes_decrypt(nax_k_ctx, ctx->header.keys[i], ctx->encrypted_keys[i], 0x10);
|
||||||
|
free_aes_ctx(nax_k_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char validation_mac[0x20];
|
||||||
|
sha256_get_buffer_hmac(validation_mac, &ctx->header.magic, 0x60, ctx->tool_ctx->settings.keyset.sd_card_keys[ctx->k] + 0x10, 0x10);
|
||||||
|
if (memcmp(ctx->header.hmac_header, validation_mac, 0x20) == 0) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
printf("Error: NAX0 key derivation failed. Check SD card seed and relative path?\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->aes_ctx = new_aes_ctx(ctx->header.keys, 0x20, AES_MODE_XTS);
|
||||||
|
|
||||||
|
if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||||
|
nax0_print(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||||
|
nax0_save(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nax0_save(nax0_ctx_t *ctx) {
|
||||||
|
/* Save Decrypted Contents. */
|
||||||
|
filepath_t *dec_path = &ctx->tool_ctx->settings.plaintext_path;
|
||||||
|
|
||||||
|
if (dec_path->valid == VALIDITY_VALID) {
|
||||||
|
printf("Saving Decrypted NAX0 Content to %s...\n", dec_path->char_path);
|
||||||
|
FILE *f_dec = os_fopen(dec_path->os_path, OS_MODE_WRITE);
|
||||||
|
|
||||||
|
if (f_dec != NULL) {
|
||||||
|
uint64_t ofs = 0x4000;
|
||||||
|
uint64_t end_ofs = ofs + ctx->header.size;
|
||||||
|
unsigned char *buf = malloc(0x400000);
|
||||||
|
if (buf == NULL) {
|
||||||
|
fprintf(stderr, "Failed to allocate file-save buffer!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t read_size = 0x400000; /* 4 MB buffer. */
|
||||||
|
memset(buf, 0xCC, read_size); /* Debug in case I fuck this up somehow... */
|
||||||
|
while (ofs < end_ofs) {
|
||||||
|
if (ofs + read_size >= end_ofs) read_size = end_ofs - ofs;
|
||||||
|
if (nax0_read(ctx, ofs, buf, read_size) != read_size) {
|
||||||
|
fprintf(stderr, "Failed to read file!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
aes_xts_decrypt(ctx->aes_ctx, buf, buf, read_size, (ofs - 0x4000) >> 14, 0x4000);
|
||||||
|
|
||||||
|
if (fwrite(buf, 1, read_size, f_dec) != read_size) {
|
||||||
|
fprintf(stderr, "Failed to write file!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
ofs += read_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Failed to open %s!\n", dec_path->char_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *nax0_get_key_summary(unsigned int k) {
|
||||||
|
switch (k) {
|
||||||
|
case 0:
|
||||||
|
return "Save";
|
||||||
|
case 1:
|
||||||
|
return "NCA";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nax0_print(nax0_ctx_t *ctx) {
|
||||||
|
printf("\nNAX0:\n");
|
||||||
|
print_magic(" Magic: ", ctx->header.magic);
|
||||||
|
printf(" Content Type: %s\n", nax0_get_key_summary(ctx->k));
|
||||||
|
printf(" Content Size: %012"PRIx64"\n", ctx->header.size);
|
||||||
|
memdump(stdout, " Header HMAC: ", ctx->header.hmac_header, 0x20);
|
||||||
|
memdump(stdout, " Encrypted Keys: ", ctx->encrypted_keys, 0x20);
|
||||||
|
memdump(stdout, " Decrypted Keys: ", ctx->header.keys, 0x20);
|
||||||
|
}
|
36
nax0.h
Normal file
36
nax0.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef HACTOOL_NAX0_H
|
||||||
|
#define HACTOOL_NAX0_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "aes.h"
|
||||||
|
|
||||||
|
#define MAGIC_NAX0 0x3058414E
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t hmac_header[0x20];
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t _0x24;
|
||||||
|
uint8_t keys[2][0x10];
|
||||||
|
uint64_t size;
|
||||||
|
uint8_t _0x50[0x30];
|
||||||
|
} nax0_header_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
filepath_t base_path;
|
||||||
|
hactool_ctx_t *tool_ctx;
|
||||||
|
aes_ctx_t *aes_ctx;
|
||||||
|
FILE **files;
|
||||||
|
unsigned int num_files;
|
||||||
|
unsigned int k;
|
||||||
|
unsigned char encrypted_keys[2][0x10];
|
||||||
|
nax0_header_t header;
|
||||||
|
} nax0_ctx_t;
|
||||||
|
|
||||||
|
void nax0_process(nax0_ctx_t *ctx);
|
||||||
|
void nax0_save(nax0_ctx_t *ctx);
|
||||||
|
void nax0_print(nax0_ctx_t *ctx);
|
||||||
|
|
||||||
|
#endif
|
2
nca.c
2
nca.c
@ -316,7 +316,7 @@ void nca_save(nca_ctx_t *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Save Decrypted NCA. */
|
/* Save Decrypted NCA. */
|
||||||
filepath_t *dec_path = &ctx->tool_ctx->settings.dec_nca_path;
|
filepath_t *dec_path = &ctx->tool_ctx->settings.plaintext_path;
|
||||||
|
|
||||||
if (dec_path->valid == VALIDITY_VALID) {
|
if (dec_path->valid == VALIDITY_VALID) {
|
||||||
printf("Saving Decrypted NCA to %s...\n", dec_path->char_path);
|
printf("Saving Decrypted NCA to %s...\n", dec_path->char_path);
|
||||||
|
34
pki.c
34
pki.c
@ -114,6 +114,11 @@ const nca_keyset_t nca_keys_retail = {
|
|||||||
ZEROES_KEY, /* Key Area Encryption Key Source System */
|
ZEROES_KEY, /* Key Area Encryption Key Source System */
|
||||||
ZEROES_KEY, /* Titlekek Source */
|
ZEROES_KEY, /* Titlekek Source */
|
||||||
ZEROES_KEY, /* Headerkek Source */
|
ZEROES_KEY, /* Headerkek Source */
|
||||||
|
ZEROES_KEY, /* SD Card kek Source. */
|
||||||
|
{
|
||||||
|
ZEROES_XTS_KEY, /* SD Card Key Source, for NCAs. */
|
||||||
|
ZEROES_XTS_KEY, /* SD Card Key Source, for saves. */
|
||||||
|
},
|
||||||
ZEROES_XTS_KEY, /* Encrypted Header Key */
|
ZEROES_XTS_KEY, /* Encrypted Header Key */
|
||||||
ZEROES_XTS_KEY, /* Header key */
|
ZEROES_XTS_KEY, /* Header key */
|
||||||
{
|
{
|
||||||
@ -184,6 +189,10 @@ const nca_keyset_t nca_keys_retail = {
|
|||||||
ZEROES_KAEKS, /* Key Area Encryption Keyset 30 */
|
ZEROES_KAEKS, /* Key Area Encryption Keyset 30 */
|
||||||
ZEROES_KAEKS /* Key Area Encryption Keyset 31 */
|
ZEROES_KAEKS /* Key Area Encryption Keyset 31 */
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ZEROES_XTS_KEY, /* SD Card Key, for NCAs. */
|
||||||
|
ZEROES_XTS_KEY, /* SD Card Key, for saves. */
|
||||||
|
},
|
||||||
{ /* Fixed RSA key used to validate NCA signature 0. */
|
{ /* Fixed RSA key used to validate NCA signature 0. */
|
||||||
0xBF, 0xBE, 0x40, 0x6C, 0xF4, 0xA7, 0x80, 0xE9, 0xF0, 0x7D, 0x0C, 0x99, 0x61, 0x1D, 0x77, 0x2F,
|
0xBF, 0xBE, 0x40, 0x6C, 0xF4, 0xA7, 0x80, 0xE9, 0xF0, 0x7D, 0x0C, 0x99, 0x61, 0x1D, 0x77, 0x2F,
|
||||||
0x96, 0xBC, 0x4B, 0x9E, 0x58, 0x38, 0x1B, 0x03, 0xAB, 0xB1, 0x75, 0x49, 0x9F, 0x2B, 0x4D, 0x58,
|
0x96, 0xBC, 0x4B, 0x9E, 0x58, 0x38, 0x1B, 0x03, 0xAB, 0xB1, 0x75, 0x49, 0x9F, 0x2B, 0x4D, 0x58,
|
||||||
@ -351,6 +360,11 @@ const nca_keyset_t nca_keys_dev = {
|
|||||||
ZEROES_KEY, /* Key Area Encryption Key Source System */
|
ZEROES_KEY, /* Key Area Encryption Key Source System */
|
||||||
ZEROES_KEY, /* Titlekek Source */
|
ZEROES_KEY, /* Titlekek Source */
|
||||||
ZEROES_KEY, /* Headerkek Source */
|
ZEROES_KEY, /* Headerkek Source */
|
||||||
|
ZEROES_KEY, /* SD Card kek Source. */
|
||||||
|
{
|
||||||
|
ZEROES_XTS_KEY, /* SD Card Key Source, for NCAs. */
|
||||||
|
ZEROES_XTS_KEY, /* SD Card Key Source, for saves. */
|
||||||
|
},
|
||||||
ZEROES_XTS_KEY, /* Encrypted Header Key */
|
ZEROES_XTS_KEY, /* Encrypted Header Key */
|
||||||
ZEROES_XTS_KEY, /* Header key */
|
ZEROES_XTS_KEY, /* Header key */
|
||||||
{
|
{
|
||||||
@ -421,6 +435,10 @@ const nca_keyset_t nca_keys_dev = {
|
|||||||
ZEROES_KAEKS, /* Key Area Encryption Keyset 30 */
|
ZEROES_KAEKS, /* Key Area Encryption Keyset 30 */
|
||||||
ZEROES_KAEKS /* Key Area Encryption Keyset 31 */
|
ZEROES_KAEKS /* Key Area Encryption Keyset 31 */
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ZEROES_XTS_KEY, /* SD Card Key, for NCAs. */
|
||||||
|
ZEROES_XTS_KEY, /* SD Card Key, for saves. */
|
||||||
|
},
|
||||||
{
|
{
|
||||||
0xD8, 0xF1, 0x18, 0xEF, 0x32, 0x72, 0x4C, 0xA7, 0x47, 0x4C, 0xB9, 0xEA, 0xB3, 0x04, 0xA8, 0xA4,
|
0xD8, 0xF1, 0x18, 0xEF, 0x32, 0x72, 0x4C, 0xA7, 0x47, 0x4C, 0xB9, 0xEA, 0xB3, 0x04, 0xA8, 0xA4,
|
||||||
0xAC, 0x99, 0x08, 0x08, 0x04, 0xBF, 0x68, 0x57, 0xB8, 0x43, 0x94, 0x2B, 0xC7, 0xB9, 0x66, 0x49,
|
0xAC, 0x99, 0x08, 0x08, 0x04, 0xBF, 0x68, 0x57, 0xB8, 0x43, 0x94, 0x2B, 0xC7, 0xB9, 0x66, 0x49,
|
||||||
@ -539,6 +557,21 @@ void pki_derive_keys(nca_keyset_t *keyset) {
|
|||||||
free_aes_ctx(header_ctx);
|
free_aes_ctx(header_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Derive SD Card Key */
|
||||||
|
if (i == 0 && memcmp(keyset->sd_card_kek_source, zeroes, 0x10) != 0) {
|
||||||
|
unsigned char sd_kek[0x10];
|
||||||
|
generate_kek(sd_kek, keyset->sd_card_kek_source, keyset->master_keys[i], keyset->aes_kek_generation_source, keyset->aes_key_generation_source);
|
||||||
|
aes_ctx_t *sd_ctx = new_aes_ctx(sd_kek, 0x10, AES_MODE_ECB);
|
||||||
|
|
||||||
|
for (unsigned int k = 0; k < 2; k++) {
|
||||||
|
if (memcmp(keyset->sd_card_key_sources[k], zeroes, 0x20) != 0) {
|
||||||
|
aes_decrypt(sd_ctx, keyset->sd_card_keys[k], keyset->sd_card_key_sources[k], 0x20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free_aes_ctx(sd_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
free_aes_ctx(master_ctx);
|
free_aes_ctx(master_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,5 +591,4 @@ void pki_initialize_keyset(nca_keyset_t *keyset, keyset_variant_t variant) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pki_derive_keys(keyset);
|
pki_derive_keys(keyset);
|
||||||
|
|
||||||
}
|
}
|
12
settings.h
12
settings.h
@ -27,10 +27,13 @@ typedef struct {
|
|||||||
unsigned char key_area_key_system_source[0x10]; /* Seed for kaek 2. */
|
unsigned char key_area_key_system_source[0x10]; /* Seed for kaek 2. */
|
||||||
unsigned char titlekek_source[0x10]; /* Seed for titlekeks. */
|
unsigned char titlekek_source[0x10]; /* Seed for titlekeks. */
|
||||||
unsigned char header_kek_source[0x10]; /* Seed for header kek. */
|
unsigned char header_kek_source[0x10]; /* Seed for header kek. */
|
||||||
|
unsigned char sd_card_kek_source[0x10]; /* Seed for SD card kek. */
|
||||||
|
unsigned char sd_card_key_sources[2][0x20]; /* Seed for SD card encryption keys. */
|
||||||
unsigned char encrypted_header_key[0x20]; /* Actual encrypted header key. */
|
unsigned char encrypted_header_key[0x20]; /* Actual encrypted header key. */
|
||||||
unsigned char header_key[0x20]; /* NCA header key. */
|
unsigned char header_key[0x20]; /* NCA header key. */
|
||||||
unsigned char titlekeks[0x20][0x10]; /* Title key encryption keys. */
|
unsigned char titlekeks[0x20][0x10]; /* Title key encryption keys. */
|
||||||
unsigned char key_area_keys[0x20][3][0x10]; /* Key area encryption keys. */
|
unsigned char key_area_keys[0x20][3][0x10]; /* Key area encryption keys. */
|
||||||
|
unsigned char sd_card_keys[2][0x20];
|
||||||
unsigned char nca_hdr_fixed_key_modulus[0x100]; /* NCA header fixed key RSA pubk. */
|
unsigned char nca_hdr_fixed_key_modulus[0x100]; /* NCA header fixed key RSA pubk. */
|
||||||
unsigned char acid_fixed_key_modulus[0x100]; /* ACID fixed key RSA pubk. */
|
unsigned char acid_fixed_key_modulus[0x100]; /* ACID fixed key RSA pubk. */
|
||||||
unsigned char package2_fixed_key_modulus[0x100]; /* Package2 Header RSA pubk. */
|
unsigned char package2_fixed_key_modulus[0x100]; /* Package2 Header RSA pubk. */
|
||||||
@ -48,6 +51,8 @@ typedef struct {
|
|||||||
unsigned char dec_titlekey[0x10];
|
unsigned char dec_titlekey[0x10];
|
||||||
int has_contentkey;
|
int has_contentkey;
|
||||||
unsigned char contentkey[0x10];
|
unsigned char contentkey[0x10];
|
||||||
|
int has_sdseed;
|
||||||
|
unsigned char sdseed[0x10];
|
||||||
filepath_t section_paths[4];
|
filepath_t section_paths[4];
|
||||||
filepath_t section_dir_paths[4];
|
filepath_t section_dir_paths[4];
|
||||||
override_filepath_t exefs_path;
|
override_filepath_t exefs_path;
|
||||||
@ -60,12 +65,14 @@ typedef struct {
|
|||||||
filepath_t pk11_dir_path;
|
filepath_t pk11_dir_path;
|
||||||
filepath_t pk21_dir_path;
|
filepath_t pk21_dir_path;
|
||||||
filepath_t ini1_dir_path;
|
filepath_t ini1_dir_path;
|
||||||
filepath_t dec_nca_path;
|
filepath_t plaintext_path;
|
||||||
filepath_t rootpt_dir_path;
|
filepath_t rootpt_dir_path;
|
||||||
filepath_t update_dir_path;
|
filepath_t update_dir_path;
|
||||||
filepath_t normal_dir_path;
|
filepath_t normal_dir_path;
|
||||||
filepath_t secure_dir_path;
|
filepath_t secure_dir_path;
|
||||||
filepath_t header_path;
|
filepath_t header_path;
|
||||||
|
filepath_t nax0_path;
|
||||||
|
filepath_t nax0_sd_path;
|
||||||
} hactool_settings_t;
|
} hactool_settings_t;
|
||||||
|
|
||||||
enum hactool_file_type
|
enum hactool_file_type
|
||||||
@ -79,7 +86,8 @@ enum hactool_file_type
|
|||||||
FILETYPE_PACKAGE1,
|
FILETYPE_PACKAGE1,
|
||||||
FILETYPE_PACKAGE2,
|
FILETYPE_PACKAGE2,
|
||||||
FILETYPE_INI1,
|
FILETYPE_INI1,
|
||||||
FILETYPE_KIP1
|
FILETYPE_KIP1,
|
||||||
|
FILETYPE_NAX0
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ACTION_INFO (1<<0)
|
#define ACTION_INFO (1<<0)
|
||||||
|
30
sha.c
30
sha.c
@ -52,4 +52,34 @@ void sha256_hash_buffer(unsigned char *digest, const void *data, size_t l) {
|
|||||||
sha_update(sha_ctx, data, l);
|
sha_update(sha_ctx, data, l);
|
||||||
sha_get_hash(sha_ctx, digest);
|
sha_get_hash(sha_ctx, digest);
|
||||||
free_sha_ctx(sha_ctx);
|
free_sha_ctx(sha_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SHA256-HMAC digest. */
|
||||||
|
void sha256_get_buffer_hmac(void *digest, const void *secret, size_t s_l, const void *data, size_t d_l) {
|
||||||
|
sha_ctx_t *ctx;
|
||||||
|
|
||||||
|
if ((ctx = malloc(sizeof(*ctx))) == NULL) {
|
||||||
|
FATAL_ERROR("Failed to allocate sha_ctx_t!");
|
||||||
|
}
|
||||||
|
|
||||||
|
mbedtls_md_init(&ctx->digest);
|
||||||
|
|
||||||
|
if (mbedtls_md_setup(&ctx->digest, mbedtls_md_info_from_type(HASH_TYPE_SHA256), 1)) {
|
||||||
|
FATAL_ERROR("Failed to set up hash context!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mbedtls_md_hmac_starts(&ctx->digest, secret, s_l)) {
|
||||||
|
FATAL_ERROR("Failed to set up HMAC secret context!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mbedtls_md_hmac_update(&ctx->digest, data, d_l)) {
|
||||||
|
FATAL_ERROR("Failed processing HMAC input!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mbedtls_md_hmac_finish(&ctx->digest, digest)) {
|
||||||
|
FATAL_ERROR("Failed getting HMAC output!");
|
||||||
|
}
|
||||||
|
|
||||||
|
mbedtls_md_free(&ctx->digest);
|
||||||
|
free(ctx);
|
||||||
}
|
}
|
2
sha.h
2
sha.h
@ -22,4 +22,6 @@ void free_sha_ctx(sha_ctx_t *ctx);
|
|||||||
|
|
||||||
void sha256_hash_buffer(unsigned char *digest, const void *data, size_t l);
|
void sha256_hash_buffer(unsigned char *digest, const void *data, size_t l);
|
||||||
|
|
||||||
|
void sha256_get_buffer_hmac(void *digest, const void *secret, size_t s_l, const void *data, size_t d_l);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user