mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-02 10:51:06 +00:00
feat(core): Implement OPTIGA provisioning in prodtest.
[no changelog]
This commit is contained in:
parent
812e77cefd
commit
5470304515
@ -24,6 +24,7 @@ FEATURES_WANTED = ["input", "sbu", "sd_card", "rdb_led", "usb", "consumption_mas
|
||||
CCFLAGS_MOD = ''
|
||||
CPPPATH_MOD = []
|
||||
CPPDEFINES_MOD = [
|
||||
'AES_128',
|
||||
'USE_INSECURE_PRNG',
|
||||
]
|
||||
SOURCE_MOD = []
|
||||
@ -50,11 +51,24 @@ CPPPATH_MOD += [
|
||||
'vendor/trezor-storage',
|
||||
]
|
||||
SOURCE_MOD += [
|
||||
'vendor/trezor-crypto/aes/aes_modes.c',
|
||||
'vendor/trezor-crypto/aes/aesccm.c',
|
||||
'vendor/trezor-crypto/aes/aescrypt.c',
|
||||
'vendor/trezor-crypto/aes/aeskey.c',
|
||||
'vendor/trezor-crypto/aes/aestab.c',
|
||||
'vendor/trezor-crypto/bignum.c',
|
||||
'vendor/trezor-crypto/chacha_drbg.c',
|
||||
'vendor/trezor-crypto/chacha20poly1305/chacha_merged.c',
|
||||
'vendor/trezor-crypto/ecdsa.c',
|
||||
'vendor/trezor-crypto/hmac.c',
|
||||
'vendor/trezor-crypto/hmac_drbg.c',
|
||||
'vendor/trezor-crypto/memzero.c',
|
||||
'vendor/trezor-crypto/nist256p1.c',
|
||||
'vendor/trezor-crypto/rand.c',
|
||||
'vendor/trezor-crypto/rfc6979.c',
|
||||
'vendor/trezor-crypto/secp256k1.c',
|
||||
'vendor/trezor-crypto/sha2.c',
|
||||
'vendor/trezor-crypto/tls_prf.c',
|
||||
]
|
||||
|
||||
# modtrezorui
|
||||
@ -80,8 +94,14 @@ SOURCE_PRODTEST = [
|
||||
'embed/prodtest/startup.s',
|
||||
'embed/prodtest/header.S',
|
||||
'embed/prodtest/main.c',
|
||||
'embed/prodtest/prodtest_common.c',
|
||||
]
|
||||
|
||||
if TREZOR_MODEL in ('R',):
|
||||
SOURCE_PRODTEST += [
|
||||
'embed/prodtest/optiga_prodtest.c',
|
||||
]
|
||||
|
||||
# fonts
|
||||
tools.add_font('NORMAL', FONT_NORMAL, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
tools.add_font('BOLD', FONT_BOLD, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
|
@ -28,17 +28,24 @@
|
||||
#include "display.h"
|
||||
#include "flash.h"
|
||||
#include "i2c.h"
|
||||
#include "mini_printf.h"
|
||||
#include "model.h"
|
||||
#include "mpu.h"
|
||||
#include "prodtest_common.h"
|
||||
#include "random_delays.h"
|
||||
#include "rng.h"
|
||||
#include "sbu.h"
|
||||
#include "sdcard.h"
|
||||
#include "secbool.h"
|
||||
#include "touch.h"
|
||||
#include "usb.h"
|
||||
|
||||
#ifdef USE_OPTIGA
|
||||
#include "optiga_commands.h"
|
||||
#include "optiga_prodtest.h"
|
||||
#include "optiga_transport.h"
|
||||
#endif
|
||||
|
||||
#include "memzero.h"
|
||||
#include "stm32f4xx_ll_utils.h"
|
||||
|
||||
#ifdef TREZOR_MODEL_T
|
||||
#define MODEL_IDENTIFIER "TREZOR2-"
|
||||
@ -46,8 +53,6 @@
|
||||
#define MODEL_IDENTIFIER "T2B1-"
|
||||
#endif
|
||||
|
||||
enum { VCP_IFACE = 0x00 };
|
||||
|
||||
static secbool startswith(const char *s, const char *prefix) {
|
||||
return sectrue * (0 == strncmp(s, prefix, strlen(prefix)));
|
||||
}
|
||||
@ -57,11 +62,6 @@ static void vcp_intr(void) {
|
||||
ensure(secfalse, "vcp_intr");
|
||||
}
|
||||
|
||||
static void vcp_puts(const char *s, size_t len) {
|
||||
int r = usb_vcp_write_blocking(VCP_IFACE, (const uint8_t *)s, len, -1);
|
||||
(void)r;
|
||||
}
|
||||
|
||||
static char vcp_getchar(void) {
|
||||
uint8_t c = 0;
|
||||
int r = usb_vcp_read_blocking(VCP_IFACE, &c, 1, -1);
|
||||
@ -91,16 +91,6 @@ static void vcp_readline(char *buf, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
static void vcp_printf(const char *fmt, ...) {
|
||||
static char buf[128];
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
int r = mini_vsnprintf(buf, sizeof(buf), fmt, va);
|
||||
va_end(va);
|
||||
vcp_puts(buf, r);
|
||||
vcp_puts("\r\n", 2);
|
||||
}
|
||||
|
||||
static void usb_init_all(void) {
|
||||
enum {
|
||||
VCP_PACKET_LEN = 64,
|
||||
@ -160,7 +150,7 @@ static void draw_border(int width, int padding) {
|
||||
|
||||
static void test_border(void) {
|
||||
draw_border(2, 0);
|
||||
vcp_printf("OK");
|
||||
vcp_println("OK");
|
||||
}
|
||||
|
||||
static void test_display(const char *colors) {
|
||||
@ -185,10 +175,10 @@ static void test_display(const char *colors) {
|
||||
c = 0xFFFF;
|
||||
break;
|
||||
}
|
||||
display_bar(i * w, 0, i * w + w, 240, c);
|
||||
display_bar(i * w, 0, i * w + w, DISPLAY_RESY, c);
|
||||
}
|
||||
display_refresh();
|
||||
vcp_printf("OK");
|
||||
vcp_println("OK");
|
||||
}
|
||||
|
||||
#ifdef USE_BUTTON
|
||||
@ -196,13 +186,13 @@ static void test_display(const char *colors) {
|
||||
static secbool test_btn_press(uint32_t deadline, uint32_t btn) {
|
||||
while (button_read() != (btn | BTN_EVT_DOWN)) {
|
||||
if (HAL_GetTick() > deadline) {
|
||||
vcp_printf("ERROR TIMEOUT");
|
||||
vcp_println("ERROR TIMEOUT");
|
||||
return secfalse;
|
||||
}
|
||||
}
|
||||
while (button_read() != (btn | BTN_EVT_UP)) {
|
||||
if (HAL_GetTick() > deadline) {
|
||||
vcp_printf("ERROR TIMEOUT");
|
||||
vcp_println("ERROR TIMEOUT");
|
||||
return secfalse;
|
||||
}
|
||||
}
|
||||
@ -231,7 +221,7 @@ static secbool test_btn_all(uint32_t deadline) {
|
||||
break;
|
||||
}
|
||||
if (HAL_GetTick() > deadline) {
|
||||
vcp_printf("ERROR TIMEOUT");
|
||||
vcp_println("ERROR TIMEOUT");
|
||||
return secfalse;
|
||||
}
|
||||
}
|
||||
@ -254,7 +244,7 @@ static secbool test_btn_all(uint32_t deadline) {
|
||||
break;
|
||||
}
|
||||
if (HAL_GetTick() > deadline) {
|
||||
vcp_printf("ERROR TIMEOUT");
|
||||
vcp_println("ERROR TIMEOUT");
|
||||
return secfalse;
|
||||
}
|
||||
}
|
||||
@ -268,21 +258,21 @@ static void test_button(const char *args) {
|
||||
timeout = args[5] - '0';
|
||||
uint32_t deadline = HAL_GetTick() + timeout * 1000;
|
||||
secbool r = test_btn_press(deadline, BTN_LEFT);
|
||||
if (r == sectrue) vcp_printf("OK");
|
||||
if (r == sectrue) vcp_println("OK");
|
||||
}
|
||||
|
||||
if (startswith(args, "RIGHT ")) {
|
||||
timeout = args[6] - '0';
|
||||
uint32_t deadline = HAL_GetTick() + timeout * 1000;
|
||||
secbool r = test_btn_press(deadline, BTN_RIGHT);
|
||||
if (r == sectrue) vcp_printf("OK");
|
||||
if (r == sectrue) vcp_println("OK");
|
||||
}
|
||||
|
||||
if (startswith(args, "BOTH ")) {
|
||||
timeout = args[5] - '0';
|
||||
uint32_t deadline = HAL_GetTick() + timeout * 1000;
|
||||
secbool r = test_btn_all(deadline);
|
||||
if (r == sectrue) vcp_printf("OK");
|
||||
if (r == sectrue) vcp_println("OK");
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,9 +325,9 @@ static void test_touch(const char *args) {
|
||||
if (touch_click_timeout(&evt, timeout * 1000)) {
|
||||
uint16_t x = touch_unpack_x(evt);
|
||||
uint16_t y = touch_unpack_y(evt);
|
||||
vcp_printf("OK %d %d", x, y);
|
||||
vcp_println("OK %d %d", x, y);
|
||||
} else {
|
||||
vcp_printf("ERROR TIMEOUT");
|
||||
vcp_println("ERROR TIMEOUT");
|
||||
}
|
||||
display_clear();
|
||||
display_refresh();
|
||||
@ -377,7 +367,7 @@ static void test_pwm(const char *args) {
|
||||
|
||||
display_backlight(v);
|
||||
display_refresh();
|
||||
vcp_printf("OK");
|
||||
vcp_println("OK");
|
||||
}
|
||||
|
||||
#ifdef USE_SD_CARD
|
||||
@ -387,13 +377,13 @@ static void test_sd(void) {
|
||||
static uint32_t buf2[BLOCK_SIZE / sizeof(uint32_t)];
|
||||
|
||||
if (sectrue != sdcard_is_present()) {
|
||||
vcp_printf("ERROR NOCARD");
|
||||
vcp_println("ERROR NOCARD");
|
||||
return;
|
||||
}
|
||||
|
||||
ensure(sdcard_power_on(), NULL);
|
||||
if (sectrue != sdcard_read_blocks(buf1, 0, BLOCK_SIZE / SDCARD_BLOCK_SIZE)) {
|
||||
vcp_printf("ERROR sdcard_read_blocks (0)");
|
||||
vcp_println("ERROR sdcard_read_blocks (0)");
|
||||
goto power_off;
|
||||
}
|
||||
for (int j = 1; j <= 2; j++) {
|
||||
@ -402,21 +392,21 @@ static void test_sd(void) {
|
||||
}
|
||||
if (sectrue !=
|
||||
sdcard_write_blocks(buf1, 0, BLOCK_SIZE / SDCARD_BLOCK_SIZE)) {
|
||||
vcp_printf("ERROR sdcard_write_blocks (%d)", j);
|
||||
vcp_println("ERROR sdcard_write_blocks (%d)", j);
|
||||
goto power_off;
|
||||
}
|
||||
HAL_Delay(1000);
|
||||
if (sectrue !=
|
||||
sdcard_read_blocks(buf2, 0, BLOCK_SIZE / SDCARD_BLOCK_SIZE)) {
|
||||
vcp_printf("ERROR sdcard_read_blocks (%d)", j);
|
||||
vcp_println("ERROR sdcard_read_blocks (%d)", j);
|
||||
goto power_off;
|
||||
}
|
||||
if (0 != memcmp(buf1, buf2, sizeof(buf1))) {
|
||||
vcp_printf("ERROR DATA MISMATCH");
|
||||
vcp_println("ERROR DATA MISMATCH");
|
||||
goto power_off;
|
||||
}
|
||||
}
|
||||
vcp_printf("OK");
|
||||
vcp_println("OK");
|
||||
|
||||
power_off:
|
||||
sdcard_power_off();
|
||||
@ -436,7 +426,7 @@ static void test_wipe(void) {
|
||||
display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY / 2 + 10, "WIPED", -1,
|
||||
FONT_BOLD, COLOR_WHITE, COLOR_BLACK);
|
||||
display_refresh();
|
||||
vcp_printf("OK");
|
||||
vcp_println("OK");
|
||||
}
|
||||
|
||||
#ifdef USE_SBU
|
||||
@ -444,7 +434,7 @@ static void test_sbu(const char *args) {
|
||||
secbool sbu1 = sectrue * (args[0] == '1');
|
||||
secbool sbu2 = sectrue * (args[1] == '1');
|
||||
sbu_set(sbu1, sbu2);
|
||||
vcp_printf("OK");
|
||||
vcp_println("OK");
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -463,9 +453,9 @@ static void test_otp_read(void) {
|
||||
|
||||
// use (null) for empty data
|
||||
if (data[0] == 0x00) {
|
||||
vcp_printf("OK (null)");
|
||||
vcp_println("OK (null)");
|
||||
} else {
|
||||
vcp_printf("OK %s", (const char *)data);
|
||||
vcp_println("OK %s", (const char *)data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -477,10 +467,23 @@ static void test_otp_write(const char *args) {
|
||||
sizeof(data)),
|
||||
NULL);
|
||||
ensure(flash_otp_lock(FLASH_OTP_BLOCK_BATCH), NULL);
|
||||
vcp_printf("OK");
|
||||
vcp_println("OK");
|
||||
}
|
||||
|
||||
static void test_otp_write_device_variant(const char *args) {
|
||||
#ifdef USE_OPTIGA
|
||||
optiga_locked_status status = get_optiga_locked_status();
|
||||
if (status == OPTIGA_LOCKED_FALSE) {
|
||||
vcp_println("ERROR NOT LOCKED");
|
||||
return;
|
||||
}
|
||||
|
||||
if (status != OPTIGA_LOCKED_TRUE) {
|
||||
// Error reported by get_optiga_locked_status().
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
volatile char data[32];
|
||||
memzero((char *)data, sizeof(data));
|
||||
data[0] = 1;
|
||||
@ -513,7 +516,17 @@ static void test_otp_write_device_variant(const char *args) {
|
||||
(const uint8_t *)data, sizeof(data)),
|
||||
NULL);
|
||||
ensure(flash_otp_lock(FLASH_OTP_BLOCK_DEVICE_VARIANT), NULL);
|
||||
vcp_printf("OK");
|
||||
vcp_println("OK");
|
||||
}
|
||||
|
||||
void cpuid_read(void) {
|
||||
uint32_t cpuid[3];
|
||||
cpuid[0] = LL_GetUID_Word0();
|
||||
cpuid[1] = LL_GetUID_Word1();
|
||||
cpuid[2] = LL_GetUID_Word2();
|
||||
|
||||
vcp_print("OK ");
|
||||
vcp_println_hex((uint8_t *)cpuid, sizeof(cpuid));
|
||||
}
|
||||
|
||||
#define BACKLIGHT_NORMAL 150
|
||||
@ -528,8 +541,10 @@ int main(void) {
|
||||
#ifdef USE_BUTTON
|
||||
button_init();
|
||||
#endif
|
||||
#ifdef USE_TOUCH
|
||||
#ifdef USE_I2C
|
||||
i2c_init();
|
||||
#endif
|
||||
#ifdef USE_TOUCH
|
||||
touch_init();
|
||||
#endif
|
||||
#ifdef USE_SBU
|
||||
@ -537,6 +552,15 @@ int main(void) {
|
||||
#endif
|
||||
usb_init_all();
|
||||
|
||||
#ifdef USE_OPTIGA
|
||||
optiga_init();
|
||||
optiga_open_application();
|
||||
pair_optiga();
|
||||
#endif
|
||||
|
||||
mpu_config_prodtest();
|
||||
drop_privileges();
|
||||
|
||||
display_clear();
|
||||
draw_border(1, 3);
|
||||
|
||||
@ -551,13 +575,17 @@ int main(void) {
|
||||
|
||||
display_fade(0, BACKLIGHT_NORMAL, 1000);
|
||||
|
||||
char line[128];
|
||||
char line[2048]; // expecting hundreds of bytes represented as hexadecimal
|
||||
// characters
|
||||
|
||||
for (;;) {
|
||||
vcp_readline(line, sizeof(line));
|
||||
|
||||
if (startswith(line, "PING")) {
|
||||
vcp_printf("OK");
|
||||
vcp_println("OK");
|
||||
|
||||
} else if (startswith(line, "CPUID READ")) {
|
||||
cpuid_read();
|
||||
|
||||
} else if (startswith(line, "BORDER")) {
|
||||
test_border();
|
||||
@ -584,6 +612,29 @@ int main(void) {
|
||||
#ifdef USE_SBU
|
||||
} else if (startswith(line, "SBU ")) {
|
||||
test_sbu(line + 4);
|
||||
#endif
|
||||
#ifdef USE_OPTIGA
|
||||
} else if (startswith(line, "OPTIGAID READ")) {
|
||||
optigaid_read();
|
||||
} else if (startswith(line, "CERTINF READ")) {
|
||||
cert_read(OID_CERT_INF);
|
||||
} else if (startswith(line, "CERTDEV WRITE ")) {
|
||||
cert_write(OID_CERT_DEV, line + 14);
|
||||
} else if (startswith(line, "CERTDEV READ")) {
|
||||
cert_read(OID_CERT_DEV);
|
||||
} else if (startswith(line, "CERTFIDO WRITE ")) {
|
||||
cert_write(OID_CERT_FIDO, line + 15);
|
||||
} else if (startswith(line, "CERTFIDO READ")) {
|
||||
cert_read(OID_CERT_FIDO);
|
||||
} else if (startswith(line, "KEYFIDO WRITE ")) {
|
||||
keyfido_write(line + 14);
|
||||
} else if (startswith(line, "KEYFIDO READ")) {
|
||||
pubkey_read(OID_KEY_FIDO);
|
||||
} else if (startswith(line, "LOCK")) {
|
||||
optiga_lock();
|
||||
} else if (startswith(line, "CHECK LOCKED")) {
|
||||
check_locked();
|
||||
|
||||
#endif
|
||||
|
||||
} else if (startswith(line, "OTP READ")) {
|
||||
@ -599,7 +650,7 @@ int main(void) {
|
||||
test_wipe();
|
||||
|
||||
} else {
|
||||
vcp_printf("UNKNOWN");
|
||||
vcp_println("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
|
500
core/embed/prodtest/optiga_prodtest.c
Normal file
500
core/embed/prodtest/optiga_prodtest.c
Normal file
@ -0,0 +1,500 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "optiga_prodtest.h"
|
||||
#include "aes/aes.h"
|
||||
#include "ecdsa.h"
|
||||
#include "memzero.h"
|
||||
#include "nist256p1.h"
|
||||
#include "optiga_commands.h"
|
||||
#include "optiga_transport.h"
|
||||
#include "prodtest_common.h"
|
||||
#include "rand.h"
|
||||
#include "secret.h"
|
||||
#include "sha2.h"
|
||||
|
||||
typedef enum {
|
||||
OPTIGA_PAIRING_UNPAIRED = 0,
|
||||
OPTIGA_PAIRING_PAIRED,
|
||||
OPTIGA_PAIRING_ERR_RNG,
|
||||
OPTIGA_PAIRING_ERR_READ,
|
||||
OPTIGA_PAIRING_ERR_HANDSHAKE,
|
||||
} optiga_pairing;
|
||||
|
||||
static optiga_pairing optiga_pairing_state = OPTIGA_PAIRING_UNPAIRED;
|
||||
|
||||
// Data object access conditions.
|
||||
static const optiga_metadata_item ACCESS_PAIRED =
|
||||
OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_CONF, OID_KEY_PAIRING);
|
||||
static const optiga_metadata_item KEY_USE_SIGN = {
|
||||
(const uint8_t[]){OPTIGA_KEY_USAGE_SIGN}, 1};
|
||||
static const optiga_metadata_item TYPE_PTFBIND = {
|
||||
(const uint8_t[]){OPTIGA_DATA_TYPE_PTFBIND}, 1};
|
||||
|
||||
static bool optiga_paired(void) {
|
||||
const char *details = "";
|
||||
|
||||
switch (optiga_pairing_state) {
|
||||
case OPTIGA_PAIRING_PAIRED:
|
||||
return true;
|
||||
case OPTIGA_PAIRING_ERR_RNG:
|
||||
details = "optiga_get_random error";
|
||||
break;
|
||||
case OPTIGA_PAIRING_ERR_READ:
|
||||
details = "failed to read pairing secret";
|
||||
break;
|
||||
case OPTIGA_PAIRING_ERR_HANDSHAKE:
|
||||
details = "optiga_sec_chan_handshake";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
vcp_println("ERROR Optiga not paired (%s).", details);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool set_metadata(uint16_t oid, const optiga_metadata *metadata) {
|
||||
uint8_t serialized[258] = {0};
|
||||
size_t size = 0;
|
||||
optiga_result ret = optiga_serialize_metadata(metadata, serialized,
|
||||
sizeof(serialized), &size);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_serialize_metadata error %d for OID 0x%04x.", ret,
|
||||
oid);
|
||||
return false;
|
||||
}
|
||||
|
||||
optiga_set_data_object(oid, true, serialized, size);
|
||||
|
||||
ret =
|
||||
optiga_get_data_object(oid, true, serialized, sizeof(serialized), &size);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_get_metadata error %d for OID 0x%04x.", ret, oid);
|
||||
return false;
|
||||
}
|
||||
|
||||
optiga_metadata metadata_stored = {0};
|
||||
ret = optiga_parse_metadata(serialized, size, &metadata_stored);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_parse_metadata error %d.", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!optiga_compare_metadata(metadata, &metadata_stored)) {
|
||||
vcp_println("ERROR optiga_compare_metadata failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pair_optiga(void) {
|
||||
// The pairing key may already be written and locked. The success of the
|
||||
// pairing procedure is determined by optiga_sec_chan_handshake(). Therefore
|
||||
// it is OK for some of the intermediate operations to fail.
|
||||
|
||||
// Enable writing the pairing secret to OPTIGA.
|
||||
optiga_metadata metadata = {0};
|
||||
metadata.change = OPTIGA_ACCESS_ALWAYS;
|
||||
metadata.execute = OPTIGA_ACCESS_ALWAYS;
|
||||
metadata.data_type = TYPE_PTFBIND;
|
||||
set_metadata(OID_KEY_PAIRING, &metadata); // Ignore result.
|
||||
|
||||
// Generate pairing secret.
|
||||
uint8_t secret[SECRET_OPTIGA_KEY_LEN] = {0};
|
||||
optiga_result ret = optiga_get_random(secret, sizeof(secret));
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
optiga_pairing_state = OPTIGA_PAIRING_ERR_RNG;
|
||||
return;
|
||||
}
|
||||
|
||||
// Store pairing secret.
|
||||
ret = optiga_set_data_object(OID_KEY_PAIRING, false, secret, sizeof(secret));
|
||||
if (OPTIGA_SUCCESS == ret) {
|
||||
secret_erase();
|
||||
secret_write_header();
|
||||
secret_write(secret, SECRET_OPTIGA_KEY_OFFSET, SECRET_OPTIGA_KEY_LEN);
|
||||
}
|
||||
|
||||
// Verify whether the secret was stored correctly in flash and OPTIGA.
|
||||
memzero(secret, sizeof(secret));
|
||||
if (secret_read(secret, SECRET_OPTIGA_KEY_OFFSET, SECRET_OPTIGA_KEY_LEN) !=
|
||||
sectrue) {
|
||||
optiga_pairing_state = OPTIGA_PAIRING_ERR_READ;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = optiga_sec_chan_handshake(secret, sizeof(secret));
|
||||
memzero(secret, sizeof(secret));
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
optiga_pairing_state = OPTIGA_PAIRING_ERR_HANDSHAKE;
|
||||
return;
|
||||
}
|
||||
|
||||
optiga_pairing_state = OPTIGA_PAIRING_PAIRED;
|
||||
return;
|
||||
}
|
||||
|
||||
void optiga_lock(void) {
|
||||
if (!optiga_paired()) return;
|
||||
|
||||
// Delete trust anchor.
|
||||
optiga_result ret =
|
||||
optiga_set_data_object(OID_TRUST_ANCHOR, false, (const uint8_t *)"\0", 1);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_set_data error %d for 0x%04x.", ret,
|
||||
OID_TRUST_ANCHOR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set data object metadata.
|
||||
optiga_metadata metadata = {0};
|
||||
|
||||
// Set metadata for device certificate.
|
||||
memzero(&metadata, sizeof(metadata));
|
||||
metadata.lcso = OPTIGA_LCS_OPERATIONAL;
|
||||
metadata.change = OPTIGA_ACCESS_NEVER;
|
||||
metadata.read = OPTIGA_ACCESS_ALWAYS;
|
||||
metadata.execute = OPTIGA_ACCESS_ALWAYS;
|
||||
if (!set_metadata(OID_CERT_DEV, &metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set metadata for FIDO attestation certificate.
|
||||
memzero(&metadata, sizeof(metadata));
|
||||
metadata.lcso = OPTIGA_LCS_OPERATIONAL;
|
||||
metadata.change = OPTIGA_ACCESS_NEVER;
|
||||
metadata.read = OPTIGA_ACCESS_ALWAYS;
|
||||
metadata.execute = OPTIGA_ACCESS_ALWAYS;
|
||||
if (!set_metadata(OID_CERT_FIDO, &metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set metadata for device private key.
|
||||
memzero(&metadata, sizeof(metadata));
|
||||
metadata.lcso = OPTIGA_LCS_OPERATIONAL;
|
||||
metadata.change = OPTIGA_ACCESS_NEVER;
|
||||
metadata.read = OPTIGA_ACCESS_NEVER;
|
||||
metadata.execute = ACCESS_PAIRED;
|
||||
metadata.key_usage = KEY_USE_SIGN;
|
||||
if (!set_metadata(OID_KEY_DEV, &metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set metadata for FIDO attestation private key.
|
||||
memzero(&metadata, sizeof(metadata));
|
||||
metadata.lcso = OPTIGA_LCS_OPERATIONAL;
|
||||
metadata.change = OPTIGA_ACCESS_NEVER;
|
||||
metadata.read = OPTIGA_ACCESS_NEVER;
|
||||
metadata.execute = ACCESS_PAIRED;
|
||||
metadata.key_usage = KEY_USE_SIGN;
|
||||
if (!set_metadata(OID_KEY_FIDO, &metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set metadata for pairing key.
|
||||
memzero(&metadata, sizeof(metadata));
|
||||
metadata.lcso = OPTIGA_LCS_OPERATIONAL;
|
||||
metadata.change = OPTIGA_ACCESS_NEVER;
|
||||
metadata.read = OPTIGA_ACCESS_NEVER;
|
||||
metadata.execute = OPTIGA_ACCESS_ALWAYS;
|
||||
metadata.data_type = TYPE_PTFBIND;
|
||||
if (!set_metadata(OID_KEY_PAIRING, &metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
vcp_println("OK");
|
||||
}
|
||||
|
||||
optiga_locked_status get_optiga_locked_status(void) {
|
||||
if (!optiga_paired()) return OPTIGA_LOCKED_ERROR;
|
||||
|
||||
const uint16_t oids[] = {OID_CERT_DEV, OID_CERT_FIDO, OID_KEY_DEV,
|
||||
OID_KEY_FIDO, OID_KEY_PAIRING};
|
||||
|
||||
optiga_metadata locked_metadata = {0};
|
||||
locked_metadata.lcso = OPTIGA_LCS_OPERATIONAL;
|
||||
for (size_t i = 0; i < sizeof(oids) / sizeof(oids[0]); ++i) {
|
||||
uint8_t metadata_buffer[258] = {0};
|
||||
size_t metadata_size = 0;
|
||||
optiga_result ret =
|
||||
optiga_get_data_object(oids[i], true, metadata_buffer,
|
||||
sizeof(metadata_buffer), &metadata_size);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_get_metadata error %d for OID 0x%04x.", ret,
|
||||
oids[i]);
|
||||
return OPTIGA_LOCKED_ERROR;
|
||||
}
|
||||
|
||||
optiga_metadata stored_metadata = {0};
|
||||
ret =
|
||||
optiga_parse_metadata(metadata_buffer, metadata_size, &stored_metadata);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_parse_metadata error %d.", ret);
|
||||
return OPTIGA_LOCKED_ERROR;
|
||||
}
|
||||
|
||||
if (!optiga_compare_metadata(&locked_metadata, &stored_metadata)) {
|
||||
return OPTIGA_LOCKED_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return OPTIGA_LOCKED_TRUE;
|
||||
}
|
||||
|
||||
void check_locked(void) {
|
||||
switch (get_optiga_locked_status()) {
|
||||
case OPTIGA_LOCKED_TRUE:
|
||||
vcp_println("OK YES");
|
||||
break;
|
||||
case OPTIGA_LOCKED_FALSE:
|
||||
vcp_println("OK NO");
|
||||
break;
|
||||
default:
|
||||
// Error reported by get_optiga_locked_status().
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void optigaid_read(void) {
|
||||
if (!optiga_paired()) return;
|
||||
|
||||
uint8_t optiga_id[27] = {0};
|
||||
size_t optiga_id_size = 0;
|
||||
|
||||
optiga_result ret =
|
||||
optiga_get_data_object(OPTIGA_OID_COPROC_UID, false, optiga_id,
|
||||
sizeof(optiga_id), &optiga_id_size);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_get_data_object error %d for 0x%04x.", ret,
|
||||
OPTIGA_OID_COPROC_UID);
|
||||
return;
|
||||
}
|
||||
|
||||
vcp_print("OK ");
|
||||
vcp_println_hex(optiga_id, optiga_id_size);
|
||||
}
|
||||
|
||||
void cert_read(uint16_t oid) {
|
||||
if (!optiga_paired()) return;
|
||||
|
||||
static uint8_t cert[2048] = {0};
|
||||
size_t cert_size = 0;
|
||||
optiga_result ret =
|
||||
optiga_get_data_object(oid, false, cert, sizeof(cert), &cert_size);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_get_data_object error %d for 0x%04x.", ret, oid);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
if (cert[0] == 0xC0) {
|
||||
// TLS identity certificate chain.
|
||||
size_t tls_identity_size = (cert[1] << 8) + cert[2];
|
||||
size_t cert_chain_size = (cert[3] << 16) + (cert[4] << 8) + cert[5];
|
||||
size_t first_cert_size = (cert[6] << 16) + (cert[7] << 8) + cert[8];
|
||||
if (tls_identity_size + 3 > cert_size ||
|
||||
cert_chain_size + 3 > tls_identity_size ||
|
||||
first_cert_size > cert_chain_size) {
|
||||
vcp_println("ERROR invalid TLS identity in 0x%04x.", oid);
|
||||
return;
|
||||
}
|
||||
offset = 9;
|
||||
cert_size = first_cert_size;
|
||||
}
|
||||
|
||||
if (cert_size == 0) {
|
||||
vcp_println("ERROR no certificate in 0x%04x.", oid);
|
||||
return;
|
||||
}
|
||||
|
||||
vcp_print("OK ");
|
||||
vcp_println_hex(cert + offset, cert_size);
|
||||
}
|
||||
|
||||
void cert_write(uint16_t oid, char *data) {
|
||||
if (!optiga_paired()) return;
|
||||
|
||||
// Enable writing to the certificate slot.
|
||||
optiga_metadata metadata = {0};
|
||||
metadata.change = OPTIGA_ACCESS_ALWAYS;
|
||||
set_metadata(oid, &metadata); // Ignore result.
|
||||
|
||||
uint8_t data_bytes[1024];
|
||||
|
||||
int len = get_from_hex(data_bytes, sizeof(data_bytes), data);
|
||||
if (len < 0) {
|
||||
vcp_println("ERROR Hexadecimal decoding error %d.", len);
|
||||
return;
|
||||
}
|
||||
|
||||
optiga_result ret = optiga_set_data_object(oid, false, data_bytes, len);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_set_data error %d for 0x%04x.", ret, oid);
|
||||
return;
|
||||
}
|
||||
|
||||
vcp_println("OK");
|
||||
}
|
||||
|
||||
void pubkey_read(uint16_t oid) {
|
||||
if (!optiga_paired()) return;
|
||||
|
||||
// Enable key agreement usage.
|
||||
|
||||
optiga_metadata metadata = {0};
|
||||
uint8_t key_usage = OPTIGA_KEY_USAGE_KEYAGREE;
|
||||
metadata.key_usage.ptr = &key_usage;
|
||||
metadata.key_usage.len = 1;
|
||||
metadata.execute = OPTIGA_ACCESS_ALWAYS;
|
||||
|
||||
if (!set_metadata(oid, &metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute ECDH with base point to get the x-coordinate of the public key.
|
||||
static const uint8_t BASE_POINT[] = {
|
||||
0x03, 0x42, 0x00, 0x04, 0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47,
|
||||
0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2, 0x77, 0x03, 0x7d, 0x81,
|
||||
0x2d, 0xeb, 0x33, 0xa0, 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96,
|
||||
0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, 0x8e, 0xe7, 0xeb, 0x4a,
|
||||
0x7c, 0x0f, 0x9e, 0x16, 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
|
||||
0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5};
|
||||
uint8_t public_key[32] = {0};
|
||||
size_t public_key_size = 0;
|
||||
optiga_result ret =
|
||||
optiga_calc_ssec(OPTIGA_CURVE_P256, oid, BASE_POINT, sizeof(BASE_POINT),
|
||||
public_key, sizeof(public_key), &public_key_size);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_calc_ssec error %d.", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
vcp_print("OK ");
|
||||
vcp_println_hex(public_key, public_key_size);
|
||||
}
|
||||
|
||||
void keyfido_write(char *data) {
|
||||
if (!optiga_paired()) return;
|
||||
|
||||
const size_t EPH_PUB_KEY_SIZE = 33;
|
||||
const size_t PAYLOAD_SIZE = 32;
|
||||
const size_t CIPHERTEXT_OFFSET = EPH_PUB_KEY_SIZE;
|
||||
const size_t EXPECTED_SIZE = EPH_PUB_KEY_SIZE + PAYLOAD_SIZE;
|
||||
|
||||
// Enable key agreement usage for device key.
|
||||
|
||||
optiga_metadata metadata = {0};
|
||||
uint8_t key_usage = OPTIGA_KEY_USAGE_KEYAGREE;
|
||||
metadata.key_usage.ptr = &key_usage;
|
||||
metadata.key_usage.len = 1;
|
||||
metadata.execute = OPTIGA_ACCESS_ALWAYS;
|
||||
|
||||
if (!set_metadata(OID_KEY_DEV, &metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read encrypted FIDO attestation private key.
|
||||
|
||||
uint8_t data_bytes[EXPECTED_SIZE];
|
||||
int len = get_from_hex(data_bytes, sizeof(data_bytes), data);
|
||||
if (len < 0) {
|
||||
vcp_println("ERROR Hexadecimal decoding error %d.", len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (len != EXPECTED_SIZE) {
|
||||
vcp_println("ERROR Unexpected input length.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Expand sender's ephemeral public key.
|
||||
uint8_t public_key[3 + 65] = {0x03, 0x42, 0x00};
|
||||
if (ecdsa_uncompress_pubkey(&nist256p1, data_bytes, &public_key[3]) != 1) {
|
||||
vcp_println("ERROR Failed to decode public key.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute ECDH with device private key.
|
||||
uint8_t secret[32] = {0};
|
||||
size_t secret_size = 0;
|
||||
optiga_result ret = optiga_calc_ssec(OPTIGA_CURVE_P256, OID_KEY_DEV,
|
||||
public_key, sizeof(public_key), secret,
|
||||
sizeof(secret), &secret_size);
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
memzero(secret, sizeof(secret));
|
||||
vcp_println("ERROR optiga_calc_ssec error %d.", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hash the shared secret. Use the result as the decryption key.
|
||||
sha256_Raw(secret, secret_size, secret);
|
||||
aes_decrypt_ctx ctx = {0};
|
||||
AES_RETURN aes_ret = aes_decrypt_key256(secret, &ctx);
|
||||
if (EXIT_SUCCESS != aes_ret) {
|
||||
vcp_println("ERROR aes_decrypt_key256 error.");
|
||||
memzero(&ctx, sizeof(ctx));
|
||||
memzero(secret, sizeof(secret));
|
||||
return;
|
||||
}
|
||||
|
||||
// Decrypt the FIDO attestation key.
|
||||
uint8_t fido_key[PAYLOAD_SIZE];
|
||||
|
||||
// The IV is intentionally all-zero, which is not a problem, because the
|
||||
// encryption key is unique for each ciphertext.
|
||||
uint8_t iv[AES_BLOCK_SIZE] = {0};
|
||||
aes_ret = aes_cbc_decrypt(&data_bytes[CIPHERTEXT_OFFSET], fido_key,
|
||||
sizeof(fido_key), iv, &ctx);
|
||||
memzero(&ctx, sizeof(ctx));
|
||||
memzero(secret, sizeof(secret));
|
||||
if (EXIT_SUCCESS != aes_ret) {
|
||||
memzero(fido_key, sizeof(fido_key));
|
||||
vcp_println("ERROR aes_cbc_decrypt error.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Write trust anchor certificate to OID 0xE0E8
|
||||
ret = optiga_set_trust_anchor();
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
memzero(fido_key, sizeof(fido_key));
|
||||
vcp_println("ERROR optiga_set_trust_anchor error %d.", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set change access condition for the FIDO key to Int(0xE0E8), so that we
|
||||
// can write the FIDO key using the trust anchor in OID 0xE0E8.
|
||||
memzero(&metadata, sizeof(metadata));
|
||||
metadata.change.ptr = (const uint8_t *)"\x21\xe0\xe8";
|
||||
metadata.change.len = 3;
|
||||
if (!set_metadata(OID_KEY_FIDO, &metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the FIDO attestation key.
|
||||
ret = optiga_set_priv_key(OID_KEY_FIDO, fido_key);
|
||||
memzero(fido_key, sizeof(fido_key));
|
||||
if (OPTIGA_SUCCESS != ret) {
|
||||
vcp_println("ERROR optiga_set_priv_key error %d.", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
vcp_println("OK");
|
||||
}
|
51
core/embed/prodtest/optiga_prodtest.h
Normal file
51
core/embed/prodtest/optiga_prodtest.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PRODTEST_OPTIGA_PRODTESTS_H
|
||||
#define PRODTEST_OPTIGA_PRODTEST_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define OID_CERT_INF OPTIGA_OID_CERT + 0
|
||||
#define OID_CERT_DEV OPTIGA_OID_CERT + 1
|
||||
#define OID_CERT_FIDO OPTIGA_OID_CERT + 2
|
||||
#define OID_KEY_DEV OPTIGA_OID_ECC_KEY + 0
|
||||
#define OID_KEY_FIDO OPTIGA_OID_ECC_KEY + 2
|
||||
#define OID_KEY_PAIRING OPTIGA_OID_PTFBIND_SECRET
|
||||
#define OID_TRUST_ANCHOR OPTIGA_OID_CA_CERT + 0
|
||||
|
||||
typedef enum {
|
||||
OPTIGA_LOCKED_TRUE,
|
||||
OPTIGA_LOCKED_FALSE,
|
||||
OPTIGA_LOCKED_ERROR,
|
||||
} optiga_locked_status;
|
||||
|
||||
void pair_optiga(void);
|
||||
void optigaid_read(void);
|
||||
void cert_read(uint16_t oid);
|
||||
void cert_write(uint16_t oid, char *data);
|
||||
void keyfido_write(char *data);
|
||||
void pubkey_read(uint16_t oid);
|
||||
void optiga_lock(void);
|
||||
optiga_locked_status get_optiga_locked_status(void);
|
||||
void check_locked(void);
|
||||
|
||||
#endif
|
102
core/embed/prodtest/prodtest_common.c
Normal file
102
core/embed/prodtest/prodtest_common.c
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "prodtest_common.h"
|
||||
#include "mini_printf.h"
|
||||
#include "usb.h"
|
||||
|
||||
void vcp_puts(const char *s, size_t len) {
|
||||
int r = usb_vcp_write_blocking(VCP_IFACE, (const uint8_t *)s, len, -1);
|
||||
(void)r;
|
||||
}
|
||||
|
||||
void vcp_print(const char *fmt, ...) {
|
||||
static char buf[128];
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
int r = mini_vsnprintf(buf, sizeof(buf), fmt, va);
|
||||
va_end(va);
|
||||
vcp_puts(buf, r);
|
||||
}
|
||||
|
||||
void vcp_println(const char *fmt, ...) {
|
||||
static char buf[128];
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
int r = mini_vsnprintf(buf, sizeof(buf), fmt, va);
|
||||
va_end(va);
|
||||
vcp_puts(buf, r);
|
||||
vcp_puts("\r\n", 2);
|
||||
}
|
||||
|
||||
void vcp_println_hex(uint8_t *data, uint16_t len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
vcp_print("%02X", data[i]);
|
||||
}
|
||||
vcp_puts("\r\n", 2);
|
||||
}
|
||||
|
||||
static uint16_t get_byte_from_hex(const char **hex) {
|
||||
uint8_t result = 0;
|
||||
|
||||
// Skip whitespace.
|
||||
while (**hex == ' ') {
|
||||
*hex += 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
result <<= 4;
|
||||
char c = **hex;
|
||||
if (c >= '0' && c <= '9') {
|
||||
result |= c - '0';
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
result |= c - 'A' + 10;
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
result |= c - 'a' + 10;
|
||||
} else if (c == '\0') {
|
||||
return 0x100;
|
||||
} else {
|
||||
return 0xFFFF;
|
||||
}
|
||||
*hex += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int get_from_hex(uint8_t *buf, uint16_t buf_len, const char *hex) {
|
||||
int len = 0;
|
||||
uint16_t b = get_byte_from_hex(&hex);
|
||||
for (len = 0; len < buf_len && b <= 0xff; ++len) {
|
||||
buf[len] = b;
|
||||
b = get_byte_from_hex(&hex);
|
||||
}
|
||||
|
||||
if (b == 0x100) {
|
||||
// Success.
|
||||
return len;
|
||||
}
|
||||
|
||||
if (b > 0xff) {
|
||||
// Non-hexadecimal character.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Buffer too small.
|
||||
return -2;
|
||||
}
|
34
core/embed/prodtest/prodtest_common.h
Normal file
34
core/embed/prodtest/prodtest_common.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PRODTEST_COMMON_H
|
||||
#define PRODTEST_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
enum { VCP_IFACE = 0x00 };
|
||||
|
||||
void vcp_puts(const char *s, size_t len);
|
||||
void vcp_print(const char *fmt, ...);
|
||||
void vcp_println(const char *fmt, ...);
|
||||
void vcp_println_hex(uint8_t *data, uint16_t len);
|
||||
int get_from_hex(uint8_t *buf, uint16_t buf_len, const char *hex);
|
||||
|
||||
#endif
|
@ -23,5 +23,6 @@
|
||||
void mpu_config_off(void);
|
||||
void mpu_config_bootloader(void);
|
||||
void mpu_config_firmware(void);
|
||||
void mpu_config_prodtest(void);
|
||||
|
||||
#endif
|
||||
|
@ -39,8 +39,10 @@ static size_t tx_size = 0;
|
||||
// TODO change to operational \x07
|
||||
const optiga_metadata_item OPTIGA_LCS_OPERATIONAL = {(const uint8_t *)"\x01",
|
||||
1};
|
||||
const optiga_metadata_item OPTIGA_ACCESS_ALWAYS = {(const uint8_t *)"\x00", 1};
|
||||
const optiga_metadata_item OPTIGA_ACCESS_NEVER = {(const uint8_t *)"\xFF", 1};
|
||||
const optiga_metadata_item OPTIGA_ACCESS_ALWAYS = {
|
||||
(const uint8_t[]){OPTIGA_ACCESS_COND_ALW}, 1};
|
||||
const optiga_metadata_item OPTIGA_ACCESS_NEVER = {
|
||||
(const uint8_t[]){OPTIGA_ACCESS_COND_NEV}, 1};
|
||||
const optiga_metadata_item OPTIGA_VERSION_DEFAULT = {
|
||||
(const uint8_t *)"\xC1\x02\x00\x00", 4};
|
||||
|
||||
@ -265,7 +267,7 @@ optiga_result optiga_get_error_code(uint8_t *error_code) {
|
||||
*(ptr++) = 0x00; // get data
|
||||
write_uint16(&ptr, tx_size - 4);
|
||||
|
||||
write_uint16(&ptr, 0xf1c2); // error code data object OID
|
||||
write_uint16(&ptr, OPTIGA_OID_ERROR_CODE);
|
||||
|
||||
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
|
||||
sizeof(tx_buffer), &tx_size);
|
||||
|
@ -25,6 +25,19 @@
|
||||
#include <stdint.h>
|
||||
#include "optiga_common.h"
|
||||
|
||||
// Data object identifiers.
|
||||
typedef enum {
|
||||
OPTIGA_OID_COPROC_UID = 0xE0C2, // Coprocessor UID.
|
||||
OPTIGA_OID_CERT = 0xE0E0, // Public key certificates [1-4].
|
||||
OPTIGA_OID_CA_CERT = 0xE0E8, // Root CA public key certificates [1-2].
|
||||
OPTIGA_OID_COUNTER = 0xE120, // Monotonic counters [1-4].
|
||||
OPTIGA_OID_ECC_KEY = 0xE0F0, // Private ECC keys [1-4].
|
||||
OPTIGA_OID_PTFBIND_SECRET = 0xE140, // Shared platform binding secret.
|
||||
OPTIGA_OID_ERROR_CODE = 0xF1C2, // Command error code.
|
||||
OPTIGA_OID_DATA = 0xF1D0, // Arbitrary 140 B data objects [1-12].
|
||||
OPTIGA_OID_BIG_DATA = 0xF1E0, // Arbitrary 1500 B data objects [1-2].
|
||||
} optiga_oid;
|
||||
|
||||
// ECC curve identifiers.
|
||||
typedef enum {
|
||||
OPTIGA_CURVE_P256 = 0x03, // NIST P256 ECC key.
|
||||
@ -79,6 +92,16 @@ typedef enum {
|
||||
OPTIGA_DATA_TYPE_AUTOREF = 0x31, // Secret for verifying external entity.
|
||||
} optiga_data_type;
|
||||
|
||||
// Access conditions.
|
||||
typedef enum {
|
||||
OPTIGA_ACCESS_COND_ALW = 0x00, // Always.
|
||||
OPTIGA_ACCESS_COND_CONF = 0x20, // Confidentiality protection required.
|
||||
OPTIGA_ACCESS_COND_INT = 0x21, // Integrity protection required.
|
||||
OPTIGA_ACCESS_COND_AUTO = 0x23, // Authorization required.
|
||||
OPTIGA_ACCESS_COND_LUC = 0x40, // Usage limited by counter.
|
||||
OPTIGA_ACCESS_COND_NEV = 0xFF, // Never.
|
||||
} optiga_access_cond;
|
||||
|
||||
typedef struct {
|
||||
const uint8_t *ptr;
|
||||
uint16_t len;
|
||||
@ -99,6 +122,9 @@ typedef struct {
|
||||
optiga_metadata_item reset_type; // F0 - Factory reset type.
|
||||
} optiga_metadata;
|
||||
|
||||
#define OPTIGA_ACCESS_CONDITION(ac_id, oid) \
|
||||
{ (const uint8_t[]){ac_id, oid >> 8, oid & 0xff}, 3 }
|
||||
|
||||
extern const optiga_metadata_item OPTIGA_LCS_OPERATIONAL;
|
||||
extern const optiga_metadata_item OPTIGA_ACCESS_ALWAYS;
|
||||
extern const optiga_metadata_item OPTIGA_ACCESS_NEVER;
|
||||
|
@ -194,3 +194,98 @@ void mpu_config_firmware(void) {
|
||||
__asm__ volatile("dsb");
|
||||
__asm__ volatile("isb");
|
||||
}
|
||||
|
||||
void mpu_config_prodtest(void) {
|
||||
// Disable MPU
|
||||
HAL_MPU_Disable();
|
||||
|
||||
// Note: later entries overwrite previous ones
|
||||
|
||||
// // Boardloader (0x08000000 - 0x0800BFFF, 48 KiB, read-only, execute never)
|
||||
// MPU->RNR = MPU_REGION_NUMBER0;
|
||||
// MPU->RBAR = FLASH_BASE;
|
||||
// MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH |
|
||||
// LL_MPU_REGION_SIZE_64KB | LL_MPU_REGION_PRIV_RO_URO |
|
||||
// MPU_RASR_XN_Msk | MPU_SUBREGION_DISABLE(0xC0);
|
||||
|
||||
// Secret area (0x08100000 - 0x08103FFF, 16 KiB, read-write, execute never)
|
||||
// MPU->RNR = MPU_REGION_NUMBER0;
|
||||
// MPU->RBAR = FLASH_BASE + 0x100000;
|
||||
// MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH |
|
||||
// LL_MPU_REGION_SIZE_16KB | LL_MPU_REGION_FULL_ACCESS |
|
||||
// MPU_RASR_XN_Msk;
|
||||
|
||||
// Bootloader (0x08020000 - 0x0803FFFF, 64 KiB, read-only)
|
||||
MPU->RNR = MPU_REGION_NUMBER1;
|
||||
MPU->RBAR = FLASH_BASE + 0x20000;
|
||||
MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH |
|
||||
LL_MPU_REGION_SIZE_64KB | LL_MPU_REGION_PRIV_RO_URO;
|
||||
|
||||
// Firmware (0x08040000 - 0x080FFFFF, 6 * 128 KiB = 1024 KiB except 2/8 at
|
||||
// start = 768 KiB, read-only)
|
||||
MPU->RNR = MPU_REGION_NUMBER2;
|
||||
MPU->RBAR = FLASH_BASE;
|
||||
MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH |
|
||||
LL_MPU_REGION_SIZE_1MB | LL_MPU_REGION_FULL_ACCESS |
|
||||
MPU_SUBREGION_DISABLE(0x03);
|
||||
|
||||
// Firmware extra (0x08120000 - 0x081FFFFF, 7 * 128 KiB = 1024 KiB except 1/8
|
||||
// at start = 896 KiB, read-only)
|
||||
MPU->RNR = MPU_REGION_NUMBER3;
|
||||
MPU->RBAR = FLASH_BASE + 0x100000;
|
||||
MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH |
|
||||
LL_MPU_REGION_SIZE_1MB | LL_MPU_REGION_FULL_ACCESS |
|
||||
MPU_SUBREGION_DISABLE(0x01);
|
||||
|
||||
// SRAM (0x20000000 - 0x2002FFFF, 192 KiB = 256 KiB except 2/8 at end,
|
||||
// read-write, execute never)
|
||||
MPU->RNR = MPU_REGION_NUMBER4;
|
||||
MPU->RBAR = SRAM_BASE;
|
||||
MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_SRAM |
|
||||
LL_MPU_REGION_SIZE_256KB | LL_MPU_REGION_FULL_ACCESS |
|
||||
MPU_RASR_XN_Msk | MPU_SUBREGION_DISABLE(0xC0);
|
||||
|
||||
#ifdef USE_SDRAM
|
||||
// Peripherals (0x40000000 - 0x5FFFFFFF, read-write, execute never)
|
||||
// SDRAM (0xC0000000 - 0xDFFFFFFF, read-write, execute never)
|
||||
MPU->RNR = MPU_REGION_NUMBER5;
|
||||
MPU->RBAR = 0;
|
||||
MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_PERIPH |
|
||||
LL_MPU_REGION_SIZE_4GB | LL_MPU_REGION_FULL_ACCESS |
|
||||
MPU_RASR_XN_Msk | MPU_SUBREGION_DISABLE(0xBB);
|
||||
#else
|
||||
// Peripherals (0x40000000 - 0x5FFFFFFF, read-write, execute never)
|
||||
// External RAM (0x60000000 - 0x7FFFFFFF, read-write, execute never)
|
||||
MPU->RNR = MPU_REGION_NUMBER5;
|
||||
MPU->RBAR = PERIPH_BASE;
|
||||
MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_PERIPH |
|
||||
LL_MPU_REGION_SIZE_1GB | LL_MPU_REGION_FULL_ACCESS |
|
||||
MPU_RASR_XN_Msk;
|
||||
#endif
|
||||
|
||||
#if defined STM32F427xx || defined STM32F429xx
|
||||
// CCMRAM (0x10000000 - 0x1000FFFF, read-write, execute never)
|
||||
MPU->RNR = MPU_REGION_NUMBER6;
|
||||
MPU->RBAR = CCMDATARAM_BASE;
|
||||
MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_SRAM |
|
||||
LL_MPU_REGION_SIZE_64KB | LL_MPU_REGION_FULL_ACCESS |
|
||||
MPU_RASR_XN_Msk;
|
||||
#elif STM32F405xx
|
||||
// no CCMRAM
|
||||
#else
|
||||
#error Unsupported MCU
|
||||
#endif
|
||||
|
||||
// OTP (0x1FFF7800 - 0x1FFF7C00, read-write, execute never)
|
||||
MPU->RNR = MPU_REGION_NUMBER7;
|
||||
MPU->RBAR = FLASH_OTP_BASE;
|
||||
MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH |
|
||||
LL_MPU_REGION_SIZE_1KB | LL_MPU_REGION_FULL_ACCESS |
|
||||
MPU_RASR_XN_Msk;
|
||||
|
||||
// Enable MPU
|
||||
HAL_MPU_Enable(LL_MPU_CTRL_HARDFAULT_NMI);
|
||||
|
||||
__asm__ volatile("dsb");
|
||||
__asm__ volatile("isb");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user