Fix bug in user_generate_specific_aes_key, add other read/write le/be funcs

This commit is contained in:
TuxSH 2018-03-07 15:11:53 +01:00
parent 71f01aaa43
commit 4f0e8b8467
3 changed files with 136 additions and 120 deletions

View file

@ -43,16 +43,16 @@ uint32_t user_exp_mod(smc_args_t *args) {
uint8_t modulus[0x100]; uint8_t modulus[0x100];
uint8_t exponent[0x100]; uint8_t exponent[0x100];
uint8_t input[0x100]; uint8_t input[0x100];
upage_ref_t page_ref; upage_ref_t page_ref;
/* Validate size. */ /* Validate size. */
if (args->X[4] == 0 || args->X[4] > 0x100 || (args->X[4] & 3) != 0) { if (args->X[4] == 0 || args->X[4] > 0x100 || (args->X[4] & 3) != 0) {
return 2; return 2;
} }
size_t exponent_size = (size_t)args->X[4]; size_t exponent_size = (size_t)args->X[4];
void *user_input = (void *)args->X[1]; void *user_input = (void *)args->X[1];
void *user_exponent = (void *)args->X[2]; void *user_exponent = (void *)args->X[2];
void *user_modulus = (void *)args->X[3]; void *user_modulus = (void *)args->X[3];
@ -66,16 +66,16 @@ uint32_t user_exp_mod(smc_args_t *args) {
} }
if (user_copy_to_secure(&page_ref, exponent, user_exponent, exponent_size) == 0) { if (user_copy_to_secure(&page_ref, exponent, user_exponent, exponent_size) == 0) {
return 2; return 2;
} }
if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) {
return 2; return 2;
} }
set_exp_mod_done(false); set_exp_mod_done(false);
/* Hardcode RSA keyslot 0. */ /* Hardcode RSA keyslot 0. */
set_rsa_keyslot(0, modulus, 0x100, exponent, exponent_size); set_rsa_keyslot(0, modulus, 0x100, exponent, exponent_size);
se_exp_mod(0, input, 0x100, exp_mod_done_handler); se_exp_mod(0, input, 0x100, exp_mod_done_handler);
return 0; return 0;
} }
@ -84,13 +84,13 @@ uint32_t user_get_random_bytes(smc_args_t *args) {
if (args->X[1] > 0x38) { if (args->X[1] > 0x38) {
return 2; return 2;
} }
size_t size = (size_t)args->X[1]; size_t size = (size_t)args->X[1];
flush_dcache_range(random_bytes, random_bytes + size); flush_dcache_range(random_bytes, random_bytes + size);
se_generate_random(KEYSLOT_SWITCH_RNGKEY, random_bytes, size); se_generate_random(KEYSLOT_SWITCH_RNGKEY, random_bytes, size);
flush_dcache_range(random_bytes, random_bytes + size); flush_dcache_range(random_bytes, random_bytes + size);
memcpy(&args->X[1], random_bytes, size); memcpy(&args->X[1], random_bytes, size);
return 0; return 0;
} }
@ -100,36 +100,36 @@ uint32_t user_generate_aes_kek(smc_args_t *args) {
uint8_t kek_source[0x10]; uint8_t kek_source[0x10];
uint64_t kek[2]; uint64_t kek[2];
uint64_t sealed_kek[2]; uint64_t sealed_kek[2];
wrapped_kek[0] = args->X[1]; wrapped_kek[0] = args->X[1];
wrapped_kek[1] = args->X[2]; wrapped_kek[1] = args->X[2];
unsigned int master_key_rev = (unsigned int)args->X[3]; unsigned int master_key_rev = (unsigned int)args->X[3];
if (master_key_rev > 0) { if (master_key_rev > 0) {
master_key_rev -= 1; /* GenerateAesKek offsets by one. */ master_key_rev -= 1; /* GenerateAesKek offsets by one. */
} }
if (master_key_rev >= MASTERKEY_REVISION_MAX) { if (master_key_rev >= MASTERKEY_REVISION_MAX) {
return 2; return 2;
} }
uint64_t packed_options = args->X[4]; uint64_t packed_options = args->X[4];
if (packed_options > 0xFF) { if (packed_options > 0xFF) {
return 2; return 2;
} }
/* Switched the output based on how the system was booted. */ /* Switched the output based on how the system was booted. */
uint8_t mask_id = (uint8_t)((packed_options >> 1) & 3); uint8_t mask_id = (uint8_t)((packed_options >> 1) & 3);
/* Switches the output based on how it will be used. */ /* Switches the output based on how it will be used. */
uint8_t usecase = (uint8_t)((packed_options >> 5) & 3); uint8_t usecase = (uint8_t)((packed_options >> 5) & 3);
/* Switched the output based on whether it should be console unique. */ /* Switched the output based on whether it should be console unique. */
bool is_personalized = (int)(packed_options & 1); bool is_personalized = (int)(packed_options & 1);
bool is_recovery_boot = configitem_is_recovery_boot(); bool is_recovery_boot = configitem_is_recovery_boot();
/* Mask 2 is only allowed when booted from recovery. */ /* Mask 2 is only allowed when booted from recovery. */
if (mask_id == 2 && !is_recovery_boot) { if (mask_id == 2 && !is_recovery_boot) {
return 2; return 2;
@ -138,9 +138,9 @@ uint32_t user_generate_aes_kek(smc_args_t *args) {
if (mask_id == 1 && is_recovery_boot) { if (mask_id == 1 && is_recovery_boot) {
return 2; return 2;
} }
/* Masks 0, 3 are allowed all the time. */ /* Masks 0, 3 are allowed all the time. */
const uint8_t kek_seeds[4][0x10] = { const uint8_t kek_seeds[4][0x10] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74}, {0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74},
@ -153,12 +153,12 @@ uint32_t user_generate_aes_kek(smc_args_t *args) {
{0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81}, {0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81},
{0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B} {0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B}
}; };
/* Create kek source. */ /* Create kek source. */
for (unsigned int i = 0; i < 0x10; i++) { for (unsigned int i = 0; i < 0x10; i++) {
kek_source[i] = kek_seeds[usecase][i] ^ kek_masks[mask_id][i]; kek_source[i] = kek_seeds[usecase][i] ^ kek_masks[mask_id][i];
} }
unsigned int keyslot; unsigned int keyslot;
if (is_personalized) { if (is_personalized) {
/* Behavior changed in 4.0.0. */ /* Behavior changed in 4.0.0. */
@ -174,39 +174,39 @@ uint32_t user_generate_aes_kek(smc_args_t *args) {
} else { } else {
keyslot = mkey_get_keyslot(master_key_rev); keyslot = mkey_get_keyslot(master_key_rev);
} }
/* Derive kek. */ /* Derive kek. */
decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, kek_source, 0x10); decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, kek_source, 0x10);
se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, kek, 0x10, wrapped_kek, 0x10); se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, kek, 0x10, wrapped_kek, 0x10);
/* Seal kek. */ /* Seal kek. */
seal_key(sealed_kek, 0x10, kek, 0x10, usecase); seal_key(sealed_kek, 0x10, kek, 0x10, usecase);
args->X[1] = sealed_kek[0]; args->X[1] = sealed_kek[0];
args->X[2] = sealed_kek[1]; args->X[2] = sealed_kek[1];
return 0; return 0;
} }
uint32_t user_load_aes_key(smc_args_t *args) { uint32_t user_load_aes_key(smc_args_t *args) {
uint64_t sealed_kek[2]; uint64_t sealed_kek[2];
uint64_t wrapped_key[2]; uint64_t wrapped_key[2];
uint32_t keyslot = (uint32_t)args->X[1]; uint32_t keyslot = (uint32_t)args->X[1];
if (keyslot > 3) { if (keyslot > 3) {
return 2; return 2;
} }
/* Copy keydata */ /* Copy keydata */
sealed_kek[0] = args->X[2]; sealed_kek[0] = args->X[2];
sealed_kek[1] = args->X[3]; sealed_kek[1] = args->X[3];
wrapped_key[0] = args->X[4]; wrapped_key[0] = args->X[4];
wrapped_key[1] = args->X[5]; wrapped_key[1] = args->X[5];
/* Unseal the kek. */ /* Unseal the kek. */
unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10, CRYPTOUSECASE_AES); unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10, CRYPTOUSECASE_AES);
/* Unwrap the key. */ /* Unwrap the key. */
decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, 0x10); decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, 0x10);
return 0; return 0;
@ -234,19 +234,19 @@ uint32_t crypt_aes_done_handler(void) {
uint32_t user_crypt_aes(smc_args_t *args) { uint32_t user_crypt_aes(smc_args_t *args) {
uint32_t keyslot = args->X[1] & 3; uint32_t keyslot = args->X[1] & 3;
uint32_t mode = (args->X[1] >> 4) & 3; uint32_t mode = (args->X[1] >> 4) & 3;
uint64_t iv_ctr[2]; uint64_t iv_ctr[2];
iv_ctr[0] = args->X[2]; iv_ctr[0] = args->X[2];
iv_ctr[1] = args->X[3]; iv_ctr[1] = args->X[3];
uint32_t in_ll_paddr = (uint32_t)(args->X[4]); uint32_t in_ll_paddr = (uint32_t)(args->X[4]);
uint32_t out_ll_paddr = (uint32_t)(args->X[5]); uint32_t out_ll_paddr = (uint32_t)(args->X[5]);
size_t size = args->X[6]; size_t size = args->X[6];
if (size & 0xF) { if (size & 0xF) {
generic_panic(); generic_panic();
} }
set_crypt_aes_done(false); set_crypt_aes_done(false);
uint64_t result = 0; uint64_t result = 0;
@ -268,7 +268,7 @@ uint32_t user_crypt_aes(smc_args_t *args) {
result = 1; result = 1;
break; break;
} }
return result; return result;
} }
@ -277,7 +277,7 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) {
uint8_t key[0x10]; uint8_t key[0x10];
unsigned int master_key_rev; unsigned int master_key_rev;
bool should_mask; bool should_mask;
wrapped_key[0] = args->X[1]; wrapped_key[0] = args->X[1];
wrapped_key[1] = args->X[2]; wrapped_key[1] = args->X[2];
if (args->X[4] > MASTERKEY_REVISION_MAX) { if (args->X[4] > MASTERKEY_REVISION_MAX) {
@ -288,9 +288,9 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) {
return 2; return 2;
} }
should_mask = args->X[3] != 0; should_mask = args->X[3] != 0;
unsigned int keyslot; unsigned int keyslot;
/* Behavior changed in 4.0.0. */ /* Behavior changed in 4.0.0. */
if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) {
if (master_key_rev >= 2) { if (master_key_rev >= 2) {
@ -301,18 +301,18 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) {
} else { } else {
keyslot = KEYSLOT_SWITCH_DEVICEKEY; keyslot = KEYSLOT_SWITCH_DEVICEKEY;
} }
if (fuse_get_bootrom_patch_version() < 0x7F) { if (fuse_get_bootrom_patch_version() < 0x7F) {
/* On dev units, use a fixed "all-zeroes" seed. */ /* On dev units, use a fixed "all-zeroes" seed. */
/* Yes, this data really is all-zero in actual TrustZone .rodata. */ /* Yes, this data really is all-zero in actual TrustZone .rodata. */
uint8_t dev_specific_aes_key_source[0x10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t dev_specific_aes_key_source[0x10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t dev_specific_aes_key_ctr[0x10] = {0x3C, 0xD5, 0x92, 0xEC, 0x68, 0x31, 0x4A, 0x06, 0xD4, 0x1B, 0x0C, 0xD9, 0xF6, 0x2E, 0xD9, 0xE9}; uint8_t dev_specific_aes_key_ctr[0x10] = {0x3C, 0xD5, 0x92, 0xEC, 0x68, 0x31, 0x4A, 0x06, 0xD4, 0x1B, 0x0C, 0xD9, 0xF6, 0x2E, 0xD9, 0xE9};
uint8_t dev_specific_aes_key_mask[0x10] = {0xAC, 0xCA, 0x9A, 0xCA, 0xFF, 0x2E, 0xB9, 0x22, 0xCC, 0x1F, 0x4F, 0xAD, 0xDD, 0x77, 0x21, 0x1E}; uint8_t dev_specific_aes_key_mask[0x10] = {0xAC, 0xCA, 0x9A, 0xCA, 0xFF, 0x2E, 0xB9, 0x22, 0xCC, 0x1F, 0x4F, 0xAD, 0xDD, 0x77, 0x21, 0x1E};
flush_dcache_range(key, key + 0x10); flush_dcache_range(key, key + 0x10);
se_aes_ctr_crypt(keyslot, key, 0x10, dev_specific_aes_key_source, 0x10, dev_specific_aes_key_ctr, 0x10); se_aes_ctr_crypt(keyslot, key, 0x10, dev_specific_aes_key_source, 0x10, dev_specific_aes_key_ctr, 0x10);
flush_dcache_range(key, key + 0x10); flush_dcache_range(key, key + 0x10);
if (should_mask) { if (should_mask) {
for (unsigned int i = 0; i < 0x10; i++) { for (unsigned int i = 0; i < 0x10; i++) {
key[i] ^= dev_specific_aes_key_mask[i]; key[i] ^= dev_specific_aes_key_mask[i];
@ -324,10 +324,10 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) {
decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, retail_specific_aes_key_source, 0x10); decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, retail_specific_aes_key_source, 0x10);
se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, key, 0x10, wrapped_key, 0x10); se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, key, 0x10, wrapped_key, 0x10);
} }
args->X[1] = key[0]; args->X[1] = read64le(key, 0);
args->X[2] = key[1]; args->X[2] = read64le(key, 8);
return 0; return 0;
} }
@ -335,27 +335,27 @@ uint32_t user_compute_cmac(smc_args_t *args) {
uint32_t keyslot = (uint32_t)args->X[1]; uint32_t keyslot = (uint32_t)args->X[1];
void *user_address = (void *)args->X[2]; void *user_address = (void *)args->X[2];
size_t size = (size_t)args->X[3]; size_t size = (size_t)args->X[3];
uint8_t user_data[0x400]; uint8_t user_data[0x400];
uint64_t result_cmac[2]; uint64_t result_cmac[2];
upage_ref_t page_ref; upage_ref_t page_ref;
/* Validate keyslot and size. */ /* Validate keyslot and size. */
if (keyslot > 3 || args->X[3] > 0x400) { if (keyslot > 3 || args->X[3] > 0x400) {
return 2; return 2;
} }
if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) {
return 2; return 2;
} }
flush_dcache_range(user_data, user_data + size); flush_dcache_range(user_data, user_data + size);
se_compute_aes_128_cmac(keyslot, result_cmac, 0x10, user_data, size); se_compute_aes_128_cmac(keyslot, result_cmac, 0x10, user_data, size);
/* Copy CMAC out. */ /* Copy CMAC out. */
args->X[1] = result_cmac[0]; args->X[1] = result_cmac[0];
args->X[2] = result_cmac[1]; args->X[2] = result_cmac[1];
return 0; return 0;
} }
@ -363,13 +363,13 @@ uint32_t user_load_rsa_oaep_key(smc_args_t *args) {
uint64_t sealed_kek[2]; uint64_t sealed_kek[2];
uint64_t wrapped_key[2]; uint64_t wrapped_key[2];
bool is_personalized; bool is_personalized;
uint8_t user_data[0x400]; uint8_t user_data[0x400];
void *user_address; void *user_address;
size_t size; size_t size;
upage_ref_t page_ref; upage_ref_t page_ref;
/* Copy keydata */ /* Copy keydata */
sealed_kek[0] = args->X[1]; sealed_kek[0] = args->X[1];
sealed_kek[1] = args->X[2]; sealed_kek[1] = args->X[2];
@ -381,23 +381,23 @@ uint32_t user_load_rsa_oaep_key(smc_args_t *args) {
size = (size_t)args->X[5]; size = (size_t)args->X[5];
wrapped_key[0] = args->X[6]; wrapped_key[0] = args->X[6];
wrapped_key[1] = args->X[7]; wrapped_key[1] = args->X[7];
if (is_personalized && size != 0x240) { if (is_personalized && size != 0x240) {
return 2; return 2;
} }
if (!is_personalized && (size != 0x220 || fuse_get_bootrom_patch_version() >= 0x7F)) { if (!is_personalized && (size != 0x220 || fuse_get_bootrom_patch_version() >= 0x7F)) {
return 2; return 2;
} }
if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) {
return 2; return 2;
} }
/* Ensure that our private key is 0x100 bytes. */ /* Ensure that our private key is 0x100 bytes. */
if (gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAOAEP, is_personalized) < 0x100) { if (gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAOAEP, is_personalized) < 0x100) {
return 2; return 2;
} }
memcpy(g_rsa_oaep_exponent, user_data, 0x100); memcpy(g_rsa_oaep_exponent, user_data, 0x100);
return 0; return 0;
} }
@ -406,13 +406,13 @@ uint32_t user_decrypt_rsa_private_key(smc_args_t *args) {
uint64_t sealed_kek[2]; uint64_t sealed_kek[2];
uint64_t wrapped_key[2]; uint64_t wrapped_key[2];
bool is_personalized; bool is_personalized;
uint8_t user_data[0x400]; uint8_t user_data[0x400];
void *user_address; void *user_address;
size_t size; size_t size;
upage_ref_t page_ref; upage_ref_t page_ref;
/* Copy keydata */ /* Copy keydata */
sealed_kek[0] = args->X[1]; sealed_kek[0] = args->X[1];
sealed_kek[1] = args->X[2]; sealed_kek[1] = args->X[2];
@ -424,28 +424,28 @@ uint32_t user_decrypt_rsa_private_key(smc_args_t *args) {
size = (size_t)args->X[5]; size = (size_t)args->X[5];
wrapped_key[0] = args->X[6]; wrapped_key[0] = args->X[6];
wrapped_key[1] = args->X[7]; wrapped_key[1] = args->X[7];
if (size > 0x240) { if (size > 0x240) {
return 2; return 2;
} }
if (is_personalized && size < 0x31) { if (is_personalized && size < 0x31) {
return 2; return 2;
} }
if (!is_personalized && (size < 0x11 || fuse_get_bootrom_patch_version() >= 0x7F)) { if (!is_personalized && (size < 0x11 || fuse_get_bootrom_patch_version() >= 0x7F)) {
return 2; return 2;
} }
if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) {
return 2; return 2;
} }
size_t out_size; size_t out_size;
if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAPRIVATE, is_personalized)) == 0) { if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAPRIVATE, is_personalized)) == 0) {
return 2; return 2;
} }
if (secure_copy_to_user(&page_ref, user_address, user_data, size) == 0) { if (secure_copy_to_user(&page_ref, user_address, user_data, size) == 0) {
return 2; return 2;
} }
@ -458,13 +458,13 @@ uint32_t user_load_secure_exp_mod_key(smc_args_t *args) {
uint64_t sealed_kek[2]; uint64_t sealed_kek[2];
uint64_t wrapped_key[2]; uint64_t wrapped_key[2];
bool is_personalized; bool is_personalized;
uint8_t user_data[0x400]; uint8_t user_data[0x400];
void *user_address; void *user_address;
size_t size; size_t size;
upage_ref_t page_ref; upage_ref_t page_ref;
/* Copy keydata */ /* Copy keydata */
sealed_kek[0] = args->X[1]; sealed_kek[0] = args->X[1];
sealed_kek[1] = args->X[2]; sealed_kek[1] = args->X[2];
@ -476,41 +476,41 @@ uint32_t user_load_secure_exp_mod_key(smc_args_t *args) {
size = (size_t)args->X[5]; size = (size_t)args->X[5];
wrapped_key[0] = args->X[6]; wrapped_key[0] = args->X[6];
wrapped_key[1] = args->X[7]; wrapped_key[1] = args->X[7];
if (is_personalized && size != 0x130) { if (is_personalized && size != 0x130) {
return 2; return 2;
} }
if (!is_personalized && (size != 0x110 || fuse_get_bootrom_patch_version() >= 0x7F)) { if (!is_personalized && (size != 0x110 || fuse_get_bootrom_patch_version() >= 0x7F)) {
return 2; return 2;
} }
if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) {
return 2; return 2;
} }
size_t out_size; size_t out_size;
/* Ensure that our key is non-zero bytes. */ /* Ensure that our key is non-zero bytes. */
if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_SECUREEXPMOD, is_personalized)) == 0) { if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_SECUREEXPMOD, is_personalized)) == 0) {
return 2; return 2;
} }
/* Copy key to global. */ /* Copy key to global. */
if (out_size <= 0x100) { if (out_size <= 0x100) {
memcpy(g_secure_exp_mod_exponent, user_data, out_size); memcpy(g_secure_exp_mod_exponent, user_data, out_size);
} else { } else {
memcpy(g_secure_exp_mod_exponent, user_data, 0x100); memcpy(g_secure_exp_mod_exponent, user_data, 0x100);
} }
return 0; return 0;
} }
uint32_t user_secure_exp_mod(smc_args_t *args) { uint32_t user_secure_exp_mod(smc_args_t *args) {
uint8_t modulus[0x100]; uint8_t modulus[0x100];
uint8_t input[0x100]; uint8_t input[0x100];
upage_ref_t page_ref; upage_ref_t page_ref;
void *user_input = (void *)args->X[1]; void *user_input = (void *)args->X[1];
void *user_modulus = (void *)args->X[2]; void *user_modulus = (void *)args->X[2];
@ -524,25 +524,25 @@ uint32_t user_secure_exp_mod(smc_args_t *args) {
if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) {
return 2; return 2;
} }
set_exp_mod_done(false); set_exp_mod_done(false);
/* Hardcode RSA keyslot 0. */ /* Hardcode RSA keyslot 0. */
set_rsa_keyslot(0, modulus, 0x100, g_secure_exp_mod_exponent, 0x100); set_rsa_keyslot(0, modulus, 0x100, g_secure_exp_mod_exponent, 0x100);
se_exp_mod(0, input, 0x100, exp_mod_done_handler); se_exp_mod(0, input, 0x100, exp_mod_done_handler);
return 0; return 0;
} }
uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) {
uint8_t modulus[0x100]; uint8_t modulus[0x100];
uint8_t wrapped_key[0x100]; uint8_t wrapped_key[0x100];
upage_ref_t page_ref; upage_ref_t page_ref;
void *user_wrapped_key = (void *)args->X[1]; void *user_wrapped_key = (void *)args->X[1];
void *user_modulus = (void *)args->X[2]; void *user_modulus = (void *)args->X[2];
unsigned int master_key_rev = (unsigned int)args->X[7]; unsigned int master_key_rev = (unsigned int)args->X[7];
if (master_key_rev >= MASTERKEY_REVISION_MAX) { if (master_key_rev >= MASTERKEY_REVISION_MAX) {
return 2; return 2;
} }
@ -557,34 +557,34 @@ uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) {
if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) {
return 2; return 2;
} }
set_exp_mod_done(false); set_exp_mod_done(false);
/* Expected label_hash occupies args->X[3] to args->X[6]. */ /* Expected label_hash occupies args->X[3] to args->X[6]. */
tkey_set_expected_label_hash(&args->X[3]); tkey_set_expected_label_hash(&args->X[3]);
tkey_set_master_key_rev(master_key_rev); tkey_set_master_key_rev(master_key_rev);
/* Hardcode RSA keyslot 0. */ /* Hardcode RSA keyslot 0. */
set_rsa_keyslot(0, modulus, 0x100, g_rsa_oaep_exponent, 0x100); set_rsa_keyslot(0, modulus, 0x100, g_rsa_oaep_exponent, 0x100);
se_exp_mod(0, wrapped_key, 0x100, exp_mod_done_handler); se_exp_mod(0, wrapped_key, 0x100, exp_mod_done_handler);
return 0; return 0;
} }
uint32_t user_load_titlekey(smc_args_t *args) { uint32_t user_load_titlekey(smc_args_t *args) {
uint64_t sealed_titlekey[2]; uint64_t sealed_titlekey[2];
uint32_t keyslot = (uint32_t)args->X[1]; uint32_t keyslot = (uint32_t)args->X[1];
if (keyslot > 3) { if (keyslot > 3) {
return 2; return 2;
} }
/* Copy keydata */ /* Copy keydata */
sealed_titlekey[0] = args->X[2]; sealed_titlekey[0] = args->X[2];
sealed_titlekey[1] = args->X[3]; sealed_titlekey[1] = args->X[3];
/* Unseal the key. */ /* Unseal the key. */
unseal_titlekey(keyslot, sealed_titlekey, 0x10); unseal_titlekey(keyslot, sealed_titlekey, 0x10);
return 0; return 0;
@ -595,7 +595,7 @@ uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args) {
uint64_t aes_wrapped_titlekey[2]; uint64_t aes_wrapped_titlekey[2];
uint8_t titlekey[0x10]; uint8_t titlekey[0x10];
uint64_t sealed_titlekey[2]; uint64_t sealed_titlekey[2];
aes_wrapped_titlekey[0] = args->X[1]; aes_wrapped_titlekey[0] = args->X[1];
aes_wrapped_titlekey[1] = args->X[2]; aes_wrapped_titlekey[1] = args->X[2];
unsigned int master_key_rev = (unsigned int)args->X[3]; unsigned int master_key_rev = (unsigned int)args->X[3];

View file

@ -32,12 +32,12 @@ void calculate_mgf1_and_xor(void *masked, size_t masked_size, const void *seed,
if (seed_size >= 0xE0) { if (seed_size >= 0xE0) {
generic_panic(); generic_panic();
} }
size_t hash_buf_size = seed_size + 4; size_t hash_buf_size = seed_size + 4;
memcpy(hash_buf, seed, seed_size); memcpy(hash_buf, seed, seed_size);
uint32_t round = 0; uint32_t round_num = 0;
uint8_t *p_out = (uint8_t *)masked; uint8_t *p_out = (uint8_t *)masked;
while (masked_size) { while (masked_size) {
@ -45,21 +45,17 @@ void calculate_mgf1_and_xor(void *masked, size_t masked_size, const void *seed,
if (cur_size > 0x20) { if (cur_size > 0x20) {
cur_size = 0x20; cur_size = 0x20;
} }
hash_buf[seed_size + 0] = (uint8_t)((round >> 24) & 0xFF); write32be(hash_buf, seed_size, round_num++);
hash_buf[seed_size + 1] = (uint8_t)((round >> 16) & 0xFF);
hash_buf[seed_size + 2] = (uint8_t)((round >> 8) & 0xFF);
hash_buf[seed_size + 3] = (uint8_t)((round >> 0) & 0xFF);
round++;
flush_dcache_range(hash_buf, hash_buf + hash_buf_size); flush_dcache_range(hash_buf, hash_buf + hash_buf_size);
se_calculate_sha256(cur_hash, hash_buf, hash_buf_size); se_calculate_sha256(cur_hash, hash_buf, hash_buf_size);
for (unsigned int i = 0; i < cur_size; i++) { for (unsigned int i = 0; i < cur_size; i++) {
*p_out ^= cur_hash[i]; *p_out ^= cur_hash[i];
p_out++; p_out++;
} }
masked_size -= cur_size; masked_size -= cur_size;
} }
} }
@ -68,7 +64,7 @@ size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_si
if (src_size != 0x100) { if (src_size != 0x100) {
generic_panic(); generic_panic();
} }
/* RSA Wrapped titlekeys use RSA-OAEP. */ /* RSA Wrapped titlekeys use RSA-OAEP. */
/* Message is of the form prefix || maskedSalt || maskedDB. */ /* Message is of the form prefix || maskedSalt || maskedDB. */
/* maskedSalt = salt ^ MGF1(maskedDB) */ /* maskedSalt = salt ^ MGF1(maskedDB) */
@ -77,43 +73,43 @@ size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_si
/* DB is of the form label_hash || 00....01 || wrapped_titlekey. */ /* DB is of the form label_hash || 00....01 || wrapped_titlekey. */
/* label_hash is, in practice, a constant in es .rodata. */ /* label_hash is, in practice, a constant in es .rodata. */
/* I have no idea why Nintendo did this, it should be either nonconstant (in tik) or in tz .rodata. */ /* I have no idea why Nintendo did this, it should be either nonconstant (in tik) or in tz .rodata. */
uint8_t *message = (uint8_t *)src; uint8_t *message = (uint8_t *)src;
/* Prefix should always be zero. */ /* Prefix should always be zero. */
if (*message != 0) { if (*message != 0) {
return 0; return 0;
} }
uint8_t *salt = message + 1; uint8_t *salt = message + 1;
uint8_t *db = message + 0x21; uint8_t *db = message + 0x21;
/* This will be passed to smc_unwrap_rsa_oaep_wrapped_titlekey. */ /* This will be passed to smc_unwrap_rsa_oaep_wrapped_titlekey. */
uint8_t *expected_label_hash = (uint8_t *)(&g_tkey_expected_label_hash[0]); uint8_t *expected_label_hash = (uint8_t *)(&g_tkey_expected_label_hash[0]);
/* Unmask the salt. */ /* Unmask the salt. */
calculate_mgf1_and_xor(salt, 0x20, db, 0xDF); calculate_mgf1_and_xor(salt, 0x20, db, 0xDF);
/* Unmask the DB. */ /* Unmask the DB. */
calculate_mgf1_and_xor(db, 0xDF, salt, 0x20); calculate_mgf1_and_xor(db, 0xDF, salt, 0x20);
/* Validate expected salt. */ /* Validate expected salt. */
for (unsigned int i = 0; i < 0x20; i++) { for (unsigned int i = 0; i < 0x20; i++) {
if (expected_label_hash[i] != db[i]) { if (expected_label_hash[i] != db[i]) {
return 0; return 0;
} }
} }
/* Don't validate salt from message[1:0x21] at all. */ /* Don't validate salt from message[1:0x21] at all. */
/* Advance pointer to DB, since we've validated the salt prefix. */ /* Advance pointer to DB, since we've validated the salt prefix. */
db += 0x20; db += 0x20;
/* DB must be of the form 0000...01 || wrapped_titlekey */ /* DB must be of the form 0000...01 || wrapped_titlekey */
if (*db != 0) { if (*db != 0) {
return 0; return 0;
} }
/* Locate wrapped_titlekey inside DB. */ /* Locate wrapped_titlekey inside DB. */
size_t wrapped_key_offset_in_db = 0; size_t wrapped_key_offset_in_db = 0;
while (wrapped_key_offset_in_db < 0xBF) { while (wrapped_key_offset_in_db < 0xBF) {
@ -127,13 +123,13 @@ size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_si
return 0; return 0;
} }
} }
/* Validate size... */ /* Validate size... */
size_t wrapped_titlekey_size = 0xBF - wrapped_key_offset_in_db; size_t wrapped_titlekey_size = 0xBF - wrapped_key_offset_in_db;
if (wrapped_titlekey_size > dst_size || wrapped_titlekey_size == 0) { if (wrapped_titlekey_size > dst_size || wrapped_titlekey_size == 0) {
return 0; return 0;
} }
/* Extract the wrapped key. */ /* Extract the wrapped key. */
memcpy(dst, &db[wrapped_key_offset_in_db], wrapped_titlekey_size); memcpy(dst, &db[wrapped_key_offset_in_db], wrapped_titlekey_size);
return wrapped_key_offset_in_db; return wrapped_key_offset_in_db;
@ -143,15 +139,15 @@ void tkey_aes_unwrap(void *dst, size_t dst_size, const void *src, size_t src_siz
if (g_tkey_master_key_rev >= MASTERKEY_REVISION_MAX || dst_size != 0x10 || src_size != 0x10) { if (g_tkey_master_key_rev >= MASTERKEY_REVISION_MAX || dst_size != 0x10 || src_size != 0x10) {
generic_panic(); generic_panic();
} }
const uint8_t titlekek_source[0x10] = { const uint8_t titlekek_source[0x10] = {
0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B 0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B
}; };
/* Generate the appropriate titlekek into keyslot 9. */ /* Generate the appropriate titlekek into keyslot 9. */
unsigned int master_keyslot = mkey_get_keyslot(g_tkey_master_key_rev); unsigned int master_keyslot = mkey_get_keyslot(g_tkey_master_key_rev);
decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, master_keyslot, titlekek_source, 0x10); decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, master_keyslot, titlekek_source, 0x10);
/* Unwrap the titlekey using the titlekek. */ /* Unwrap the titlekey using the titlekek. */
se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10); se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10);
} }

View file

@ -78,6 +78,26 @@ static inline uint64_t read64le(const volatile void *qword, size_t offset) {
return *(uint64_t *)((uintptr_t)qword + offset); return *(uint64_t *)((uintptr_t)qword + offset);
} }
static inline uint32_t read64be(const volatile void *qword, size_t offset) {
return __builtin_bswap64(read64le(qword, offset));
}
static inline void write32le(volatile void *dword, size_t offset, uint32_t value) {
*(uint32_t *)((uintptr_t)dword + offset) = value;
}
static inline void write32be(volatile void *dword, size_t offset, uint32_t value) {
write32le(dword, offset, __builtin_bswap32(value));
}
static inline void write64le(volatile void *qword, size_t offset, uint64_t value) {
*(uint64_t *)((uintptr_t)qword + offset) = value;
}
static inline void write64be(volatile void *qword, size_t offset, uint64_t value) {
write64le(qword, offset, __builtin_bswap64(value));
}
static inline unsigned int get_core_id(void) { static inline unsigned int get_core_id(void) {
uint64_t core_id; uint64_t core_id;
__asm__ __volatile__ ("mrs %0, mpidr_el1" : "=r"(core_id)); __asm__ __volatile__ ("mrs %0, mpidr_el1" : "=r"(core_id));