From bb562525faa08afc2fc57a0d98c514047af53650 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Wed, 4 Sep 2024 11:18:25 +0200 Subject: [PATCH] fix(core): use secure-unprivileged SAES XOR key for storage encryption [no changelog] --- core/embed/trezorhal/mpu.h | 1 + core/embed/trezorhal/secure_aes.h | 11 +- core/embed/trezorhal/stm32f4/syscall.c | 75 +++++++- core/embed/trezorhal/stm32f4/syscall.h | 3 + core/embed/trezorhal/stm32u5/mpu.c | 15 +- core/embed/trezorhal/stm32u5/secret.c | 5 +- core/embed/trezorhal/stm32u5/secure_aes.c | 212 ++++++++++++++++++++-- storage/storage.c | 4 +- 8 files changed, 295 insertions(+), 31 deletions(-) diff --git a/core/embed/trezorhal/mpu.h b/core/embed/trezorhal/mpu.h index 49e97787b..7ab5ecd96 100644 --- a/core/embed/trezorhal/mpu.h +++ b/core/embed/trezorhal/mpu.h @@ -40,6 +40,7 @@ typedef enum { MPU_MODE_SECRET, // + secret area (priviledeg RW) MPU_MODE_STORAGE, // + both storage areas (privilehed RW) MPU_MODE_ASSETS, // + assets (privileged RW) + MPU_MODE_SAES, // + unprivileged SAES code MPU_MODE_APP, // + unprivileged DMA2D (RW) & Assets (RO) } mpu_mode_t; diff --git a/core/embed/trezorhal/secure_aes.h b/core/embed/trezorhal/secure_aes.h index 69eac50dc..88944b973 100644 --- a/core/embed/trezorhal/secure_aes.h +++ b/core/embed/trezorhal/secure_aes.h @@ -24,10 +24,17 @@ #include #include +// only some of the keys are supported depending on execution environment typedef enum { - SECURE_AES_KEY_DHUK, + SECURE_AES_KEY_DHUK_SP, // secure-privileged + SECURE_AES_KEY_DHUK_SN, // secure-nonprivileged + SECURE_AES_KEY_DHUK_NP, // nonsecure-privileged + SECURE_AES_KEY_DHUK_NN, // nonsecure-nonprivileged SECURE_AES_KEY_BHK, - SECURE_AES_KEY_XORK, + SECURE_AES_KEY_XORK_SP, // secure-privileged + SECURE_AES_KEY_XORK_SN, // secure-nonprivileged + SECURE_AES_KEY_XORK_NP, // nonsecure-privileged + SECURE_AES_KEY_XORK_NN, // nonsecure-nonprivileged } secure_aes_keysel_t; // Initializes secure AES module diff --git a/core/embed/trezorhal/stm32f4/syscall.c b/core/embed/trezorhal/stm32f4/syscall.c index c58a8f2c3..89ec5f585 100644 --- a/core/embed/trezorhal/stm32f4/syscall.c +++ b/core/embed/trezorhal/stm32f4/syscall.c @@ -23,10 +23,12 @@ #ifdef SYSCALL_DISPATCH __attribute__((naked, no_stack_protector)) static uint32_t _invoke_app_callback( - uint32_t args1, uint32_t arg2, uint32_t arg3, void* callback) { + uint32_t args1, uint32_t arg2, uint32_t arg3, void *callback) { __asm__ volatile( "push {r1-r12, lr} \n" + "mrs r12, PSPLIM \n" // backup unprivileged stack lim + "push {r12} \n" "mrs r12, PSP \n" // reserved frame on unprivileged stack (!@# // TODO check PSP value???) "push {r12} \n" @@ -73,7 +75,7 @@ __attribute__((naked, no_stack_protector)) static uint32_t _invoke_app_callback( } uint32_t invoke_app_callback(uint32_t args1, uint32_t arg2, uint32_t arg3, - void* callback) { + void *callback) { mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_APP); uint32_t retval = _invoke_app_callback(args1, arg2, arg3, callback); mpu_reconfig(mpu_mode); @@ -81,13 +83,80 @@ uint32_t invoke_app_callback(uint32_t args1, uint32_t arg2, uint32_t arg3, } __attribute__((naked, no_stack_protector)) void return_from_app_callback( - uint32_t retval, uint32_t* msp) { + uint32_t retval, uint32_t *msp) { __asm__ volatile( "MSR MSP, R1 \n" "POP {R1} \n" "MSR PSP, R1 \n" + "POP {R1} \n" + "MSR PSPLIM, R1 \n" "POP {R1-R12, LR} \n" "BX LR \n"); } +__attribute__((naked, no_stack_protector)) static uint32_t _invoke_unpriv( + uint32_t stack_addr, uint32_t stack_lim, void *callback) { + __asm__ volatile( + "push {r1-r12, lr} \n" + + "mrs r12, PSPLIM \n" // backup unprivileged stack lim + "push {r12} \n" + "mrs r12, PSP \n" // backup unprivileged stack + "push {r12} \n" + + "mov r12, r0 \n" // setup stack for unprivileged call inside + // kernel + "sub r12, r12, #32 \n" + "msr PSP, r12 \n" + "msr PSPLIM, r1 \n" + + "mov r3, #0 \n" + + "mov r4, r3 \n" // Clear registers r4-r11 + "mov r5, r3 \n" + "mov r6, r3 \n" + "mov r7, r3 \n" + "mov r8, r3 \n" + "mov r9, r3 \n" + "mov r10, r3 \n" + "mov r11, r3 \n" + + "str r3, [r12, #0] \n" // r0 + "str r3, [r12, #4] \n" // r1" + "str r3, [r12, #8] \n" // r2" + "str r3, [r12, #12] \n" // r3" + "str r3, [r12, #16] \n" // r12" + "str r3, [r12, #20] \n" // lr" + + "bic r3, r2, #1 \n" + "str r3, [r12, #24] \n" // return address + + "ldr r1, = 0x01000000 \n" + "str r1, [r12, #28] \n" // xPSR + + "ldr r1, = 0xE000EF34 \n" // FPU->FPPCCR + "ldr r0, [r1] \n" + "bic r0, r0, #1 \n" // Clear LSPACT to suppress lazy stacking to + "str r0, [r1] \n" // avoid potential PSP stack overwrite. + + "mrs r1, CONTROL \n" + "bic r1, r1, #4 \n" // Clear FPCA to suppress lazy stacking to + "msr CONTROL, r1 \n" // avoid potential PSP stack overwrite. + + // return to Secure Thread mode (use Secure PSP) + "ldr lr, = 0xFFFFFFFD \n" + "bx lr \n"); +} + +extern const void _eustack; +extern const void _sustack; + +uint32_t invoke_unpriv(void *func) { + uint32_t *stack = (uint32_t *)&_eustack; + uint32_t *stack_lim = (uint32_t *)&_sustack; + + uint32_t retval = _invoke_unpriv((uint32_t)stack, (uint32_t)stack_lim, func); + return retval; +} + #endif // SYSCALL_DISPATCH diff --git a/core/embed/trezorhal/stm32f4/syscall.h b/core/embed/trezorhal/stm32f4/syscall.h index a0cefd53a..bb3b14cb7 100644 --- a/core/embed/trezorhal/stm32f4/syscall.h +++ b/core/embed/trezorhal/stm32f4/syscall.h @@ -50,6 +50,9 @@ uint32_t invoke_app_callback(uint32_t args1, uint32_t arg2, uint32_t arg3, void return_from_app_callback(uint32_t retval, uint32_t* msp); +// Invokes unprivileged function run +uint32_t invoke_unpriv(void* func); + #else // KERNEL_MODE static inline uint32_t __attribute__((no_stack_protector)) diff --git a/core/embed/trezorhal/stm32u5/mpu.c b/core/embed/trezorhal/stm32u5/mpu.c index fc30de454..651a0940a 100644 --- a/core/embed/trezorhal/stm32u5/mpu.c +++ b/core/embed/trezorhal/stm32u5/mpu.c @@ -310,9 +310,12 @@ mpu_mode_t mpu_reconfig(mpu_mode_t mode) { // clang-format off switch (mode) { + case MPU_MODE_SAES: + SET_REGION( 5, PERIPH_BASE_NS, SIZE_512M, PERIPHERAL, YES, YES ); // Peripherals - SAES, TAMP + break; default: - SET_REGION( 5, GRAPHICS_START, GRAPHICS_SIZE, SRAM, YES, YES ); // Peripherals - break; + SET_REGION( 5, GRAPHICS_START, GRAPHICS_SIZE, SRAM, YES, YES ); // Frame buffer or display interface + break; } // clang-format on @@ -341,6 +344,9 @@ mpu_mode_t mpu_reconfig(mpu_mode_t mode) { case MPU_MODE_ASSETS: SET_REGION( 6, ASSETS_START, ASSETS_SIZE, FLASH_DATA, YES, NO ); break; + case MPU_MODE_SAES: + SET_REGION( 6, KERNEL_FLASH_U_START, KERNEL_FLASH_U_SIZE,FLASH_CODE, NO, YES ); // Unprivileged kernal flash + break; case MPU_MODE_APP: SET_REGION( 6, ASSETS_START, ASSETS_SIZE, FLASH_DATA, NO, YES ); break; @@ -356,8 +362,11 @@ mpu_mode_t mpu_reconfig(mpu_mode_t mode) { // clang-format off switch (mode) { - case MPU_MODE_APP: // REGION ADDRESS SIZE TYPE WRITE UNPRIV + case MPU_MODE_SAES: + SET_REGION( 7, KERNEL_RAM_U_START, KERNEL_RAM_U_SIZE, SRAM, YES, YES ); // Unprivileged kernel SRAM + break; + case MPU_MODE_APP: // DMA2D peripherals (Uprivileged, Read-Write, Non-Executable) SET_REGION( 7, 0x5002B000, SIZE_3K, PERIPHERAL, YES, YES ); break; diff --git a/core/embed/trezorhal/stm32u5/secret.c b/core/embed/trezorhal/stm32u5/secret.c index 66fd507e9..485e777c6 100644 --- a/core/embed/trezorhal/stm32u5/secret.c +++ b/core/embed/trezorhal/stm32u5/secret.c @@ -255,7 +255,8 @@ static void secret_optiga_cache(void) { secbool secret_optiga_set(const uint8_t secret[SECRET_OPTIGA_KEY_LEN]) { uint8_t secret_enc[SECRET_OPTIGA_KEY_LEN] = {0}; if (sectrue != secure_aes_ecb_encrypt_hw(secret, sizeof(secret_enc), - secret_enc, SECURE_AES_KEY_DHUK)) { + secret_enc, + SECURE_AES_KEY_DHUK_SP)) { return secfalse; } secret_write(secret_enc, SECRET_OPTIGA_KEY_OFFSET, SECRET_OPTIGA_KEY_LEN); @@ -282,7 +283,7 @@ secbool secret_optiga_get(uint8_t dest[SECRET_OPTIGA_KEY_LEN]) { } secbool res = secure_aes_ecb_decrypt_hw( - (uint8_t *)secret, SECRET_OPTIGA_KEY_LEN, dest, SECURE_AES_KEY_DHUK); + (uint8_t *)secret, SECRET_OPTIGA_KEY_LEN, dest, SECURE_AES_KEY_DHUK_SP); memzero(secret, sizeof(secret)); return res; diff --git a/core/embed/trezorhal/stm32u5/secure_aes.c b/core/embed/trezorhal/stm32u5/secure_aes.c index 94f8afe5c..fb304de10 100644 --- a/core/embed/trezorhal/stm32u5/secure_aes.c +++ b/core/embed/trezorhal/stm32u5/secure_aes.c @@ -17,33 +17,24 @@ * along with this program. If not, see . */ +#include #include #include STM32_HAL_H #include #include #include -#include "memzero.h" +#include "model.h" +#include "syscall.h" -#ifdef KERNEL_MODE +#include "memzero.h" +#define KERNEL_UNPRIVILEGED_SIZE 32 #define AES_BLOCK_SIZE 16 -secbool secure_aes_init(void) { - RCC_OscInitTypeDef osc_init_def = {0}; - osc_init_def.OscillatorType = RCC_OSCILLATORTYPE_SHSI; - osc_init_def.SHSIState = RCC_SHSI_ON; - - // Enable SHSI clock - if (HAL_RCC_OscConfig(&osc_init_def) != HAL_OK) { - return secfalse; - } - - // Enable SAES peripheral clock - __HAL_RCC_SAES_CLK_ENABLE(); +#ifdef KERNEL_MODE - return sectrue; -} +#include "irq.h" static void secure_aes_load_bhk(void) { TAMP->BKP0R; @@ -58,19 +49,182 @@ static void secure_aes_load_bhk(void) { static uint32_t get_keysel(secure_aes_keysel_t key) { switch (key) { - case SECURE_AES_KEY_DHUK: + case SECURE_AES_KEY_DHUK_SP: + case SECURE_AES_KEY_DHUK_SN: + case SECURE_AES_KEY_DHUK_NP: + case SECURE_AES_KEY_DHUK_NN: return CRYP_KEYSEL_HW; case SECURE_AES_KEY_BHK: return CRYP_KEYSEL_SW; - case SECURE_AES_KEY_XORK: + case SECURE_AES_KEY_XORK_SP: + case SECURE_AES_KEY_XORK_SN: + case SECURE_AES_KEY_XORK_NP: + case SECURE_AES_KEY_XORK_NN: return CRYP_KEYSEL_HSW; default: return 0; } } +static secbool is_key_supported(secure_aes_keysel_t key) { + switch (key) { + case SECURE_AES_KEY_DHUK_SP: + case SECURE_AES_KEY_BHK: + case SECURE_AES_KEY_XORK_SP: + return sectrue; + default: + return secfalse; + } +} + +#ifdef SYSCALL_DISPATCH + +__attribute__((section(".udata"))) +uint32_t saes_input[KERNEL_UNPRIVILEGED_SIZE / sizeof(uint32_t)]; + +__attribute__((section(".udata"))) +uint32_t saes_output[KERNEL_UNPRIVILEGED_SIZE / sizeof(uint32_t)]; + +__attribute__((section(".uflash"), used, naked, no_stack_protector)) uint32_t +saes_invoke(void) { + // reset the key loaded in SAES + MODIFY_REG(SAES->CR, AES_CR_KEYSEL, CRYP_KEYSEL_NORMAL); + + while (HAL_IS_BIT_SET(SAES->SR, CRYP_FLAG_BUSY)) { + } + while (HAL_IS_BIT_SET(SAES->ISR, CRYP_FLAG_RNGEIF)) { + } + + MODIFY_REG(SAES->CR, + AES_CR_KMOD | AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD | + AES_CR_KEYSEL | AES_CR_KEYPROT, + CRYP_KEYMODE_NORMAL | CRYP_NO_SWAP | CRYP_KEYSIZE_256B | + CRYP_AES_ECB | CRYP_KEYSEL_HSW | CRYP_KEYPROT_DISABLE); + + TAMP->BKP0R; + TAMP->BKP1R; + TAMP->BKP2R; + TAMP->BKP3R; + TAMP->BKP4R; + TAMP->BKP5R; + TAMP->BKP6R; + TAMP->BKP7R; + +#define CRYP_OPERATINGMODE_ENCRYPT 0x00000000U /*!< Encryption mode(Mode 1) */ + + /* Set the operating mode and normal key selection */ + MODIFY_REG(SAES->CR, AES_CR_MODE | AES_CR_KMOD, + CRYP_OPERATINGMODE_ENCRYPT | CRYP_KEYMODE_NORMAL); + + SAES->CR |= AES_CR_EN; + + for (int j = 0; j < KERNEL_UNPRIVILEGED_SIZE / AES_BLOCK_SIZE; j++) { + /* Write the input block in the IN FIFO */ + SAES->DINR = saes_input[j * 4 + 0]; + SAES->DINR = saes_input[j * 4 + 1]; + SAES->DINR = saes_input[j * 4 + 2]; + SAES->DINR = saes_input[j * 4 + 3]; + + while (HAL_IS_BIT_CLR(SAES->ISR, AES_ISR_CCF)) { + } + + /* Clear CCF Flag */ + SET_BIT(SAES->ICR, CRYP_CLEAR_CCF); + + /* Read the output block from the output FIFO */ + for (int i = 0U; i < 4U; i++) { + saes_output[j * 4 + i] = SAES->DOUTR; + } + } + + SAES->CR &= ~AES_CR_EN; + + // reset the key loaded in SAES + MODIFY_REG(SAES->CR, AES_CR_KEYSEL, CRYP_KEYSEL_NORMAL); + + register uint32_t r0 __asm__("r0") = sectrue; + __asm__ volatile("svc %[svid]\n" + : + : [svid] "i"(SVC_CALLBACK_RETURN), "r"(r0) + : "memory"); + return 0; +} + +extern uint32_t sram2_u_start; +extern uint32_t sram2_u_end; + +secbool unpriv_encrypt(const uint8_t* input, size_t size, uint8_t* output, + secure_aes_keysel_t key) { + if (size != KERNEL_UNPRIVILEGED_SIZE) { + return secfalse; + } + + if (key != SECURE_AES_KEY_XORK_SN) { + return secfalse; + } + + NVIC_SetPriority(SVCall_IRQn, IRQ_PRI_HIGHEST); + uint32_t basepri = __get_BASEPRI(); + __set_BASEPRI(1); + + mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SAES); + + for (uint32_t* m = &sram2_u_start; m < &sram2_u_end; m++) { + *m = 0; + } + + for (int i = 0; i < KERNEL_UNPRIVILEGED_SIZE; i++) { + ((uint8_t*)saes_input)[i] = input[i]; + } + + SAES->CR |= AES_CR_KEYSEL_0; + + __HAL_RCC_SAES_CLK_DISABLE(); + __HAL_RCC_SAES_FORCE_RESET(); + __HAL_RCC_SAES_RELEASE_RESET(); + __HAL_RCC_SAES_CLK_ENABLE(); + + secbool retval = invoke_unpriv(saes_invoke); + + __HAL_RCC_SAES_CLK_DISABLE(); + __HAL_RCC_SAES_FORCE_RESET(); + __HAL_RCC_SAES_RELEASE_RESET(); + __HAL_RCC_SAES_CLK_ENABLE(); + + for (int i = 0; i < KERNEL_UNPRIVILEGED_SIZE; i++) { + output[i] = ((uint8_t*)saes_output)[i]; + } + + for (int i = 0; i < KERNEL_UNPRIVILEGED_SIZE; i++) { + ((uint8_t*)saes_input)[i] = 0; + ((uint8_t*)saes_output)[i] = 0; + } + + for (uint32_t* m = &sram2_u_start; m < &sram2_u_end; m++) { + *m = 0; + } + + mpu_reconfig(mpu_mode); + + __set_BASEPRI(basepri); + NVIC_SetPriority(SVCall_IRQn, IRQ_PRI_LOWEST); + + return retval; +} +#endif + secbool secure_aes_ecb_encrypt_hw(const uint8_t* input, size_t size, uint8_t* output, secure_aes_keysel_t key) { +#ifdef SYSCALL_DISPATCH + if (key == SECURE_AES_KEY_XORK_SN) { + return unpriv_encrypt(input, size, output, key); + } +#endif + + if (sectrue != is_key_supported(key)) { + return secfalse; + } + CRYP_HandleTypeDef hcryp = {0}; uint32_t iv[] = {0, 0, 0, 0}; @@ -136,8 +290,28 @@ secbool secure_aes_ecb_encrypt_hw(const uint8_t* input, size_t size, return sectrue; } +secbool secure_aes_init(void) { + RCC_OscInitTypeDef osc_init_def = {0}; + osc_init_def.OscillatorType = RCC_OSCILLATORTYPE_SHSI; + osc_init_def.SHSIState = RCC_SHSI_ON; + + // Enable SHSI clock + if (HAL_RCC_OscConfig(&osc_init_def) != HAL_OK) { + return secfalse; + } + + // Enable SAES peripheral clock + __HAL_RCC_SAES_CLK_ENABLE(); + + return sectrue; +} + secbool secure_aes_ecb_decrypt_hw(const uint8_t* input, size_t size, uint8_t* output, secure_aes_keysel_t key) { + if (sectrue != is_key_supported(key)) { + return secfalse; + } + CRYP_HandleTypeDef hcryp = {0}; uint32_t iv[] = {0, 0, 0, 0}; @@ -202,4 +376,4 @@ secbool secure_aes_ecb_decrypt_hw(const uint8_t* input, size_t size, return sectrue; } -#endif // KERNEL_MODE \ No newline at end of file +#endif diff --git a/storage/storage.c b/storage/storage.c index 6ac55a255..31bb4be8e 100644 --- a/storage/storage.c +++ b/storage/storage.c @@ -559,7 +559,7 @@ static void derive_kek_v4(const uint8_t *pin, size_t pin_len, uint8_t pre_kek[SHA256_DIGEST_LENGTH] = {0}; pbkdf2_hmac_sha256_Final(&ctx, pre_kek); ensure(secure_aes_ecb_encrypt_hw(pre_kek, SHA256_DIGEST_LENGTH, kek, - SECURE_AES_KEY_XORK), + SECURE_AES_KEY_XORK_SN), "secure_aes derive kek failed"); memzero(pre_kek, sizeof(pre_kek)); #else @@ -617,7 +617,7 @@ static void stretch_pin(const uint8_t *pin, size_t pin_len, uint8_t stretched_pin_tmp[SHA256_DIGEST_LENGTH] = {0}; pbkdf2_hmac_sha256_Final(&ctx, stretched_pin_tmp); ensure(secure_aes_ecb_encrypt_hw(stretched_pin_tmp, SHA256_DIGEST_LENGTH, - stretched_pin, SECURE_AES_KEY_XORK), + stretched_pin, SECURE_AES_KEY_XORK_SN), "secure_aes pin stretch failed"); memzero(stretched_pin_tmp, sizeof(stretched_pin_tmp)); #else