diff --git a/exosphere/src/bootconfig.c b/exosphere/src/bootconfig.c index a476c93cb..b4d054267 100644 --- a/exosphere/src/bootconfig.c +++ b/exosphere/src/bootconfig.c @@ -1,40 +1,107 @@ +#include +#include +#include + +#include "utils.h" +#include "se.h" +#include "configitem.h" +#include "fuse.h" #include "bootconfig.h" +static bootconfig_t g_loaded_bootconfig = {0}; + +bool bootconfig_matches_hardware_info(void) { + uint32_t hardware_info[4]; + fuse_get_hardware_info(hardware_info); + return memcmp(g_loaded_bootconfig.signed_config.hardware_info, hardware_info, sizeof(hardware_info)) == 0; +} + void bootconfig_load_and_verify(const bootconfig_t *bootconfig) { - /* TODO */ + static const uint8_t bootconfig_modulus[RSA_2048_BYTES] = { + 0xB5, 0x96, 0x87, 0x31, 0x39, 0xAA, 0xBB, 0x3C, 0x28, 0xF3, 0xF0, 0x65, 0xF1, 0x50, 0x70, 0x64, + 0xE6, 0x6C, 0x97, 0x50, 0xCD, 0xA6, 0xEE, 0xEA, 0xC3, 0x8F, 0xE6, 0xB5, 0x81, 0x54, 0x65, 0x33, + 0x1B, 0x88, 0x4B, 0xCE, 0x9F, 0x53, 0xDF, 0xE4, 0xF6, 0xAD, 0xC3, 0x78, 0xD7, 0x3C, 0xD1, 0xDB, + 0x27, 0x21, 0xA0, 0x24, 0x30, 0x2D, 0x98, 0x41, 0xA8, 0xDF, 0x50, 0x7D, 0xAB, 0xCE, 0x00, 0xD9, + 0xCB, 0xAC, 0x8F, 0x37, 0xF5, 0x53, 0xE4, 0x97, 0x1F, 0x13, 0x3C, 0x19, 0xFF, 0x05, 0xA7, 0x3B, + 0xF6, 0xF4, 0x01, 0xDE, 0xF0, 0xC3, 0x77, 0x7B, 0x83, 0xBA, 0xAF, 0x99, 0x30, 0x94, 0x87, 0x25, + 0x4E, 0x54, 0x42, 0x3F, 0xAC, 0x27, 0xF9, 0xCC, 0x87, 0xDD, 0xAE, 0xF2, 0x54, 0xF3, 0x97, 0x49, + 0xF4, 0xB0, 0xF8, 0x6D, 0xDA, 0x60, 0xE0, 0xFD, 0x57, 0xAE, 0x55, 0xA9, 0x76, 0xEA, 0x80, 0x24, + 0xA0, 0x04, 0x7D, 0xBE, 0xD1, 0x81, 0xD3, 0x0C, 0x95, 0xCF, 0xB7, 0xE0, 0x2D, 0x21, 0x21, 0xFF, + 0x97, 0x1E, 0xB3, 0xD7, 0x9F, 0xBB, 0x33, 0x0C, 0x23, 0xC5, 0x88, 0x4A, 0x33, 0xB9, 0xC9, 0x4E, + 0x1E, 0x65, 0x51, 0x45, 0xDE, 0xF9, 0x64, 0x7C, 0xF0, 0xBF, 0x11, 0xB4, 0x93, 0x8D, 0x5D, 0xC6, + 0xAB, 0x37, 0x9E, 0xE9, 0x39, 0xC1, 0xC8, 0xDB, 0xB9, 0xFE, 0x45, 0xCE, 0x7B, 0xDD, 0x72, 0xD9, + 0x6F, 0x68, 0x13, 0xC0, 0x4B, 0xBA, 0x00, 0xF4, 0x1E, 0x89, 0x71, 0x91, 0x26, 0xA6, 0x46, 0x12, + 0xDF, 0x29, 0x6B, 0xC2, 0x5A, 0x53, 0xAF, 0xB9, 0x5B, 0xFD, 0x13, 0x9F, 0xD1, 0x8A, 0x7C, 0xB5, + 0x04, 0xFD, 0x69, 0xEA, 0x23, 0xB4, 0x6D, 0x16, 0x21, 0x98, 0x54, 0xB4, 0xDF, 0xE6, 0xAB, 0x93, + 0x36, 0xB6, 0xD2, 0x43, 0xCF, 0x2B, 0x98, 0x1D, 0x45, 0xC9, 0xBB, 0x20, 0x42, 0xB1, 0x9D, 0x1D + }; + memcpy(&g_loaded_bootconfig, bootconfig, sizeof(bootconfig_t)); + /* TODO: Should these restrictions be loosened for Exosphere? */ + if (configitem_is_retail() + || se_rsa2048_pss_verify(g_loaded_bootconfig.signature, RSA_2048_BYTES, bootconfig_modulus, RSA_2048_BYTES, &g_loaded_bootconfig.signed_config, sizeof(g_loaded_bootconfig.signed_config)) != 0 + || !bootconfig_matches_hardware_info()) { + /* Clear signed config. */ + memset(&g_loaded_bootconfig.signed_config, 0, sizeof(g_loaded_bootconfig.signed_config)); + } } void bootconfig_clear(void){ - /* TODO */ + memset(&g_loaded_bootconfig, 0, sizeof(bootconfig_t)); } /* Actual configuration getters. */ bool bootconfig_is_package2_plaintext(void) { - return false; - /* TODO */ + return (g_loaded_bootconfig.signed_config.package2_config & 1) != 0; } bool bootconfig_is_package2_unsigned(void) { - return false; - /* TODO */ + return (g_loaded_bootconfig.signed_config.package2_config & 2) != 0; } bool bootconfig_disable_program_verification(void) { - return false; - /* TODO */ + return g_loaded_bootconfig.signed_config.disable_program_verification != 0; } bool bootconfig_is_debug_mode(void) { - return false; - /* TODO */ + return (g_loaded_bootconfig.unsigned_config.data[0x10] & 2) != 0; } uint64_t bootconfig_get_memory_arrangement(void) { - return 0ull; - /* TODO */ + if (bootconfig_is_debug_mode()) { + if (fuse_get_dram_id() == 4) { + if (g_loaded_bootconfig.unsigned_config.data[0x23]) { + return (uint64_t)(g_loaded_bootconfig.unsigned_config.data[0x23]); + } else { + return 0x11ull; + } + } else { + if (g_loaded_bootconfig.unsigned_config.data[0x23]) { + if ((g_loaded_bootconfig.unsigned_config.data[0x23] & 0x30) == 0) { + return (uint64_t)(g_loaded_bootconfig.unsigned_config.data[0x23]); + } else { + return 1ull; + } + } else { + return 1ull; + } + } + } else { + return 1ull; + } } uint64_t bootconfig_get_kernel_memory_configuration(void) { - return 0ull; - /* TODO */ + if (bootconfig_is_debug_mode()) { + uint64_t high_val = 0; + if (fuse_get_dram_id() == 4) { + if (g_loaded_bootconfig.unsigned_config.data[0x23]) { + high_val = ((uint64_t)(g_loaded_bootconfig.unsigned_config.data[0x23]) >> 4) & 0x3; + } else { + high_val = 0x1; + } + } + return (high_val << 16) | (((uint64_t)(g_loaded_bootconfig.unsigned_config.data[0x21])) << 8) | ((uint64_t)(g_loaded_bootconfig.unsigned_config.data[0x11])); + } else { + return 0ull; + } } diff --git a/exosphere/src/bootconfig.h b/exosphere/src/bootconfig.h index 5fda11563..b6735f2b5 100644 --- a/exosphere/src/bootconfig.h +++ b/exosphere/src/bootconfig.h @@ -7,9 +7,22 @@ /* This provides management for Switch BootConfig. */ typedef struct { - uint8_t unsigned_config[0x200]; + uint8_t data[0x200]; +} bootconfig_unsigned_config_t; + +typedef struct { + uint8_t _0x0[8]; + uint8_t package2_config; + uint8_t _0x9[7]; + uint8_t hardware_info[0x10]; + uint8_t disable_program_verification; + uint8_t _0x21[0xDF]; +} bootconfig_signed_config_t; + +typedef struct { + bootconfig_unsigned_config_t unsigned_config; uint8_t signature[0x100]; - uint8_t signed_config[0x100]; + bootconfig_signed_config_t signed_config; uint8_t unknown_config[0x240]; } bootconfig_t; diff --git a/exosphere/src/interrupt.c b/exosphere/src/interrupt.c index 418fc895a..36265502d 100644 --- a/exosphere/src/interrupt.c +++ b/exosphere/src/interrupt.c @@ -31,7 +31,7 @@ void intr_initialize_gic(void) { /* Initializes Interrupt Groups 1-7 in the GIC. Called by pk2ldr. */ void intr_initialize_gic_nonsecure(void) { for (unsigned int i = 1; i < 8; i++) { - GICD_IGROUPR[i] = 0xFFFFFFFF + GICD_IGROUPR[i] = 0xFFFFFFFF; } for (unsigned int i = 0x20; i < 0xE0; i++) { diff --git a/exosphere/src/package2.c b/exosphere/src/package2.c index b214f1bc9..8cc793a9a 100644 --- a/exosphere/src/package2.c +++ b/exosphere/src/package2.c @@ -7,6 +7,7 @@ #include "package2.h" #include "configitem.h" #include "se.h" +#include "interrupt.h" #include "masterkey.h" #include "arm.h" #include "randomcache.h" @@ -79,62 +80,6 @@ static void setup_boot_config(void) { } } -static bool rsa2048_pss_verify(const void *signature, size_t signature_size, const void *modulus, size_t modulus_size, const void *data, size_t data_size) { - uint8_t message[RSA_2048_BYTES]; - uint8_t h_buf[0x24]; - - /* Hardcode RSA with keyslot 0. */ - const uint8_t public_exponent[4] = {0x00, 0x01, 0x00, 0x01}; - set_rsa_keyslot(0, modulus, modulus_size, public_exponent, sizeof(public_exponent)); - se_synchronous_exp_mod(0, message, sizeof(message), signature, signature_size); - - /* Validate sanity byte. */ - if (message[RSA_2048_BYTES - 1] != 0xBC) { - return false; - } - - /* Copy Salt into MGF1 Hash Buffer. */ - memset(h_buf, 0, sizeof(h_buf)); - memcpy(h_buf, message + RSA_2048_BYTES - 0x20 - 0x1, 0x20); - - /* Decrypt maskedDB (via inline MGF1). */ - uint8_t seed = 0; - uint8_t mgf1_buf[0x20]; - for (unsigned int ofs = 0; ofs < RSA_2048_BYTES - 0x20 - 1; ofs += 0x20) { - h_buf[sizeof(h_buf) - 1] = seed++; - flush_dcache_range(h_buf, h_buf + sizeof(h_buf)); - se_calculate_sha256(mgf1_buf, h_buf, sizeof(h_buf)); - for (unsigned int i = ofs; i < ofs + 0x20 && i < RSA_2048_BYTES - 0x20 - 1; i++) { - message[i] ^= mgf1_buf[i - ofs]; - } - } - - /* Constant lmask for rsa-2048-pss. */ - message[0] &= 0x7F; - - /* Validate DB is of the form 0000...0001. */ - for (unsigned int i = 0; i < RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1; i++) { - if (message[i] != 0) { - return false; - } - } - if (message[RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1] != 1) { - return false; - } - - /* Check hash correctness. */ - uint8_t validate_buf[8 + 0x20 + 0x20]; - uint8_t validate_hash[0x20]; - - memset(validate_buf, 0, sizeof(validate_buf)); - flush_dcache_range((uint8_t *)data, (uint8_t *)data + data_size); - se_calculate_sha256(&validate_buf[8], data, data_size); - memcpy(&validate_buf[0x28], &message[RSA_2048_BYTES - 0x20 - 0x20 - 1], 0x20); - flush_dcache_range(validate_buf, validate_buf + sizeof(validate_buf)); - se_calculate_sha256(validate_hash, validate_buf, sizeof(validate_buf)); - return memcmp(h_buf, validate_hash, 0x20) == 0; -} - static void package2_crypt_ctr(unsigned int master_key_rev, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size) { /* Derive package2 key. */ const uint8_t package2_key_source[0x10] = {0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7}; @@ -152,7 +97,7 @@ static void verify_header_signature(package2_header_t *header) { const uint8_t *modulus; if (configitem_is_retail()) { - const uint8_t package2_modulus_retail[0x100] = { + static const uint8_t package2_modulus_retail[0x100] = { 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59, 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, @@ -172,7 +117,7 @@ static void verify_header_signature(package2_header_t *header) { }; modulus = package2_modulus_retail; } else { - const uint8_t package2_modulus_dev[0x100] = { + static const uint8_t package2_modulus_dev[0x100] = { 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99, 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, @@ -194,7 +139,7 @@ static void verify_header_signature(package2_header_t *header) { } /* This is normally only allowed on dev units, but we'll allow it anywhere. */ - if (bootconfig_is_package2_unsigned() == 0 && rsa2048_pss_verify(header->signature, 0x100, modulus, 0x100, header->encrypted_header, 0x100) == 0) { + if (bootconfig_is_package2_unsigned() == 0 && se_rsa2048_pss_verify(header->signature, 0x100, modulus, 0x100, header->encrypted_header, 0x100) == 0) { generic_panic(); } } diff --git a/exosphere/src/se.c b/exosphere/src/se.c index 50d6c9592..c6a9ff418 100644 --- a/exosphere/src/se.c +++ b/exosphere/src/se.c @@ -340,6 +340,63 @@ void se_get_exp_mod_output(void *buf, size_t size) { } } +bool se_rsa2048_pss_verify(const void *signature, size_t signature_size, const void *modulus, size_t modulus_size, const void *data, size_t data_size) { + uint8_t message[RSA_2048_BYTES]; + uint8_t h_buf[0x24]; + + /* Hardcode RSA with keyslot 0. */ + const uint8_t public_exponent[4] = {0x00, 0x01, 0x00, 0x01}; + set_rsa_keyslot(0, modulus, modulus_size, public_exponent, sizeof(public_exponent)); + se_synchronous_exp_mod(0, message, sizeof(message), signature, signature_size); + + /* Validate sanity byte. */ + if (message[RSA_2048_BYTES - 1] != 0xBC) { + return false; + } + + /* Copy Salt into MGF1 Hash Buffer. */ + memset(h_buf, 0, sizeof(h_buf)); + memcpy(h_buf, message + RSA_2048_BYTES - 0x20 - 0x1, 0x20); + + /* Decrypt maskedDB (via inline MGF1). */ + uint8_t seed = 0; + uint8_t mgf1_buf[0x20]; + for (unsigned int ofs = 0; ofs < RSA_2048_BYTES - 0x20 - 1; ofs += 0x20) { + h_buf[sizeof(h_buf) - 1] = seed++; + flush_dcache_range(h_buf, h_buf + sizeof(h_buf)); + se_calculate_sha256(mgf1_buf, h_buf, sizeof(h_buf)); + for (unsigned int i = ofs; i < ofs + 0x20 && i < RSA_2048_BYTES - 0x20 - 1; i++) { + message[i] ^= mgf1_buf[i - ofs]; + } + } + + /* Constant lmask for rsa-2048-pss. */ + message[0] &= 0x7F; + + /* Validate DB is of the form 0000...0001. */ + for (unsigned int i = 0; i < RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1; i++) { + if (message[i] != 0) { + return false; + } + } + if (message[RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1] != 1) { + return false; + } + + /* Check hash correctness. */ + uint8_t validate_buf[8 + 0x20 + 0x20]; + uint8_t validate_hash[0x20]; + + memset(validate_buf, 0, sizeof(validate_buf)); + flush_dcache_range((uint8_t *)data, (uint8_t *)data + data_size); + se_calculate_sha256(&validate_buf[8], data, data_size); + memcpy(&validate_buf[0x28], &message[RSA_2048_BYTES - 0x20 - 0x20 - 1], 0x20); + flush_dcache_range(validate_buf, validate_buf + sizeof(validate_buf)); + se_calculate_sha256(validate_hash, validate_buf, sizeof(validate_buf)); + return memcmp(h_buf, validate_hash, 0x20) == 0; +} + + void trigger_se_rsa_op(void *buf, size_t size) { se_ll_t in_ll; ll_init(&in_ll, (void *)buf, size); diff --git a/exosphere/src/se.h b/exosphere/src/se.h index b10a81e75..279106ae5 100644 --- a/exosphere/src/se.h +++ b/exosphere/src/se.h @@ -208,6 +208,7 @@ void se_calculate_sha256(void *dst, const void *src, size_t src_size); void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*callback)(void)); void se_get_exp_mod_output(void *buf, size_t size); void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); +bool se_rsa2048_pss_verify(const void *signature, size_t signature_size, const void *modulus, size_t modulus_size, const void *data, size_t data_size); /* RNG API */ void se_initialize_rng(unsigned int keyslot);