From 75ec0592072208111d9903c56bd30f5ae9c0f44d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vejpustek?= Date: Mon, 18 Aug 2025 15:26:59 +0200 Subject: [PATCH 1/4] style(core): make style consistent [no changelog] --- core/embed/sys/smcall/stm32/smcall_dispatch.c | 4 +--- core/embed/sys/syscall/stm32/syscall_dispatch.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/core/embed/sys/smcall/stm32/smcall_dispatch.c b/core/embed/sys/smcall/stm32/smcall_dispatch.c index 4ad326fa6b5..8ed9ecd299f 100644 --- a/core/embed/sys/smcall/stm32/smcall_dispatch.c +++ b/core/embed/sys/smcall/stm32/smcall_dispatch.c @@ -325,18 +325,16 @@ __attribute((no_stack_protector)) void smcall_handler(uint32_t *args, case SMCALL_TROPIC_ECC_KEY_GENERATE: { uint16_t slot_index = (uint16_t)args[0]; args[0] = tropic_ecc_key_generate__verified(slot_index); - } break; + case SMCALL_TROPIC_ECC_SIGN: { uint16_t key_slot_index = (uint16_t)args[0]; const uint8_t *dig = (const uint8_t *)args[1]; uint16_t dig_len = (uint16_t)args[2]; uint8_t *sig = (uint8_t *)args[3]; uint16_t sig_len = (uint16_t)args[4]; - args[0] = tropic_ecc_sign__verified(key_slot_index, dig, dig_len, sig, sig_len); - } break; #endif diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c index 8dbe4d499f9..b4ff882af82 100644 --- a/core/embed/sys/syscall/stm32/syscall_dispatch.c +++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c @@ -866,18 +866,16 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args, case SYSCALL_TROPIC_ECC_KEY_GENERATE: { uint16_t slot_index = (uint16_t)args[0]; args[0] = tropic_ecc_key_generate__verified(slot_index); - } break; + case SYSCALL_TROPIC_ECC_SIGN: { uint16_t key_slot_index = (uint16_t)args[0]; const uint8_t *dig = (const uint8_t *)args[1]; uint16_t dig_len = (uint16_t)args[2]; uint8_t *sig = (uint8_t *)args[3]; uint16_t sig_len = (uint16_t)args[4]; - args[0] = tropic_ecc_sign__verified(key_slot_index, dig, dig_len, sig, sig_len); - } break; #endif From 836e9def325b27c8c056a24034e575ba3fad1b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vejpustek?= Date: Thu, 24 Jul 2025 15:24:20 +0200 Subject: [PATCH 2/4] fix(core): fixes after libtropic update [no changelog] --- core/embed/sec/tropic/inc/sec/tropic.h | 2 +- core/embed/sec/tropic/stm32/tropic01.c | 12 +++++------- core/embed/sec/tropic/tropic.c | 4 ++-- core/embed/sys/smcall/stm32/smcall_dispatch.c | 4 +--- core/embed/sys/smcall/stm32/smcall_stubs.c | 6 +++--- core/embed/sys/smcall/stm32/smcall_verifiers.c | 8 ++++---- core/embed/sys/smcall/stm32/smcall_verifiers.h | 3 +-- core/embed/sys/syscall/stm32/syscall_dispatch.c | 4 +--- core/embed/sys/syscall/stm32/syscall_stubs.c | 6 +++--- core/embed/sys/syscall/stm32/syscall_verifiers.c | 8 ++++---- core/embed/sys/syscall/stm32/syscall_verifiers.h | 3 +-- .../upymod/modtrezorcrypto/modtrezorcrypto-tropic.h | 7 +++---- 12 files changed, 29 insertions(+), 38 deletions(-) diff --git a/core/embed/sec/tropic/inc/sec/tropic.h b/core/embed/sec/tropic/inc/sec/tropic.h index ba2e2859b12..312ca35f774 100644 --- a/core/embed/sec/tropic/inc/sec/tropic.h +++ b/core/embed/sec/tropic/inc/sec/tropic.h @@ -39,4 +39,4 @@ bool tropic_ping(const uint8_t* msg_out, uint8_t* msg_in, uint16_t msg_len); bool tropic_ecc_key_generate(uint16_t slot_index); bool tropic_ecc_sign(uint16_t key_slot_index, const uint8_t* dig, - uint16_t dig_len, uint8_t* sig, uint16_t sig_len); + uint16_t dig_len, uint8_t* sig); diff --git a/core/embed/sec/tropic/stm32/tropic01.c b/core/embed/sec/tropic/stm32/tropic01.c index 3687173c52d..d48c1c8cc0b 100644 --- a/core/embed/sec/tropic/stm32/tropic01.c +++ b/core/embed/sec/tropic/stm32/tropic01.c @@ -27,6 +27,8 @@ #include #include +#include "rand.h" + typedef struct { bool initialized; SPI_HandleTypeDef spi; @@ -183,13 +185,9 @@ lt_ret_t lt_port_delay(lt_handle_t *h, uint32_t ms) { return LT_OK; } -lt_ret_t lt_port_random_bytes(uint32_t *buff, uint16_t len) { - while (len > 0) { - uint32_t random = rng_get(); - *buff = random; - buff++; - len--; - } +lt_ret_t lt_port_random_bytes(lt_l2_state_t *s2, void *buff, size_t count) { + (void)s2; + random_buffer((uint8_t *)buff, count); return LT_OK; } diff --git a/core/embed/sec/tropic/tropic.c b/core/embed/sec/tropic/tropic.c index 68c86c7750f..33ad962dab6 100644 --- a/core/embed/sec/tropic/tropic.c +++ b/core/embed/sec/tropic/tropic.c @@ -137,7 +137,7 @@ bool tropic_ecc_key_generate(uint16_t slot_index) { } bool tropic_ecc_sign(uint16_t key_slot_index, const uint8_t *dig, - uint16_t dig_len, uint8_t *sig, uint16_t sig_len) { + uint16_t dig_len, uint8_t *sig) { tropic_driver_t *drv = &g_tropic_driver; if (!drv->initialized) { @@ -151,7 +151,7 @@ bool tropic_ecc_sign(uint16_t key_slot_index, const uint8_t *dig, lt_ret_t res = lt_ecc_eddsa_sign(&drv->handle, key_slot_index, dig, dig_len, sig); if (res != LT_OK) { - memzero(sig, sig_len); + memzero(sig, ECDSA_RAW_SIGNATURE_SIZE); return false; } diff --git a/core/embed/sys/smcall/stm32/smcall_dispatch.c b/core/embed/sys/smcall/stm32/smcall_dispatch.c index 8ed9ecd299f..740d087a348 100644 --- a/core/embed/sys/smcall/stm32/smcall_dispatch.c +++ b/core/embed/sys/smcall/stm32/smcall_dispatch.c @@ -332,9 +332,7 @@ __attribute((no_stack_protector)) void smcall_handler(uint32_t *args, const uint8_t *dig = (const uint8_t *)args[1]; uint16_t dig_len = (uint16_t)args[2]; uint8_t *sig = (uint8_t *)args[3]; - uint16_t sig_len = (uint16_t)args[4]; - args[0] = - tropic_ecc_sign__verified(key_slot_index, dig, dig_len, sig, sig_len); + args[0] = tropic_ecc_sign__verified(key_slot_index, dig, dig_len, sig); } break; #endif diff --git a/core/embed/sys/smcall/stm32/smcall_stubs.c b/core/embed/sys/smcall/stm32/smcall_stubs.c index d65f60fdf22..28bdc16748d 100644 --- a/core/embed/sys/smcall/stm32/smcall_stubs.c +++ b/core/embed/sys/smcall/stm32/smcall_stubs.c @@ -329,9 +329,9 @@ bool tropic_ecc_key_generate(uint16_t slot_index) { } bool tropic_ecc_sign(uint16_t key_slot_index, const uint8_t *dig, - uint16_t dig_len, uint8_t *sig, uint16_t sig_len) { - return (bool)smcall_invoke5((uint32_t)key_slot_index, (uint32_t)dig, dig_len, - (uint32_t)sig, sig_len, SMCALL_TROPIC_ECC_SIGN); + uint16_t dig_len, uint8_t *sig) { + return (bool)smcall_invoke4((uint32_t)key_slot_index, (uint32_t)dig, dig_len, + (uint32_t)sig, SMCALL_TROPIC_ECC_SIGN); } #endif diff --git a/core/embed/sys/smcall/stm32/smcall_verifiers.c b/core/embed/sys/smcall/stm32/smcall_verifiers.c index ee94f13f9f8..ff1fc96de34 100644 --- a/core/embed/sys/smcall/stm32/smcall_verifiers.c +++ b/core/embed/sys/smcall/stm32/smcall_verifiers.c @@ -406,6 +406,7 @@ secbool firmware_get_vendor__verified(char *buff, size_t buff_size) { #ifdef USE_TROPIC #include +#include "ecdsa.h" bool tropic_ping__verified(const uint8_t *msg_out, uint8_t *msg_in, uint16_t msg_len) { @@ -428,17 +429,16 @@ bool tropic_ecc_key_generate__verified(uint16_t slot_index) { } bool tropic_ecc_sign__verified(uint16_t key_slot_index, const uint8_t *dig, - uint16_t dig_len, uint8_t *sig, - uint16_t sig_len) { + uint16_t dig_len, uint8_t *sig) { if (!probe_read_access(dig, dig_len)) { goto access_violation; } - if (!probe_write_access(sig, sig_len)) { + if (!probe_write_access(sig, ECDSA_RAW_SIGNATURE_SIZE)) { goto access_violation; } - return tropic_ecc_sign(key_slot_index, dig, dig_len, sig, sig_len); + return tropic_ecc_sign(key_slot_index, dig, dig_len, sig); access_violation: apptask_access_violation(); return false; diff --git a/core/embed/sys/smcall/stm32/smcall_verifiers.h b/core/embed/sys/smcall/stm32/smcall_verifiers.h index e23b3958188..a3f62723b27 100644 --- a/core/embed/sys/smcall/stm32/smcall_verifiers.h +++ b/core/embed/sys/smcall/stm32/smcall_verifiers.h @@ -116,8 +116,7 @@ bool tropic_ping__verified(const uint8_t *msg_out, uint8_t *msg_in, bool tropic_ecc_key_generate__verified(uint16_t slot_index); bool tropic_ecc_sign__verified(uint16_t key_slot_index, const uint8_t *dig, - uint16_t dig_len, uint8_t *sig, - uint16_t sig_len); + uint16_t dig_len, uint8_t *sig); #endif diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c index b4ff882af82..e93cf8572a8 100644 --- a/core/embed/sys/syscall/stm32/syscall_dispatch.c +++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c @@ -873,9 +873,7 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args, const uint8_t *dig = (const uint8_t *)args[1]; uint16_t dig_len = (uint16_t)args[2]; uint8_t *sig = (uint8_t *)args[3]; - uint16_t sig_len = (uint16_t)args[4]; - args[0] = - tropic_ecc_sign__verified(key_slot_index, dig, dig_len, sig, sig_len); + args[0] = tropic_ecc_sign__verified(key_slot_index, dig, dig_len, sig); } break; #endif diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c index 6d50066271a..12e2150ebe6 100644 --- a/core/embed/sys/syscall/stm32/syscall_stubs.c +++ b/core/embed/sys/syscall/stm32/syscall_stubs.c @@ -842,9 +842,9 @@ bool tropic_ecc_key_generate(uint16_t slot_index) { } bool tropic_ecc_sign(uint16_t key_slot_index, const uint8_t *dig, - uint16_t dig_len, uint8_t *sig, uint16_t sig_len) { - return (bool)syscall_invoke5((uint32_t)key_slot_index, (uint32_t)dig, dig_len, - (uint32_t)sig, sig_len, SYSCALL_TROPIC_ECC_SIGN); + uint16_t dig_len, uint8_t *sig) { + return (bool)syscall_invoke4((uint32_t)key_slot_index, (uint32_t)dig, dig_len, + (uint32_t)sig, SYSCALL_TROPIC_ECC_SIGN); } #endif diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.c b/core/embed/sys/syscall/stm32/syscall_verifiers.c index d17d1ea7499..b2802dd3edf 100644 --- a/core/embed/sys/syscall/stm32/syscall_verifiers.c +++ b/core/embed/sys/syscall/stm32/syscall_verifiers.c @@ -1213,6 +1213,7 @@ bool button_get_event__verified(button_event_t *event) { #ifdef USE_TROPIC #include +#include "ecdsa.h" bool tropic_ping__verified(const uint8_t *msg_out, uint8_t *msg_in, uint16_t msg_len) { @@ -1235,17 +1236,16 @@ bool tropic_ecc_key_generate__verified(uint16_t slot_index) { } bool tropic_ecc_sign__verified(uint16_t key_slot_index, const uint8_t *dig, - uint16_t dig_len, uint8_t *sig, - uint16_t sig_len) { + uint16_t dig_len, uint8_t *sig) { if (!probe_read_access(dig, dig_len)) { goto access_violation; } - if (!probe_write_access(sig, sig_len)) { + if (!probe_write_access(sig, ECDSA_RAW_SIGNATURE_SIZE)) { goto access_violation; } - return tropic_ecc_sign(key_slot_index, dig, dig_len, sig, sig_len); + return tropic_ecc_sign(key_slot_index, dig, dig_len, sig); access_violation: apptask_access_violation(); return false; diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h index 2ef25363a7f..a2a62177890 100644 --- a/core/embed/sys/syscall/stm32/syscall_verifiers.h +++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h @@ -298,8 +298,7 @@ bool tropic_ping__verified(const uint8_t *msg_out, uint8_t *msg_in, bool tropic_ecc_key_generate__verified(uint16_t slot_index); bool tropic_ecc_sign__verified(uint16_t key_slot_index, const uint8_t *dig, - uint16_t dig_len, uint8_t *sig, - uint16_t sig_len); + uint16_t dig_len, uint8_t *sig); #endif diff --git a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-tropic.h b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-tropic.h index 702a0c34bd2..aa4ec05e62b 100644 --- a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-tropic.h +++ b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-tropic.h @@ -30,7 +30,6 @@ MP_DEFINE_EXCEPTION(TropicError, Exception) #define PING_MSG_MAX_LEN 64 #define ECC_SLOT_COUNT 32 -#define SIG_SIZE 64 #define CERT_SIZE 512 @@ -102,17 +101,17 @@ STATIC mp_obj_t mod_trezorcrypto_tropic_sign(mp_obj_t key_index, } vstr_t sig = {0}; - vstr_init_len(&sig, SIG_SIZE); + vstr_init_len(&sig, ECDSA_RAW_SIGNATURE_SIZE); bool ret = tropic_ecc_sign(idx, (const uint8_t *)dig.buf, dig.len, - ((uint8_t *)sig.buf), SIG_SIZE); + ((uint8_t *)sig.buf)); if (!ret) { vstr_clear(&sig); mp_raise_msg(&mp_type_TropicError, MP_ERROR_TEXT("lt_ecc_eddsa_sign failed.")); } - sig.len = SIG_SIZE; + sig.len = ECDSA_RAW_SIGNATURE_SIZE; return mp_obj_new_str_from_vstr(&mp_type_bytes, &sig); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_tropic_sign_obj, From d03974f065ec0e5992cd0058976262302c94c1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vejpustek?= Date: Thu, 24 Jul 2025 15:34:09 +0200 Subject: [PATCH 3/4] chore(core): update ts-tvl to 2.2 [no changelog] --- .github/workflows/core.yml | 2 +- vendor/ts-tvl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index ec9ac7a0160..77ead86e220 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -206,7 +206,7 @@ jobs: submodules: recursive - uses: ./.github/actions/environment - run: nix-shell --run "poetry run make -C core build_unix" - - run: nix-shell --arg fullDeps true --run "cd vendor/ts-tvl/tvl/server/model_config && poetry env use 3.12 && poetry install && poetry run model_server tcp -c model_config.yml &" + - run: nix-shell --arg fullDeps true --run "cd vendor/ts-tvl/model_configs/example_config && poetry env use 3.12 && poetry install && poetry run model_server tcp -c example_config.yml &" - run: nix-shell --run "poetry run make -C core test" - run: nix-shell --run "poetry run make -C core test_emu_sanity" # sanity check non-frozen emulator diff --git a/vendor/ts-tvl b/vendor/ts-tvl index 40e6d24f921..55a06f5e20d 160000 --- a/vendor/ts-tvl +++ b/vendor/ts-tvl @@ -1 +1 @@ -Subproject commit 40e6d24f92144d3b0f4981f76fa7adf735d2da01 +Subproject commit 55a06f5e20deb6e5305cb7fa78abb720db30a936 From 3d04afea6f55e199ea61f26b74503393d20c33d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vejpustek?= Date: Thu, 24 Jul 2025 14:50:13 +0200 Subject: [PATCH 4/4] feat(core/prodtest): implement tropic prodtest --- .../projects/prodtest/.changelog.d/5525.added | 1 + core/embed/projects/prodtest/README.md | 182 ++- core/embed/projects/prodtest/cmd/common.c | 3 + .../projects/prodtest/cmd/prodtest_secrets.c | 119 ++ .../prodtest/cmd/prodtest_secure_channel.c | 6 +- .../projects/prodtest/cmd/prodtest_tropic.c | 1375 +++++++++++++++-- .../projects/prodtest/cmd/secure_channel.c | 27 +- .../projects/prodtest/cmd/secure_channel.h | 2 - core/embed/sec/secret/inc/sec/secret_keys.h | 6 +- core/embed/sec/secret/stm32u5/secret_keys.c | 2 +- core/embed/sec/secret/unix/secret_keys.c | 5 + 11 files changed, 1594 insertions(+), 134 deletions(-) create mode 100644 core/embed/projects/prodtest/.changelog.d/5525.added diff --git a/core/embed/projects/prodtest/.changelog.d/5525.added b/core/embed/projects/prodtest/.changelog.d/5525.added new file mode 100644 index 00000000000..3475cc93125 --- /dev/null +++ b/core/embed/projects/prodtest/.changelog.d/5525.added @@ -0,0 +1 @@ +Add Tropic provisioning commands. diff --git a/core/embed/projects/prodtest/README.md b/core/embed/projects/prodtest/README.md index 9a0becf5177..c2260d15839 100644 --- a/core/embed/projects/prodtest/README.md +++ b/core/embed/projects/prodtest/README.md @@ -666,6 +666,33 @@ Lock successful OK ``` +### secrets-get-mcu-device-key +Returns a cryptogram that encrypts and authenticates the device attestation public key stored in MCU. The commands `secrets-init` and `secure-channel-handshake-2` must be executed before calling this command. + +Example: +``` +secrets-get-mcu-device-key +OK 638c8a83ddc8fd84cddf5a0a4fa3d9615146cd341685dca942bab1132c2bc99b +``` + +### secrets-certdev-write +Writes the X.509 device attestation certificate issued by the Trezor Company for the attestation key stored in MCU. + +Example: +``` +secrets-certdev-write +OK +``` + +### secrets-certdev-read +Retrieves the X.509 device attestation certificate issued by the Trezor Company for the attestation key stored in MCU. + +Example: +``` +secrets-certdev-read +OK +``` + ### optiga-pair Writes the pairing secret to the Optiga chip to pair it with the MCU. The command `secrets-init` must be executed before calling this command. @@ -693,17 +720,17 @@ optiga-certinf-read OK ``` -### optiga-certinf-write -Writes the X.509 certificate issued by the Trezor Company for the device. +### optiga-certdev-write +Writes the X.509 certificate issued by the Trezor Company for the device attestation key stored in Optiga. Example: ``` -optiga-certinf-write +optiga-certdev-write OK ``` -### optiga-certdev-red -Retrieves the X.509 certificate issued by the Trezor Company for the device. +### optiga-certdev-read +Retrieves the X.509 certificate issued by the Trezor Company for the device attestation key stored in Optiga. Example: ``` @@ -712,7 +739,7 @@ OK ``` ### optiga-certfido-write -Writes the X.509 certificate issued by the Trezor Company for the FIDO attestation key. +Writes the X.509 certificate issued by the Trezor Company for the FIDO attestation key stored in Optiga. Example: ``` @@ -721,7 +748,7 @@ OK ``` ### optiga-certfido-read -Retrieves the X.509 certificate issued by the Trezor Company for the FIDO attestation key. +Retrieves the X.509 certificate issued by the Trezor Company for the FIDO attestation key stored in Optiga. Example: ``` @@ -986,6 +1013,144 @@ tropic-update-fw OK ``` +### tropic-certtropic-read + +Reads the X.509 certificate issued by Tropic Square for the Tropic chip. + +Example: +``` +tropic-certtropic-read +OK 308201CB30820151A00302010202100200110308861906100F32000000045B300A06082A8648CE3D0403033047310B300906035504061302435A311D301B060355040A0C1454726F7069632053717561726520732E722E6F2E3119301706035504030C1054524F50494330312D54204341207631301E170D3235303730313130353533325A170D3435303730313130353533325A30173115301306035504030C0C54524F504943303120655345302A300506032B656E032100F582E78C4ECCB186D72A29B6B54E8CAD931C765DBA0C3EDE9405602CB1065246A37E307C300C0603551D130101FF04023000300E0603551D0F0101FF040403020308301F0603551D2304183016801433C711060CE80513B5677B019650644E3B43FAE7303B0603551D1F043430323030A02EA02C862A687474703A2F2F706B692E74726F7069637371756172652E636F6D2F6C332F7430312D5476312E63726C300A06082A8648CE3D0403030368003065023100C46E44F9D1FE26A4DC8AC659D1B6A9A82CEEBE9D283726633053FE410FF665073B7FB6ECE235FD8AB7F87336DDBCF96202300F919622C0A1CF6D00CF43CE4229AC44548055030566E8E03CA98B15D6B29B04DF231B9BF7006A9E28C15E88B07141893082025E308201E4A00302010202027531300A06082A8648CE3D0403033045310B300906035504061302435A311D301B060355040A0C1454726F7069632053717561726520732E722E6F2E3117301506035504030C0E54524F50494330312043412076313020170D3235303333313132303833305A180F32303630303333313132303833305A3047310B300906035504061302435A311D301B060355040A0C1454726F7069632053717561726520732E722E6F2E3119301706035504030C1054524F50494330312D542043412076313076301006072A8648CE3D020106052B8104002203620004A70C3273AE3227DC767EF0293D95CC106691E5BC9AA6C0282BAA8FD4B37CFAC30FEE0D879C32D8D9CE9B0BD7924B5C10097B8C4A5E7ED68D690185E3D128161256C01033C0293DE39A7188A72CFF9EEAF5B3B5DEE898F954C4F226C2ADE70BC6A381A230819F301D0603551D0E0416041433C711060CE80513B5677B019650644E3B43FAE730120603551D130101FF040830060101FF020100300E0603551D0F0101FF040403020106301F0603551D2304183016801443BAB7BDA7CDE728945CF142CBD2F9CD5588A93F30390603551D1F04323030302EA02CA02A8628687474703A2F2F706B692E74726F7069637371756172652E636F6D2F6C322F74303176312E63726C300A06082A8648CE3D0403030368003065023014AEC525E5E8311B5D6312CF0EBB2286700552EEBA32D641672C20F02A612B77E9FC3709C9657CEC6D82D6CDBDDE57C4023100BB9B77CCBBD9DE11086481D4BA9772C38743591E722B9E4D08A89940D879DA2447A55C15F84175C479946326E0F482AF3082028B308201ECA00302010202020BB9300A06082A8648CE3D040304304F310B300906035504061302435A311D301B060355040A0C1454726F7069632053717561726520732E722E6F2E3121301F06035504030C1854726F7069632053717561726520526F6F742043412076313020170D3235303333313132303832395A180F32303635303333313132303832395A3045310B300906035504061302435A311D301B060355040A0C1454726F7069632053717561726520732E722E6F2E3117301506035504030C0E54524F50494330312043412076313076301006072A8648CE3D020106052B81040022036200042301BE5B6ED9A858153F57C6BEBC9F37B858BC2874DDC90C1041BE6D04E7BBF24A7968F2E51173D0ACAC892E65E4FC03EA5BC4381A60154D7CD7CC6DF94591650F5FDC008919157314FC1F8D8295F1A10571DD1573E868BFECA96C92CCBB816FA381A230819F301D0603551D0E0416041443BAB7BDA7CDE728945CF142CBD2F9CD5588A93F30120603551D130101FF040830060101FF020101300E0603551D0F0101FF040403020106301F0603551D230418301680143C18AF711A6699B37914E363963FE25CF304B3BF30390603551D1F04323030302EA02CA02A8628687474703A2F2F706B692E74726F7069637371756172652E636F6D2F6C312F74737276312E63726C300A06082A8648CE3D04030403818C00308188024200BCD02D464329F3FC7DC81723B0C26437E35C2B49782BAE97432789F508B5A220240E6E3E4D12C15C3BDB15A8D3F90CDD19071E2227C4898220B2BEF584B2C20F8F024201EB854F05F9A2C5B466D798FE627C539B98703531735F7AB49546FE5CFB9DF0BF3B6985D700EFBC36DF3FF01692F0ECE98BB8DB2FBB9BF40913EA87EA121A7AD2E730820258308201BBA0030201020202012D300A06082A8648CE3D040304304F310B300906035504061302435A311D301B060355040A0C1454726F7069632053717561726520732E722E6F2E3121301F06035504030C1854726F7069632053717561726520526F6F742043412076313020170D3235303333313132303832355A180F32303735303333313132303832355A304F310B300906035504061302435A311D301B060355040A0C1454726F7069632053717561726520732E722E6F2E3121301F06035504030C1854726F7069632053717561726520526F6F7420434120763130819B301006072A8648CE3D020106052B8104002303818600040187CCEA62837E23092D8A7135789FCC6FBC3D35E79FC01F4F498FC5C2C409CE772F901340090403E8BA4D97E13F1E7594AC6D2F51FD2239F8D457769F378440A18000712BF16A48EA2025837BEFD0502A562FD93941D52CC40ED9553CA79B145BA585F32492BFD792EB96D949D31676CD099F19CE8848697B8C3430AF016FED985E1EB4A3423040301D0603551D0E041604143C18AF711A6699B37914E363963FE25CF304B3BF300F0603551D130101FF040530030101FF300E0603551D0F0101FF040403020106300A06082A8648CE3D04030403818A0030818602416841837339337C182A4EE896CBFD5DA5925F0026E7A6FA3DEE61F49A46B5D9856858D3D86501BE64B0F2F33B05D856DE96F57B947F49E720E875090B30C337791802412FDDB68D166510451FE4C62DBAE0CCD952DC34E03AE6617818CCD0EA28A9DFF045AA13A248A5F066B51139C9BEF471DD004DAC4F78DB56CF7B3E8D6F8F87D048D2 +``` + +### tropic-lock-check + +Returns 'YES' if the Tropic chip has been locked, otherwise returns 'NO'. + +Example: +``` +tropic-lock-check +OK YES +``` + +### tropic-pair + +Pairs the MCU with the Tropic chip. This command is idempotent, meaning it can be called multiple times without changing the state of the device. This command is irreversible and cannot be undone. The command `secrets-init` must be executed before calling this command. + +Example: +``` +tropic-pair +OK +``` + +### tropic-get-access-credential + +Returns a cryptogram that encrypts and authenticates the Tropic pairing private key and authenticates the Tropic public key. The commands `secrets-init` and `secure-channel-handshake-2` must be executed before calling this command. + +Example: +``` +tropic-get-access-credential +OK 03ca0e9d74ef59fa80a06161f3d2fceeb3e0c5e2db8182526d337aac78bad2d2ce4cacf05cdcd879843bcc43ed330199 +``` + +### tropic-get-fido-masking-key + +Returns a cryptogram that encrypts and authenticates the FIDO masking key for the Tropic chip. The commands `secrets-init` and `secure-channel-handshake-2` must be executed before calling this command. + +Example: +``` +tropic-get-fido-masking-key +OK dc106118a32feeef8d9211f54b9c8e9d571abe4cb104dc4ab087531cfee4574283ccf9c6f45e68be712f630d72d4999c +``` + +### tropic-handshake + +Establishes a secure channel with the Tropic chip. Expects a handshake request as input, returns a handshake response. + +``` +tropic-handshake 648724356a6bb22b258557927287af52133a27b7317d3c919db23395cae03d853422af +OK 09ad6ec70806318313c903094ae8fb63698051210dfa540ea7c7f7e588601dac478eee30432063964574879dee93250d8a5049 +``` + +### tropic-send-command + +Sends a command to the Tropic chip and returns the response. The command `tropic-handshake` must be executed before calling this command. + +Example: +``` +tropic-send-command +OK +``` + +### tropic-certdev-read + +Retrieves the X.509 certificate issued by the Trezor Company for the device attestation key stored in Tropic. + +Example: +``` +tropic-certdev-read +OK +``` + +### tropic-certdev-write + +Writes the X.509 certificate issued by the Trezor Company for the device attestation key stored in Tropic. + +Example: +``` +tropic-certdev-write +OK +``` + +### tropic-certfido-read + +Retrieves the X.509 certificate issued by the Trezor Company for the FIDO attestation key stored in Tropic. + +Example: +``` +tropic-certfido-read +OK +``` + +### tropic-certfido-write + +Writes the X.509 certificate issued by the Trezor Company for the FIDO attestation key stored in Tropic. + +Example: +``` +tropic-certfido-write +OK +``` + +### tropic-lock + +Configures the Tropic chip. This command is idempotent, meaning it can be called multiple times without changing the state of the device. This command is irreversible and cannot be undone. The command `tropic-pair` must be executed before calling this command. + +Example: +``` +tropic-lock +OK +``` + +### secure-channel-handshake-1 + +Returns the first handshake message for establishing a secure channel between the device and HSM. + +Example: +``` +secure-channel-handshake-1 +OK 1e85285cbf805d0418be1f502a325806f68fa07c78fd63b7b960b2d0416f8b49 +``` + +### secure-channel-handshake-2 + +Establishes a secure channel between the device and HSM. Expects the second handshake message as input. The command `secure-channel-handshake-1` must be executed before calling this command. + +Example: +``` +secure-channel-handshake-2 e08e84b91413ad8f7b07853c8ce4c1b5547a12d9dd65f30e3adaa1e2398e0359bd7ba0e9fb2c64130c25d56abb811f72 +OK +``` ### wpc-info Retrieves detailed information from the wireless power receiver, including chip identification, firmware version, configuration settings, and error status. @@ -1127,6 +1292,3 @@ Example: rtc-get OK 2025 07 03 14 23 00 4 ``` - - - diff --git a/core/embed/projects/prodtest/cmd/common.c b/core/embed/projects/prodtest/cmd/common.c index fcab7e14e8b..4d0186f5735 100644 --- a/core/embed/projects/prodtest/cmd/common.c +++ b/core/embed/projects/prodtest/cmd/common.c @@ -73,6 +73,9 @@ static const uint8_t SUBJECT_COMMON_NAME[] = { #ifdef TREZOR_MODEL_T3T1 'T', '3', 'T', '1', ' ', 'T', 'r', 'e', 'z', 'o', 'r', ' ', 'S', 'a', 'f', 'e', ' ', '5', #endif +#ifdef TREZOR_MODEL_T3W1 + 'T', '3', 'W', '1', ' ', 'T', 'r', 'e', 'z', 'o', 'r', ' ', 'S', 'a', 'f', 'e', ' ', '7', +#endif }; // clang-format on diff --git a/core/embed/projects/prodtest/cmd/prodtest_secrets.c b/core/embed/projects/prodtest/cmd/prodtest_secrets.c index 0dc4fe1ac85..176353cd501 100644 --- a/core/embed/projects/prodtest/cmd/prodtest_secrets.c +++ b/core/embed/projects/prodtest/cmd/prodtest_secrets.c @@ -31,6 +31,11 @@ #include "memzero.h" #include "rand.h" #include "secbool.h" +#include "secure_channel.h" + +#ifndef TREZOR_EMULATOR +#include +#endif secbool generate_random_secret(uint8_t* secret, size_t length) { random_buffer(secret, length); @@ -128,6 +133,97 @@ static void prodtest_secrets_init(cli_t* cli) { cli_ok(cli, ""); } +#ifdef SECRET_MASTER_KEY_SLOT_SIZE +static void prodtest_secrets_get_mcu_device_key(cli_t* cli) { + if (cli_arg_count(cli) > 0) { + cli_error_arg_count(cli); + return; + } + + ed25519_secret_key mcu_private = {0}; + if (secret_key_mcu_device_auth(mcu_private) != sectrue) { + cli_error(cli, CLI_ERROR, "`secret_key_mcu_device_auth()` failed."); + return; + } + ed25519_public_key mcu_public = {0}; + ed25519_publickey(mcu_private, mcu_public); + + uint8_t output[sizeof(ed25519_public_key) + NOISE_TAG_SIZE] = {0}; + if (!secure_channel_encrypt(mcu_public, sizeof(mcu_public), NULL, 0, + output)) { + // `secure_channel_handshake_2()` might not have been called + cli_error(cli, CLI_ERROR, "`secure_channel_encrypt()` failed."); + goto cleanup; + } + + cli_ok_hexdata(cli, output, sizeof(output)); + +cleanup: + memzero(mcu_private, sizeof(mcu_private)); +} + +static void prodtest_secrets_certdev_write(cli_t* cli) { + if (cli_arg_count(cli) > 0) { + cli_error_arg_count(cli); + return; + } + +#ifdef TREZOR_EMULATOR + cli_error(cli, CLI_ERROR, "Not implemented"); +#else + const size_t prefix_length = 2; + size_t certificate_length = 0; + uint8_t prefixed_certificate[SECRET_MCU_DEVICE_CERT_SIZE] = {0}; + if (!cli_arg_hex(cli, "hex-data", prefixed_certificate + prefix_length, + sizeof(prefixed_certificate) - prefix_length, + &certificate_length)) { + if (certificate_length == sizeof(prefixed_certificate) - prefix_length) { + cli_error(cli, CLI_ERROR, "Certificate too long."); + } else { + cli_error(cli, CLI_ERROR, "Hexadecimal decoding error."); + } + return; + } + prefixed_certificate[0] = (certificate_length >> 8) & 0xFF; + prefixed_certificate[1] = certificate_length & 0xFF; + + secret_write(prefixed_certificate, SECRET_MCU_DEVICE_CERT_OFFSET, + sizeof(prefixed_certificate)); + + cli_ok(cli, ""); +#endif +} + +static void prodtest_secrets_certdev_read(cli_t* cli) { + if (cli_arg_count(cli) > 0) { + cli_error_arg_count(cli); + return; + } + +#ifdef TREZOR_EMULATOR + cli_error(cli, CLI_ERROR, "Not implemented"); +#else + const size_t prefix_length = 2; + uint8_t prefixed_certificate[SECRET_MCU_DEVICE_CERT_SIZE] = {0}; + + if (secret_read(prefixed_certificate, SECRET_MCU_DEVICE_CERT_OFFSET, + sizeof(prefixed_certificate)) != sectrue) { + cli_error(cli, CLI_ERROR, "`secret_read()` failed."); + return; + } + + size_t certificate_length = + prefixed_certificate[0] << 8 | prefixed_certificate[1]; + + if (certificate_length > sizeof(prefixed_certificate) - prefix_length) { + cli_error(cli, CLI_ERROR, "Invalid certificate data."); + return; + } + cli_ok_hexdata(cli, prefixed_certificate + prefix_length, certificate_length); +#endif +} +#endif + #ifdef SECRET_LOCK_SLOT_OFFSET static void prodtest_secrets_lock(cli_t* cli) { if (cli_arg_count(cli) > 0) { @@ -160,6 +256,29 @@ PRODTEST_CLI_CMD( .args = "" ); +#ifdef SECRET_MASTER_KEY_SLOT_SIZE +PRODTEST_CLI_CMD( + .name = "secrets-get-mcu-device-key", + .func = prodtest_secrets_get_mcu_device_key, + .info = "Get MCU device attestation public key", + .args = "" +); + +PRODTEST_CLI_CMD( + .name = "secrets-certdev-write", + .func = prodtest_secrets_certdev_write, + .info = "Write the device's X.509 certificate to flash", + .args = "" +); + +PRODTEST_CLI_CMD( + .name = "secrets-certdev-read", + .func = prodtest_secrets_certdev_read, + .info = "Read the device's X.509 certificate from flash", + .args = "" +); +#endif + #ifdef SECRET_LOCK_SLOT_OFFSET PRODTEST_CLI_CMD( .name = "secrets-lock", diff --git a/core/embed/projects/prodtest/cmd/prodtest_secure_channel.c b/core/embed/projects/prodtest/cmd/prodtest_secure_channel.c index b5d60ce8d6f..b24dc32b762 100644 --- a/core/embed/projects/prodtest/cmd/prodtest_secure_channel.c +++ b/core/embed/projects/prodtest/cmd/prodtest_secure_channel.c @@ -59,9 +59,9 @@ static void prodtest_secure_channel_handshake_2(cli_t* cli) { } if (!secure_channel_handshake_2(input)) { - // TODO: Consider distinguishing between cryptography error and state error - cli_error(cli, CLI_ERROR, - "You have to call `secure-channel-handshake-1` first."); + // Either `secure_channel_handshake_1()` has not been called or the keys do + // not match. + cli_error(cli, CLI_ERROR, "`secure_channel_handshake_2()` failed."); } cli_ok(cli, ""); diff --git a/core/embed/projects/prodtest/cmd/prodtest_tropic.c b/core/embed/projects/prodtest/cmd/prodtest_tropic.c index d7c3833c16d..2a37c073f52 100644 --- a/core/embed/projects/prodtest/cmd/prodtest_tropic.c +++ b/core/embed/projects/prodtest/cmd/prodtest_tropic.c @@ -19,22 +19,43 @@ #ifdef USE_TROPIC +#include #include #include #include #include +#include #include #include "memzero.h" +#include "common.h" #include "fw_CPU.h" #include "fw_SPECT.h" #include "libtropic.h" +#include "lt_l2.h" #include "secure_channel.h" +#define FACTORY_PAIRING_KEY_SLOT \ + PAIRING_KEY_SLOT_INDEX_0 // This key is used by prodtest to inject the + // privileged and unprivileged pairing keys +#define UNPRIVILEGED_PAIRING_KEY_SLOT \ + PAIRING_KEY_SLOT_INDEX_1 // This key is used by HSM to inject the attestation + // FIDO key and generate the device key, and by an + // unofficial firwmare with an unlocked bootloader +#define PRIVILEGED_PAIRING_KEY_SLOT \ + PAIRING_KEY_SLOT_INDEX_2 // This key is used by an official firmware + +#define TROPIC_FIDO_CERT_FIRST_SLOT 0 +#define TROPIC_FIDO_CERT_SLOTS_COUNT 3 +#define TROPIC_DEV_CERT_FIRST_SLOT 3 +#define TROPIC_DEV_CERT_SLOTS_COUNT 3 +#define TROPIC_DEV_KEY_SLOT 0 +#define TROPIC_FIDO_KEY_SLOT 1 + typedef enum { TROPIC_HANDSHAKE_STATE_0, // Handshake has not been initiated yet TROPIC_HANDSHAKE_STATE_1, // Handshake completed (after calling @@ -45,7 +66,449 @@ typedef enum { static tropic_handshake_state_t tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0; -static bool locked = false; +static bool tropic_is_paired = false; + +static uint8_t tropic_cert_chain[LT_NUM_CERTIFICATES * + LT_L2_GET_INFO_REQ_CERT_SIZE_SINGLE] = {0}; +static size_t tropic_cert_chain_length = 0; +static curve25519_key tropic_public_cached = {0}; + +// TODO: Update this link to correspond with the latest chip revision when it +// becomes available. +// https://github.com/tropicsquare/tropic01/blob/da459d18db7aea107419035b9cdf316d89a73445/doc/api/tropic01_user_api_v1.1.2.pdf +// TODO: Adjust the configuration to match the revision of the provisioned +// tropics. +// clang-format off +static struct lt_config_t irreversible_configuration = { + .obj = { + // # CFG_START_UP (0x00) + // | Setting | Value | + // |-------------------------|-------------------------| + // | RFU_1 (bit 0) | 1 | + // | MBIST_DIS (bit 1) | 0 (TEST_ON) | + // | RNGTEST_DIS (bit 2) | 0 (TEST_ON) | + // | MAINTENANCE_ENA (bit 3) | 1 (MAINTENANCE_ALLOWED) | + ~0U & ~BIT(1) & ~BIT(2), + // # CFG_SENSORS (0x08) + // | Setting | Value | + // |---------------------------------|----------------------| + // | PTRNG0_TEST_DIS (bit 0) | 1 (NO_ACTION) | + // | PTRNG1_TEST_DIS (bit 1) | 1 (NO_ACTION) | + // | OSCILLATOR_MON_DIS (bit 2) | 1 (NO_ACTION) | + // | SHIELD_DIS (bit 3) | 1 (NO_ACTION) | + // | VOLTAGE_MON_DIS (bit 4) | 1 (NO_ACTION) | + // | GLITCH_DET_DIS (bit 5) | 1 (NO_ACTION) | + // | TEMP_SENS_DIS (bit 6) | 1 (NO_ACTION) | + // | LASER_DET_DIS (bit 7) | 1 (NO_ACTION) | + // | EM_PULSE_DET_DIS (bit 8) | 1 (NO_ACTION) | + // | CPU_ALERT_DIS (bit 9) | 1 (NO_ACTION) | + // | PIN_VERIF_BIT_FLIP_DIS (bit 10) | 1 (NO_ACTION) | + // | SCB_BIT_FLIP_DIS (bit 11) | 1 (NO_ACTION) | + // | CPB_BIT_FLIP_DIS (bit 12) | 1 (NO_ACTION) | + // | ECC_BIT_FLIP_DIS (bit 13) | 1 (NO_ACTION) | + // | R_MEM_BIT_FLIP_DIS (bit 14) | 1 (NO_ACTION) | + // | EKDB_BIT_FLIP_DIS (bit 15) | 1 (NO_ACTION) | + // | I_MEM_BIT_FLIP_DIS (bit 16) | 1 (NO_ACTION) | + // | PLATFORM_BIT_FLIP_DIS (bit 17) | 1 (NO_ACTION) | + ~0U, + // # CFG_DEBUG (0x10) + // | Setting | Value | + // |-------------------|-------| + // | FW_LOG_EN (bit 0) | 0 | + ~0U & ~BIT(0), + // # CFG_GPO (0x14) + ~0U, + // # CFG_SLEEP_MODE (0x18) + // | Setting | Value | + // |-----------------------|-------| + // | SLEEP_MODE_EN (bit 0) | 1 | + ~0U, + // # CFG_UAP_PAIRING_KEY_WRITE (0x20) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | WRITE_PKEY_SLOT_0 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | WRITE_PKEY_SLOT_1 | 0 (bit 8) | 0 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | WRITE_PKEY_SLOT_2 | 0 (bit 16) | 0 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | WRITE_PKEY_SLOT_3 | 0 (bit 24) | 0 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(1) & ~BIT(8) & ~BIT(9) & ~BIT(16) & ~BIT(17) & + ~BIT(24) & ~BIT(25), + // # CFG_UAP_PAIRING_KEY_READ (0x24) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | READ_PKEY_SLOT_0 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | READ_PKEY_SLOT_1 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | READ_PKEY_SLOT_2 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | READ_PKEY_SLOT_3 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_PAIRING_KEY_INVALIDATE (0x28) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | INVALIDATE_PKEY_SLOT_0 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | INVALIDATE_PKEY_SLOT_1 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | INVALIDATE_PKEY_SLOT_2 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | INVALIDATE_PKEY_SLOT_3 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_R_CONFIG_WRITE_ERASE (0x30) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | R_CONFIG_WRITE_ERASE | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 1 (bit 3) | + ~0U & ~BIT(0) & ~BIT(1), + // # CFG_UAP_R_CONFIG_READ (0x34) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | R_CONFIG_READ_CFG | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | R_CONFIG_READ_FUNC | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + ~0U & ~BIT(0) & ~BIT(8), + // # CFG_UAP_I_CONFIG_WRITE (0x40) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | I_CONFIG_WRITE_CFG | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | I_CONFIG_WRITE_FUNC | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + ~0U & ~BIT(0) & ~BIT(8), + // # CFG_UAP_I_CONFIG_READ (0x44) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | I_CONFIG_READ_CFG | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | I_CONFIG_READ_FUNC | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + ~0U & ~BIT(0) & ~BIT(8), + // # CFG_UAP_PING (0x100) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |---------|----------------|---------------|---------------|---------------| + // | PING | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + ~0U & ~BIT(0), + // # CFG_UAP_R_MEM_DATA_WRITE (0x110) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | WRITE_UDATA_SLOT_0_127 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | WRITE_UDATA_SLOT_128_255 | 0 (bit 8) | 0 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | WRITE_UDATA_SLOT_256_383 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | WRITE_UDATA_SLOT_384_511 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(1) & ~BIT(8) & ~BIT(9) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_R_MEM_DATA_READ (0x114) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | READ_UDATA_SLOT_0_127 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | READ_UDATA_SLOT_128_255 | 0 (bit 8) | 0 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | READ_UDATA_SLOT_256_383 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | READ_UDATA_SLOT_384_511 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(8) & ~BIT(9) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_R_MEM_DATA_ERASE (0x118) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | ERASE_UDATA_SLOT_0_127 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | ERASE_UDATA_SLOT_128_255 | 0 (bit 8) | 0 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | ERASE_UDATA_SLOT_256_383 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | ERASE_UDATA_SLOT_384_511 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(8) & ~BIT(9) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_RANDOM_VALUE_GET (0x120) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | RANDOM_VALUE_GET | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + ~0U & ~BIT(0), + // # CFG_UAP_ECC_KEY_GENERATE (0x130) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | GEN_ECCKEY_SLOT_0_7 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | GEN_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | GEN_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | GEN_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(1) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_ECC_KEY_STORE (0x134) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | STORE_ECCKEY_SLOT_0_7 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | STORE_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | STORE_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | STORE_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(1) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_ECC_KEY_READ (0x138) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | READ_ECCKEY_SLOT_0_7 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | READ_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | READ_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | READ_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_ECC_KEY_ERASE (0x13c) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | ERASE_ECCKEY_SLOT_0_7 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | ERASE_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | ERASE_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | ERASE_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_ECDSA_SIGN (0x140) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | ECDSA_ECCKEY_SLOT_0_7 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | ECDSA_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | ECDSA_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | ECDSA_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(1) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_EDDSA_SIGN (0x144) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | EDDSA_ECCKEY_SLOT_0_7 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | EDDSA_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | EDDSA_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | EDDSA_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(1) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_MCOUNTER_INIT (0x150) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | MCOUNTER_INIT_0_3 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | MCOUNTER_INIT_4_7 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | MCOUNTER_INIT_8_11 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | MCOUNTER_INIT_12_15 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(1) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_MCOUNTER_GET (0x154) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | MCOUNTER_GET_0_3 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | MCOUNTER_GET_4_7 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | MCOUNTER_GET_8_11 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | MCOUNTER_GET_12_15 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_MCOUNTER_UPDATE (0x158) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | MCOUNTER_UPDATE_0_3 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | MCOUNTER_UPDATE_4_7 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | MCOUNTER_UPDATE_8_11 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | MCOUNTER_UPDATE_12_15 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(8) & ~BIT(16) & ~BIT(24), + // # CFG_UAP_MAC_AND_DESTROY (0x160) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | MACANDD_0_31 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 1 (bit 3) | + // | MACANDD_32_63 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 1 (bit 11) | + // | MACANDD_64_95 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 1 (bit 19) | + // | MACANDD_96_127 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 1 (bit 27) | + ~0U & ~BIT(0) & ~BIT(8) & ~BIT(16) & ~BIT(24), + }}; + +// TODO: Adjust the configuration to match the revision of the provisioned +// tropics. +static struct lt_config_t reversible_configuration = { + .obj = { + // # CFG_START_UP (0x00) + // | Setting | Value | + // |-------------------------|-------------------------| + // | RFU_1 (bit 0) | 1 | + // | MBIST_DIS (bit 1) | 0 (TEST_ON) | + // | RNGTEST_DIS (bit 2) | 0 (TEST_ON) | + // | MAINTENANCE_ENA (bit 3) | 1 (MAINTENANCE_ALLOWED) | + BIT(0) | BIT(3), + // # CFG_SENSORS (0x08) + // | Setting | Value | + // |---------------------------------|----------------------| + // | PTRNG0_TEST_DIS (bit 0) | 0 (ENTER_ALARM_MODE) | + // | PTRNG1_TEST_DIS (bit 1) | 0 (ENTER_ALARM_MODE) | + // | OSCILLATOR_MON_DIS (bit 2) | 0 (ENTER_ALARM_MODE) | + // | SHIELD_DIS (bit 3) | 0 (ENTER_ALARM_MODE) | + // | VOLTAGE_MON_DIS (bit 4) | 0 (ENTER_ALARM_MODE) | + // | GLITCH_DET_DIS (bit 5) | 0 (ENTER_ALARM_MODE) | + // | TEMP_SENS_DIS (bit 6) | 0 (ENTER_ALARM_MODE) | + // | LASER_DET_DIS (bit 7) | 0 (ENTER_ALARM_MODE) | + // | EM_PULSE_DET_DIS (bit 8) | 0 (ENTER_ALARM_MODE) | + // | CPU_ALERT_DIS (bit 9) | 0 (ENTER_ALARM_MODE) | + // | PIN_VERIF_BIT_FLIP_DIS (bit 10) | 0 (ENTER_ALARM_MODE) | + // | SCB_BIT_FLIP_DIS (bit 11) | 0 (ENTER_ALARM_MODE) | + // | CPB_BIT_FLIP_DIS (bit 12) | 0 (ENTER_ALARM_MODE) | + // | ECC_BIT_FLIP_DIS (bit 13) | 0 (ENTER_ALARM_MODE) | + // | R_MEM_BIT_FLIP_DIS (bit 14) | 0 (ENTER_ALARM_MODE) | + // | EKDB_BIT_FLIP_DIS (bit 15) | 0 (ENTER_ALARM_MODE) | + // | I_MEM_BIT_FLIP_DIS (bit 16) | 0 (ENTER_ALARM_MODE) | + // | PLATFORM_BIT_FLIP_DIS (bit 17) | 0 (ENTER_ALARM_MODE) | + 0, + // # CFG_DEBUG (0x10) + // | Setting | Value | + // |-------------------|-------| + // | FW_LOG_EN (bit 0) | 0 | + 0, + // # CFG_GPO (0x14) + 0, + // # CFG_SLEEP_MODE (0x18) + // | Setting | Value | + // |-----------------------|-------| + // | SLEEP_MODE_EN (bit 0) | 1 | + BIT(0), + // # CFG_UAP_PAIRING_KEY_WRITE (0x20) + // | Target | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | WRITE_PKEY_SLOT_0 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | WRITE_PKEY_SLOT_1 | 0 (bit 8) | 0 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | WRITE_PKEY_SLOT_2 | 0 (bit 16) | 0 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | WRITE_PKEY_SLOT_3 | 0 (bit 24) | 0 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(2) | BIT(10) | BIT(18) | BIT(26), + // # CFG_UAP_PAIRING_KEY_READ (0x24) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | READ_PKEY_SLOT_0 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | READ_PKEY_SLOT_1 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | READ_PKEY_SLOT_2 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | READ_PKEY_SLOT_3 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(1) | BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | + BIT(26), + // # CFG_UAP_PAIRING_KEY_INVALIDATE (0x28) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | INVALIDATE_PKEY_SLOT_0 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | INVALIDATE_PKEY_SLOT_1 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | INVALIDATE_PKEY_SLOT_2 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | INVALIDATE_PKEY_SLOT_3 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(1) | BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | + BIT(26), + // # CFG_UAP_R_CONFIG_WRITE_ERASE (0x30) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | R_CONFIG_WRITE_ERASE | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 0 (bit 3) | + BIT(2), + // # CFG_UAP_R_CONFIG_READ (0x34) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | R_CONFIG_READ_CFG | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | R_CONFIG_READ_FUNC | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + BIT(1) | BIT(2) | BIT(9) | BIT(10), + // # CFG_UAP_I_CONFIG_WRITE (0x40) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | I_CONFIG_WRITE_CFG | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | I_CONFIG_WRITE_FUNC | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + BIT(1) | BIT(2) | BIT(9) | BIT(10), + // # CFG_UAP_I_CONFIG_READ (0x44) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | I_CONFIG_READ_CFG | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | I_CONFIG_READ_FUNC | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + BIT(1) | BIT(2) | BIT(9) | BIT(10), + // # CFG_UAP_PING (0x100) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |---------|----------------|---------------|---------------|---------------| + // | PING | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + BIT(1) | BIT(2), + // # CFG_UAP_R_MEM_DATA_WRITE (0x110) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | WRITE_UDATA_SLOT_0_127 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | WRITE_UDATA_SLOT_128_255 | 0 (bit 8) | 0 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | WRITE_UDATA_SLOT_256_383 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | WRITE_UDATA_SLOT_384_511 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(2) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | BIT(26), + // # CFG_UAP_R_MEM_DATA_READ (0x114) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | READ_UDATA_SLOT_0_127 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | READ_UDATA_SLOT_128_255 | 0 (bit 8) | 0 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | READ_UDATA_SLOT_256_383 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | READ_UDATA_SLOT_384_511 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(1) | BIT(2) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | BIT(26), + // # CFG_UAP_R_MEM_DATA_ERASE (0x118) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | ERASE_UDATA_SLOT_0_127 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | ERASE_UDATA_SLOT_128_255 | 0 (bit 8) | 0 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | ERASE_UDATA_SLOT_256_383 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | ERASE_UDATA_SLOT_384_511 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(1) | BIT(2) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | BIT(26), + // # CFG_UAP_RANDOM_VALUE_GET (0x120) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | RANDOM_VALUE_GET | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + BIT(1) | BIT(2), + // # CFG_UAP_ECC_KEY_GENERATE (0x130) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | GEN_ECCKEY_SLOT_0_7 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | GEN_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | GEN_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | GEN_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | BIT(26), + // # CFG_UAP_ECC_KEY_STORE (0x134) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | STORE_ECCKEY_SLOT_0_7 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | STORE_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | STORE_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | STORE_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | BIT(26), + // # CFG_UAP_ECC_KEY_READ (0x138) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | READ_ECCKEY_SLOT_0_7 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | READ_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | READ_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | READ_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(1) | BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | + BIT(26), + // # CFG_UAP_ECC_KEY_ERASE (0x13c) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | ERASE_ECCKEY_SLOT_0_7 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | ERASE_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | ERASE_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | ERASE_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(1) | BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | + BIT(26), + // # CFG_UAP_ECDSA_SIGN (0x140) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | ECDSA_ECCKEY_SLOT_0_7 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | ECDSA_ECCKEY_SLOT_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | ECDSA_ECCKEY_SLOT_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | ECDSA_ECCKEY_SLOT_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | BIT(26), + // # CFG_UAP_EDDSA_SIGN (0x144) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | EDDSA_ECCKEY_0_7 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | EDDSA_ECCKEY_8_15 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | EDDSA_ECCKEY_16_23 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | EDDSA_ECCKEY_24_31 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | BIT(26), + // # CFG_UAP_MCOUNTER_INIT (0x148) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | MCOUNTER_INIT_0_3 | 0 (bit 0) | 0 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | MCOUNTER_INIT_4_7 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | MCOUNTER_INIT_8_11 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | MCOUNTER_INIT_12_15 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | BIT(26), + // # CFG_UAP_MCOUNTER_GET (0x154) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | MCOUNTER_GET_0_3 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | MCOUNTER_GET_4_7 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | MCOUNTER_GET_8_11 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | MCOUNTER_GET_12_15 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(1) | BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | + BIT(26), + // # CFG_UAP_MCOUNTER_UPDATE (0x158) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | MCOUNTER_UPDATE_0_3 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | MCOUNTER_UPDATE_4_7 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | MCOUNTER_UPDATE_8_11 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | MCOUNTER_UPDATE_12_15 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(1) | BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | + BIT(26), + // # CFG_UAP_MAC_AND_DESTROY (0x160) + // | Setting | Pairing Key 0 | Pairing Key 1 | Pairing Key 2 | Pairing Key 3 | + // |--------------------------|---------------|---------------|---------------|---------------| + // | MACANDD_0_31 | 0 (bit 0) | 1 (bit 1) | 1 (bit 2) | 0 (bit 3) | + // | MACANDD_32_63 | 0 (bit 8) | 1 (bit 9) | 1 (bit 10) | 0 (bit 11) | + // | MACANDD_64_95 | 0 (bit 16) | 1 (bit 17) | 1 (bit 18) | 0 (bit 19) | + // | MACANDD_96_127 | 0 (bit 24) | 1 (bit 25) | 1 (bit 26) | 0 (bit 27) | + BIT(1) | BIT(2) | BIT(9) | BIT(10) | BIT(17) | BIT(18) | BIT(25) | + BIT(26), + }}; +// clang-format-on + +// TODO: Implement a function `lt_handle_t* start_session(const pkey_index_t +// pkey_index)`. This function will check whether a session with a specified +// pairing key is active. If a session is not active, it will retrieve the +// pairing key and initiate a session using it. The function will be used in +// this file and in `prodtest_secrets.c`. This is only an optimization. static void prodtest_tropic_get_riscv_fw_version(cli_t* cli) { if (cli_arg_count(cli) > 0) { @@ -53,10 +516,11 @@ static void prodtest_tropic_get_riscv_fw_version(cli_t* cli) { return; } - lt_handle_t* handle = tropic_get_handle(); + lt_handle_t* tropic_handle = tropic_get_handle(); uint8_t version[LT_L2_GET_INFO_RISCV_FW_SIZE] = {0}; - if (lt_get_info_riscv_fw_ver(handle, version, sizeof(version)) != LT_OK) { + if (lt_get_info_riscv_fw_ver(tropic_handle, version, sizeof(version)) != + LT_OK) { cli_error(cli, CLI_ERROR, "Unable to get RISCV FW version"); return; } @@ -71,10 +535,11 @@ static void prodtest_tropic_get_spect_fw_version(cli_t* cli) { return; } - lt_handle_t* handle = tropic_get_handle(); + lt_handle_t* tropic_handle = tropic_get_handle(); - uint8_t version[LT_L2_GET_INFO_SPECT_FW_SIZE] = {0}; - if (lt_get_info_spect_fw_ver(handle, version, sizeof(version)) != LT_OK) { + uint8_t version[LT_L2_GET_INFO_SPECT_FW_SIZE]; + if (lt_get_info_spect_fw_ver(tropic_handle, version, sizeof(version)) != + LT_OK) { cli_error(cli, CLI_ERROR, "Unable to get SPECT FW version"); return; } @@ -89,10 +554,10 @@ static void prodtest_tropic_get_chip_id(cli_t* cli) { return; } - lt_handle_t* handle = tropic_get_handle(); + lt_handle_t* tropic_handle = tropic_get_handle(); - lt_chip_id_t chip_id = {0}; - if (lt_get_info_chip_id(handle, &chip_id) != LT_OK) { + struct lt_chip_id_t chip_id; + if (lt_get_info_chip_id(tropic_handle, &chip_id) != LT_OK) { cli_error(cli, CLI_ERROR, "Unable to get CHIP ID"); return; } @@ -105,17 +570,55 @@ static void prodtest_tropic_get_chip_id(cli_t* cli) { cli_ok_hexdata(cli, &chip_id, sizeof(chip_id)); } +static bool cache_tropic_cert_chain(lt_handle_t* handle) { + if (tropic_cert_chain_length > 0) { + return true; + } + + struct lt_cert_store_t cert_store = {0}; + for (size_t i = 0; i < LT_NUM_CERTIFICATES; i++) { + cert_store.certs[i] = + &tropic_cert_chain[i * LT_L2_GET_INFO_REQ_CERT_SIZE_SINGLE]; + cert_store.buf_len[i] = LT_L2_GET_INFO_REQ_CERT_SIZE_SINGLE; + } + + lt_ret_t ret = LT_FAIL; + + ret = lt_get_info_cert_store(handle, &cert_store); + if (ret != LT_OK) { + return false; + } + + ret = lt_get_st_pub(&cert_store, tropic_public_cached, sizeof(tropic_public_cached)); + if (ret != LT_OK) { + return false; + } + + // Compactify tropic_cert_chain for future use. This invalidates the cert_store. + size_t length = 0; + for (size_t i = 0; i < LT_NUM_CERTIFICATES; i++) { + memmove(&tropic_cert_chain[length], cert_store.certs[i], + cert_store.cert_len[i]); + length += cert_store.cert_len[i]; + } + + tropic_cert_chain_length = length; + + return true; +} + static void prodtest_tropic_certtropic_read(cli_t* cli) { if (cli_arg_count(cli) > 0) { cli_error_arg_count(cli); return; } - static uint8_t certificate[3000] = {0}; - - // TODO: Implement properly + if (!cache_tropic_cert_chain(tropic_get_handle())) { + cli_error(cli, CLI_ERROR, "`cache_tropic_cert_chain()` failed"); + return; + } - cli_ok_hexdata(cli, certificate, sizeof(certificate)); + cli_ok_hexdata(cli, tropic_cert_chain, tropic_cert_chain_length); } static void prodtest_tropic_lock_check(cli_t* cli) { @@ -126,52 +629,329 @@ static void prodtest_tropic_lock_check(cli_t* cli) { tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0; - // TODO: Implement properly + lt_handle_t* tropic_handle = tropic_get_handle(); + lt_ret_t ret = LT_FAIL; + + curve25519_key tropic_public = {0}; + if (secret_key_tropic_public(tropic_public) != sectrue) { + cli_error(cli, CLI_ERROR, "`secret_key_tropic_public()` failed."); + goto cleanup; + } + + curve25519_key privileged_private = {0}; + if (secret_key_tropic_pairing_privileged(privileged_private) != sectrue) { + cli_error(cli, CLI_ERROR, + "`secret_key_tropic_pairing_privileged()` failed."); + goto cleanup; + } + curve25519_key privileged_public = {0}; + curve25519_scalarmult_basepoint(privileged_public, privileged_private); + + ret = + lt_session_start(tropic_handle, tropic_public, PRIVILEGED_PAIRING_KEY_SLOT, + privileged_private, privileged_public); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_session_start()` for privileged key failed with error %d", + ret); + goto cleanup; + } + + struct lt_config_t configuration_read = {0}; + + ret = lt_read_whole_R_config(tropic_handle, &configuration_read); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`lt_read_whole_R_config()` failed with error %d", + ret); + return; + } + + if (memcmp(&reversible_configuration, (uint8_t*)&configuration_read, + sizeof(reversible_configuration)) != 0) { + cli_ok(cli, "NO"); + return; + } + + ret = lt_read_whole_I_config(tropic_handle, &configuration_read); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`lt_read_whole_I_config()` failed with error %d", + ret); + return; + } - if (locked) { - cli_ok(cli, "YES"); - } else { + if (memcmp(&irreversible_configuration, (uint8_t*)&configuration_read, + sizeof(irreversible_configuration)) != 0) { cli_ok(cli, "NO"); + return; + } + + cli_ok(cli, "YES"); + +cleanup: + memzero(privileged_private, sizeof(privileged_private)); +} + +static lt_ret_t pairing_key_write(lt_handle_t* handle, pkey_index_t slot, + const ed25519_secret_key public_key) { + // If this function returns `LT_OK`, it is ensured that the pairing key + // `public_key` is written in the slot `slot`. + lt_ret_t ret = lt_pairing_key_write(handle, public_key, slot); + if (ret != LT_OK && ret != LT_L3_FAIL) { + return ret; + } + // If the pairing has already been written, `lt_pairing_key_write()` returns + // `LT_L3_FAIL`. + curve25519_key public_key_read = {0}; + ret = lt_pairing_key_read(handle, public_key_read, slot); + if (ret != LT_OK) { + return ret; + } + if (memcmp(public_key, public_key_read, sizeof(ed25519_public_key)) != 0) { + return LT_FAIL; } + + return LT_OK; +} + +static bool is_paired(cli_t* cli) { + lt_handle_t* tropic_handle = tropic_get_handle(); + lt_ret_t ret = LT_FAIL; + + // Retrieve the tropic public key. + curve25519_key tropic_public = {0}; + if (secret_key_tropic_public(tropic_public) != sectrue) { + cli_error(cli, CLI_ERROR, "`secret_key_tropic_public()` failed."); + goto cleanup; + } + + // Retrieve the unprivileged key pair and try to establish a session using it. + curve25519_key unprivileged_private = {0}; + if (secret_key_tropic_pairing_unprivileged(unprivileged_private) != sectrue) { + cli_error(cli, CLI_ERROR, + "`secret_key_tropic_pairing_unprivileged()` failed."); + goto cleanup; + } + curve25519_key unprivileged_public = {0}; + curve25519_scalarmult_basepoint(unprivileged_public, unprivileged_private); + ret = lt_session_start(tropic_handle, tropic_public, + UNPRIVILEGED_PAIRING_KEY_SLOT, unprivileged_private, + unprivileged_public); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_session_start()` for unprivileged key failed with error %d", + ret); + goto cleanup; + } + + // Retrieve the privileged key pair and try to establish a session using it. + curve25519_key privileged_private = {0}; + if (secret_key_tropic_pairing_privileged(privileged_private) != sectrue) { + cli_error(cli, CLI_ERROR, + "`secret_key_tropic_pairing_privileged()` failed."); + goto cleanup; + } + curve25519_key privileged_public = {0}; + curve25519_scalarmult_basepoint(privileged_public, privileged_private); + + ret = + lt_session_start(tropic_handle, tropic_public, PRIVILEGED_PAIRING_KEY_SLOT, + privileged_private, privileged_public); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_session_start()` for privileged key failed with error %d", + ret); + goto cleanup; + } + + // Read the factory pairing key to ensure it is invalidated. + curve25519_key public_read = {0}; + ret = + lt_pairing_key_read(tropic_handle, public_read, FACTORY_PAIRING_KEY_SLOT); + if (ret != LT_L3_PAIRING_KEY_INVALID) { + cli_error(cli, CLI_ERROR, + "`lt_pairing_key_read()` for factory pairing key failed with " + "error %d", + ret); + goto cleanup; + } + + // Read the fourth pairing key to ensure it is empty. + ret = + lt_pairing_key_read(tropic_handle, public_read, PAIRING_KEY_SLOT_INDEX_3); + if (ret != LT_L3_PAIRING_KEY_EMPTY) { + cli_error(cli, CLI_ERROR, + "`lt_pairing_key_read()` for pairing key slot 3 failed with " + "error %d", + ret); + goto cleanup; + } + + tropic_is_paired = true; + ret = LT_OK; + +cleanup: + memzero(privileged_private, sizeof(privileged_private)); + memzero(unprivileged_private, sizeof(unprivileged_private)); + + return ret == LT_OK; } static void prodtest_tropic_pair(cli_t* cli) { // If this functions successfully completes, it is ensured that: - // * The factory pairing key in `PAIRING_KEY_SLOT_INDEX_0` is invalidated. - // * The unprivileged pairing key is written to `PAIRING_KEY_SLOT_INDEX_1`. - // * The privileged pairing key is written to `PAIRING_KEY_SLOT_INDEX_2`. - // * The pairing key in `PAIRING_KEY_SLOT_INDEX_3` is empty. + // * The public tropic key is written to MCU's flash. + // * The factory pairing key in tropic's `PAIRING_KEY_SLOT_INDEX_0` is + // invalidated. + // * The unprivileged pairing key is written to tropic's + // `PAIRING_KEY_SLOT_INDEX_1`. + // * The privileged pairing key is written to tropic's + // `PAIRING_KEY_SLOT_INDEX_2`. + // * The pairing key in tropic's `PAIRING_KEY_SLOT_INDEX_3` is empty. + // This function is: + // * idempotent (it can be called multiple times without changing the state + // of the device), + // * irreversible (it cannot be undone), + // * self-recovering (if the device is powered off during execution, it can + // be called again to continue from where it left off). if (cli_arg_count(cli) > 0) { cli_error_arg_count(cli); - return; + goto cleanup; } tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0; + lt_handle_t* tropic_handle = tropic_get_handle(); + + // Ensure that tropic_public_cached is cached. + if (!cache_tropic_cert_chain(tropic_handle)) { + cli_error(cli, CLI_ERROR, "`cache_tropic_cert_chain()` failed"); + goto cleanup; + } + + // Retrieve the tropic public key and write it to MCU's flash if it has not + // been written yet. + curve25519_key tropic_public_read = {0}; + if (secret_key_tropic_public(tropic_public_read) != sectrue) { +#ifdef SECRET_TROPIC_TROPIC_PUBKEY_SLOT + // This is skipped in the prodtest emulator. + if (secret_key_set(SECRET_TROPIC_TROPIC_PUBKEY_SLOT, tropic_public_cached, + sizeof(tropic_public_cached)) != sectrue) { + cli_error(cli, CLI_ERROR, + "`secret_key_set()` failed for tropic public key."); + goto cleanup; + } +#endif + if (secret_key_tropic_public(tropic_public_read) != sectrue) { + cli_error(cli, CLI_ERROR, "`secret_key_tropic_public()` failed."); + goto cleanup; + } + } + if (memcmp(tropic_public_cached, tropic_public_read, sizeof(tropic_public_cached)) != 0) { + cli_error(cli, CLI_ERROR, + "Tropic public key does not match the expected value."); + goto cleanup; + } + + lt_ret_t ret = LT_FAIL; + + // Retrieve the factory pairing key pair. +#ifdef TREZOR_EMULATOR + curve25519_key factory_private = { + 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}; +#else +#ifdef ABAB + // Testing keys (used in TROPIC01-P2S-P001) + curve25519_key factory_private = { + 0xd0, 0x99, 0x92, 0xb1, 0xf1, 0x7a, 0xbc, 0x4d, 0xb9, 0x37, 0x17, + 0x68, 0xa2, 0x7d, 0xa0, 0x5b, 0x18, 0xfa, 0xb8, 0x56, 0x13, 0xa7, + 0x84, 0x2c, 0xa6, 0x4c, 0x79, 0x10, 0xf2, 0x2e, 0x71, 0x6b}; +#else + // Production keys + curve25519_key factory_private = { + 0x28, 0x3f, 0x5a, 0x0f, 0xfc, 0x41, 0xcf, 0x50, 0x98, 0xa8, 0xe1, + 0x7d, 0xb6, 0x37, 0x2c, 0x3c, 0xaa, 0xd1, 0xee, 0xee, 0xdf, 0x0f, + 0x75, 0xbc, 0x3f, 0xbf, 0xcd, 0x9c, 0xab, 0x3d, 0xe9, 0x72}; +#endif +#endif + curve25519_key factory_public = {0}; + curve25519_scalarmult_basepoint(factory_public, factory_private); + + // Retrieve the unprivileged pairing key pair. curve25519_key unprivileged_private = {0}; if (secret_key_tropic_pairing_unprivileged(unprivileged_private) != sectrue) { cli_error(cli, CLI_ERROR, "`secret_key_tropic_pairing_unprivileged()` failed."); goto cleanup; } + curve25519_key unprivileged_public = {0}; + curve25519_scalarmult_basepoint(unprivileged_public, unprivileged_private); + // Retrieve the privileged pairing key pair. curve25519_key privileged_private = {0}; if (secret_key_tropic_pairing_privileged(privileged_private) != sectrue) { cli_error(cli, CLI_ERROR, "`secret_key_tropic_pairing_unprivileged()` failed."); goto cleanup; } + curve25519_key privileged_public = {0}; + curve25519_scalarmult_basepoint(privileged_public, privileged_private); + + // Try to establish a session using the factory pairing key. + ret = lt_session_start(tropic_handle, tropic_public_cached, FACTORY_PAIRING_KEY_SLOT, + factory_private, factory_public); + if (ret == LT_OK) { + // Write the privileged pairing key to the tropic's pairing key slot if it + // has not been written yet. + ret = pairing_key_write(tropic_handle, PRIVILEGED_PAIRING_KEY_SLOT, + privileged_public); + // If the pairing key has already been written, `pairing_key_write()` + // returns `LT_OK`. + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`pairing_key_write()` failed for privileged pairing key with " + "error %d", + ret); + goto cleanup; + } - // TODO: Implement properly + // Write the unprivileged pairing key to the tropic's pairing key slot if it + // has not been written yet. + ret = pairing_key_write(tropic_handle, UNPRIVILEGED_PAIRING_KEY_SLOT, + unprivileged_public); + // If the pairing key has already been written, `pairing_key_write()` + // returns `LT_OK`. + if (ret != LT_OK) { + cli_error( + cli, CLI_ERROR, + "`pairing_key_write()` failed for unprivileged pairing key with " + "error %d", + ret); + goto cleanup; + } - systick_delay_ms(600); + // Invalidate the factory pairing key if it has not been invalidated yet. + ret = lt_pairing_key_invalidate(tropic_handle, FACTORY_PAIRING_KEY_SLOT); + // If the factory has already been invalidated, + // `lt_pairing_key_invalidate()` returns `LT_OK`. + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_pairing_key_invalidate()` failed for factory pairing key " + "with error %d", + ret); + goto cleanup; + } + } - cli_ok(cli, ""); + if (is_paired(cli) == true) { + cli_ok(cli, ""); + } cleanup: - memzero(unprivileged_private, sizeof(unprivileged_private)); memzero(privileged_private, sizeof(privileged_private)); + memzero(unprivileged_private, sizeof(unprivileged_private)); + return; } static void prodtest_tropic_get_access_credential(cli_t* cli) { @@ -182,31 +962,31 @@ static void prodtest_tropic_get_access_credential(cli_t* cli) { tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0; - curve25519_key hsm_private_key = {0}; - if (secret_key_tropic_pairing_unprivileged(hsm_private_key) != sectrue) { + curve25519_key unprivileged_private = {0}; + if (secret_key_tropic_pairing_unprivileged(unprivileged_private) != sectrue) { cli_error(cli, CLI_ERROR, "`secret_key_tropic_pairing_unprivileged()` failed."); goto cleanup; } - // TODO: Retrieve the certificate - uint8_t tropic_certificate[600] = {0}; + if (!cache_tropic_cert_chain(tropic_get_handle())) { + cli_error(cli, CLI_ERROR, "`cache_tropic_cert_chain()` failed"); + goto cleanup; + } - uint8_t output[sizeof(hsm_private_key) + NOISE_TAG_SIZE] = {0}; - if (!secure_channel_encrypt((uint8_t*)hsm_private_key, - sizeof(hsm_private_key), tropic_certificate, - sizeof(tropic_certificate), output)) { - // TODO: Consider distinguishing between cryptography error and state error - cli_error(cli, CLI_ERROR, - "`secure_channel_encrypt()` failed. You have to " - "call `secure-channel-handshake-2` first."); + uint8_t output[sizeof(unprivileged_private) + NOISE_TAG_SIZE] = {0}; + if (!secure_channel_encrypt((uint8_t*)unprivileged_private, + sizeof(unprivileged_private), tropic_public_cached, + sizeof(tropic_public_cached), output)) { + // `secure_channel_handshake_2()` might not have been called + cli_error(cli, CLI_ERROR, "`secure_channel_encrypt()` failed."); goto cleanup; } cli_ok_hexdata(cli, output, sizeof(output)); cleanup: - memzero(hsm_private_key, sizeof(hsm_private_key)); + memzero(unprivileged_private, sizeof(unprivileged_private)); } static void prodtest_tropic_get_fido_masking_key(cli_t* cli) { @@ -224,10 +1004,8 @@ static void prodtest_tropic_get_fido_masking_key(cli_t* cli) { uint8_t output[sizeof(fido_masking_key) + NOISE_TAG_SIZE] = {0}; if (!secure_channel_encrypt(fido_masking_key, sizeof(fido_masking_key), NULL, 0, output)) { - // TODO: Consider distinguishing between cryptography error and state error - cli_error(cli, CLI_ERROR, - "`secure_channel_encrypt()` failed. You have to call " - "`secure-channel-handshake-2` first."); + // `secure_channel_handshake_2()` might not have been called + cli_error(cli, CLI_ERROR, "`secure_channel_encrypt()` failed."); goto cleanup; } @@ -237,13 +1015,60 @@ static void prodtest_tropic_get_fido_masking_key(cli_t* cli) { memzero(fido_masking_key, sizeof(fido_masking_key)); } +static lt_ret_t l2_get_req_len(const uint8_t* buffer, size_t buffer_length, + size_t* req_length) { + if (!buffer || !req_length) { + return LT_PARAM_ERR; + } + + if (buffer_length < 2) { + return LT_PARAM_ERR; + } + + size_t length = buffer[1] + 2; + + if (length > buffer_length) { + return LT_PARAM_ERR; + } + + *req_length = length; + + return LT_OK; +} + +static lt_ret_t l2_get_rsp_len(const uint8_t* buffer, size_t buffer_length, + size_t* rsp_length) { + if (!buffer || !rsp_length) { + return LT_PARAM_ERR; + } + + if (buffer_length < 3) { + return LT_PARAM_ERR; + } + + size_t length = buffer[2] + 3; + + if (length > buffer_length) { + return LT_PARAM_ERR; + } + + *rsp_length = length; + + return LT_OK; +} + static void prodtest_tropic_handshake(cli_t* cli) { if (cli_arg_count(cli) != 1) { cli_error_arg_count(cli); return; } - uint8_t input[35] = {0}; + if (tropic_is_paired != true) { + cli_error(cli, CLI_ERROR, "You have to call `tropic-pair` first."); + return; + } + + uint8_t input[35] = {0}; // 35 is the expected size of the handshake request size_t input_length = 0; if (!cli_arg_hex(cli, "hex-data", input, sizeof(input), &input_length)) { if (input_length == sizeof(input)) { @@ -259,15 +1084,74 @@ static void prodtest_tropic_handshake(cli_t* cli) { return; } - // TODO: Implement properly + lt_ret_t ret = LT_FAIL; + lt_l2_state_t l2_state = tropic_get_handle()->l2; + + size_t request_length = 0; + ret = l2_get_req_len(input, sizeof(input), &request_length); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`get_req_len()` failed with error %d.", ret); + return; + } + + if (input_length != request_length) { + cli_error(cli, CLI_ERROR, "Request was damaged or truncated."); + return; + } - systick_delay_ms(150); + memcpy(&l2_state.buff, input, request_length); - uint8_t output[51] = {0}; + ret = lt_l2_send(&l2_state); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`lt_l2_send()` failed with error %d.", ret); + return; + } + + ret = lt_l2_receive(&l2_state); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`lt_l2_receive()` failed with error %d.", ret); + return; + } + + size_t response_length = 0; + ret = l2_get_rsp_len(l2_state.buff, sizeof(l2_state.buff), &response_length); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`get_rsp_len()` failed with error %d.", ret); + return; + } + + if (response_length != + 51) { // 51 is the expected size of the handshake response + cli_error(cli, CLI_ERROR, + "Unexpected response length. Expecting 51 bytes, got %d bytes.", + (int)response_length); + return; + } tropic_handshake_state = TROPIC_HANDSHAKE_STATE_1; - cli_ok_hexdata(cli, output, sizeof(output)); + cli_ok_hexdata(cli, &l2_state.buff, response_length); +} + +static lt_ret_t l3_get_frame_len(const uint8_t* input, size_t input_length, + size_t* cmd_length) { + if (!input || !cmd_length) { + return LT_PARAM_ERR; + } + + if (input_length < 2) { + return LT_PARAM_ERR; + } + + size_t length = input[0] + (input[1] << 8) + 2 + 16; + + if (length > input_length) { + return LT_PARAM_ERR; + } + + *cmd_length = length; + + return LT_OK; } static void prodtest_tropic_send_command(cli_t* cli) { @@ -276,7 +1160,7 @@ static void prodtest_tropic_send_command(cli_t* cli) { return; } - uint8_t input[60] = {0}; + uint8_t input[L2_MAX_FRAME_SIZE] = {0}; size_t input_length = 0; if (!cli_arg_hex(cli, "hex-data", input, sizeof(input), &input_length)) { if (input_length == sizeof(input)) { @@ -292,16 +1176,54 @@ static void prodtest_tropic_send_command(cli_t* cli) { return; } - // TODO: Implement properly + lt_ret_t ret = LT_FAIL; + lt_l2_state_t l2_state = tropic_get_handle()->l2; + + size_t command_length = 0; + ret = l3_get_frame_len(input, sizeof(input), &command_length); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`l3_get_cmd_len()` failed with error %d.", ret); + return; + } - systick_delay_ms(90); + if (input_length != command_length) { + cli_error(cli, CLI_ERROR, "Request was damaged or truncated."); + return; + } - uint8_t output[60] = {0}; + ret = lt_l2_send_encrypted_cmd(&l2_state, (uint8_t*)input, input_length); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_l2_send_encrypted_cmd()` failed with error %d.", ret); + return; + } - cli_ok_hexdata(cli, output, sizeof(output)); + uint8_t output[L2_MAX_FRAME_SIZE] = {0}; + ret = lt_l2_recv_encrypted_res(&l2_state, output, sizeof(output)); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_l2_recv_encrypted_res()` failed with error %d.", ret); + return; + } + + size_t output_length = 0; + ret = l3_get_frame_len(output, sizeof(output), &output_length); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`l3_get_cmd_len()` failed with error %d.", ret); + return; + } + + cli_ok_hexdata(cli, output, output_length); } static void prodtest_tropic_lock(cli_t* cli) { + // This function is: + // * idempotent (it can be called multiple times without changing the state + // of the device), + // * irreversible (it cannot be undone), + // * self-recovering (if the device is powered off during execution, it can + // be called again to continue from where it left off). + if (cli_arg_count(cli) > 0) { cli_error_arg_count(cli); return; @@ -309,44 +1231,215 @@ static void prodtest_tropic_lock(cli_t* cli) { tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0; - // TODO: Implement properly + lt_handle_t* tropic_handle = tropic_get_handle(); + lt_ret_t ret = LT_FAIL; - systick_delay_ms(500); + curve25519_key tropic_public = {0}; + if (secret_key_tropic_public(tropic_public) != sectrue) { + cli_error(cli, CLI_ERROR, "`secret_key_tropic_public()` failed."); + goto cleanup; + } - locked = true; + curve25519_key privileged_private = {0}; + if (secret_key_tropic_pairing_privileged(privileged_private) != sectrue) { + cli_error(cli, CLI_ERROR, + "`secret_key_tropic_pairing_privileged()` failed."); + goto cleanup; + } + curve25519_key privileged_public = {0}; + curve25519_scalarmult_basepoint(privileged_public, privileged_private); + + ret = + lt_session_start(tropic_handle, tropic_public, PRIVILEGED_PAIRING_KEY_SLOT, + privileged_private, privileged_public); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_session_start()` for privileged key failed with error %d", + ret); + goto cleanup; + } + + struct lt_config_t configuration_read = {0}; + + ret = lt_r_config_erase(tropic_handle); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`lt_r_config_erase()` failed with error %d", + ret); + return; + } + + ret = lt_write_whole_R_config(tropic_handle, &reversible_configuration); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_write_whole_R_config()` failed with error %d", ret); + return; + } + + ret = lt_read_whole_R_config(tropic_handle, &configuration_read); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`lt_read_whole_R_config()` failed with error %d", + ret); + return; + } + + if (memcmp(&reversible_configuration, (uint8_t*)&configuration_read, + sizeof(reversible_configuration)) != 0) { + cli_error(cli, CLI_ERROR, "Reversible configuration mismatch after write."); + return; + } + + ret = lt_write_whole_I_config(tropic_handle, &irreversible_configuration); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_write_whole_I_config()` failed with error %d", ret); + return; + } + + ret = lt_read_whole_I_config(tropic_handle, &configuration_read); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "`lt_read_whole_I_config()` failed with error %d", + ret); + return; + } + + if (memcmp(&irreversible_configuration, (uint8_t*)&configuration_read, + sizeof(irreversible_configuration)) != 0) { + cli_error(cli, CLI_ERROR, + "Irreversible configuration mismatch after write."); + return; + } cli_ok(cli, ""); + +cleanup: + memzero(privileged_private, sizeof(privileged_private)); } -static void prodtest_tropic_certdev_read(cli_t* cli) { - if (cli_arg_count(cli) > 0) { - cli_error_arg_count(cli); - return; +static lt_ret_t data_write(lt_handle_t* h, uint16_t first_slot, + uint16_t slots_count, uint8_t* data, + size_t data_length) { + const uint16_t last_data_slot = first_slot + slots_count - 1; + if (slots_count == 0 || last_data_slot > R_MEM_DATA_SLOT_MAX) { + return LT_PARAM_ERR; } - tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0; + const size_t prefix_length = 2; + const size_t prefixed_data_length = data_length + prefix_length; + const size_t total_slots_length = R_MEM_DATA_SIZE_MAX * slots_count; + if (prefixed_data_length > total_slots_length) { + return LT_PARAM_ERR; + } + + // The following of code can be further optimized: + // * It uses unnecessary amount of memory. + // * It writes to a data slot even if there is no data to be written. - // TODO: Implement properly + uint8_t prefixed_data[total_slots_length]; + memset(prefixed_data, 0, sizeof(prefixed_data)); + prefixed_data[0] = (data_length >> 8) & 0xFF; + prefixed_data[1] = data_length & 0xFF; + memcpy(prefixed_data + prefix_length, data, data_length); - systick_delay_ms(35); + size_t position = 0; + uint16_t slot = first_slot; - uint8_t output[600] = {0}; + while (slot <= last_data_slot) { + lt_ret_t ret = LT_FAIL; - cli_ok_hexdata(cli, &output, sizeof(output)); + ret = lt_r_mem_data_erase(h, slot); + if (ret != LT_OK) { + return ret; + } + + ret = lt_r_mem_data_write(h, slot, prefixed_data + position, + R_MEM_DATA_SIZE_MAX); + if (ret != LT_OK) { + return ret; + } + + position += R_MEM_DATA_SIZE_MAX; + slot += 1; + } + + return LT_OK; } -static void prodtest_tropic_certdev_write(cli_t* cli) { - if (cli_arg_count(cli) != 1) { +static lt_ret_t data_read(lt_handle_t* h, uint16_t first_slot, + uint16_t slots_count, uint8_t* data, + size_t max_data_length, size_t* data_length) { + const uint16_t last_data_slot = first_slot + slots_count - 1; + if (slots_count == 0 || last_data_slot > R_MEM_DATA_SLOT_MAX) { + return LT_PARAM_ERR; + } + + // The following code can be further optimized: + // * It uses unnecessary amount of memory. + // * It reads from a data slot even if there is no data to be read. + + const size_t total_slots_length = R_MEM_DATA_SIZE_MAX * slots_count; + uint8_t prefixed_data[total_slots_length]; + size_t position = 0; + uint16_t slot = first_slot; + + while (slot <= last_data_slot) { + uint16_t slot_length = 0; + lt_ret_t ret = + lt_r_mem_data_read(h, slot, prefixed_data + position, &slot_length); + if (ret != LT_OK) { + return ret; + } + + if (slot_length != R_MEM_DATA_SIZE_MAX) { + return LT_FAIL; + } + + position += R_MEM_DATA_SIZE_MAX; + slot += 1; + } + + const size_t prefix_length = 2; + size_t length = prefixed_data[0] << 8 | prefixed_data[1]; + if (length > max_data_length || length + prefix_length > total_slots_length) { + return LT_PARAM_ERR; + } + + *data_length = length; + memcpy(data, prefixed_data + prefix_length, length); + + return LT_OK; +} + +static bool check_device_cert_chain(cli_t* cli, const uint8_t* chain, + size_t chain_size) { + uint8_t challenge[CHALLENGE_SIZE] = { + 0}; // The challenge is intentionally constant zero. + + ed25519_signature signature = {0}; + + if (lt_ecc_eddsa_sign(tropic_get_handle(), TROPIC_DEV_KEY_SLOT, challenge, + sizeof(challenge), signature) != LT_OK) { + return false; + } + + if (!check_cert_chain(cli, chain, chain_size, signature, sizeof(signature), + challenge)) { + return false; + } + + return true; +} + +static void cert_write(cli_t* cli, uint16_t first_slot, uint16_t slots_count) { + if (cli_arg_count(cli) > 1) { cli_error_arg_count(cli); return; } - tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0; - - uint8_t input[600] = {0}; - size_t input_length = 0; - if (!cli_arg_hex(cli, "hex-data", input, sizeof(input), &input_length)) { - if (input_length == sizeof(input)) { + size_t certificate_length = 0; + uint8_t certificate[R_MEM_DATA_SIZE_MAX * slots_count]; + if (!cli_arg_hex(cli, "hex-data", certificate, sizeof(certificate), + &certificate_length)) { + if (certificate_length == sizeof(certificate)) { cli_error(cli, CLI_ERROR, "Certificate too long."); } else { cli_error(cli, CLI_ERROR, "Hexadecimal decoding error."); @@ -354,14 +1447,68 @@ static void prodtest_tropic_certdev_write(cli_t* cli) { return; } - // TODO: Implement properly + tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0; + + uint8_t certificate_read[R_MEM_DATA_SIZE_MAX * slots_count]; - systick_delay_ms(80); + lt_handle_t* tropic_handle = tropic_get_handle(); + lt_ret_t ret = LT_FAIL; + + curve25519_key tropic_public = {0}; + if (secret_key_tropic_public(tropic_public) != sectrue) { + cli_error(cli, CLI_ERROR, "`secret_key_tropic_public()` failed."); + goto cleanup; + } + + curve25519_key privileged_private = {0}; + if (secret_key_tropic_pairing_privileged(privileged_private) != sectrue) { + cli_error(cli, CLI_ERROR, + "`secret_key_tropic_pairing_privileged()` failed."); + goto cleanup; + } + curve25519_key privileged_public = {0}; + curve25519_scalarmult_basepoint(privileged_public, privileged_private); + + ret = + lt_session_start(tropic_handle, tropic_public, PRIVILEGED_PAIRING_KEY_SLOT, + privileged_private, privileged_public); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_session_start()` for privileged key failed with error %d", + ret); + goto cleanup; + } + + ret = data_write(tropic_handle, first_slot, slots_count, certificate, + certificate_length); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "Unable to write certificate"); + goto cleanup; + } + + size_t certificate_read_length = 0; + ret = data_read(tropic_handle, first_slot, slots_count, certificate_read, + sizeof(certificate_read), &certificate_read_length); + if (ret != LT_OK || certificate_read_length != certificate_length || + memcmp(certificate, certificate_read, certificate_length) != 0) { + cli_error(cli, CLI_ERROR, "Unable to read certificate"); + goto cleanup; + } + + if (first_slot == TROPIC_DEV_CERT_FIRST_SLOT && + !check_device_cert_chain(cli, certificate, certificate_length)) { + // Error returned by check_device_cert_chain(). + return; + } + // TODO: call `check_device_cert_chain()` for FIDO certificate cli_ok(cli, ""); + +cleanup: + memzero(privileged_private, sizeof(privileged_private)); } -static void prodtest_tropic_certfido_read(cli_t* cli) { +static void cert_read(cli_t* cli, uint16_t first_slot, uint16_t slots_count) { if (cli_arg_count(cli) > 0) { cli_error_arg_count(cli); return; @@ -369,39 +1516,65 @@ static void prodtest_tropic_certfido_read(cli_t* cli) { tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0; - // TODO: Implement properly - - systick_delay_ms(35); + lt_handle_t* tropic_handle = tropic_get_handle(); - uint8_t output[600] = {0}; + uint8_t certificate[R_MEM_DATA_SIZE_MAX * slots_count]; + lt_ret_t ret = LT_FAIL; - cli_ok_hexdata(cli, &output, sizeof(output)); -} + curve25519_key tropic_public = {0}; + if (secret_key_tropic_public(tropic_public) != sectrue) { + cli_error(cli, CLI_ERROR, "`secret_key_tropic_public()` failed."); + goto cleanup; + } -static void prodtest_tropic_certfido_write(cli_t* cli) { - if (cli_arg_count(cli) != 1) { - cli_error_arg_count(cli); - return; + curve25519_key privileged_private = {0}; + if (secret_key_tropic_pairing_privileged(privileged_private) != sectrue) { + cli_error(cli, CLI_ERROR, + "`secret_key_tropic_pairing_privileged()` failed."); + goto cleanup; } + curve25519_key privileged_public = {0}; + curve25519_scalarmult_basepoint(privileged_public, privileged_private); - tropic_handshake_state = TROPIC_HANDSHAKE_STATE_0; + ret = + lt_session_start(tropic_handle, tropic_public, PRIVILEGED_PAIRING_KEY_SLOT, + privileged_private, privileged_public); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, + "`lt_session_start()` for privileged key failed with error %d", + ret); + goto cleanup; + } - uint8_t input[600] = {0}; - size_t input_length = 0; - if (!cli_arg_hex(cli, "hex-data", input, sizeof(input), &input_length)) { - if (input_length == sizeof(input)) { - cli_error(cli, CLI_ERROR, "Certificate too long."); - } else { - cli_error(cli, CLI_ERROR, "Hexadecimal decoding error."); - } - return; + size_t certificate_length = 0; + ret = data_read(tropic_handle, first_slot, slots_count, certificate, + sizeof(certificate), &certificate_length); + if (ret != LT_OK) { + cli_error(cli, CLI_ERROR, "Unable to read certificate"); + goto cleanup; } - // TODO: Implement properly + cli_ok_hexdata(cli, certificate, certificate_length); - systick_delay_ms(80); +cleanup: + memzero(privileged_private, sizeof(privileged_private)); +} - cli_ok(cli, ""); +static void prodtest_tropic_certfido_write(cli_t* cli) { + cert_write(cli, TROPIC_FIDO_CERT_FIRST_SLOT, TROPIC_FIDO_CERT_SLOTS_COUNT); +} + +static void prodtest_tropic_certdev_write(cli_t* cli) { + cert_write(cli, TROPIC_DEV_CERT_FIRST_SLOT, TROPIC_DEV_CERT_SLOTS_COUNT); + // TODO: Parse the device serial number from the certificate and verify that it matches the device serial number written by `otp-device-sn-write`. Do the same for the MCU device certificate and the Optiga device certificate. +} + +static void prodtest_tropic_certfido_read(cli_t* cli) { + cert_read(cli, TROPIC_FIDO_CERT_FIRST_SLOT, TROPIC_FIDO_CERT_SLOTS_COUNT); +} + +static void prodtest_tropic_certdev_read(cli_t* cli) { + cert_read(cli, TROPIC_DEV_CERT_FIRST_SLOT, TROPIC_DEV_CERT_SLOTS_COUNT); } static void prodtest_tropic_update_fw(cli_t* cli) { @@ -555,7 +1728,7 @@ PRODTEST_CLI_CMD( PRODTEST_CLI_CMD( .name = "tropic-lock-check", .func = prodtest_tropic_lock_check, - .info = "Check whether Tropic is locked", + .info = "Check whether Tropic has been configured", .args = "" ); @@ -625,7 +1798,7 @@ PRODTEST_CLI_CMD( PRODTEST_CLI_CMD( .name = "tropic-lock", .func = prodtest_tropic_lock, - .info = "Lock Tropic", + .info = "Irreversibly configure Tropic", .args = "" ); diff --git a/core/embed/projects/prodtest/cmd/secure_channel.c b/core/embed/projects/prodtest/cmd/secure_channel.c index dd292a924ac..a790f1c9fd9 100644 --- a/core/embed/projects/prodtest/cmd/secure_channel.c +++ b/core/embed/projects/prodtest/cmd/secure_channel.c @@ -34,9 +34,15 @@ typedef enum { static noise_state_t noise_state = SECURE_CHANNEL_STATE_0; static noise_context_t noise_context = {0}; -// TODO: Use real keys -static curve25519_key prodtest_private_key = {0}; -static curve25519_key hsm_public_key = {0}; +static curve25519_key prodtest_private_key = { + 0xc8, 0x56, 0x36, 0x89, 0xf5, 0xa6, 0x70, 0x66, 0x43, 0xeb, 0xe3, + 0x7e, 0xff, 0x7a, 0x2c, 0x20, 0x31, 0x27, 0x58, 0xbe, 0x5f, 0x01, + 0xc8, 0x6f, 0x9b, 0xe7, 0xe2, 0xe6, 0x0b, 0xee, 0x7e, 0x55}; +// TODO: Generate the key on HSM and use it here. +static curve25519_key hsm_public_key = { + 0xcf, 0xce, 0x80, 0xf7, 0xc8, 0x7e, 0xa1, 0xe9, 0x3d, 0x0d, 0x80, + 0x98, 0x3f, 0xec, 0xc9, 0x98, 0xa0, 0xdd, 0xb6, 0xaa, 0x7a, 0x36, + 0x36, 0x6b, 0x6c, 0x7d, 0xd4, 0x09, 0x32, 0x5f, 0x67, 0x4b}; bool secure_channel_handshake_1(uint8_t output[SECURE_CHANNEL_OUTPUT_SIZE]) { if (!noise_create_handshake_request(&noise_context, @@ -58,8 +64,7 @@ bool secure_channel_handshake_2( if (!noise_handle_handshake_response(&noise_context, prodtest_private_key, hsm_public_key, (const noise_response_t*)input)) { - // TODO: Uncomment the following line - // return false; + return false; } noise_state = SECURE_CHANNEL_STATE_2; @@ -75,13 +80,7 @@ bool secure_channel_encrypt(const uint8_t* plaintext, size_t plaintext_length, return false; } - // TODO: Remove the following 3 lines - memcpy(ciphertext, plaintext, plaintext_length); - memset(ciphertext + plaintext_length, 0, NOISE_TAG_SIZE); - return true; - - // TODO: Uncomment the following 3 lines - // return noise_send_message(&noise_context, associated_data, - // associated_data_length, plaintext, - // plaintext_length, ciphertext); + return noise_send_message(&noise_context, associated_data, + associated_data_length, plaintext, plaintext_length, + ciphertext); } diff --git a/core/embed/projects/prodtest/cmd/secure_channel.h b/core/embed/projects/prodtest/cmd/secure_channel.h index 55a66027ce7..e14a09026ea 100644 --- a/core/embed/projects/prodtest/cmd/secure_channel.h +++ b/core/embed/projects/prodtest/cmd/secure_channel.h @@ -25,8 +25,6 @@ #define SECURE_CHANNEL_OUTPUT_SIZE (sizeof(noise_request_t)) #define SECURE_CHANNEL_TAG_SIZE (NOISE_TAG_SIZE) -// TODO: Consider returning an enum indiciating success, cryptography error or -// state error bool secure_channel_handshake_1(uint8_t output[SECURE_CHANNEL_OUTPUT_SIZE]); bool secure_channel_handshake_2(const uint8_t input[SECURE_CHANNEL_INPUT_SIZE]); bool secure_channel_encrypt(const uint8_t* plaintext, size_t plaintext_length, diff --git a/core/embed/sec/secret/inc/sec/secret_keys.h b/core/embed/sec/secret/inc/sec/secret_keys.h index 59fbf82af0f..99c51a646c2 100644 --- a/core/embed/sec/secret/inc/sec/secret_keys.h +++ b/core/embed/sec/secret/inc/sec/secret_keys.h @@ -23,15 +23,15 @@ #ifdef SECURE_MODE -#ifdef SECRET_MASTER_KEY_SLOT +#ifdef SECRET_MASTER_KEY_SLOT_SIZE #define SECRET_KEY_MASKING #include -secbool secret_key_mcu_device_auth(curve25519_key dest); +secbool secret_key_mcu_device_auth(ed25519_secret_key dest); -#endif // SECRET_MASTER_KEY_SLOT +#endif // SECRET_MASTER_KEY_SLOT_SIZE #ifdef USE_OPTIGA diff --git a/core/embed/sec/secret/stm32u5/secret_keys.c b/core/embed/sec/secret/stm32u5/secret_keys.c index a607fef7e5f..6924151e023 100644 --- a/core/embed/sec/secret/stm32u5/secret_keys.c +++ b/core/embed/sec/secret/stm32u5/secret_keys.c @@ -114,7 +114,7 @@ static secbool secret_key_derive_nist256p1( } #endif -secbool secret_key_mcu_device_auth(curve25519_key dest) { +secbool secret_key_mcu_device_auth(ed25519_secret_key dest) { return secret_key_derive_curve25519(SECRET_PRIVILEGED_MASTER_KEY_SLOT, KEY_INDEX_MCU_DEVICE_AUTH, dest); } diff --git a/core/embed/sec/secret/unix/secret_keys.c b/core/embed/sec/secret/unix/secret_keys.c index 7334afb3832..660401d1640 100644 --- a/core/embed/sec/secret/unix/secret_keys.c +++ b/core/embed/sec/secret/unix/secret_keys.c @@ -44,6 +44,11 @@ _Static_assert(sizeof(SECRET_TROPIC_PAIRING_BYTES) == sizeof(curve25519_key), _Static_assert(sizeof(SECRET_TROPIC_PUBKEY_BYTES) == sizeof(curve25519_key), "Invalid size of Tropic public key"); +secbool secret_key_mcu_device_auth(ed25519_secret_key dest) { + memset(dest, 3, sizeof(ed25519_secret_key)); + return sectrue; +} + secbool secret_key_tropic_public(curve25519_key dest) { memcpy(dest, SECRET_TROPIC_PUBKEY_BYTES, sizeof(curve25519_key)); return sectrue;