Add Amiibo key dump support

This commit is contained in:
shchmue 2022-03-31 18:30:57 -06:00
parent 5f35c8396d
commit 065ba8bc11
4 changed files with 182 additions and 25 deletions

View File

@ -209,6 +209,8 @@ static const u8 device_master_kek_sources_dev[KB_FIRMWARE_VERSION_MAX - KB_FIRMW
// from SPL
static const u8 aes_key_generation_source[0x10] __attribute__((aligned(4))) = {
0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8};
static const u8 aes_key_decryption_source[0x10] __attribute__((aligned(4))) = {
0x11, 0x70, 0x24, 0x2B, 0x48, 0x69, 0x11, 0xF1, 0x11, 0xB0, 0x0C, 0x47, 0x7C, 0xC3, 0xEF, 0x7E};
// from FS
static const u8 bis_kek_source[0x10] __attribute__((aligned(4))) = {
@ -250,3 +252,31 @@ static const u8 sd_card_nca_key_source[0x20] __attribute__((aligned(4))) = {
static const u8 sd_card_save_key_source[0x20] __attribute__((aligned(4))) = {
0X24, 0X49, 0XB7, 0X22, 0X72, 0X67, 0X03, 0XA8, 0X19, 0X65, 0XE6, 0XE3, 0XEA, 0X58, 0X2F, 0XDD,
0X9A, 0X95, 0X15, 0X17, 0XB1, 0X6E, 0X8F, 0X7F, 0X1F, 0X68, 0X26, 0X31, 0X52, 0XEA, 0X29, 0X6A};
// from NFC
static const u8 nfc_key_source[0x10] __attribute__((aligned(4))) = {
0x83, 0xF6, 0xEF, 0xD8, 0x13, 0x26, 0x49, 0xAB, 0x97, 0x5F, 0xEA, 0xBA, 0x65, 0x71, 0xCA, 0xCA};
static const u8 encrypted_nfc_keys[0x80] __attribute__((aligned(4))) = {
0x76, 0x50, 0x87, 0x02, 0x40, 0xA6, 0x5A, 0x98, 0xCE, 0x39, 0x2F, 0xC8, 0x83, 0xAF, 0x54, 0x76,
0x28, 0xFF, 0x50, 0xFC, 0xC1, 0xFB, 0x26, 0x14, 0xA2, 0x4A, 0xA6, 0x74, 0x90, 0xA4, 0x37, 0x06,
0x03, 0x63, 0xC2, 0xB1, 0xAF, 0x9F, 0xF7, 0x07, 0xFC, 0x8A, 0xB9, 0xCA, 0x28, 0x68, 0x6E, 0xF7,
0x42, 0xCD, 0x68, 0x13, 0xCD, 0x7B, 0x3A, 0x60, 0x3E, 0x8B, 0xAB, 0x3A, 0xCC, 0xED, 0xE0, 0xDD,
0x71, 0x1F, 0xA5, 0xDE, 0xB8, 0xB1, 0xF5, 0x1D, 0x14, 0x73, 0xBE, 0x27, 0xCC, 0xA1, 0x9B, 0x23,
0x06, 0x91, 0x89, 0x05, 0xED, 0xD6, 0x92, 0x76, 0x3F, 0x42, 0xFB, 0xD1, 0x8F, 0x2D, 0x6D, 0x72,
0xC8, 0x9E, 0x48, 0xE8, 0x03, 0x64, 0xF0, 0x3C, 0x0E, 0x2A, 0xF1, 0x26, 0x83, 0x02, 0x4F, 0xE2,
0x41, 0xAA, 0xC8, 0x33, 0x68, 0x84, 0x3A, 0xFB, 0x87, 0x18, 0xEA, 0xF7, 0x36, 0xA2, 0x4E, 0xA9};
static const u8 encrypted_nfc_keys_dev[0x80] __attribute__((aligned(4))) = {
0x13, 0xB0, 0xFB, 0xC2, 0x91, 0x6D, 0x6E, 0x5A, 0x10, 0x31, 0x40, 0xB7, 0xDF, 0xCF, 0x69, 0x69,
0xB0, 0xFA, 0xAE, 0x7F, 0xB2, 0x4D, 0x27, 0xC9, 0xE9, 0x3F, 0x5B, 0x38, 0x39, 0x24, 0x98, 0xCE,
0xED, 0xD2, 0xA9, 0x6C, 0x6F, 0xA7, 0x72, 0xD7, 0x11, 0x31, 0x17, 0x93, 0x12, 0x49, 0x32, 0x85,
0x21, 0xE5, 0xE1, 0x88, 0x0F, 0x08, 0xF2, 0x30, 0x5C, 0xC3, 0xAA, 0xFF, 0xC0, 0xAB, 0x21, 0x96,
0x74, 0x39, 0xED, 0xE0, 0x5A, 0xB6, 0x75, 0xC2, 0x3B, 0x08, 0x61, 0xE4, 0xA7, 0xD6, 0xED, 0x8C,
0xA9, 0x02, 0x12, 0xA6, 0xCC, 0x27, 0x4C, 0x1C, 0x41, 0x9C, 0xD8, 0x4C, 0x00, 0xC7, 0x5B, 0x5D,
0xED, 0xC2, 0x3D, 0x5E, 0x00, 0xF5, 0x49, 0xFA, 0x6C, 0x75, 0x67, 0xCF, 0x1F, 0x73, 0x1A, 0xE8,
0x47, 0xD4, 0x3D, 0x9B, 0x83, 0x5B, 0x18, 0x2F, 0x95, 0xA9, 0x04, 0xBC, 0x2E, 0xBB, 0x64, 0x4A};
static const u8 nfc_blob_hash[0x20] __attribute__((aligned(4))) = {
0x7F, 0x92, 0x83, 0x65, 0x4E, 0xC1, 0x09, 0x7F, 0xBD, 0xFF, 0x31, 0xDE, 0x94, 0x66, 0x51, 0xAE,
0x60, 0xC2, 0x85, 0x4A, 0xFB, 0x54, 0x4A, 0xBE, 0x89, 0x63, 0xD3, 0x89, 0x63, 0x9C, 0x71, 0x0E};
static const u8 nfc_blob_hash_dev[0x20] __attribute__((aligned(4))) = {
0x4E, 0x36, 0x59, 0x1C, 0x75, 0x80, 0x23, 0x03, 0x98, 0x2D, 0x45, 0xD9, 0x85, 0xB8, 0x60, 0x18,
0x7C, 0x85, 0x37, 0x9B, 0xCB, 0xBA, 0xF3, 0xDC, 0x25, 0x38, 0x73, 0xDB, 0x2F, 0xFA, 0xAE, 0x26};

View File

@ -74,6 +74,7 @@ static int _key_exists(const void *data) { return memcmp(data, "\x00\x00\x00\x0
static void _save_key(const char *name, const void *data, u32 len, char *outbuf);
static void _save_key_family(const char *name, const void *data, u32 start_key, u32 num_keys, u32 len, char *outbuf);
static void _generate_kek(u32 ks, const void *key_source, const void *master_key, const void *kek_seed, const void *key_seed);
static void _decrypt_aes_key(u32 ks, void *dst, const void *key_source, const void *master_key);
static void _generate_specific_aes_key(u32 ks, key_derivation_ctx_t *keys, void *out_key, const void *key_source, u32 key_generation);
static void _get_device_key(u32 ks, key_derivation_ctx_t *keys, void *out_device_key, u32 revision);
// titlekey functions
@ -584,6 +585,11 @@ static bool _derive_emmc_keys(key_derivation_ctx_t *keys, titlekey_buffer_t *tit
// This allows for a manageable brute force on a PC
// Then the Mariko AES class keys, KEK, BEK, unique SBK and SSK can be recovered
int save_mariko_partial_keys(u32 start, u32 count, bool append) {
const char *keyfile_path = "sd:/switch/partialaes.keys";
if (!f_stat(keyfile_path, NULL)) {
f_unlink(keyfile_path);
}
if (start + count > SE_AES_KEYSLOT_COUNT) {
return 1;
}
@ -592,6 +598,8 @@ int save_mariko_partial_keys(u32 start, u32 count, bool append) {
gfx_clear_partial_grey(0x1B, 32, 1224);
gfx_con_setpos(0, 32);
color_idx = 0;
u32 pos = 0;
u32 zeros[AES_128_KEY_SIZE / 4] = {0};
u8 *data = malloc(4 * AES_128_KEY_SIZE);
@ -655,7 +663,7 @@ int save_mariko_partial_keys(u32 start, u32 count, bool append) {
return 3;
}
if (f_open(&fp, "sd:/switch/partialaes.keys", mode)) {
if (f_open(&fp, keyfile_path, mode)) {
EPRINTF("Unable to write partial keys to SD.");
free(text_buffer);
return 3;
@ -664,7 +672,7 @@ int save_mariko_partial_keys(u32 start, u32 count, bool append) {
f_write(&fp, text_buffer, strlen(text_buffer), NULL);
f_close(&fp);
gfx_printf("%kWrote partials to sd:/switch/partialaes.keys\n", colors[(color_idx++) % 6]);
gfx_printf("%kWrote partials to %s\n", colors[(color_idx++) % 6], keyfile_path);
free(text_buffer);
@ -757,16 +765,15 @@ static void _save_keys_to_sd(key_derivation_ctx_t *keys, titlekey_buffer_t *titl
gfx_printf("%kFound through master_key_%02x.\n\n", colors[(color_idx++) % 6], KB_FIRMWARE_VERSION_MAX);
f_mkdir("sd:/switch");
char keyfile_path[30] = "sd:/switch/prod.keys";
if (is_dev) {
s_printf(&keyfile_path[11], "dev.keys");
}
const char *keyfile_path = is_dev ? "sd:/switch/dev.keys" : "sd:/switch/prod.keys";
FILINFO fno;
if (!sd_save_to_file(text_buffer, strlen(text_buffer), keyfile_path) && !f_stat(keyfile_path, &fno)) {
gfx_printf("%kWrote %d bytes to %s\n", colors[(color_idx++) % 6], (u32)fno.fsize, keyfile_path);
} else
} else {
EPRINTF("Unable to save keys to SD.");
}
if (_titlekey_count == 0 || !titlekey_buffer) {
free(text_buffer);
@ -784,11 +791,13 @@ static void _save_keys_to_sd(key_derivation_ctx_t *keys, titlekey_buffer_t *titl
s_printf(&titlekey_text[i].titlekey[j * 2], "%02x", titlekey_buffer->titlekeys[i][j]);
s_printf(titlekey_text[i].newline, "\n");
}
s_printf(&keyfile_path[11], "title.keys");
keyfile_path = "sd:/switch/title.keys";
if (!sd_save_to_file(text_buffer, strlen(text_buffer), keyfile_path) && !f_stat(keyfile_path, &fno)) {
gfx_printf("%kWrote %d bytes to %s\n", colors[(color_idx++) % 6], (u32)fno.fsize, keyfile_path);
} else
} else {
EPRINTF("Unable to save titlekeys to SD.");
}
free(text_buffer);
}
@ -831,10 +840,6 @@ static void _derive_master_keys(key_derivation_ctx_t *prod_keys, key_derivation_
}
static void _derive_keys() {
if (!f_stat("sd:/switch/partialaes.keys", NULL)) {
f_unlink("sd:/switch/partialaes.keys");
}
minerva_periodic_training();
if (!_check_keyslot_access()) {
@ -875,10 +880,13 @@ static void _derive_keys() {
minerva_periodic_training();
_derive_non_unique_keys(&prod_keys, is_dev);
minerva_periodic_training();
_derive_non_unique_keys(&dev_keys, is_dev);
minerva_periodic_training();
_derive_per_generation_keys(&prod_keys);
minerva_periodic_training();
_derive_per_generation_keys(&dev_keys);
@ -907,6 +915,89 @@ static void _derive_keys() {
}
}
void derive_amiibo_keys() {
minerva_change_freq(FREQ_1600);
bool is_dev = fuse_read_hw_state() == FUSE_NX_HW_STATE_DEV;
key_derivation_ctx_t __attribute__((aligned(4))) prod_keys = {0}, dev_keys = {0};
key_derivation_ctx_t *keys = is_dev ? &dev_keys : &prod_keys;
const u8 *encrypted_keys = is_dev ? encrypted_nfc_keys_dev : encrypted_nfc_keys;
_derive_master_keys(&prod_keys, &dev_keys, is_dev);
minerva_periodic_training();
display_backlight_brightness(h_cfg.backlight, 1000);
gfx_clear_partial_grey(0x1B, 32, 1224);
gfx_con_setpos(0, 32);
color_idx = 0;
minerva_periodic_training();
if (!_key_exists(keys->master_key[0])) {
EPRINTF("Unable to derive master keys for NFC.");
minerva_change_freq(FREQ_800);
btn_wait();
return;
}
_decrypt_aes_key(8, keys->temp_key, nfc_key_source, keys->master_key[0]);
nfc_keyblob_t __attribute__((aligned(4))) nfc_keyblob;
static const u8 nfc_iv[AES_128_KEY_SIZE] = {
0xB9, 0x1D, 0xC1, 0xCF, 0x33, 0x5F, 0xA6, 0x13, 0x2A, 0xEF, 0x90, 0x99, 0xAA, 0xCA, 0x93, 0xC8};
se_aes_key_set(6, keys->temp_key, AES_128_KEY_SIZE);
se_aes_crypt_ctr(6, &nfc_keyblob, sizeof(nfc_keyblob), encrypted_keys, sizeof(nfc_keyblob), &nfc_iv);
minerva_periodic_training();
u8 xor_pad[0x20] __attribute__((aligned(4))) = {0};
se_aes_key_set(6, nfc_keyblob.ctr_key, AES_128_KEY_SIZE);
se_aes_crypt_ctr(6, xor_pad, sizeof(xor_pad), xor_pad, sizeof(xor_pad), nfc_keyblob.ctr_iv);
minerva_periodic_training();
nfc_save_key_t __attribute__((aligned(4))) nfc_save_keys[2] = {0};
memcpy(nfc_save_keys[0].hmac_key, nfc_keyblob.hmac_key, sizeof(nfc_keyblob.hmac_key));
memcpy(nfc_save_keys[0].phrase, nfc_keyblob.phrase, sizeof(nfc_keyblob.phrase));
nfc_save_keys[0].seed_size = sizeof(nfc_keyblob.seed);
memcpy(nfc_save_keys[0].seed, nfc_keyblob.seed, sizeof(nfc_keyblob.seed));
memcpy(nfc_save_keys[0].xor_pad, xor_pad, sizeof(xor_pad));
memcpy(nfc_save_keys[1].hmac_key, nfc_keyblob.hmac_key_for_verif, sizeof(nfc_keyblob.hmac_key_for_verif));
memcpy(nfc_save_keys[1].phrase, nfc_keyblob.phrase_for_verif, sizeof(nfc_keyblob.phrase_for_verif));
nfc_save_keys[1].seed_size = sizeof(nfc_keyblob.seed_for_verif);
memcpy(nfc_save_keys[1].seed, nfc_keyblob.seed_for_verif, sizeof(nfc_keyblob.seed_for_verif));
memcpy(nfc_save_keys[1].xor_pad, xor_pad, sizeof(xor_pad));
minerva_periodic_training();
u8 hash[0x20] = {0};
se_calc_sha256_oneshot(hash, &nfc_save_keys[0], sizeof(nfc_save_keys));
if (memcmp(hash, is_dev ? nfc_blob_hash_dev : nfc_blob_hash, sizeof(hash)) != 0) {
EPRINTF("Amiibo hash mismatch. Skipping save.");
minerva_change_freq(FREQ_800);
btn_wait();
return;
}
const char *keyfile_path = is_dev ? "sd:/switch/key_dev.bin" : "sd:/switch/key_retail.bin";
if (!sd_save_to_file(&nfc_save_keys[0], sizeof(nfc_save_keys), keyfile_path)) {
gfx_printf("%kWrote Amiibo keys to\n %s\n", colors[(color_idx++) % 6], keyfile_path);
} else {
EPRINTF("Unable to save Amiibo keys to SD.");
}
gfx_printf("\n%kPress a button to return to the menu.", colors[(color_idx++) % 6]);
minerva_change_freq(FREQ_800);
btn_wait();
gfx_clear_grey(0x1B);
}
void dump_keys() {
minerva_change_freq(FREQ_1600);
@ -969,6 +1060,8 @@ static void _save_key_family(const char *name, const void *data, u32 start_key,
free(temp_name);
}
// Equivalent to spl::GenerateAesKek. When key_seed is set, the result is as if spl::GenerateAesKey was called immediately after.
// The generation and option args are dictated by master_key and kek_seed.
static void _generate_kek(u32 ks, const void *key_source, const void *master_key, const void *kek_seed, const void *key_seed) {
if (!_key_exists(key_source) || !_key_exists(master_key) || !_key_exists(kek_seed))
return;
@ -980,6 +1073,12 @@ static void _generate_kek(u32 ks, const void *key_source, const void *master_key
se_aes_unwrap_key(ks, ks, key_seed);
}
// Equivalent to spl::DecryptAesKey.
static void _decrypt_aes_key(u32 ks, void *dst, const void *key_source, const void *master_key) {
_generate_kek(ks, aes_key_decryption_source, master_key, aes_kek_generation_source, aes_key_generation_source);
se_aes_crypt_block_ecb(ks, 0, dst, key_source);
}
static void _get_secure_data(key_derivation_ctx_t *keys, void *dst) {
se_aes_key_set(6, keys->device_key, AES_128_KEY_SIZE);
u8 *d = (u8 *)dst;

View File

@ -83,6 +83,27 @@ typedef struct {
u8 unused[0x150];
} encrypted_keyblob_t;
typedef struct {
char phrase[0xE];
u8 seed[0xE];
u8 hmac_key[0x10];
char phrase_for_verif[0xE];
u8 seed_for_verif[0x10];
u8 hmac_key_for_verif[0x10];
u8 ctr_key[0x10];
u8 ctr_iv[0x10];
u8 pad[6];
} nfc_keyblob_t;
typedef struct {
u8 hmac_key[0x10];
char phrase[0xE];
u8 rsvd;
u8 seed_size;
u8 seed[0x10];
u8 xor_pad[0x20];
} nfc_save_key_t;
typedef struct {
u8 temp_key[AES_128_KEY_SIZE],
bis_key[4][AES_128_KEY_SIZE * 2],
@ -142,5 +163,6 @@ typedef struct {
void dump_keys();
int save_mariko_partial_keys(u32 start, u32 count, bool append);
void derive_amiibo_keys();
#endif

View File

@ -304,6 +304,11 @@ void dump_emunand()
dump_keys();
}
void dump_amiibo_keys()
{
derive_amiibo_keys();
}
void dump_mariko_partial_keys();
ment_t ment_partials[] = {
@ -340,14 +345,15 @@ ment_t ment_top[] = {
MDEF_HANDLER("Dump from SysNAND", dump_sysnand, colors[0]),
MDEF_HANDLER("Dump from EmuNAND", dump_emunand, colors[1]),
MDEF_CAPTION("---------------", colors[2]),
MDEF_MENU("Dump Mariko Partials (requires reboot)", &menu_partials, colors[3]),
MDEF_CAPTION("---------------", colors[4]),
MDEF_HANDLER("Payloads...", launch_tools, colors[5]),
MDEF_HANDLER("Reboot to hekate", launch_hekate, colors[0]),
MDEF_CAPTION("---------------", colors[1]),
MDEF_HANDLER_EX("Reboot (OFW)", &STATE_REBOOT_BYPASS_FUSES, power_set_state_ex, colors[2]),
MDEF_HANDLER_EX("Reboot (RCM)", &STATE_REBOOT_RCM, power_set_state_ex, colors[3]),
MDEF_HANDLER_EX("Power off", &STATE_POWER_OFF, power_set_state_ex, colors[4]),
MDEF_HANDLER("Dump Amiibo Keys", dump_amiibo_keys, colors[3]),
MDEF_MENU("Dump Mariko Partials (requires reboot)", &menu_partials, colors[4]),
MDEF_CAPTION("---------------", colors[5]),
MDEF_HANDLER("Payloads...", launch_tools, colors[0]),
MDEF_HANDLER("Reboot to hekate", launch_hekate, colors[1]),
MDEF_CAPTION("---------------", colors[2]),
MDEF_HANDLER_EX("Reboot (OFW)", &STATE_REBOOT_BYPASS_FUSES, power_set_state_ex, colors[3]),
MDEF_HANDLER_EX("Reboot (RCM)", &STATE_REBOOT_RCM, power_set_state_ex, colors[4]),
MDEF_HANDLER_EX("Power off", &STATE_POWER_OFF, power_set_state_ex, colors[5]),
MDEF_END()
};
@ -369,7 +375,7 @@ void dump_mariko_partial_keys()
// Grey out dumping menu items as the keyslots have been invalidated.
grey_out_menu_item(&ment_top[0]);
grey_out_menu_item(&ment_top[1]);
grey_out_menu_item(&ment_top[3]);
grey_out_menu_item(&ment_top[4]);
grey_out_menu_item(&ment_partials[18]);
}
@ -433,18 +439,18 @@ void ipl_main()
// Grey out reboot to RCM option if on Mariko or patched console.
if (h_cfg.t210b01 || h_cfg.rcm_patched)
{
grey_out_menu_item(&ment_top[9]);
grey_out_menu_item(&ment_top[10]);
}
// Grey out Mariko partial dump option on Erista.
if (!h_cfg.t210b01) {
grey_out_menu_item(&ment_top[3]);
grey_out_menu_item(&ment_top[4]);
}
// Grey out reboot to hekate option if no update.bin found.
if (f_stat("bootloader/update.bin", NULL))
{
grey_out_menu_item(&ment_top[6]);
grey_out_menu_item(&ment_top[7]);
}
minerva_change_freq(FREQ_800);