diff --git a/source/keys/key_sources.inl b/source/keys/key_sources.inl index 9bfdbb0..02063b2 100644 --- a/source/keys/key_sources.inl +++ b/source/keys/key_sources.inl @@ -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}; diff --git a/source/keys/keys.c b/source/keys/keys.c index 43702ff..f17a66d 100644 --- a/source/keys/keys.c +++ b/source/keys/keys.c @@ -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; diff --git a/source/keys/keys.h b/source/keys/keys.h index 8e2fb23..ccb1e66 100644 --- a/source/keys/keys.h +++ b/source/keys/keys.h @@ -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 diff --git a/source/main.c b/source/main.c index b4aa670..368b820 100644 --- a/source/main.c +++ b/source/main.c @@ -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);