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:
parent
34921907b0
commit
c8bf5be9b8
@ -178,6 +178,7 @@ SOURCE_PRODTEST = [
|
|||||||
'embed/projects/prodtest/cmd/prodtest_reboot.c',
|
'embed/projects/prodtest/cmd/prodtest_reboot.c',
|
||||||
'embed/projects/prodtest/cmd/prodtest_rgbled.c',
|
'embed/projects/prodtest/cmd/prodtest_rgbled.c',
|
||||||
'embed/projects/prodtest/cmd/prodtest_sdcard.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_tamper.c',
|
||||||
'embed/projects/prodtest/cmd/prodtest_sbu.c',
|
'embed/projects/prodtest/cmd/prodtest_sbu.c',
|
||||||
'embed/projects/prodtest/cmd/prodtest_touch.c',
|
'embed/projects/prodtest/cmd/prodtest_touch.c',
|
||||||
|
@ -165,6 +165,7 @@ SOURCE_PRODTEST = [
|
|||||||
'embed/projects/prodtest/cmd/prodtest_reboot.c',
|
'embed/projects/prodtest/cmd/prodtest_reboot.c',
|
||||||
'embed/projects/prodtest/cmd/prodtest_rgbled.c',
|
'embed/projects/prodtest/cmd/prodtest_rgbled.c',
|
||||||
'embed/projects/prodtest/cmd/prodtest_sdcard.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_tamper.c',
|
||||||
'embed/projects/prodtest/cmd/prodtest_sbu.c',
|
'embed/projects/prodtest/cmd/prodtest_sbu.c',
|
||||||
'embed/projects/prodtest/cmd/prodtest_touch.c',
|
'embed/projects/prodtest/cmd/prodtest_touch.c',
|
||||||
|
1
core/embed/projects/prodtest/.changelog.d/5281.added.1
Normal file
1
core/embed/projects/prodtest/.changelog.d/5281.added.1
Normal file
@ -0,0 +1 @@
|
|||||||
|
Add the `secrets-init` command to generate and write secrets to flash.
|
@ -615,6 +615,15 @@ prodtest-homescreen
|
|||||||
OK
|
OK
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### secrets-init
|
||||||
|
Generates random secrets and stores them in the protected storage.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
secrets-init
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
### optiga-id-read
|
### optiga-id-read
|
||||||
Retrieves the coprocessor UID of the Optiga chip as a 27 byte hexadecimal string.
|
Retrieves the coprocessor UID of the Optiga chip as a 27 byte hexadecimal string.
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
#include <rtl/cli.h>
|
#include <rtl/cli.h>
|
||||||
#include <sec/optiga_commands.h>
|
#include <sec/optiga_commands.h>
|
||||||
#include <sec/optiga_transport.h>
|
#include <sec/optiga_transport.h>
|
||||||
#include <sec/secret.h>
|
|
||||||
#include <sec/secret_keys.h>
|
#include <sec/secret_keys.h>
|
||||||
|
|
||||||
#include "aes/aes.h"
|
#include "aes/aes.h"
|
||||||
@ -54,9 +53,7 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
OPTIGA_PAIRING_UNPAIRED = 0,
|
OPTIGA_PAIRING_UNPAIRED = 0,
|
||||||
OPTIGA_PAIRING_PAIRED,
|
OPTIGA_PAIRING_PAIRED,
|
||||||
OPTIGA_PAIRING_ERR_RNG,
|
|
||||||
OPTIGA_PAIRING_ERR_READ_FLASH,
|
OPTIGA_PAIRING_ERR_READ_FLASH,
|
||||||
OPTIGA_PAIRING_ERR_WRITE_FLASH,
|
|
||||||
OPTIGA_PAIRING_ERR_WRITE_OPTIGA,
|
OPTIGA_PAIRING_ERR_WRITE_OPTIGA,
|
||||||
OPTIGA_PAIRING_ERR_HANDSHAKE,
|
OPTIGA_PAIRING_ERR_HANDSHAKE,
|
||||||
} optiga_pairing;
|
} optiga_pairing;
|
||||||
@ -77,15 +74,9 @@ static bool optiga_paired(cli_t* cli) {
|
|||||||
switch (optiga_pairing_state) {
|
switch (optiga_pairing_state) {
|
||||||
case OPTIGA_PAIRING_PAIRED:
|
case OPTIGA_PAIRING_PAIRED:
|
||||||
return true;
|
return true;
|
||||||
case OPTIGA_PAIRING_ERR_RNG:
|
|
||||||
details = "optiga_get_random error";
|
|
||||||
break;
|
|
||||||
case OPTIGA_PAIRING_ERR_READ_FLASH:
|
case OPTIGA_PAIRING_ERR_READ_FLASH:
|
||||||
details = "failed to read pairing secret from flash";
|
details = "failed to read pairing secret from flash";
|
||||||
break;
|
break;
|
||||||
case OPTIGA_PAIRING_ERR_WRITE_FLASH:
|
|
||||||
details = "failed to write pairing secret to flash";
|
|
||||||
break;
|
|
||||||
case OPTIGA_PAIRING_ERR_WRITE_OPTIGA:
|
case OPTIGA_PAIRING_ERR_WRITE_OPTIGA:
|
||||||
details = "failed to write pairing secret to Optiga";
|
details = "failed to write pairing secret to Optiga";
|
||||||
break;
|
break;
|
||||||
@ -145,56 +136,13 @@ static bool set_metadata(cli_t* cli, uint16_t oid,
|
|||||||
return true;
|
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) {
|
void pair_optiga(cli_t* cli) {
|
||||||
uint8_t pairing_secret[OPTIGA_PAIRING_SECRET_SIZE] = {0};
|
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 != secret_key_optiga_pairing(pairing_secret)) {
|
||||||
if (sectrue != initialize_secrets()) {
|
optiga_pairing_state = OPTIGA_PAIRING_ERR_READ_FLASH;
|
||||||
// optiga_pairing_state set by initialize_secrets().
|
goto cleanup;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the handshake to verify that the secret is stored in Optiga.
|
// Execute the handshake to verify that the secret is stored in Optiga.
|
||||||
|
136
core/embed/projects/prodtest/cmd/prodtest_secrets.c
Normal file
136
core/embed/projects/prodtest/cmd/prodtest_secrets.c
Normal 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 = ""
|
||||||
|
);
|
Loading…
Reference in New Issue
Block a user