1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-21 14:08:13 +00:00

feat(core/prodtest): add secrets-write command

This commit is contained in:
Ondřej Vejpustek 2025-06-29 20:54:19 +02:00
parent 34921907b0
commit c8bf5be9b8
6 changed files with 151 additions and 55 deletions

View File

@ -178,6 +178,7 @@ SOURCE_PRODTEST = [
'embed/projects/prodtest/cmd/prodtest_reboot.c',
'embed/projects/prodtest/cmd/prodtest_rgbled.c',
'embed/projects/prodtest/cmd/prodtest_sdcard.c',
'embed/projects/prodtest/cmd/prodtest_secrets.c',
'embed/projects/prodtest/cmd/prodtest_tamper.c',
'embed/projects/prodtest/cmd/prodtest_sbu.c',
'embed/projects/prodtest/cmd/prodtest_touch.c',

View File

@ -165,6 +165,7 @@ SOURCE_PRODTEST = [
'embed/projects/prodtest/cmd/prodtest_reboot.c',
'embed/projects/prodtest/cmd/prodtest_rgbled.c',
'embed/projects/prodtest/cmd/prodtest_sdcard.c',
'embed/projects/prodtest/cmd/prodtest_secrets.c',
'embed/projects/prodtest/cmd/prodtest_tamper.c',
'embed/projects/prodtest/cmd/prodtest_sbu.c',
'embed/projects/prodtest/cmd/prodtest_touch.c',

View File

@ -0,0 +1 @@
Add the `secrets-init` command to generate and write secrets to flash.

View File

@ -615,6 +615,15 @@ prodtest-homescreen
OK
```
### secrets-init
Generates random secrets and stores them in the protected storage.
Example:
```
secrets-init
OK
```
### optiga-id-read
Retrieves the coprocessor UID of the Optiga chip as a 27 byte hexadecimal string.

View File

@ -25,7 +25,6 @@
#include <rtl/cli.h>
#include <sec/optiga_commands.h>
#include <sec/optiga_transport.h>
#include <sec/secret.h>
#include <sec/secret_keys.h>
#include "aes/aes.h"
@ -54,9 +53,7 @@
typedef enum {
OPTIGA_PAIRING_UNPAIRED = 0,
OPTIGA_PAIRING_PAIRED,
OPTIGA_PAIRING_ERR_RNG,
OPTIGA_PAIRING_ERR_READ_FLASH,
OPTIGA_PAIRING_ERR_WRITE_FLASH,
OPTIGA_PAIRING_ERR_WRITE_OPTIGA,
OPTIGA_PAIRING_ERR_HANDSHAKE,
} optiga_pairing;
@ -77,15 +74,9 @@ static bool optiga_paired(cli_t* cli) {
switch (optiga_pairing_state) {
case OPTIGA_PAIRING_PAIRED:
return true;
case OPTIGA_PAIRING_ERR_RNG:
details = "optiga_get_random error";
break;
case OPTIGA_PAIRING_ERR_READ_FLASH:
details = "failed to read pairing secret from flash";
break;
case OPTIGA_PAIRING_ERR_WRITE_FLASH:
details = "failed to write pairing secret to flash";
break;
case OPTIGA_PAIRING_ERR_WRITE_OPTIGA:
details = "failed to write pairing secret to Optiga";
break;
@ -145,56 +136,13 @@ static bool set_metadata(cli_t* cli, uint16_t oid,
return true;
}
static secbool initialize_secrets(void) {
#ifdef SECRET_PRIVILEGED_MASTER_KEY_SLOT
uint8_t secret[2 * SECRET_MASTER_KEY_SLOT_SIZE] = {0};
#else
uint8_t secret[OPTIGA_PAIRING_SECRET_SIZE] = {0};
#endif
// Generate the pairing secret or master keys.
if (OPTIGA_SUCCESS != optiga_get_random(secret, sizeof(secret))) {
optiga_pairing_state = OPTIGA_PAIRING_ERR_RNG;
return secfalse;
}
random_xor(secret, sizeof(secret));
#ifdef SECRET_PRIVILEGED_MASTER_KEY_SLOT
// Store the master keys in the flash memory.
secbool ret = secret_key_set(SECRET_PRIVILEGED_MASTER_KEY_SLOT, secret,
SECRET_MASTER_KEY_SLOT_SIZE);
if (sectrue == ret) {
ret = secret_key_set(SECRET_UNPRIVILEGED_MASTER_KEY_SLOT,
&secret[SECRET_MASTER_KEY_SLOT_SIZE],
SECRET_MASTER_KEY_SLOT_SIZE);
}
#else
// Store the pairing secret in the flash memory.
secbool ret =
secret_key_set(SECRET_OPTIGA_SLOT, secret, OPTIGA_PAIRING_SECRET_SIZE);
#endif
memzero(secret, sizeof(secret));
if (sectrue != ret) {
optiga_pairing_state = OPTIGA_PAIRING_ERR_WRITE_FLASH;
}
return ret;
}
void pair_optiga(cli_t* cli) {
uint8_t pairing_secret[OPTIGA_PAIRING_SECRET_SIZE] = {0};
// Load the pairing secret from the flash memory.
if (sectrue != secret_key_optiga_pairing(pairing_secret)) {
if (sectrue != initialize_secrets()) {
// optiga_pairing_state set by initialize_secrets().
goto cleanup;
}
// Load the pairing secret from the flash memory.
if (sectrue != secret_key_optiga_pairing(pairing_secret)) {
optiga_pairing_state = OPTIGA_PAIRING_ERR_READ_FLASH;
goto cleanup;
}
optiga_pairing_state = OPTIGA_PAIRING_ERR_READ_FLASH;
goto cleanup;
}
// Execute the handshake to verify that the secret is stored in Optiga.

View File

@ -0,0 +1,136 @@
/*
* 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/>.
*/
#include <trezor_model.h>
#include <string.h>
#include <rtl/cli.h>
#ifdef USE_OPTIGA
#include <sec/optiga_commands.h>
#endif
#include <sec/secret.h>
#include <sec/secret_keys.h>
#include "memzero.h"
#include "rand.h"
#include "secbool.h"
secbool generate_random_secret(uint8_t* secret, size_t length) {
random_buffer(secret, length);
#ifdef USE_OPTIGA
uint8_t optiga_secret[length];
if (OPTIGA_SUCCESS != optiga_get_random(optiga_secret, length)) {
return secfalse;
}
for (size_t i = 0; i < length; i++) {
secret[i] ^= optiga_secret[i];
}
memzero(optiga_secret, sizeof(optiga_secret));
#endif
#ifdef USE_TROPIC
// TODO: Generate randomness using tropic and xor it with `secret`.
#endif
return sectrue;
}
secbool set_random_secret(uint8_t slot, size_t length) {
uint8_t secret[length];
uint8_t secret_read[length];
secbool ret = secfalse;
if (secret_key_writable(slot) != sectrue) {
if (generate_random_secret(secret, sizeof(secret)) != sectrue) {
ret = secfalse;
goto cleanup;
}
if (secret_key_set(slot, secret, sizeof(secret)) != sectrue) {
ret = secfalse;
goto cleanup;
}
if (secret_key_get(slot, secret_read, sizeof(secret_read)) != sectrue) {
ret = secfalse;
goto cleanup;
}
if (memcmp(secret, secret_read, sizeof(secret)) != 0) {
ret = secfalse;
goto cleanup;
}
}
ret = sectrue;
cleanup:
memzero(secret, sizeof(secret));
memzero(secret_read, sizeof(secret_read));
return ret;
}
static void prodtest_secrets_init(cli_t* cli) {
if (cli_arg_count(cli) > 0) {
cli_error_arg_count(cli);
return;
}
#ifdef SECRET_PRIVILEGED_MASTER_KEY_SLOT
if (set_random_secret(SECRET_PRIVILEGED_MASTER_KEY_SLOT,
SECRET_MASTER_KEY_SLOT_SIZE) != sectrue) {
cli_error(cli, CLI_ERROR,
"`set_random_secret` failed for privileged master key.");
return;
}
#endif
#ifdef SECRET_UNPRIVILEGED_MASTER_KEY_SLOT
if (set_random_secret(SECRET_UNPRIVILEGED_MASTER_KEY_SLOT,
SECRET_MASTER_KEY_SLOT_SIZE) != sectrue) {
cli_error(cli, CLI_ERROR,
"`set_random_secret` failed for unprivileged master key.");
return;
}
#endif
#ifdef USE_OPTIGA
#ifdef SECRET_OPTIGA_SLOT
if (set_random_secret(SECRET_OPTIGA_SLOT, OPTIGA_PAIRING_SECRET_SIZE) !=
sectrue) {
cli_error(cli, CLI_ERROR,
"`set_random_secret` failed for optiga pairing secret.");
return;
}
#endif
#endif
cli_ok(cli, "");
}
// clang-format off
PRODTEST_CLI_CMD(
.name = "secrets-init",
.func = prodtest_secrets_init,
.info = "Generate and write secrets to flash",
.args = ""
);