1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-31 10:30:58 +00:00

fix(core): use secure-unprivileged SAES XOR key for storage encryption

[no changelog]
This commit is contained in:
tychovrahe 2024-09-04 11:18:25 +02:00 committed by cepetr
parent 28f420189a
commit 57f72d5aa7
8 changed files with 267 additions and 39 deletions

View File

@ -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)

View File

@ -24,10 +24,12 @@
#include <stddef.h>
#include <stdint.h>
// 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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -17,33 +17,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mpu.h>
#include <secure_aes.h>
#include STM32_HAL_H
#include <stdio.h>
#include <stm32u5xx_hal_cryp.h>
#include <string.h>
#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
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

View File

@ -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