1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-10 06:01:52 +00:00
Atmosphere/exosphere/smc_user.c
2018-02-19 23:41:27 -08:00

295 lines
No EOL
7.3 KiB
C

#include <stdint.h>
#include "utils.h"
#include "cache.h"
#include "masterkey.h"
#include "smc_api.h"
#include "smc_user.h"
#include "se.h"
#include "userpage.h"
#include "titlekey.h"
/* Globals. */
int g_crypt_aes_done = 0;
int g_exp_mod_done = 0;
uint8_t g_rsa_oaep_exponent[0x100];
uint8_t g_rsa_private_exponent[0x100];
void set_exp_mod_done(int done) {
g_exp_mod_done = done & 1;
}
int get_exp_mod_done(void) {
return g_exp_mod_done;
}
uint32_t exp_mod_done_handler(void) {
set_exp_mod_done(1);
se_trigger_interrupt();
return 0;
}
uint32_t user_exp_mod(smc_args_t *args) {
uint8_t modulus[0x100];
uint8_t exponent[0x100];
uint8_t input[0x100];
upage_ref_t page_ref;
/* Validate size. */
if (args->X[4] == 0 || args->X[4] > 0x100 || (args->X[4] & 3) != 0) {
return 2;
}
size_t exponent_size = (size_t)args->X[4];
void *user_input = (void *)args->X[1];
void *user_exponent = (void *)args->X[2];
void *user_modulus = (void *)args->X[3];
/* Copy user data into secure memory. */
if (upage_init(&page_ref, user_input) == 0) {
return 2;
}
if (user_copy_to_secure(&page_ref, input, user_input, 0x100) == 0) {
return 2;
}
if (user_copy_to_secure(&page_ref, exponent, user_exponent, exponent_size) == 0) {
return 2;
}
if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) {
return 2;
}
set_exp_mod_done(0);
/* Hardcode RSA keyslot 0. */
set_rsa_keyslot(0, modulus, 0x100, exponent, exponent_size);
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
return 0;
}
uint32_t user_load_aes_key(smc_args_t *args) {
uint64_t sealed_kek[2];
uint64_t wrapped_key[2];
uint32_t keyslot = (uint32_t)args->X[1];
if (keyslot > 3) {
return 2;
}
/* Copy keydata */
sealed_kek[0] = args->X[2];
sealed_kek[1] = args->X[3];
wrapped_key[0] = args->X[4];
wrapped_key[1] = args->X[5];
/* TODO: Unseal the kek. */
set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10);
/* Unwrap the key. */
decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, 0x10);
return 0;
}
void set_crypt_aes_done(int done) {
g_crypt_aes_done = done & 1;
}
int get_crypt_aes_done(void) {
return g_crypt_aes_done;
}
uint32_t crypt_aes_done_handler(void) {
se_check_for_error();
set_crypt_aes_done(1);
se_trigger_interrupt();
return 0;
}
uint32_t user_crypt_aes(smc_args_t *args) {
uint32_t keyslot = args->X[1] & 3;
uint32_t mode = (args->X[1] >> 4) & 3;
uint64_t iv_ctr[2];
iv_ctr[0] = args->X[2];
iv_ctr[1] = args->X[3];
uint32_t in_ll_paddr = (uint32_t)(args->X[4]);
uint32_t out_ll_paddr = (uint32_t)(args->X[5]);
size_t size = args->X[6];
if (size & 0xF) {
panic();
}
set_crypt_aes_done(0);
uint64_t result = 0;
switch (mode) {
case 0: /* CBC Encryption */
se_aes_cbc_encrypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler);
result = 0;
break;
case 1: /* CBC Decryption */
se_aes_cbc_decrypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler);
result = 0;
break;
case 2: /* CTR "Encryption" */
se_aes_ctr_crypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler);
result = 0;
break;
case 3:
default:
result = 1;
break;
}
return result;
}
uint32_t user_compute_cmac(smc_args_t *args) {
uint32_t keyslot = (uint32_t)args->X[1];
void *user_address = (void *)args->X[2];
size_t size = (size_t)args->X[3];
uint8_t user_data[0x400];
uint64_t result_cmac[2];
upage_ref_t page_ref;
/* Validate keyslot and size. */
if (keyslot > 3 || args->X[3] > 0x400) {
return 2;
}
if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) {
return 2;
}
flush_dcache_range(user_data, user_data + size);
se_compute_aes_128_cmac(keyslot, result_cmac, 0x10, user_data, size);
/* Copy CMAC out. */
args->X[1] = result_cmac[0];
args->X[2] = result_cmac[1];
return 0;
}
uint32_t user_rsa_oaep(smc_args_t *args) {
uint8_t modulus[0x100];
uint8_t input[0x100];
upage_ref_t page_ref;
void *user_input = (void *)args->X[1];
void *user_modulus = (void *)args->X[2];
/* Copy user data into secure memory. */
if (upage_init(&page_ref, user_input) == 0) {
return 2;
}
if (user_copy_to_secure(&page_ref, input, user_input, 0x100) == 0) {
return 2;
}
if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) {
return 2;
}
set_exp_mod_done(0);
/* Hardcode RSA keyslot 0. */
set_rsa_keyslot(0, modulus, 0x100, g_rsa_oaep_exponent, 0x100);
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
return 0;
}
uint32_t user_unwrap_rsa_wrapped_titlekey(smc_args_t *args) {
uint8_t modulus[0x100];
uint8_t wrapped_key[0x100];
upage_ref_t page_ref;
void *user_wrapped_key = (void *)args->X[1];
void *user_modulus = (void *)args->X[2];
unsigned int master_key_rev = (unsigned int)args->X[7];
if (master_key_rev >= MASTERKEY_REVISION_MAX) {
return 2;
}
/* Copy user data into secure memory. */
if (upage_init(&page_ref, user_wrapped_key) == 0) {
return 2;
}
if (user_copy_to_secure(&page_ref, wrapped_key, user_wrapped_key, 0x100) == 0) {
return 2;
}
if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) {
return 2;
}
set_exp_mod_done(0);
/* Expected salt occupies args->X[3] to args->X[6]. */
tkey_set_expected_salt(&args->X[3]);
tkey_set_master_key_rev(master_key_rev);
/* Hardcode RSA keyslot 0. */
set_rsa_keyslot(0, modulus, 0x100, g_rsa_private_exponent, 0x100);
se_exp_mod(0, wrapped_key, 0x100, exp_mod_done_handler);
return 0;
}
uint32_t user_load_titlekey(smc_args_t *args) {
uint64_t sealed_titlekey[2];
uint32_t keyslot = (uint32_t)args->X[1];
if (keyslot > 3) {
return 2;
}
/* Copy keydata */
sealed_titlekey[0] = args->X[2];
sealed_titlekey[1] = args->X[3];
/* Unseal the key. */
tkey_unseal(keyslot, sealed_titlekey, 0x10);
return 0;
}
uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args) {
uint64_t aes_wrapped_titlekey[2];
uint8_t titlekey[0x10];
uint64_t sealed_titlekey[2];
aes_wrapped_titlekey[0] = args->X[1];
aes_wrapped_titlekey[1] = args->X[2];
unsigned int master_key_rev = (unsigned int)args->X[3];
if (master_key_rev >= MASTERKEY_REVISION_MAX) {
return 2;
}
tkey_set_master_key_rev(master_key_rev);
tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10);
tkey_seal(sealed_titlekey, 0x10, titlekey, 0x10);
args->X[1] = sealed_titlekey[0];
args->X[2] = sealed_titlekey[1];
}