You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
7.5 KiB
282 lines
7.5 KiB
#include "secret.h"
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "common.h"
|
|
#include "flash.h"
|
|
#include "memzero.h"
|
|
#include "model.h"
|
|
#include "rng.h"
|
|
#include "secure_aes.h"
|
|
|
|
static secbool bootloader_locked = secfalse;
|
|
|
|
secbool secret_verify_header(void) {
|
|
uint8_t *addr = (uint8_t *)flash_area_get_address(
|
|
&SECRET_AREA, 0, sizeof(SECRET_HEADER_MAGIC));
|
|
|
|
if (addr == NULL) {
|
|
return secfalse;
|
|
}
|
|
|
|
bootloader_locked =
|
|
memcmp(addr, SECRET_HEADER_MAGIC, sizeof(SECRET_HEADER_MAGIC)) == 0
|
|
? sectrue
|
|
: secfalse;
|
|
return bootloader_locked;
|
|
}
|
|
|
|
secbool secret_ensure_initialized(void) {
|
|
if (sectrue != secret_verify_header()) {
|
|
ensure(flash_area_erase_bulk(STORAGE_AREAS, STORAGE_AREAS_COUNT, NULL),
|
|
"erase storage failed");
|
|
secret_erase();
|
|
secret_write_header();
|
|
return secfalse;
|
|
}
|
|
return sectrue;
|
|
}
|
|
|
|
secbool secret_bootloader_locked(void) {
|
|
#ifdef FIRMWARE
|
|
return TAMP->BKP8R != 0 * sectrue;
|
|
#else
|
|
return sectrue;
|
|
#endif
|
|
}
|
|
|
|
void secret_write_header(void) {
|
|
uint8_t header[SECRET_HEADER_LEN] = {0};
|
|
memcpy(header, SECRET_HEADER_MAGIC, 4);
|
|
secret_write(header, 0, SECRET_HEADER_LEN);
|
|
}
|
|
|
|
void secret_write(const uint8_t *data, uint32_t offset, uint32_t len) {
|
|
ensure(flash_unlock_write(), "secret write");
|
|
for (int i = 0; i < len / 16; i++) {
|
|
ensure(flash_area_write_quadword(&SECRET_AREA, offset + (i * 16),
|
|
(uint32_t *)&data[(i * 16)]),
|
|
"secret write");
|
|
}
|
|
ensure(flash_lock_write(), "secret write");
|
|
}
|
|
|
|
secbool secret_read(uint8_t *data, uint32_t offset, uint32_t len) {
|
|
if (sectrue != secret_verify_header()) {
|
|
return secfalse;
|
|
}
|
|
uint8_t *addr = (uint8_t *)flash_area_get_address(&SECRET_AREA, offset, len);
|
|
|
|
if (addr == NULL) {
|
|
return secfalse;
|
|
}
|
|
memcpy(data, addr, len);
|
|
|
|
return sectrue;
|
|
}
|
|
|
|
static void secret_disable_access(void) {
|
|
FLASH->SECHDPCR |= FLASH_SECHDPCR_HDP1_ACCDIS_Msk;
|
|
FLASH->SECHDPCR |= FLASH_SECHDPCR_HDP2_ACCDIS_Msk;
|
|
}
|
|
|
|
// Locks the BHK register. Once locked, the BHK register can't be accessed by
|
|
// the software. BHK is made available to the SAES peripheral
|
|
static void secret_bhk_lock(void) {
|
|
TAMP_S->SECCFGR = 8 << TAMP_SECCFGR_BKPRWSEC_Pos | TAMP_SECCFGR_BHKLOCK;
|
|
}
|
|
|
|
// Verifies that access to the register has been disabled
|
|
static secbool secret_bhk_locked(void) {
|
|
return ((TAMP_S->SECCFGR & TAMP_SECCFGR_BHKLOCK) == TAMP_SECCFGR_BHKLOCK) *
|
|
sectrue;
|
|
}
|
|
|
|
static secbool secret_present(uint32_t offset, uint32_t len) {
|
|
uint8_t *secret =
|
|
(uint8_t *)flash_area_get_address(&SECRET_AREA, offset, len);
|
|
|
|
if (secret == NULL) {
|
|
return secfalse;
|
|
}
|
|
|
|
int secret_empty_bytes = 0;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
if (secret[i] == 0xFF) {
|
|
secret_empty_bytes++;
|
|
}
|
|
}
|
|
return sectrue * (secret_empty_bytes != len);
|
|
}
|
|
|
|
// Provision the secret BHK from the secret storage to the BHK register
|
|
// which makes the BHK usable for encryption by the firmware, without having
|
|
// read access to it.
|
|
static void secret_bhk_load(void) {
|
|
if (sectrue == secret_bhk_locked()) {
|
|
delete_secrets();
|
|
NVIC_SystemReset();
|
|
}
|
|
|
|
uint32_t secret[SECRET_BHK_LEN / sizeof(uint32_t)] = {0};
|
|
|
|
if (sectrue != secret_present(SECRET_BHK_OFFSET, SECRET_BHK_LEN)) {
|
|
secret_bhk_regenerate();
|
|
}
|
|
|
|
secbool ok =
|
|
secret_read((uint8_t *)secret, SECRET_BHK_OFFSET, SECRET_BHK_LEN);
|
|
|
|
volatile uint32_t *reg1 = &TAMP->BKP0R;
|
|
if (sectrue == ok) {
|
|
for (int i = 0; i < 8; i++) {
|
|
*reg1 = ((uint32_t *)secret)[i];
|
|
reg1++;
|
|
}
|
|
} else {
|
|
for (int i = 0; i < 8; i++) {
|
|
*reg1 = 0;
|
|
reg1++;
|
|
}
|
|
}
|
|
|
|
memzero(secret, sizeof(secret));
|
|
}
|
|
|
|
void secret_bhk_regenerate(void) {
|
|
ensure(flash_area_erase(&BHK_AREA, NULL), "Failed regenerating BHK");
|
|
ensure(flash_unlock_write(), "Failed regenerating BHK");
|
|
for (int i = 0; i < 2; i++) {
|
|
uint32_t val[4] = {0};
|
|
for (int j = 0; j < 4; j++) {
|
|
val[j] = rng_get();
|
|
}
|
|
secbool res =
|
|
flash_area_write_quadword(&BHK_AREA, i * 4 * sizeof(uint32_t), val);
|
|
memzero(val, sizeof(val));
|
|
ensure(res, "Failed regenerating BHK");
|
|
}
|
|
ensure(flash_lock_write(), "Failed regenerating BHK");
|
|
}
|
|
|
|
#ifdef USE_OPTIGA
|
|
// Checks that the optiga pairing secret is present in the secret storage.
|
|
// This functions only works when software has access to the secret storage,
|
|
// i.e. in bootloader. Access to secret storage is restricted by calling
|
|
// secret_hide.
|
|
static secbool secret_optiga_present(void) {
|
|
return secret_present(SECRET_OPTIGA_KEY_OFFSET, SECRET_OPTIGA_KEY_LEN);
|
|
}
|
|
|
|
// Backs up the optiga pairing secret from the secret storage to the backup
|
|
// register
|
|
static void secret_optiga_cache(void) {
|
|
uint32_t secret[SECRET_OPTIGA_KEY_LEN / sizeof(uint32_t)] = {0};
|
|
secbool ok = secret_read((uint8_t *)secret, SECRET_OPTIGA_KEY_OFFSET,
|
|
SECRET_OPTIGA_KEY_LEN);
|
|
|
|
volatile uint32_t *reg1 = &TAMP->BKP8R;
|
|
if (sectrue == ok) {
|
|
for (int i = 0; i < 8; i++) {
|
|
*reg1 = ((uint32_t *)secret)[i];
|
|
reg1++;
|
|
}
|
|
} else {
|
|
for (int i = 0; i < 8; i++) {
|
|
*reg1 = 0;
|
|
reg1++;
|
|
}
|
|
}
|
|
memzero(secret, sizeof(secret));
|
|
}
|
|
|
|
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)) {
|
|
return secfalse;
|
|
}
|
|
secret_write(secret_enc, SECRET_OPTIGA_KEY_OFFSET, SECRET_OPTIGA_KEY_LEN);
|
|
memzero(secret_enc, sizeof(secret_enc));
|
|
secret_optiga_cache();
|
|
return sectrue;
|
|
}
|
|
|
|
secbool secret_optiga_get(uint8_t dest[SECRET_OPTIGA_KEY_LEN]) {
|
|
uint32_t secret[SECRET_OPTIGA_KEY_LEN / sizeof(uint32_t)] = {0};
|
|
|
|
bool all_zero = true;
|
|
volatile uint32_t *reg1 = &TAMP->BKP8R;
|
|
for (int i = 0; i < 8; i++) {
|
|
secret[i] = reg1[i];
|
|
|
|
if (secret[i] != 0) {
|
|
all_zero = false;
|
|
}
|
|
}
|
|
|
|
if (all_zero) {
|
|
return secfalse;
|
|
}
|
|
|
|
secbool res = secure_aes_ecb_decrypt_hw(
|
|
(uint8_t *)secret, SECRET_OPTIGA_KEY_LEN, dest, SECURE_AES_KEY_DHUK);
|
|
|
|
memzero(secret, sizeof(secret));
|
|
return res;
|
|
}
|
|
|
|
// Deletes the optiga pairing secret from the register
|
|
static void secret_optiga_uncache(void) {
|
|
volatile uint32_t *reg1 = &TAMP->BKP8R;
|
|
for (int i = 0; i < 8; i++) {
|
|
reg1[i] = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void secret_erase(void) {
|
|
ensure(flash_area_erase(&SECRET_AREA, NULL), "secret erase");
|
|
}
|
|
|
|
void secret_show_install_restricted_screen(void) {
|
|
// this should never happen on U5
|
|
__fatal_error("INTERNAL ERROR", "Install restricted", __FILE__, __LINE__,
|
|
__func__);
|
|
}
|
|
|
|
void secret_prepare_fw(secbool allow_run_with_secret, secbool trust_all) {
|
|
/**
|
|
* The BHK is copied to the backup registers, which are accessible by the SAES
|
|
* peripheral. The BHK register is locked, so the BHK can't be accessed by the
|
|
* software.
|
|
*
|
|
* When optiga is paired, pairing secret is copied to the backup registers
|
|
* and access to the secret storage is disabled. Otherwise, access to the
|
|
* secret storage kept to allow optiga pairing in prodtest.
|
|
*
|
|
* Access to the secret storage is disabled for non-official firmware in
|
|
* all-cases.
|
|
*/
|
|
|
|
secret_bhk_load();
|
|
secret_bhk_lock();
|
|
#ifdef USE_OPTIGA
|
|
secret_optiga_uncache();
|
|
if (sectrue == allow_run_with_secret) {
|
|
if (secfalse != secret_optiga_present()) {
|
|
secret_optiga_cache();
|
|
secret_disable_access();
|
|
}
|
|
} else {
|
|
secret_disable_access();
|
|
}
|
|
#else
|
|
secret_disable_access();
|
|
#endif
|
|
|
|
if (sectrue != trust_all) {
|
|
secret_disable_access();
|
|
}
|
|
}
|