diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest index d6a6e34375..140001ab9a 100644 --- a/core/SConscript.prodtest +++ b/core/SConscript.prodtest @@ -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', diff --git a/core/SConscript.prodtest_emu b/core/SConscript.prodtest_emu index f695ddf8e0..ab8e9a2bd3 100644 --- a/core/SConscript.prodtest_emu +++ b/core/SConscript.prodtest_emu @@ -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', diff --git a/core/embed/projects/prodtest/.changelog.d/5281.added.1 b/core/embed/projects/prodtest/.changelog.d/5281.added.1 new file mode 100644 index 0000000000..13fe6d1445 --- /dev/null +++ b/core/embed/projects/prodtest/.changelog.d/5281.added.1 @@ -0,0 +1 @@ +Add the `secrets-init` command to generate and write secrets to flash. diff --git a/core/embed/projects/prodtest/README.md b/core/embed/projects/prodtest/README.md index 27dfaba0eb..1c758f64df 100644 --- a/core/embed/projects/prodtest/README.md +++ b/core/embed/projects/prodtest/README.md @@ -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. diff --git a/core/embed/projects/prodtest/cmd/prodtest_optiga.c b/core/embed/projects/prodtest/cmd/prodtest_optiga.c index aca776c02b..e34643df2b 100644 --- a/core/embed/projects/prodtest/cmd/prodtest_optiga.c +++ b/core/embed/projects/prodtest/cmd/prodtest_optiga.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #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. diff --git a/core/embed/projects/prodtest/cmd/prodtest_secrets.c b/core/embed/projects/prodtest/cmd/prodtest_secrets.c new file mode 100644 index 0000000000..a3bc7b656e --- /dev/null +++ b/core/embed/projects/prodtest/cmd/prodtest_secrets.c @@ -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 . + */ + +#include + +#include + +#include +#ifdef USE_OPTIGA +#include +#endif +#include +#include + +#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 = "" +);