1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-09 16:18:10 +00:00

refactor(core): make secret api more versatile, secret layout model dependent

[no changelog]
This commit is contained in:
tychovrahe 2025-06-02 21:40:34 +02:00 committed by TychoVrahe
parent 2a6189c3ae
commit e53cddf65e
28 changed files with 982 additions and 481 deletions

View File

@ -21,6 +21,7 @@
#include <rtl/sizedefs.h>
#include "bootloaders/bootloader_hashes.h"
#include "secret_layout.h"
#define MODEL_NAME "D002"
#define MODEL_FULL_NAME "Trezor DIY 2"

View File

@ -0,0 +1,36 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define SECRET_NUM_KEY_SLOTS 0
// first page: static
#define SECRET_HEADER_OFFSET 0x00
#define SECRET_HEADER_LEN 0x10
#define SECRET_MONOTONIC_COUNTER_0_OFFSET 0x10
#define SECRET_MONOTONIC_COUNTER_0_LEN 0x400
#define SECRET_MONOTONIC_COUNTER_1_OFFSET (0x410)
#define SECRET_MONOTONIC_COUNTER_1_LEN 0x400
// second page: refreshed on wallet wipe
#define SECRET_BHK_OFFSET 0x2000
#define SECRET_BHK_LEN 0x20

View File

@ -20,6 +20,7 @@
#pragma once
#include "bootloaders/bootloader_hashes.h"
#include "secret_layout.h"
#define MODEL_NAME "Safe 3"
#define MODEL_FULL_NAME "Trezor Safe 3"

View File

@ -0,0 +1,31 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define SECRET_NUM_KEY_SLOTS 1
// first page: static
#define SECRET_HEADER_OFFSET 0x00
#define SECRET_HEADER_LEN 0x10
#define SECRET_KEY_SLOT_0_OFFSET 0x10
#define SECRET_KEY_SLOT_0_LEN 0x20
#define SECRET_OPTIGA_SLOT 0

View File

@ -20,6 +20,7 @@
#pragma once
#include "bootloaders/bootloader_hashes.h"
#include "secret_layout.h"
#include <rtl/sizedefs.h>

View File

@ -0,0 +1,41 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define SECRET_NUM_KEY_SLOTS 1
// first page: static
#define SECRET_HEADER_OFFSET 0x00
#define SECRET_HEADER_LEN 0x10
#define SECRET_KEY_SLOT_0_OFFSET 0x10
#define SECRET_KEY_SLOT_0_LEN 0x20
#define SECRET_MONOTONIC_COUNTER_0_OFFSET 0x30
#define SECRET_MONOTONIC_COUNTER_0_LEN 0x400
#define SECRET_MONOTONIC_COUNTER_1_OFFSET (0x430)
#define SECRET_MONOTONIC_COUNTER_1_LEN 0x400
// second page: refreshed on wallet wipe
#define SECRET_BHK_OFFSET 0x2000
#define SECRET_BHK_LEN 0x20
#define SECRET_OPTIGA_SLOT 0

View File

@ -20,6 +20,7 @@
#pragma once
#include "bootloaders/bootloader_hashes.h"
#include "secret_layout.h"
#include <rtl/sizedefs.h>

View File

@ -0,0 +1,41 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define SECRET_NUM_KEY_SLOTS 1
// first page: static
#define SECRET_HEADER_OFFSET 0x00
#define SECRET_HEADER_LEN 0x10
#define SECRET_KEY_SLOT_0_OFFSET 0x10
#define SECRET_KEY_SLOT_0_LEN 0x20
#define SECRET_MONOTONIC_COUNTER_0_OFFSET 0x30
#define SECRET_MONOTONIC_COUNTER_0_LEN 0x400
#define SECRET_MONOTONIC_COUNTER_1_OFFSET (0x430)
#define SECRET_MONOTONIC_COUNTER_1_LEN 0x400
// second page: refreshed on wallet wipe
#define SECRET_BHK_OFFSET 0x2000
#define SECRET_BHK_LEN 0x20
#define SECRET_OPTIGA_SLOT 0

View File

@ -21,6 +21,7 @@
#pragma once
#include <rtl/sizedefs.h>
#include "secret_layout.h"
#include "bootloaders/bootloader_hashes.h"

View File

@ -0,0 +1,51 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define SECRET_NUM_KEY_SLOTS 3
// first page: static
#define SECRET_HEADER_OFFSET 0x00
#define SECRET_HEADER_LEN 0x10
#define SECRET_KEY_SLOT_0_OFFSET 0x10
#define SECRET_KEY_SLOT_0_LEN 0x20
#define SECRET_MONOTONIC_COUNTER_0_OFFSET 0x30
#define SECRET_MONOTONIC_COUNTER_0_LEN 0x400
#define SECRET_MONOTONIC_COUNTER_1_OFFSET (0x430)
#define SECRET_MONOTONIC_COUNTER_1_LEN 0x400
#define SECRET_KEY_SLOT_1_OFFSET 0x830
#define SECRET_KEY_SLOT_1_LEN 0x20
#define SECRET_KEY_SLOT_2_OFFSET 0x850
#define SECRET_KEY_SLOT_2_LEN 0x20
#define SECRET_KEY_SLOT_2_PUBLIC 1
// second page: refreshed on wallet wipe
#define SECRET_BHK_OFFSET 0x2000
#define SECRET_BHK_LEN 0x20
// slot assignments
#define SECRET_OPTIGA_SLOT 0
#define SECRET_TROPIC_TREZOR_PRIVKEY_SLOT 1
#define SECRET_TROPIC_TROPIC_PUBKEY_SLOT 2

View File

@ -155,8 +155,7 @@ int main(int argc, char **argv) {
} break;
#ifdef LOCKABLE_BOOTLOADER
case 'l':
// write bootloader-lock secret
secret_write_header();
secret_lock_bootloader();
break;
#endif
default:

View File

@ -19,6 +19,7 @@
#ifdef USE_OPTIGA
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <rtl/cli.h>
@ -164,10 +165,10 @@ static bool set_metadata(cli_t* cli, uint16_t oid,
}
void pair_optiga(cli_t* cli) {
uint8_t secret[SECRET_KEY_LEN] = {0};
uint8_t secret[32] = {0};
if (secret_optiga_get(secret) != sectrue) {
if (secret_optiga_writable() != sectrue) {
if (secret_key_get(SECRET_OPTIGA_SLOT, secret, sizeof(secret)) != sectrue) {
if (secret_key_writable(SECRET_OPTIGA_SLOT) != sectrue) {
// optiga pairing secret is unwritable, so fail
optiga_pairing_state = OPTIGA_PAIRING_ERR_WRITE_FLASH;
return;
@ -203,14 +204,14 @@ void pair_optiga(cli_t* cli) {
}
// Store the pairing secret in the flash memory.
if (sectrue != secret_optiga_set(secret)) {
if (sectrue != secret_key_set(SECRET_OPTIGA_SLOT, secret, sizeof(secret))) {
optiga_pairing_state = OPTIGA_PAIRING_ERR_WRITE_FLASH;
return;
}
// Reload the pairing secret from the flash memory.
memzero(secret, sizeof(secret));
if (sectrue != secret_optiga_get(secret)) {
if (sectrue != secret_key_get(SECRET_OPTIGA_SLOT, secret, sizeof(secret))) {
optiga_pairing_state = OPTIGA_PAIRING_ERR_READ_FLASH;
return;
}

View File

@ -1,16 +1,16 @@
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <stdlib.h>
#include <unistd.h>
#include <SDL.h>
#include <io/display.h>
#include <sec/secret.h>
#include <util/flash.h>
#include <util/flash_otp.h>
#include <stdlib.h>
int prodtest_main(void);
void usage(void) {
@ -54,6 +54,10 @@ int main(int argc, char **argv) {
flash_init();
flash_otp_init();
#ifdef LOCKABLE_BOOTLOADER
secret_lock_bootloader();
#endif
int opt;
while ((opt = getopt(argc, argv, "h")) != -1) {
switch (opt) {

View File

@ -37,6 +37,7 @@
#include <unistd.h>
#include <io/display.h>
#include <sec/secret.h>
#include <sys/system.h>
#include <sys/systimer.h>
#include <util/flash.h>
@ -57,7 +58,6 @@
#endif
#ifdef USE_TROPIC
#include <sec/secret.h>
#include <sec/tropic.h>
#endif
@ -528,6 +528,10 @@ MP_NOINLINE int main_(int argc, char **argv) {
pre_process_options(argc, argv);
#ifdef LOCKABLE_BOOTLOADER
secret_lock_bootloader();
#endif
system_init(&rsod_panic_handler);
drivers_init();

View File

@ -29,14 +29,25 @@
static int32_t get_offset(monoctr_type_t type) {
switch (type) {
case MONOCTR_BOOTLOADER_VERSION:
return SECRET_MONOTONIC_COUNTER_OFFSET;
return SECRET_MONOTONIC_COUNTER_0_OFFSET;
case MONOCTR_FIRMWARE_VERSION:
return SECRET_MONOTONIC_COUNTER2_OFFSET;
return SECRET_MONOTONIC_COUNTER_1_OFFSET;
default:
return -1;
}
}
static size_t get_length(monoctr_type_t type) {
switch (type) {
case MONOCTR_BOOTLOADER_VERSION:
return SECRET_MONOTONIC_COUNTER_0_LEN;
case MONOCTR_FIRMWARE_VERSION:
return SECRET_MONOTONIC_COUNTER_1_LEN;
default:
return 0;
}
}
secbool monoctr_write(monoctr_type_t type, uint8_t value) {
if (value > MONOCTR_MAX_VALUE) {
return secfalse;
@ -72,13 +83,14 @@ secbool monoctr_write(monoctr_type_t type, uint8_t value) {
secbool monoctr_read(monoctr_type_t type, uint8_t *value) {
int32_t offset = get_offset(type);
size_t length = get_length(type);
if (offset < 0) {
return secfalse;
}
const uint8_t *counter_addr = flash_area_get_address(
&SECRET_AREA, offset, SECRET_MONOTONIC_COUNTER_LEN);
const uint8_t *counter_addr =
flash_area_get_address(&SECRET_AREA, offset, length);
if (counter_addr == NULL) {
return secfalse;
@ -90,7 +102,7 @@ secbool monoctr_read(monoctr_type_t type, uint8_t *value) {
int i = 0;
for (i = 0; i < SECRET_MONOTONIC_COUNTER_LEN / 16; i++) {
for (i = 0; i < length / 16; i++) {
secbool not_cleared = sectrue;
for (int j = 0; j < 16; j++) {
if (counter_addr[i * 16 + j] != 0xFF) {
@ -106,7 +118,7 @@ secbool monoctr_read(monoctr_type_t type, uint8_t *value) {
}
}
for (; i < SECRET_MONOTONIC_COUNTER_LEN / 16; i++) {
for (; i < length / 16; i++) {
secbool not_cleared = sectrue;
for (int j = 0; j < 16; j++) {
if (counter_addr[i * 16 + j] != 0xFF) {

View File

@ -24,7 +24,7 @@
#include <sec/optiga.h>
#include <sec/optiga_commands.h>
#include <sec/optiga_transport.h>
#include <sec/secret.h>
#include <sec/secret_keys.h>
#include <sys/systick.h>
#include "memzero.h"
@ -57,8 +57,8 @@ void optiga_init_and_configure(void) {
optiga_init();
uint8_t secret[SECRET_KEY_LEN] = {0};
secbool secret_ok = secret_optiga_get(secret);
uint8_t secret[OPTIGA_PAIRING_SECRET_SIZE] = {0};
secbool secret_ok = secret_key_optiga_pairing(secret);
if (sectrue == secret_ok) {
// If the shielded connection cannot be established, reset Optiga and

View File

@ -23,105 +23,112 @@
#ifdef SECURE_MODE
#define SECRET_KEY_LEN 32
// first page: static
#define SECRET_HEADER_MAGIC "TRZS"
#define SECRET_HEADER_LEN 16
#define SECRET_OPTIGA_KEY_OFFSET 16
#define SECRET_MONOTONIC_COUNTER_OFFSET 48
#define SECRET_MONOTONIC_COUNTER_LEN 1024
#define SECRET_MONOTONIC_COUNTER2_OFFSET (SECRET_MONOTONIC_COUNTER_LEN + 48)
#define SECRET_TROPIC_TRZ_PRIVKEY_OFFSET \
(SECRET_MONOTONIC_COUNTER_LEN + SECRET_MONOTONIC_COUNTER2_OFFSET)
#define SECRET_TROPIC_TRO_PUBKEY_OFFSET \
(SECRET_TROPIC_TRZ_PRIVKEY_OFFSET + SECRET_KEY_LEN)
// second page: refreshed on wallet wipe
#define SECRET_BHK_OFFSET (1024 * 8)
// Writes data to the secret storage
/**
* @brief Writes data to the secret storage.
*
* @param data Pointer to the data to write.
* @param offset Offset in the storage to begin writing.
* @param len Number of bytes to write.
*/
void secret_write(const uint8_t* data, uint32_t offset, uint32_t len);
// Reads data from the secret storage
/**
* @brief Reads data from the secret storage.
*
* @param data Pointer to buffer where read data will be stored.
* @param offset Offset in the storage to begin reading.
* @param len Number of bytes to read.
* @return secbool sectrue on successful read, secfalse otherwise.
*/
secbool secret_read(uint8_t* data, uint32_t offset, uint32_t len);
// Checks if the secret storage has been wiped
secbool secret_wiped(void);
/**
* @brief Writes a key to the secret storage.
*
* Encrypts the secret if encryption is available on the platform.
*
* @param slot Index of the key slot.
* @param key Pointer to the key data.
* @param len Length of the key in bytes.
* @return secbool sectrue if the key was written successfully, secfalse
* otherwise.
*/
secbool secret_key_set(uint8_t slot, const uint8_t* key, size_t len);
// Verifies that the secret storage has the correct header
secbool secret_verify_header(void);
/**
* @brief Reads a secret key from the storage.
*
* Decrypts the secret if encryption is available on the platform.
*
* @param slot Index of the key slot.
* @param dest Pointer to destination buffer for the key.
* @param len Length of the dest buffer.
* @return secbool secrue if the key was read successfully, secfalse otherwise.
*/
secbool secret_key_get(uint8_t slot, uint8_t* dest, size_t len);
// Erases the entire secret storage
void secret_erase(void);
/**
* @brief Checks if a secret key slot is writable.
*
* @param slot Index of the key slot.
* @return secbool sectrue if the key slot can be written, secfalse otherwise.
*/
secbool secret_key_writable(uint8_t slot);
// Writes the secret header to the secret storage
void secret_write_header(void);
#ifdef USE_OPTIGA
// OPTIGA KEYS
// Writes optiga pairing secret to the secret storage
// Encrypts the secret if encryption is available on the platform
// Returns true if the secret was written successfully
secbool secret_optiga_set(const uint8_t secret[SECRET_KEY_LEN]);
// Reads optiga pairing secret
// Decrypts the secret if encryption is available on the platform
// Returns true if the secret was read successfully
// Reading can fail if optiga is not paired, the pairing secret was not
// provisioned to the firmware (by calling secret_optiga_backup), or the secret
// was made unavailable by calling secret_optiga_hide
secbool secret_optiga_get(uint8_t dest[SECRET_KEY_LEN]);
// Checks if the optiga pairing secret is present in the secret storage
secbool secret_optiga_present(void);
// Checks if the optiga pairing secret can be written to the secret storage
secbool secret_optiga_writable(void);
#endif
#ifdef USE_TROPIC
// TROPIC KEYS
secbool secret_tropic_get_trezor_privkey(uint8_t dest[SECRET_KEY_LEN]);
secbool secret_tropic_get_tropic_pubkey(uint8_t dest[SECRET_KEY_LEN]);
secbool secret_tropic_set(const uint8_t privkey[SECRET_KEY_LEN],
const uint8_t pubkey[SECRET_KEY_LEN]);
secbool secret_tropic_present(void);
secbool secret_tropic_writable(void);
#endif
// Regenerates the BHK and writes it to the secret storage
/**
* @brief Regenerates the BHK and writes it to the secret storage.
*/
void secret_bhk_regenerate(void);
// Prepares the secret storage for running the firmware
// Provisions secrets/keys to the firmware, depending on the trust level
// Disables access to the secret storage until next reset, if possible
// This function is called by the bootloader before starting the firmware
/**
* @brief Prepares the secret storage for running the firmware.
*
* Provisions secrets and keys to the firmware depending on the trust level.
* Disables access to the secret storage until next reset, if possible.
* This function is called by the bootloader before starting the firmware.
*
* @param allow_run_with_secret Allow firmware to run with secret access.
* @param allow_provisioning_access Allow provisioning access to secrets.
*/
void secret_prepare_fw(secbool allow_run_with_secret,
secbool allow_provisioning_access);
// Prepares the secret storage for running the boardloader and next stages
// Ensures that secret storage access is enabled
// This function is called by the boardloader
/**
* @brief Initializes the secret storage for running the boardloader and next
* stages.
*
* Ensures that secret storage access is enabled.
* This function is called by the boardloader.
*/
void secret_init(void);
#ifdef LOCKABLE_BOOTLOADER
// Unlocks the bootloader, all neccessary keys are erased
/**
* @brief Unlocks the bootloader and erases all necessary keys.
*/
void secret_unlock_bootloader(void);
#ifdef TREZOR_EMULATOR
/**
* @brief Locks the bootloader (emulator only).
*/
void secret_lock_bootloader(void);
#endif
#endif
#endif // SECURE_MODE
#ifdef LOCKABLE_BOOTLOADER
// Checks if bootloader is locked, that is the secret storage contains optiga
// pairing secret on platforms where access to the secret storage cannot be
// restricted for unofficial firmware
/**
* @brief Checks if the bootloader is locked.
*
* On platforms where secret storage access cannot be restricted for unofficial
* firmware, a locked bootloader indicates presence of a non-public key.
*
* @return secbool sectrue if bootloader is locked, secfalse otherwise.
*/
secbool secret_bootloader_locked(void);
#endif

View File

@ -0,0 +1,42 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <trezor_types.h>
#ifdef SECURE_MODE
#ifdef USE_OPTIGA
#define OPTIGA_PAIRING_SECRET_SIZE 32
secbool secret_key_optiga_pairing(uint8_t dest[OPTIGA_PAIRING_SECRET_SIZE]);
#endif
#ifdef USE_TROPIC
#include <ed25519-donna/ed25519.h>
secbool secret_key_tropic_public(curve25519_key dest);
secbool secret_key_tropic_pairing(curve25519_key dest);
#endif
#endif

View File

@ -26,12 +26,23 @@
#ifdef KERNEL_MODE
#ifdef SECRET_NUM_KEY_SLOTS
#define SECRET_HEADER_MAGIC "TRZS"
#define SECRET_HEADER_MAGIC_LEN (sizeof(SECRET_HEADER_MAGIC) - 1)
#define SECRET_NUM_MAX_SLOTS 1
_Static_assert(SECRET_NUM_MAX_SLOTS >= SECRET_NUM_KEY_SLOTS,
"Exceeded max slots");
_Static_assert(SECRET_KEY_SLOT_0_LEN == 32, "Invalid key slot length");
static secbool bootloader_locked_set = secfalse;
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));
uint8_t* addr = (uint8_t*)flash_area_get_address(
&SECRET_AREA, SECRET_HEADER_OFFSET, SECRET_HEADER_LEN);
if (addr == NULL) {
return secfalse;
@ -40,7 +51,7 @@ secbool secret_verify_header(void) {
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SECRET);
bootloader_locked =
memcmp(addr, SECRET_HEADER_MAGIC, sizeof(SECRET_HEADER_MAGIC)) == 0
memcmp(addr, SECRET_HEADER_MAGIC, SECRET_HEADER_MAGIC_LEN) == 0
? sectrue
: secfalse;
@ -65,8 +76,8 @@ void secret_unlock_bootloader(void) { secret_erase(); }
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);
memcpy(header, SECRET_HEADER_MAGIC, SECRET_HEADER_MAGIC_LEN);
secret_write(header, SECRET_HEADER_OFFSET, SECRET_HEADER_LEN);
}
void secret_write(const uint8_t* data, uint32_t offset, uint32_t len) {
@ -98,7 +109,7 @@ secbool secret_read(uint8_t* data, uint32_t offset, uint32_t len) {
return sectrue;
}
secbool secret_wiped(void) {
static secbool secret_wiped(void) {
uint32_t size = flash_area_get_size(&SECRET_AREA);
secbool wiped = sectrue;
@ -127,22 +138,54 @@ void secret_erase(void) {
mpu_restore(mpu_mode);
}
secbool secret_optiga_set(const uint8_t secret[SECRET_KEY_LEN]) {
secbool secret_key_set(uint8_t slot, const uint8_t* key, size_t len) {
if (slot >= SECRET_NUM_KEY_SLOTS) {
return secfalse;
}
if (len != SECRET_KEY_SLOT_0_LEN) {
return secfalse;
}
uint32_t offset = SECRET_KEY_SLOT_0_OFFSET;
secret_erase();
secret_write_header();
secret_write(secret, SECRET_OPTIGA_KEY_OFFSET, SECRET_KEY_LEN);
secret_write(key, offset, len);
return sectrue;
}
secbool secret_optiga_get(uint8_t dest[SECRET_KEY_LEN]) {
return secret_read(dest, SECRET_OPTIGA_KEY_OFFSET, SECRET_KEY_LEN);
secbool secret_key_get(uint8_t slot, uint8_t* dest, size_t len) {
if (slot >= SECRET_NUM_KEY_SLOTS) {
return secfalse;
}
if (len != SECRET_KEY_SLOT_0_LEN) {
return secfalse;
}
uint32_t offset = SECRET_KEY_SLOT_0_OFFSET;
return secret_read(dest, offset, len);
}
secbool secret_optiga_present(void) {
static secbool secret_key_present(uint8_t slot) {
if (slot >= SECRET_NUM_KEY_SLOTS) {
return secfalse;
}
return (sectrue != secret_wiped()) * sectrue;
}
secbool secret_optiga_writable(void) { return secret_wiped(); }
secbool secret_key_writable(uint8_t slot) {
if (slot >= SECRET_NUM_KEY_SLOTS) {
return secfalse;
}
return secret_wiped();
}
#endif
void secret_prepare_fw(secbool allow_run_with_secret,
secbool allow_provisioning_access) {

View File

@ -0,0 +1,35 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef SECURE_MODE
#include <trezor_bsp.h>
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <sec/secret.h>
#include <sec/secret_keys.h>
#ifdef USE_OPTIGA
secbool secret_key_optiga_pairing(uint8_t dest[OPTIGA_PAIRING_SECRET_SIZE]) {
return secret_key_get(SECRET_OPTIGA_SLOT, dest, OPTIGA_PAIRING_SECRET_SIZE);
}
#endif
#endif

View File

@ -32,14 +32,75 @@
#ifdef SECURE_MODE
#define REG_BHK_OFFSET 0
#define REG_OPTIGA_KEY_OFFSET 8
#define REG_TROPIC_TRZ_PRIVKEY_OFFSET 16
#define REG_TROPIC_TRO_PUBKEY_OFFSET 24
#define SECRET_HEADER_MAGIC "TRZS"
#define SECRET_HEADER_MAGIC_LEN (sizeof(SECRET_HEADER_MAGIC) - 1)
secbool secret_verify_header(void) {
#define SECRET_BHK_REG_OFFSET 0
#define SECRET_NUM_MAX_SLOTS 3
#ifndef SECRET_KEY_SLOT_0_OFFSET
#define SECRET_KEY_SLOT_0_OFFSET 0
#define SECRET_KEY_SLOT_0_LEN 0
#endif
#ifndef SECRET_KEY_SLOT_1_OFFSET
#define SECRET_KEY_SLOT_1_OFFSET 0
#define SECRET_KEY_SLOT_1_LEN 0
#endif
#ifndef SECRET_KEY_SLOT_2_OFFSET
#define SECRET_KEY_SLOT_2_OFFSET 0
#define SECRET_KEY_SLOT_2_LEN 0
#endif
#define SECRET_KEY_MAX_LEN (24 * sizeof(uint32_t))
_Static_assert(SECRET_NUM_MAX_SLOTS >= SECRET_NUM_KEY_SLOTS);
_Static_assert(SECRET_KEY_SLOT_0_LEN + SECRET_KEY_SLOT_1_LEN +
SECRET_KEY_SLOT_2_LEN <=
SECRET_KEY_MAX_LEN,
"secret key slots too large");
_Static_assert(SECRET_KEY_SLOT_0_LEN % 16 == 0,
"secret key length must be multiple of 16 bytes");
_Static_assert(SECRET_KEY_SLOT_1_LEN % 16 == 0,
"secret key length must be multiple of 16 bytes");
_Static_assert(SECRET_KEY_SLOT_2_LEN % 16 == 0,
"secret key length must be multiple of 16 bytes");
static uint32_t secret_slot_offsets[SECRET_NUM_MAX_SLOTS] = {
SECRET_KEY_SLOT_0_OFFSET,
SECRET_KEY_SLOT_1_OFFSET,
SECRET_KEY_SLOT_2_OFFSET,
};
static uint32_t secret_slot_lengths[SECRET_NUM_MAX_SLOTS] = {
SECRET_KEY_SLOT_0_LEN,
SECRET_KEY_SLOT_1_LEN,
SECRET_KEY_SLOT_2_LEN,
};
static secbool secret_slot_public[SECRET_NUM_MAX_SLOTS] = {
#ifdef SECRET_KEY_SLOT_0_PUBLIC
sectrue,
#else
secfalse,
#endif
#ifdef SECRET_KEY_SLOT_1_PUBLIC
sectrue,
#else
secfalse,
#endif
#ifdef SECRET_KEY_SLOT_2_PUBLIC
sectrue,
#else
secfalse,
#endif
};
static secbool secret_verify_header(void) {
uint8_t *addr = (uint8_t *)flash_area_get_address(
&SECRET_AREA, 0, sizeof(SECRET_HEADER_MAGIC));
&SECRET_AREA, SECRET_HEADER_OFFSET, SECRET_HEADER_LEN);
if (addr == NULL) {
return secfalse;
@ -48,7 +109,7 @@ secbool secret_verify_header(void) {
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SECRET);
secbool header_present =
memcmp(addr, SECRET_HEADER_MAGIC, sizeof(SECRET_HEADER_MAGIC)) == 0
memcmp(addr, SECRET_HEADER_MAGIC, SECRET_HEADER_MAGIC_LEN) == 0
? sectrue
: secfalse;
@ -57,6 +118,18 @@ secbool secret_verify_header(void) {
return header_present;
}
static void secret_erase(void) {
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SECRET);
ensure(flash_area_erase(&SECRET_AREA, NULL), "secret erase");
mpu_restore(mpu_mode);
}
static void secret_write_header(void) {
uint8_t header[SECRET_HEADER_LEN] = {0};
memcpy(header, SECRET_HEADER_MAGIC, SECRET_HEADER_MAGIC_LEN);
secret_write(header, SECRET_HEADER_OFFSET, SECRET_HEADER_LEN);
}
static secbool secret_ensure_initialized(void) {
if (sectrue != secret_verify_header()) {
ensure(erase_storage(NULL), "erase storage failed");
@ -67,12 +140,6 @@ static secbool secret_ensure_initialized(void) {
return sectrue;
}
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) {
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SECRET);
ensure(flash_unlock_write(), "secret write");
@ -119,9 +186,41 @@ static secbool secret_bhk_locked(void) {
sectrue;
}
static secbool secret_key_present(uint32_t offset) {
static secbool secret_is_slot_valid(uint8_t slot) {
return ((slot < SECRET_NUM_KEY_SLOTS) && (secret_slot_offsets[slot] != 0)) *
sectrue;
}
static uint32_t secret_get_slot_offset(uint8_t slot) {
if (slot >= SECRET_NUM_KEY_SLOTS) {
return 0;
}
return secret_slot_offsets[slot];
}
static uint32_t secret_get_slot_len(uint8_t slot) {
if (slot >= SECRET_NUM_KEY_SLOTS) {
return 0;
}
return secret_slot_lengths[slot];
}
static size_t secret_get_reg_offset(uint8_t slot) {
// SECRET_BHK_LEN is in bytes; each reg is 32 bits = 4 bytes
size_t cumulative = SECRET_BHK_LEN / sizeof(uint32_t);
for (uint8_t i = 0; i < slot; i++) {
if (sectrue == secret_is_slot_valid(i)) {
cumulative += (secret_slot_lengths[i] / sizeof(uint32_t));
}
}
return cumulative;
}
static secbool secret_record_present(uint32_t offset, uint32_t len) {
uint8_t *secret =
(uint8_t *)flash_area_get_address(&SECRET_AREA, offset, SECRET_KEY_LEN);
(uint8_t *)flash_area_get_address(&SECRET_AREA, offset, len);
if (secret == NULL) {
return secfalse;
@ -131,7 +230,7 @@ static secbool secret_key_present(uint32_t offset) {
int secret_empty_bytes = 0;
for (int i = 0; i < SECRET_KEY_LEN; i++) {
for (int i = 0; i < len; i++) {
// 0xFF being the default value of the flash memory (before any write)
// 0x00 being the value of the flash memory after manual erase
if (secret[i] == 0xFF || secret[i] == 0x00) {
@ -141,12 +240,30 @@ static secbool secret_key_present(uint32_t offset) {
mpu_restore(mpu_mode);
return sectrue * (secret_empty_bytes != SECRET_KEY_LEN);
return sectrue * (secret_empty_bytes != len);
}
__attribute__((unused)) static secbool secret_key_writable(uint32_t offset) {
static secbool secret_key_present(uint8_t slot) {
if (sectrue != secret_is_slot_valid(slot)) {
return secfalse;
}
uint32_t offset = secret_get_slot_offset(slot);
uint32_t len = secret_get_slot_len(slot);
return secret_record_present(offset, len);
}
secbool secret_key_writable(uint8_t slot) {
if (sectrue != secret_is_slot_valid(slot)) {
return secfalse;
}
uint32_t offset = secret_get_slot_offset(slot);
uint32_t len = secret_get_slot_len(slot);
const uint8_t *const secret =
(uint8_t *)flash_area_get_address(&SECRET_AREA, offset, SECRET_KEY_LEN);
(uint8_t *)flash_area_get_address(&SECRET_AREA, offset, len);
if (secret == NULL) {
return secfalse;
@ -156,7 +273,7 @@ __attribute__((unused)) static secbool secret_key_writable(uint32_t offset) {
int secret_empty_bytes = 0;
for (int i = 0; i < SECRET_KEY_LEN; i++) {
for (int i = 0; i < len; i++) {
// 0xFF being the default value of the flash memory (before any write)
// 0x00 being the value of the flash memory after manual erase
if (secret[i] == 0xFF) {
@ -166,24 +283,27 @@ __attribute__((unused)) static secbool secret_key_writable(uint32_t offset) {
mpu_restore(mpu_mode);
return sectrue * (secret_empty_bytes == SECRET_KEY_LEN);
return sectrue * (secret_empty_bytes == len);
}
__attribute__((unused)) static void secret_key_cache(uint8_t reg_offset,
uint32_t key_offset) {
uint32_t secret[SECRET_KEY_LEN / sizeof(uint32_t)] = {0};
static void secret_key_cache(uint8_t slot) {
uint32_t offset = secret_get_slot_offset(slot);
uint32_t len = secret_get_slot_len(slot);
size_t reg_offset = secret_get_reg_offset(slot);
secbool ok = secret_read((uint8_t *)secret, key_offset, SECRET_KEY_LEN);
uint32_t secret[SECRET_KEY_MAX_LEN] = {0};
secbool ok = secret_read((uint8_t *)secret, offset, len);
volatile uint32_t *reg = &TAMP->BKP0R;
reg += reg_offset;
if (sectrue == ok) {
for (int i = 0; i < (SECRET_KEY_LEN / sizeof(uint32_t)); i++) {
*reg = ((uint32_t *)secret)[i];
for (int i = 0; i < (len / sizeof(uint32_t)); i++) {
*reg = secret[i];
reg++;
}
} else {
for (int i = 0; i < (SECRET_KEY_LEN / sizeof(uint32_t)); i++) {
for (int i = 0; i < (len / sizeof(uint32_t)); i++) {
*reg = 0;
reg++;
}
@ -191,28 +311,46 @@ __attribute__((unused)) static void secret_key_cache(uint8_t reg_offset,
memzero(secret, sizeof(secret));
}
__attribute__((unused)) static secbool secret_key_set(
const uint8_t secret[SECRET_KEY_LEN], uint8_t reg_offset,
uint32_t key_offset) {
uint8_t secret_enc[SECRET_KEY_LEN] = {0};
if (sectrue != secure_aes_ecb_encrypt_hw(secret, sizeof(secret_enc),
secret_enc,
SECURE_AES_KEY_DHUK_SP)) {
secbool secret_key_set(uint8_t slot, const uint8_t *key, size_t len) {
if (sectrue != secret_is_slot_valid(slot)) {
return secfalse;
}
secret_write(secret_enc, key_offset, SECRET_KEY_LEN);
uint32_t offset = secret_get_slot_offset(slot);
uint32_t slot_len = secret_get_slot_len(slot);
if (slot_len != len) {
return secfalse;
}
uint8_t secret_enc[SECRET_KEY_MAX_LEN] = {0};
if (sectrue !=
secure_aes_ecb_encrypt_hw(key, len, secret_enc, SECURE_AES_KEY_DHUK_SP)) {
return secfalse;
}
secret_write(secret_enc, offset, len);
memzero(secret_enc, sizeof(secret_enc));
secret_key_cache(reg_offset, key_offset);
secret_key_cache(slot);
return sectrue;
}
__attribute__((unused)) static secbool secret_key_get(
uint8_t dest[SECRET_KEY_LEN], uint8_t reg_offset) {
uint32_t secret[SECRET_KEY_LEN / sizeof(uint32_t)] = {0};
secbool secret_key_get(uint8_t slot, uint8_t *dest, size_t len) {
if (sectrue != secret_is_slot_valid(slot)) {
return secfalse;
}
uint32_t slot_len = secret_get_slot_len(slot);
size_t reg_offset = secret_get_reg_offset(slot);
if (slot_len != len) {
return secfalse;
}
uint32_t secret[SECRET_KEY_MAX_LEN] = {0};
bool all_zero = true;
volatile uint32_t *reg = &TAMP->BKP0R;
for (int i = 0; i < (SECRET_KEY_LEN / sizeof(uint32_t)); i++) {
for (int i = 0; i < (len / sizeof(uint32_t)); i++) {
secret[i] = reg[i + reg_offset];
if (secret[i] != 0) {
@ -224,21 +362,33 @@ __attribute__((unused)) static secbool secret_key_get(
return secfalse;
}
secbool res = secure_aes_ecb_decrypt_hw((uint8_t *)secret, SECRET_KEY_LEN,
dest, SECURE_AES_KEY_DHUK_SP);
secbool res = secure_aes_ecb_decrypt_hw((uint8_t *)secret, len, dest,
SECURE_AES_KEY_DHUK_SP);
memzero(secret, sizeof(secret));
return res;
}
// Deletes the secret from the register
__attribute__((unused)) static void secret_key_uncache(uint8_t reg_offset) {
__attribute__((unused)) static void secret_key_uncache(uint8_t slot) {
size_t reg_offset = secret_get_reg_offset(slot);
uint32_t slot_len = secret_get_slot_len(slot);
volatile uint32_t *reg = &TAMP->BKP0R;
for (int i = 0; i < 8; i++) {
for (int i = 0; i < slot_len / sizeof(uint32_t); i++) {
reg[i + reg_offset] = 0;
}
}
static void secret_key_erase(uint8_t slot) {
uint8_t value[SECRET_KEY_MAX_LEN] = {0};
uint32_t offset = secret_get_slot_offset(slot);
uint32_t slot_len = secret_get_slot_len(slot);
secret_write(value, offset, slot_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.
@ -247,23 +397,23 @@ static void secret_bhk_load(void) {
reboot_device();
}
uint32_t secret[SECRET_KEY_LEN / sizeof(uint32_t)] = {0};
uint32_t secret[SECRET_BHK_LEN / sizeof(uint32_t)] = {0};
if (sectrue != secret_key_present(SECRET_BHK_OFFSET)) {
if (sectrue != secret_record_present(SECRET_BHK_OFFSET, SECRET_BHK_LEN)) {
secret_bhk_regenerate();
}
secbool ok =
secret_read((uint8_t *)secret, SECRET_BHK_OFFSET, SECRET_KEY_LEN);
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 < (SECRET_KEY_LEN / sizeof(uint32_t)); i++) {
for (int i = 0; i < (SECRET_BHK_LEN / sizeof(uint32_t)); i++) {
*reg1 = ((uint32_t *)secret)[i];
reg1++;
}
} else {
for (int i = 0; i < (SECRET_KEY_LEN / sizeof(uint32_t)); i++) {
for (int i = 0; i < (SECRET_BHK_LEN / sizeof(uint32_t)); i++) {
*reg1 = 0;
reg1++;
}
@ -293,199 +443,88 @@ void secret_bhk_regenerate(void) {
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.
secbool secret_optiga_present(void) {
return secret_key_present(SECRET_OPTIGA_KEY_OFFSET);
}
secbool secret_optiga_writable(void) {
return secret_key_writable(SECRET_OPTIGA_KEY_OFFSET);
}
secbool secret_optiga_set(const uint8_t secret[SECRET_KEY_LEN]) {
return secret_key_set(secret, REG_OPTIGA_KEY_OFFSET,
SECRET_OPTIGA_KEY_OFFSET);
}
secbool secret_optiga_get(uint8_t dest[SECRET_KEY_LEN]) {
return secret_key_get(dest, REG_OPTIGA_KEY_OFFSET);
}
// Backs up the optiga pairing secret from the secret storage to the backup
// register
static void secret_optiga_cache(void) {
if (sectrue == secret_optiga_present()) {
secret_key_cache(REG_OPTIGA_KEY_OFFSET, SECRET_OPTIGA_KEY_OFFSET);
static void secret_keys_uncache(void) {
for (uint8_t i = 0; i < SECRET_NUM_KEY_SLOTS; i++) {
if (sectrue == secret_is_slot_valid(i)) {
secret_key_uncache(i);
}
}
}
// Deletes the optiga pairing secret from the register
static void secret_optiga_uncache(void) { secret_key_uncache(8); }
static void secret_optiga_erase(void) {
uint8_t value[SECRET_KEY_LEN] = {0};
secret_write(value, SECRET_OPTIGA_KEY_OFFSET, SECRET_KEY_LEN);
}
#endif
#ifdef USE_TROPIC
secbool secret_tropic_get_trezor_privkey(uint8_t dest[SECRET_KEY_LEN]) {
return secret_key_get(dest, REG_TROPIC_TRZ_PRIVKEY_OFFSET);
}
secbool secret_tropic_get_tropic_pubkey(uint8_t dest[SECRET_KEY_LEN]) {
return secret_key_get(dest, REG_TROPIC_TRO_PUBKEY_OFFSET);
}
secbool secret_tropic_set(const uint8_t privkey[SECRET_KEY_LEN],
const uint8_t pubkey[SECRET_KEY_LEN]) {
secbool res1 = secret_key_set(privkey, REG_TROPIC_TRZ_PRIVKEY_OFFSET,
SECRET_TROPIC_TRZ_PRIVKEY_OFFSET);
if (sectrue != res1) {
return secfalse;
}
secbool res2 = secret_key_set(pubkey, REG_TROPIC_TRO_PUBKEY_OFFSET,
SECRET_TROPIC_TRO_PUBKEY_OFFSET);
return res2;
}
secbool secret_tropic_present(void) {
secbool res1 = secret_key_present(SECRET_TROPIC_TRZ_PRIVKEY_OFFSET);
secbool res2 = secret_key_present(SECRET_TROPIC_TRO_PUBKEY_OFFSET);
return secbool_and(res1, res2);
}
secbool secret_tropic_present_any(void) {
secbool res1 = secret_key_present(SECRET_TROPIC_TRZ_PRIVKEY_OFFSET);
secbool res2 = secret_key_present(SECRET_TROPIC_TRO_PUBKEY_OFFSET);
return secbool_or(res1, res2);
}
secbool secret_tropic_writable(void) {
secbool res1 = secret_key_writable(SECRET_TROPIC_TRZ_PRIVKEY_OFFSET);
secbool res2 = secret_key_writable(SECRET_TROPIC_TRO_PUBKEY_OFFSET);
return secbool_or(res1, res2);
}
static void secret_tropic_erase(void) {
uint8_t value[SECRET_KEY_LEN] = {0};
secret_write(value, SECRET_TROPIC_TRZ_PRIVKEY_OFFSET, SECRET_KEY_LEN);
secret_write(value, SECRET_TROPIC_TRO_PUBKEY_OFFSET, SECRET_KEY_LEN);
}
// Backs up the tropic pairing secret from the secret storage to the backup
// register
static void secret_tropic_cache(void) {
if (sectrue == secret_tropic_present()) {
secret_key_cache(REG_TROPIC_TRZ_PRIVKEY_OFFSET,
SECRET_TROPIC_TRZ_PRIVKEY_OFFSET);
secret_key_cache(REG_TROPIC_TRO_PUBKEY_OFFSET,
SECRET_TROPIC_TRO_PUBKEY_OFFSET);
static void secret_keys_cache(void) {
for (uint8_t i = 0; i < SECRET_NUM_KEY_SLOTS; i++) {
if (sectrue == secret_is_slot_valid(i) &&
sectrue == secret_key_present(i)) {
secret_key_cache(i);
}
}
}
// Deletes the tropic pairing secret from the register
static void secret_tropic_uncache(void) {
secret_key_uncache(REG_TROPIC_TRZ_PRIVKEY_OFFSET);
secret_key_uncache(REG_TROPIC_TRO_PUBKEY_OFFSET);
static void secret_keys_cache_public(void) {
for (uint8_t i = 0; i < SECRET_NUM_KEY_SLOTS; i++) {
if ((sectrue == secret_is_slot_valid(i)) &&
(sectrue == secret_key_present(i)) &&
(sectrue == secret_slot_public[i])) {
secret_key_cache(i);
}
}
}
#endif
// return sectrue if all the key slots are valid and contain a key
static secbool secret_keys_present(void) {
secbool result = sectrue;
void secret_erase(void) {
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SECRET);
ensure(flash_area_erase(&SECRET_AREA, NULL), "secret erase");
mpu_restore(mpu_mode);
for (uint8_t i = 0; i < SECRET_NUM_KEY_SLOTS; i++) {
if (sectrue == secret_is_slot_valid(i)) {
result = secbool_and(result, secret_key_present(i));
}
}
return result;
}
static void secret_se_uncache(void) {
#ifdef USE_OPTIGA
secret_optiga_uncache();
#endif
#ifdef USE_TROPIC
secret_tropic_uncache();
#endif
#if defined BOOTLOADER || defined BOARDLOADER
// return sectrue if any non-public key slot is valid and contains a key
static secbool secret_keys_present_any(void) {
secbool result = secfalse;
for (uint8_t i = 0; i < SECRET_NUM_KEY_SLOTS; i++) {
if (sectrue == secret_is_slot_valid(i) &&
sectrue != secret_slot_public[i]) {
result = secbool_or(result, secret_key_present(i));
}
}
return result;
}
static void secret_se_cache(void) {
#ifdef USE_OPTIGA
secret_optiga_cache();
#endif
#ifdef USE_TROPIC
secret_tropic_cache();
#endif
}
static secbool secret_se_present(void) {
#ifdef USE_OPTIGA
secbool res1 = secret_optiga_present();
#else
secbool res1 = sectrue;
#endif
#ifdef USE_TROPIC
secbool res2 = secret_tropic_present();
#else
secbool res2 = sectrue;
#endif
// return sectrue if at least one key slot is writable
static secbool secret_keys_writable(void) {
secbool result = secfalse;
return secbool_and(res1, res2);
}
for (uint8_t i = 0; i < SECRET_NUM_KEY_SLOTS; i++) {
if (sectrue == secret_is_slot_valid(i)) {
result = secbool_or(result, secret_key_writable(i));
}
}
__attribute__((unused)) static secbool secret_se_present_any(void) {
#ifdef USE_OPTIGA
secbool res1 = secret_optiga_present();
#else
secbool res1 = secfalse;
#endif
#ifdef USE_TROPIC
secbool res2 = secret_tropic_present_any();
#else
secbool res2 = secfalse;
#endif
return secbool_or(res1, res2);
}
static secbool secret_se_writable(void) {
#ifdef USE_OPTIGA
secbool res1 = secret_optiga_writable();
#else
secbool res1 = secfalse;
#endif
#ifdef USE_TROPIC
secbool res2 = secret_tropic_writable();
#else
secbool res2 = secfalse;
#endif
return secbool_or(res1, res2);
return result;
}
#ifdef LOCKABLE_BOOTLOADER
secbool secret_bootloader_locked(void) {
#if defined BOOTLOADER || defined BOARDLOADER
return secret_se_present_any();
return secret_keys_present_any();
#else
const volatile uint32_t *reg1 = &TAMP->BKP8R;
for (int i = 0; i < 24; i++) {
if (reg1[i] != 0) {
// in firmware, we determine bootloader state by checking if bootloader
// has provided any non-public key
for (int i = 0; i < SECRET_NUM_KEY_SLOTS; i++) {
uint32_t val[SECRET_KEY_MAX_LEN] = {0};
size_t len = secret_get_slot_len(i);
if (secfalse == secret_slot_public[i] &&
sectrue == secret_key_get(i, (uint8_t *)val, len)) {
memzero(val, sizeof(val));
return sectrue;
}
}
@ -494,12 +533,12 @@ secbool secret_bootloader_locked(void) {
}
void secret_unlock_bootloader(void) {
#ifdef USE_OPTIGA
secret_optiga_erase();
#endif
#ifdef USE_TROPIC
secret_tropic_erase();
#endif
for (uint8_t i = 0; i < SECRET_NUM_KEY_SLOTS; i++) {
if (sectrue == secret_is_slot_valid(i) &&
sectrue != secret_slot_public[i]) {
secret_key_erase(i);
}
}
}
#endif
@ -519,29 +558,35 @@ void secret_prepare_fw(secbool allow_run_with_secret,
* all-cases.
*/
secret_bhk_load();
secret_bhk_lock();
secret_se_uncache();
secbool se_secret_present = secret_se_present();
secbool se_secret_writable = secret_se_writable();
if (sectrue == allow_provisioning_access && sectrue == se_secret_writable &&
secfalse == se_secret_present) {
// SE Secret is not present and the secret sector is writable.
// This means the U5 chip is unprovisioned.
// Allow trusted firmware (prodtest presumably) to access the secret sector,
// early return here.
return;
}
if (sectrue == allow_run_with_secret && sectrue == se_secret_present) {
// Firmware is trusted, and the SE secret is present, make it available.
secret_se_cache();
}
// Disable access unconditionally.
secret_disable_access();
if (sectrue != allow_run_with_secret && sectrue == se_secret_present) {
if (sectrue != allow_run_with_secret &&
secfalse != secret_bootloader_locked()) {
// Untrusted firmware, locked bootloader. Show the restricted screen.
show_install_restricted_screen();
}
secret_bhk_load();
secret_bhk_lock();
secret_keys_uncache();
secbool secret_present = secret_keys_present();
secbool secret_writable = secret_keys_writable();
if (sectrue == allow_provisioning_access && sectrue == secret_writable &&
secfalse == secret_present) {
// Secret keys are not present and they are writable.
// This means the U5 chip is unprovisioned.
// Allow trusted firmware (prodtest presumably) to access the secret sector,
// early return here.
secret_keys_cache();
return;
}
if (sectrue == allow_run_with_secret && sectrue == secret_present) {
// Firmware is trusted, and the secret keys are present, make it available.
secret_keys_cache();
} else {
// Make only public keys available.
secret_keys_cache_public();
}
// Disable access unconditionally.
secret_disable_access();
}
void secret_init(void) {

View File

@ -0,0 +1,47 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef SECURE_MODE
#include <trezor_bsp.h>
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <sec/secret.h>
#include <sec/secret_keys.h>
#ifdef USE_OPTIGA
secbool secret_key_optiga_pairing(uint8_t dest[OPTIGA_PAIRING_SECRET_SIZE]) {
return secret_key_get(SECRET_OPTIGA_SLOT, dest, OPTIGA_PAIRING_SECRET_SIZE);
}
#endif
#ifdef USE_TROPIC
secbool secret_key_tropic_public(curve25519_key dest) {
return secret_key_get(SECRET_TROPIC_TROPIC_PUBKEY_SLOT, dest,
sizeof(curve25519_key));
}
secbool secret_key_tropic_pairing(curve25519_key dest) {
return secret_key_get(SECRET_TROPIC_TREZOR_PRIVKEY_SLOT, dest,
sizeof(curve25519_key));
}
#endif
#endif

View File

@ -17,158 +17,156 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <memzero.h>
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <sec/secret.h>
#include <sys/mpu.h>
#include <util/flash.h>
#ifdef KERNEL_MODE
static uint8_t SECRET_TROPIC_TREZOR_PRIVKEY_BYTES[] = {
0xf0, 0xc4, 0xaa, 0x04, 0x8f, 0x00, 0x13, 0xa0, 0x96, 0x84, 0xdf,
0x05, 0xe8, 0xa2, 0x2e, 0xf7, 0x21, 0x38, 0x98, 0x28, 0x2b, 0xa9,
0x43, 0x12, 0xf3, 0x13, 0xdf, 0x2d, 0xce, 0x8d, 0x41, 0x64};
static uint8_t SECRET_TROPIC_PUBKEY_BYTES[] = {
0x31, 0xE9, 0x0A, 0xF1, 0x50, 0x45, 0x10, 0xEE, 0x4E, 0xFD, 0x79,
0x13, 0x33, 0x41, 0x48, 0x15, 0x89, 0xA2, 0x89, 0x5C, 0xC5, 0xFB,
0xB1, 0x3E, 0xD5, 0x71, 0x1C, 0x1E, 0x9B, 0x81, 0x98, 0x72};
static secbool bootloader_locked_set = secfalse;
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;
}
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SECRET);
bootloader_locked =
memcmp(addr, SECRET_HEADER_MAGIC, sizeof(SECRET_HEADER_MAGIC)) == 0
? sectrue
: secfalse;
mpu_restore(mpu_mode);
bootloader_locked_set = sectrue;
return bootloader_locked;
}
#ifdef LOCKABLE_BOOTLOADER
secbool secret_bootloader_locked(void) {
if (bootloader_locked_set != sectrue) {
// Set bootloader_locked.
secret_verify_header();
}
return bootloader_locked;
}
void secret_unlock_bootloader(void) { secret_erase(); }
static secbool bootloader_locked = secfalse;
#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);
}
#ifndef SECRET_NUM_KEY_SLOTS
#define SECRET_NUM_KEY_SLOTS 0
#endif
void secret_write(const uint8_t* data, uint32_t offset, uint32_t len) {
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SECRET);
ensure(flash_unlock_write(), "secret write");
for (int i = 0; i < len; i++) {
ensure(flash_area_write_byte(&SECRET_AREA, offset + i, data[i]),
"secret write");
}
ensure(flash_lock_write(), "secret write");
mpu_restore(mpu_mode);
}
#ifdef SECRET_KEY_SLOT_0_LEN
static uint8_t secret_key_slot0[SECRET_KEY_SLOT_0_LEN] = {0};
#endif
#ifdef SECRET_KEY_SLOT_1_LEN
static uint8_t secret_key_slot1[SECRET_KEY_SLOT_1_LEN] = {0};
#endif
#ifdef SECRET_KEY_SLOT_2_LEN
static uint8_t secret_key_slot2[SECRET_KEY_SLOT_2_LEN] = {0};
#endif
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;
}
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SECRET);
memcpy(data, addr, len);
mpu_restore(mpu_mode);
return sectrue;
}
secbool secret_wiped(void) {
uint32_t size = flash_area_get_size(&SECRET_AREA);
secbool wiped = sectrue;
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SECRET);
for (int i = 0; i < size; i += 4) {
uint32_t* addr = (uint32_t*)flash_area_get_address(&SECRET_AREA, i, 4);
if (addr == NULL) {
wiped = secfalse;
size_t secret_get_slot_len(uint8_t slot) {
switch (slot) {
#ifdef SECRET_KEY_SLOT_0_LEN
case 0:
return SECRET_KEY_SLOT_0_LEN;
#endif
#ifdef SECRET_KEY_SLOT_1_LEN
case 1:
return SECRET_KEY_SLOT_1_LEN;
#endif
#ifdef SECRET_KEY_SLOT_2_LEN
case 2:
return SECRET_KEY_SLOT_2_LEN;
#endif
default:
break;
}
if (*addr != 0xFFFFFFFF) {
wiped = secfalse;
break;
}
}
return 0;
}
mpu_restore(mpu_mode);
return wiped;
uint8_t* secret_get_slot_ptr(uint8_t slot) {
switch (slot) {
#ifdef SECRET_KEY_SLOT_0_LEN
case 0:
return secret_key_slot0;
#endif
#ifdef SECRET_KEY_SLOT_1_LEN
case 1:
return secret_key_slot1;
#endif
#ifdef SECRET_KEY_SLOT_2_LEN
case 2:
return secret_key_slot2;
#endif
default:
break;
}
return NULL;
}
void secret_erase(void) {
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_SECRET);
ensure(flash_area_erase(&SECRET_AREA, NULL), "secret erase");
mpu_restore(mpu_mode);
for (uint8_t i = 0; i < SECRET_NUM_KEY_SLOTS; i++) {
uint8_t* slot_ptr = secret_get_slot_ptr(i);
if (slot_ptr != NULL) {
memzero(slot_ptr, secret_get_slot_len(i));
}
}
}
secbool secret_optiga_set(const uint8_t secret[SECRET_KEY_LEN]) {
#ifdef LOCKABLE_BOOTLOADER
secbool secret_bootloader_locked(void) { return bootloader_locked; }
void secret_unlock_bootloader(void) {
secret_erase();
secret_write_header();
secret_write(secret, SECRET_OPTIGA_KEY_OFFSET, SECRET_KEY_LEN);
bootloader_locked = secfalse;
}
void secret_lock_bootloader(void) { bootloader_locked = sectrue; }
#endif
secbool secret_key_set(uint8_t slot, const uint8_t* key, size_t len) {
if (slot >= SECRET_NUM_KEY_SLOTS) {
return secfalse;
}
if (len != secret_get_slot_len(slot)) {
return secfalse;
}
uint8_t* slot_ptr = secret_get_slot_ptr(slot);
if (slot_ptr == NULL) {
return secfalse;
}
memcpy(slot_ptr, key, len);
return sectrue;
}
secbool secret_optiga_get(uint8_t dest[SECRET_KEY_LEN]) {
return secret_read(dest, SECRET_OPTIGA_KEY_OFFSET, SECRET_KEY_LEN);
}
secbool secret_key_get(uint8_t slot, uint8_t* dest, size_t len) {
if (slot >= SECRET_NUM_KEY_SLOTS) {
return secfalse;
}
secbool secret_optiga_present(void) {
return (sectrue != secret_wiped()) * sectrue;
}
if (len != secret_get_slot_len(slot)) {
return secfalse;
}
secbool secret_optiga_writable(void) { return secret_wiped(); }
uint8_t* slot_ptr = secret_get_slot_ptr(slot);
if (slot_ptr == NULL) {
return secfalse;
}
secbool secret_tropic_get_trezor_privkey(uint8_t dest[SECRET_KEY_LEN]) {
memcpy(dest, &SECRET_TROPIC_TREZOR_PRIVKEY_BYTES, SECRET_KEY_LEN);
memcpy(dest, slot_ptr, len);
return sectrue;
}
secbool secret_tropic_get_tropic_pubkey(uint8_t dest[SECRET_KEY_LEN]) {
memcpy(dest, &SECRET_TROPIC_PUBKEY_BYTES, SECRET_KEY_LEN);
return sectrue;
static secbool secret_key_present(uint8_t slot) {
if (slot >= SECRET_NUM_KEY_SLOTS) {
return secfalse;
}
uint8_t* slot_ptr = secret_get_slot_ptr(slot);
if (slot_ptr == NULL) {
return secfalse;
}
for (size_t i = 0; i < secret_get_slot_len(slot); i++) {
if (slot_ptr[i] != 0) {
return sectrue;
}
}
return secfalse;
}
secbool secret_key_writable(uint8_t slot) {
return secret_key_present(slot) == secfalse;
}
void secret_prepare_fw(secbool allow_run_with_secret,
secbool allow_provisioning_access) {
(void)allow_provisioning_access;
#ifdef LOCKABLE_BOOTLOADER
if (sectrue != allow_run_with_secret && sectrue != secret_wiped()) {
if (sectrue != allow_run_with_secret && sectrue != bootloader_locked) {
// This function does not return
show_install_restricted_screen();
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef SECURE_MODE
#include <trezor_bsp.h>
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <sec/secret.h>
#include <sec/secret_keys.h>
#ifdef USE_TROPIC
static uint8_t SECRET_TROPIC_PAIRING_BYTES[] = {
0xf0, 0xc4, 0xaa, 0x04, 0x8f, 0x00, 0x13, 0xa0, 0x96, 0x84, 0xdf,
0x05, 0xe8, 0xa2, 0x2e, 0xf7, 0x21, 0x38, 0x98, 0x28, 0x2b, 0xa9,
0x43, 0x12, 0xf3, 0x13, 0xdf, 0x2d, 0xce, 0x8d, 0x41, 0x64};
static uint8_t SECRET_TROPIC_PUBKEY_BYTES[] = {
0x31, 0xE9, 0x0A, 0xF1, 0x50, 0x45, 0x10, 0xEE, 0x4E, 0xFD, 0x79,
0x13, 0x33, 0x41, 0x48, 0x15, 0x89, 0xA2, 0x89, 0x5C, 0xC5, 0xFB,
0xB1, 0x3E, 0xD5, 0x71, 0x1C, 0x1E, 0x9B, 0x81, 0x98, 0x72};
_Static_assert(sizeof(SECRET_TROPIC_PAIRING_BYTES) == sizeof(curve25519_key),
"Invalid size of Tropic pairing key");
_Static_assert(sizeof(SECRET_TROPIC_PUBKEY_BYTES) == sizeof(curve25519_key),
"Invalid size of Tropic public key");
secbool secret_key_tropic_public(curve25519_key dest) {
memcpy(dest, SECRET_TROPIC_PUBKEY_BYTES, sizeof(curve25519_key));
return sectrue;
}
secbool secret_key_tropic_pairing(curve25519_key dest) {
memcpy(dest, SECRET_TROPIC_PAIRING_BYTES, sizeof(curve25519_key));
return sectrue;
}
#endif
#endif

View File

@ -22,7 +22,7 @@
#include <trezor_rtl.h>
#include <trezor_types.h>
#include <sec/secret.h>
#include <sec/secret_keys.h>
#include <sec/tropic.h>
#include <libtropic.h>
@ -48,8 +48,8 @@ bool tropic_init(void) {
return true;
}
uint8_t tropic_secret_tropic_pubkey[SECRET_KEY_LEN] = {0};
uint8_t tropic_secret_trezor_privkey[SECRET_KEY_LEN] = {0};
curve25519_key tropic_secret_tropic_pubkey = {0};
curve25519_key tropic_secret_trezor_privkey = {0};
if (!tropic_hal_init()) {
goto cleanup;
@ -60,13 +60,11 @@ bool tropic_init(void) {
goto cleanup;
}
secbool pubkey_ok =
secret_tropic_get_tropic_pubkey(tropic_secret_tropic_pubkey);
secbool privkey_ok =
secret_tropic_get_trezor_privkey(tropic_secret_trezor_privkey);
secbool pubkey_ok = secret_key_tropic_public(tropic_secret_tropic_pubkey);
secbool privkey_ok = secret_key_tropic_pairing(tropic_secret_trezor_privkey);
if (pubkey_ok == sectrue && privkey_ok == sectrue) {
uint8_t trezor_pubkey[SECRET_KEY_LEN] = {0};
uint8_t trezor_pubkey[32] = {0};
curve25519_scalarmult_basepoint(trezor_pubkey,
tropic_secret_trezor_privkey);

View File

@ -69,6 +69,7 @@ def stm32f4_common_files(env, defines, sources, paths):
"embed/sec/random_delays/stm32/random_delays.c",
"embed/sec/rng/stm32/rng.c",
"embed/sec/secret/stm32f4/secret.c",
"embed/sec/secret/stm32f4/secret_keys.c",
"embed/sec/time_estimate/stm32/time_estimate.c",
"embed/sys/dbg/stm32/dbg_printf.c",
"embed/sys/irq/stm32/irq.c",

View File

@ -88,6 +88,7 @@ def stm32u5_common_files(env, features_wanted, defines, sources, paths):
"embed/sec/random_delays/stm32/random_delays.c",
"embed/sec/rng/stm32/rng.c",
"embed/sec/secret/stm32u5/secret.c",
"embed/sec/secret/stm32u5/secret_keys.c",
"embed/sec/secure_aes/stm32u5/secure_aes.c",
"embed/sec/secure_aes/stm32u5/secure_aes_unpriv.c",
"embed/sec/time_estimate/stm32/time_estimate.c",

View File

@ -36,6 +36,7 @@ def unix_common_files(env, defines, sources, paths):
"embed/sec/entropy/unix/entropy.c",
"embed/sec/random_delays/unix/random_delays.c",
"embed/sec/secret/unix/secret.c",
"embed/sec/secret/unix/secret_keys.c",
"embed/sec/monoctr/unix/monoctr.c",
"embed/sec/rng/unix/rng.c",
"embed/sec/time_estimate/unix/time_estimate.c",