Fusee: Decrypt all keyblobs during key derivation.

This commit is contained in:
Michael Scire 2018-05-16 02:06:59 -06:00
parent f84645e91f
commit fa71e9cb15
2 changed files with 54 additions and 23 deletions

View file

@ -33,6 +33,8 @@ static const uint8_t masterkey_4x_seed[0x10] = {
0x2D, 0xC1, 0xF4, 0x8D, 0xF3, 0x5B, 0x69, 0x33, 0x42, 0x10, 0xAC, 0x65, 0xDA, 0x90, 0x46, 0x66 0x2D, 0xC1, 0xF4, 0x8D, 0xF3, 0x5B, 0x69, 0x33, 0x42, 0x10, 0xAC, 0x65, 0xDA, 0x90, 0x46, 0x66
}; };
static nx_dec_keyblob_t __attribute__((aligned(16))) g_dec_keyblobs[32];
static int get_tsec_key(void *dst, const void *tsec_fw, size_t tsec_fw_size, uint32_t tsec_key_id) { static int get_tsec_key(void *dst, const void *tsec_fw, size_t tsec_fw_size, uint32_t tsec_key_id) {
return tsec_query((u32)tsec_fw, dst, tsec_key_id); return tsec_query((u32)tsec_fw, dst, tsec_key_id);
} }
@ -61,10 +63,43 @@ static bool safe_memcmp(uint8_t *a, uint8_t *b, size_t sz) {
return different != 0; return different != 0;
} }
static int decrypt_keyblob(const nx_keyblob_t *keyblobs, uint32_t revision, uint32_t available_revision) {
nx_keyblob_t keyblob;
nx_dec_keyblob_t *dec = &g_dec_keyblobs[revision];
uint8_t work_buffer[0x10];
if (get_keyblob(&keyblob, revision, keyblobs, available_revision) != 0) {
return -1;
}
se_aes_ecb_decrypt_block(0xD, work_buffer, 0x10, keyblob_seeds[revision], 0x10);
decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, 0xE, work_buffer, 0x10);
decrypt_data_into_keyslot(0xB, KEYSLOT_SWITCH_TEMPKEY, keyblob_mac_seed, 0x10);
/* Validate keyblob. */
se_compute_aes_128_cmac(0xB, work_buffer, 0x10, keyblob.mac + sizeof(keyblob.mac), sizeof(keyblob) - sizeof(keyblob.mac));
if (safe_memcmp(keyblob.mac, work_buffer, 0x10)) {
return -1;
}
/* Decrypt keyblob. */
se_aes_ctr_crypt(0xD, dec, sizeof(dec), keyblob.data, sizeof(keyblob.data), keyblob.ctr, sizeof(keyblob.ctr));
return 0;
}
int load_package1_key(uint32_t revision) {
if (revision > MASTERKEY_REVISION_500_CURRENT) {
return -1;
}
set_aes_keyslot(0xB, g_dec_keyblobs[revision].keys[8], 0x10);
return 0;
}
/* Derive all Switch keys. */ /* Derive all Switch keys. */
int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size) { int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size) {
uint8_t work_buffer[0x10]; uint8_t work_buffer[0x10];
nx_keyblob_t keyblob; nx_dec_keyblob_t dec_keyblob;
/* TODO: Set keyslot flags properly in preparation of derivation. */ /* TODO: Set keyslot flags properly in preparation of derivation. */
set_aes_keyslot_flags(0xE, 0x15); set_aes_keyslot_flags(0xE, 0x15);
@ -75,13 +110,16 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
return -1; return -1;
} }
set_aes_keyslot(0xD, work_buffer, 0x10); set_aes_keyslot(0xD, work_buffer, 0x10);
/* Get keyblob, always try to set up the highest possible master key. */ /* Decrypt all keyblobs. */
/* TODO: Should we iterate, trying lower keys on failure? */ for (unsigned int rev = MASTERKEY_REVISION_100_230; rev < MASTERKEY_REVISION_500_CURRENT; rev++) {
if (get_keyblob(&keyblob, MASTERKEY_REVISION_500_CURRENT, keyblobs, available_revision) != 0) { int ret = decrypt_keyblob(keyblobs, rev, available_revision);
return -1; if (ret) {
return ret;
}
} }
/* Always try to set up the highest possible master key. */
/* Derive both keyblob key 1, and keyblob key latest. */ /* Derive both keyblob key 1, and keyblob key latest. */
se_aes_ecb_decrypt_block(0xD, work_buffer, 0x10, keyblob_seeds[MASTERKEY_REVISION_100_230], 0x10); se_aes_ecb_decrypt_block(0xD, work_buffer, 0x10, keyblob_seeds[MASTERKEY_REVISION_100_230], 0x10);
decrypt_data_into_keyslot(0xF, 0xE, work_buffer, 0x10); decrypt_data_into_keyslot(0xF, 0xE, work_buffer, 0x10);
@ -90,24 +128,12 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
/* Clear the SBK. */ /* Clear the SBK. */
clear_aes_keyslot(0xE); clear_aes_keyslot(0xE);
se_aes_ecb_decrypt_block(0xD, work_buffer, 0x10, keyblob_mac_seed, 0x10);
decrypt_data_into_keyslot(0xB, 0xD, keyblob_mac_seed, 0x10); /* Get latest keyblob. */
dec_keyblob = g_dec_keyblobs[MASTERKEY_REVISION_500_CURRENT];
/* Validate keyblob. */
se_compute_aes_128_cmac(0xB, work_buffer, 0x10, keyblob.mac + sizeof(keyblob.mac), sizeof(keyblob) - sizeof(keyblob.mac));
if (safe_memcmp(keyblob.mac, work_buffer, 0x10)) {
return -1;
}
/* Decrypt keyblob. */
se_aes_ctr_crypt(0xD, keyblob.data, sizeof(keyblob.data), keyblob.data, sizeof(keyblob.data), keyblob.ctr, sizeof(keyblob.ctr));
/* Get needed data. */ /* Get needed data. */
set_aes_keyslot(0xC, keyblob.keys[0], 0x10); set_aes_keyslot(0xC, dec_keyblob.keys[0], 0x10);
set_aes_keyslot(0xB, keyblob.keys[8], 0x10);
/* Clear keyblob. */
memset(keyblob.data, 0, sizeof(keyblob.data));
/* Derive keys for Exosphere, lock critical keyslots. */ /* Derive keys for Exosphere, lock critical keyslots. */
switch (target_firmware) { switch (target_firmware) {

View file

@ -22,17 +22,22 @@ typedef enum BisPartition {
BisPartition_UserSystem = 2, BisPartition_UserSystem = 2,
} BisPartition; } BisPartition;
typedef struct {
uint8_t keys[9][0x10];
} nx_dec_keyblob_t;
typedef struct nx_keyblob_t { typedef struct nx_keyblob_t {
uint8_t mac[0x10]; uint8_t mac[0x10];
uint8_t ctr[0x10]; uint8_t ctr[0x10];
union { union {
uint8_t data[0x90]; uint8_t data[0x90];
uint8_t keys[9][0x10]; nx_dec_keyblob_t dec_blob;
}; };
} nx_keyblob_t; } nx_keyblob_t;
/* TSEC fw must be 0x100-byte-aligned. */ /* TSEC fw must be 0x100-byte-aligned. */
int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size); int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size);
int load_package1_key(uint32_t revision);
void finalize_nx_keydata(uint32_t target_firmware); void finalize_nx_keydata(uint32_t target_firmware);
void derive_bis_key(void *dst, BisPartition partition_id, uint32_t target_firmware); void derive_bis_key(void *dst, BisPartition partition_id, uint32_t target_firmware);