diff --git a/exosphere/configitem.c b/exosphere/configitem.c index 9d02e79ab..051b75f50 100644 --- a/exosphere/configitem.c +++ b/exosphere/configitem.c @@ -13,6 +13,15 @@ uint32_t configitem_set(enum ConfigItem item, uint64_t value) { g_battery_profile = ((int)(value != 0)) & 1; } +uint64_t configitem_is_recovery_boot(void) { + uint64_t is_recovery_boot; + if (configitem_get(CONFIGITEM_ISRECOVERYBOOT, &is_recovery_boot) != 0) { + panic(); + } + + return is_recovery_boot; +} + uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue) { uint32_t result = 0; switch (item) { diff --git a/exosphere/configitem.h b/exosphere/configitem.h index e1cd0d3aa..58e2d4706 100644 --- a/exosphere/configitem.h +++ b/exosphere/configitem.h @@ -23,4 +23,6 @@ enum ConfigItem { uint32_t configitem_set(enum ConfigItem item, uint64_t value); uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue); +uint64_t configitem_is_recovery_boot(void); + #endif \ No newline at end of file diff --git a/exosphere/se.h b/exosphere/se.h index 4fdf7d3ca..ae6964bb5 100644 --- a/exosphere/se.h +++ b/exosphere/se.h @@ -12,8 +12,8 @@ #define KEYSLOT_SWITCH_MASTERKEY 0xC #define KEYSLOT_SWITCH_DEVICEKEY 0xD -/* This key was added in 4.0.0. */ -#define KEYSLOT_SWITCH_NEWDEVICEKEY 0xF +/* This keyslot was added in 4.0.0. */ +#define KEYSLOT_SWITCH_4XOLDDEVICEKEY 0xF #define KEYSLOT_AES_MAX 0x10 #define KEYSLOT_RSA_MAX 0x2 diff --git a/exosphere/sealedkeys.c b/exosphere/sealedkeys.c new file mode 100644 index 000000000..f99351bf2 --- /dev/null +++ b/exosphere/sealedkeys.c @@ -0,0 +1,63 @@ +#include + +#include "utils.h" +#include "sealedkeys.h" +#include "se.h" + +const uint8_t g_titlekey_seal_key_source[0x10] = { + 0xCB, 0xB7, 0x6E, 0x38, 0xA1, 0xCB, 0x77, 0x0F, 0xB2, 0xA5, 0xB2, 0x9D, 0xD8, 0x56, 0x9F, 0x76 +}; + + +const uint8_t g_sealed_key_sources[CRYPTOUSECASE_MAX][0x10] = { + {0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9}, + {0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77}, + {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} +}; + +void seal_key_internal(void *dst, const void *src, const uint8_t *seal_key_source) { + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_SESSIONKEY, seal_key_source, 0x10); + se_aes_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10); +} + +void unseal_key_internal(unsigned int keyslot, const void *src, const uint8_t *seal_key_source) { + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_SESSIONKEY, seal_key_source, 0x10); + decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, src, 0x10); +} + + +void seal_titlekey(void *dst, size_t dst_size, const void *src, size_t src_size) { + if (usecase >= CRYPTOUSECASE_MAX || dst_size != 0x10 || src_size != 0x10) { + panic(); + } + + seal_key_internal(dst, src, g_titlekey_seal_key_source); + +} + +void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size) { + if (src_size != 0x10) { + panic(); + } + + unseal_key_internal(keyslot, src, g_titlekey_seal_key_source); +} + + +void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int usecase) { + if (usecase >= CRYPTOUSECASE_MAX || dst_size != 0x10 || src_size != 0x10) { + panic(); + } + + + seal_key_internal(dst, src, g_sealed_key_sources[usecase]); +} + +void unseal_key(unsigned int keyslot, const void *src, size_t src_size, unsigned int usecase) { + if (usecase >= CRYPTOUSECASE_MAX || src_size != 0x10) { + panic(); + } + + seal_key_internal(dst, src, g_sealed_key_sources[usecase]); +} \ No newline at end of file diff --git a/exosphere/sealedkeys.h b/exosphere/sealedkeys.h new file mode 100644 index 000000000..7e4f2380b --- /dev/null +++ b/exosphere/sealedkeys.h @@ -0,0 +1,21 @@ +#ifndef EXOSPHERE_SEALED_KEYS_H +#define EXOSPHERE_SEALED_KEYS_H + +#include + +/* Key sealing/unsealing functionality. */ + +#define CRYPTOUSECASE_AES 0 +#define CRYPTOUSECASE_RSAPRIVATE 1 +#define CRYPTOUSECASE_RSAOAEP 2 +#define CRYPTOUSECASE_RSATICKET 3 + +#define CRYPTOUSECASE_MAX 4 + +void seal_titlekey(void *dst, size_t dst_size, const void *src, size_t src_size); +void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size); + +void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int usecase); +void unseal_key(unsigned int keyslot, const void *src, size_t src_size, unsigned int usecase); + +#endif \ No newline at end of file diff --git a/exosphere/smc_api.c b/exosphere/smc_api.c index 4ec1bf8f7..80f0f17d6 100644 --- a/exosphere/smc_api.c +++ b/exosphere/smc_api.c @@ -279,6 +279,10 @@ uint32_t smc_exp_mod(smc_args_t *args) { return smc_wrapper_async(args, user_exp_mod, smc_exp_mod_get_result); } +uint32_t smc_generate_aes_kek(smc_args_t *args) { + return smc_wrapper_sync(args, user_generate_aes_kek); +} + uint32_t smc_load_aes_key(smc_args_t *args) { return smc_wrapper_sync(args, user_load_aes_key); } @@ -327,7 +331,7 @@ uint32_t smc_unwrap_rsa_wrapped_titlekey_get_result(void *buf, uint64_t size) { } tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10); - tkey_seal(sealed_titlekey, 0x10, titlekey, 0x10); + seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10); p_sealed_key[0] = sealed_titlekey[0]; p_sealed_key[1] = sealed_titlekey[1]; diff --git a/exosphere/smc_user.c b/exosphere/smc_user.c index fd8b6945f..2278b0c92 100644 --- a/exosphere/smc_user.c +++ b/exosphere/smc_user.c @@ -2,10 +2,12 @@ #include "utils.h" #include "cache.h" +#incoude "configitem.h" #include "masterkey.h" #include "smc_api.h" #include "smc_user.h" #include "se.h" +#include "sealedkeys.h" #include "userpage.h" #include "titlekey.h" @@ -73,6 +75,100 @@ uint32_t user_exp_mod(smc_args_t *args) { return 0; } +uint32_t user_generate_aes_kek(smc_args_t *args) { + uint64_t wrapped_kek[2]; + uint8_t kek_source[0x10]; + uint64_t kek[2]; + uint64_t sealed_kek[2]; + + wrapped_kek[0] = args->X[1]; + wrapped_kek[1] = args->X[2]; + + unsigned int master_key_rev = (unsigned int)args->X[3]; + + if (master_key_rev > 0) { + master_key_rev -= 1; /* GenerateAesKek offsets by one. */ + } + + if (master_key_rev >= MASTERKEY_REVISION_MAX) { + return 2; + } + + uint64_t packed_options = args->X[4]; + if (packed_options > 0xFF) { + return 2; + } + + /* Switched the output based on how the system was booted. */ + uint8_t mask_id = (uint8_t)((packed_options >> 1) & 3); + + /* Switches the output based on how it will be used. */ + uint8_t usecase = (uint8_t)((packed_options >> 5) & 3); + + /* Switched the output based on whether it should be console unique. */ + int is_personalized = (int)(packed_options & 1); + + uint64_t is_recovery_boot = configitem_is_recovery_boot(); + + /* Mask 2 is only allowed when booted normally. */ + if (mask_id == 2 && is_recovery_boot == 0) { + return 2; + } + /* Mask 1 is only allowed when booted from recovery. */ + if (mask_id == 1 && is_recovery_boot != 0) { + return 2; + } + + /* Masks 0, 3 are allowed all the time. */ + + const uint8_t kek_seeds[4][0x10] = { + {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}, + {0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C}, + {0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB} + }; + const uint8_t kek_masks[4][0x10] = { + {0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9}, + {0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77}, + {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} + }; + + /* Create kek source. */ + for (unsigned int i = 0; i < 0x10; i++) { + kek_source[i] = kek_seeds[usecase][i] ^ kek_masks[mask_id][i]; + } + + unsigned int keyslot; + if (is_personalized) { + /* Behavior changed in 4.0.0. */ + if (mkey_get_revision() >= 4) { + if (master_key_rev >= 1) { + keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ + } else { + keyslot = KEYSLOT_SWITCH_4XOLDDEVICEKEY; /* Old device key, 4.x. */ + } + } else { + keyslot = KEYSLOT_SWITCH_DEVICEKEY; + } + } else { + keyslot = mkey_get_keyslot(master_key_rev); + } + + /* Derive kek. */ + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, kek_source, 0x10); + se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, kek, 0x10, wrapped_kek, 0x10); + + + /* Seal kek. */ + seal_key(sealed_kek, 0x10, kek, 0x10, usecase); + + args->X[1] = sealed_kek[0]; + args->X[2] = sealed_kek[1]; + + return 0; +} + uint32_t user_load_aes_key(smc_args_t *args) { uint64_t sealed_kek[2]; uint64_t wrapped_key[2]; @@ -89,7 +185,7 @@ uint32_t user_load_aes_key(smc_args_t *args) { wrapped_key[1] = args->X[5]; /* TODO: Unseal the kek. */ - set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10); + unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10, CRYPTOUSECASE_AES); /* Unwrap the key. */ decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, 0x10); @@ -265,7 +361,7 @@ uint32_t user_load_titlekey(smc_args_t *args) { sealed_titlekey[1] = args->X[3]; /* Unseal the key. */ - tkey_unseal(keyslot, sealed_titlekey, 0x10); + unseal_titlekey(keyslot, sealed_titlekey, 0x10); return 0; } @@ -288,7 +384,7 @@ uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args) { tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10); - tkey_seal(sealed_titlekey, 0x10, titlekey, 0x10); + seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10); args->X[1] = sealed_titlekey[0]; args->X[2] = sealed_titlekey[1];