From 57f72d5aa706d074c2dd0bcedb2906c2d5fddd05 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 | 6 +- core/embed/trezorhal/stm32f4/syscall.c | 67 +++++++- core/embed/trezorhal/stm32f4/syscall.h | 11 +- core/embed/trezorhal/stm32u5/mpu.c | 21 ++- core/embed/trezorhal/stm32u5/secret.c | 5 +- core/embed/trezorhal/stm32u5/secure_aes.c | 191 +++++++++++++++++++--- storage/storage.c | 4 +- 8 files changed, 267 insertions(+), 39 deletions(-) diff --git a/core/embed/trezorhal/mpu.h b/core/embed/trezorhal/mpu.h index 895d1331c8..8729c62e8c 100644 --- a/core/embed/trezorhal/mpu.h +++ b/core/embed/trezorhal/mpu.h @@ -40,6 +40,7 @@ typedef enum { MPU_MODE_SECRET, // + secret area (privileged RW) MPU_MODE_STORAGE, // + both storage areas (privileged RW) MPU_MODE_ASSETS, // + assets (privileged RW) + MPU_MODE_SAES, // + unprivileged SAES code MPU_MODE_KERNEL_SRAM, // + extra kernel SRAM (STM32F4 Only) (privileged RW) MPU_MODE_UNUSED_FLASH, // + unused flash areas (privileged RW) MPU_MODE_APP, // + unprivileged DMA2D (RW) & Assets (RO) diff --git a/core/embed/trezorhal/secure_aes.h b/core/embed/trezorhal/secure_aes.h index 69eac50dc6..9849e68c03 100644 --- a/core/embed/trezorhal/secure_aes.h +++ b/core/embed/trezorhal/secure_aes.h @@ -24,10 +24,12 @@ #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_BHK, - SECURE_AES_KEY_XORK, + SECURE_AES_KEY_XORK_SP, // secure-privileged + SECURE_AES_KEY_XORK_SN, // secure-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 2846ba68e6..eadac85245 100644 --- a/core/embed/trezorhal/stm32f4/syscall.c +++ b/core/embed/trezorhal/stm32f4/syscall.c @@ -23,7 +23,7 @@ #ifdef SYSCALL_DISPATCH __attribute__((naked, no_stack_protector)) static uint32_t _invoke_app_callback( - uint32_t arg1, uint32_t arg2, uint32_t arg3, void* callback) { + uint32_t arg1, uint32_t arg2, uint32_t arg3, void *callback) { __asm__ volatile( "push {r1-r12, lr} \n" @@ -71,7 +71,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); @@ -79,7 +79,7 @@ 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" @@ -98,4 +98,65 @@ __attribute__((naked, no_stack_protector)) void return_from_app_callback( "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" + +#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__) + "mrs r12, PSPLIM \n" // Backup unprivileged stack limit + "push {r12} \n" +#endif + "mrs r12, PSP \n" // Backup unprivileged stack pointer + "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 + + "vmov r0, s0 \n" // Use FPU instruction to ensure lazy + // stacking + + // 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 de64aaa906..944cd2981d 100644 --- a/core/embed/trezorhal/stm32f4/syscall.h +++ b/core/embed/trezorhal/stm32f4/syscall.h @@ -57,6 +57,13 @@ uint32_t invoke_app_callback(uint32_t args1, uint32_t arg2, uint32_t arg3, // the stack pointer and returns control to the privileged caller. void return_from_app_callback(uint32_t retval, uint32_t* msp); +// Invokes an unprivileged function from privileged mode. +// +// This is a *temporary* helper function used to control the STM32 SAES +// peripheral from unprivileged mode for backward compatibility (due to +// different hardware keys being used in privileged and unprivileged modes). +uint32_t invoke_unpriv(void* func); + #else // KERNEL_MODE static inline uint32_t __attribute__((no_stack_protector)) @@ -199,6 +206,8 @@ syscall_invoke6(uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4, return ret; } +#endif // KERNEL_MODE + static inline void __attribute__((no_stack_protector)) syscall_return_from_callback(uint32_t retval) { register uint32_t r0 __asm__("r0") = retval; @@ -208,6 +217,4 @@ syscall_return_from_callback(uint32_t retval) { : "memory"); } -#endif // KERNEL_MODE - #endif // TREZORHAL_SYSCALL_H diff --git a/core/embed/trezorhal/stm32u5/mpu.c b/core/embed/trezorhal/stm32u5/mpu.c index 69b3e81856..7cdd189487 100644 --- a/core/embed/trezorhal/stm32u5/mpu.c +++ b/core/embed/trezorhal/stm32u5/mpu.c @@ -217,7 +217,6 @@ static void mpu_init_fixed_regions(void) { SET_REGION( 2, BOOTLOADER_START, BOOTLOADER_SIZE, FLASH_DATA, YES, NO ); SET_REGION( 3, FIRMWARE_START, FIRMWARE_SIZE, FLASH_DATA, YES, NO ); DIS_REGION( 4 ); - SET_REGION( 5, GRAPHICS_START, GRAPHICS_SIZE, SRAM, YES, NO ); #endif #if defined(BOOTLOADER) // REGION ADDRESS SIZE TYPE WRITE UNPRIV @@ -226,7 +225,6 @@ static void mpu_init_fixed_regions(void) { SET_REGION( 2, FIRMWARE_START, FIRMWARE_SIZE, FLASH_DATA, YES, NO ); DIS_REGION( 3 ); DIS_REGION( 4 ); - SET_REGION( 5, GRAPHICS_START, GRAPHICS_SIZE, SRAM, YES, NO ); #endif #if defined(KERNEL) // REGION ADDRESS SIZE TYPE WRITE UNPRIV @@ -239,7 +237,6 @@ static void mpu_init_fixed_regions(void) { #else DIS_REGION( 4 ); #endif - SET_REGION( 5, GRAPHICS_START, GRAPHICS_SIZE, SRAM, YES, YES ); // Frame buffer or display interface #endif #if defined(FIRMWARE) // REGION ADDRESS SIZE TYPE WRITE UNPRIV @@ -248,7 +245,6 @@ static void mpu_init_fixed_regions(void) { DIS_REGION( 2 ); DIS_REGION( 3 ); DIS_REGION( 4 ); - SET_REGION( 5, GRAPHICS_START, GRAPHICS_SIZE, SRAM, YES, NO ); #endif #if defined(TREZOR_PRODTEST) SET_REGION( 0, FIRMWARE_START, 1024, FLASH_DATA, YES, NO ); @@ -256,11 +252,11 @@ static void mpu_init_fixed_regions(void) { SET_REGION( 2, SRAM1_BASE, SRAM_SIZE, SRAM, YES, NO ); DIS_REGION( 3 ); DIS_REGION( 4 ); - SET_REGION( 5, GRAPHICS_START, GRAPHICS_SIZE, SRAM, YES, NO ); #endif // Regions #6 and #7 are banked + DIS_REGION( 5 ); DIS_REGION( 6 ); DIS_REGION( 7 ); // clang-format on @@ -314,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 @@ -349,6 +348,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; @@ -362,8 +364,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 (Unprivileged, 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 47602e50b8..f6b3a2d4f5 100644 --- a/core/embed/trezorhal/stm32u5/secret.c +++ b/core/embed/trezorhal/stm32u5/secret.c @@ -252,7 +252,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); @@ -279,7 +280,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 94f8afe5c8..036c3958a3 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 "model.h" +#include "syscall.h" + #include "memzero.h" +#define SAES_DATA_SIZE_WITH_UPRIV_KEY 32 +#define AES_BLOCK_SIZE 16 + #ifdef KERNEL_MODE -#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(); - - return sectrue; -} +#include "irq.h" static void secure_aes_load_bhk(void) { TAMP->BKP0R; @@ -58,19 +49,159 @@ 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: 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: 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[SAES_DATA_SIZE_WITH_UPRIV_KEY / sizeof(uint32_t)]; + +__attribute__((section(".udata"))) +uint32_t saes_output[SAES_DATA_SIZE_WITH_UPRIV_KEY / 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 < SAES_DATA_SIZE_WITH_UPRIV_KEY / 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); + + syscall_return_from_callback(sectrue); + return 0; +} + +extern uint32_t sram_u_start; +extern uint32_t sram_u_end; + +secbool unpriv_encrypt(const uint8_t* input, size_t size, uint8_t* output, + secure_aes_keysel_t key) { + if (size != SAES_DATA_SIZE_WITH_UPRIV_KEY) { + return secfalse; + } + + if (key != SECURE_AES_KEY_XORK_SN) { + return secfalse; + } + + uint32_t prev_svc_prio = NVIC_GetPriority(SVCall_IRQn); + NVIC_SetPriority(SVCall_IRQn, IRQ_PRI_HIGHEST); + uint32_t basepri = __get_BASEPRI(); + __set_BASEPRI(IRQ_PRI_HIGHEST + 1); + + mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SAES); + + memset(&sram_u_start, 0, &sram_u_end - &sram_u_start); + memcpy(saes_input, input, size); + + 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(); + + memcpy(output, saes_output, size); + memset(&sram_u_start, 0, &sram_u_end - &sram_u_start); + + mpu_reconfig(mpu_mode); + + __set_BASEPRI(basepri); + NVIC_SetPriority(SVCall_IRQn, prev_svc_prio); + + 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}; @@ -138,6 +269,10 @@ secbool secure_aes_ecb_encrypt_hw(const uint8_t* input, size_t size, 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 +337,20 @@ secbool secure_aes_ecb_decrypt_hw(const uint8_t* input, size_t size, return sectrue; } -#endif // KERNEL_MODE \ No newline at end of file +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; +} + +#endif diff --git a/storage/storage.c b/storage/storage.c index fe36b52e06..cff331cfb3 100644 --- a/storage/storage.c +++ b/storage/storage.c @@ -557,7 +557,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 @@ -615,7 +615,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