Andrew Kozlik 1 month ago committed by GitHub
commit efb710f70e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -104,6 +104,26 @@
// from util.s
extern void shutdown_privileged(void);
#ifdef USE_OPTIGA
#if !PYOPT
#include <inttypes.h>
#if 1 // color log
#define OPTIGA_LOG_FORMAT \
"%" PRIu32 " \x1b[35moptiga\x1b[0m \x1b[32mDEBUG\x1b[0m %s: "
#else
#define OPTIGA_LOG_FORMAT "%" PRIu32 " optiga DEBUG %s: "
#endif
static void optiga_log_hex(const char *prefix, const uint8_t *data,
size_t data_size) {
printf(OPTIGA_LOG_FORMAT, hal_ticks_ms() * 1000, prefix);
for (size_t i = 0; i < data_size; i++) {
printf("%02x", data[i]);
}
printf("\n");
}
#endif
#endif
int main(void) {
random_delays_init();
@ -199,6 +219,14 @@ int main(void) {
#endif
#ifdef USE_OPTIGA
#if !PYOPT
// command log is relatively quiet so we enable it in debug builds
optiga_command_set_log_hex(optiga_log_hex);
// transport log can be spammy, uncomment if you want it:
// optiga_transport_set_log_hex(optiga_log_hex);
#endif
optiga_init();
optiga_open_application();
if (sectrue == secret_ok) {

@ -46,10 +46,10 @@ 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 const optiga_metadata_item KEY_USE_SIGN =
OPTIGA_META_VALUE(OPTIGA_KEY_USAGE_SIGN);
static const optiga_metadata_item TYPE_PTFBIND =
OPTIGA_META_VALUE(OPTIGA_DATA_TYPE_PTFBIND);
// Identifier of context-specific constructed tag 3, which is used for
// extensions in X.509.
@ -173,6 +173,13 @@ void pair_optiga(void) {
return;
}
#if PRODUCTION
#define METADATA_SET_LOCKED(metadata) \
{ metadata.lcso = OPTIGA_META_LCS_OPERATIONAL; }
#else
#define METADATA_SET_LOCKED(metadata)
#endif
void optiga_lock(void) {
if (!optiga_paired()) return;
@ -190,7 +197,7 @@ void optiga_lock(void) {
// Set metadata for device certificate.
memzero(&metadata, sizeof(metadata));
metadata.lcso = OPTIGA_META_LCS_OPERATIONAL;
METADATA_SET_LOCKED(metadata);
metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_ALWAYS;
metadata.execute = OPTIGA_META_ACCESS_ALWAYS;
@ -200,7 +207,7 @@ void optiga_lock(void) {
// Set metadata for FIDO attestation certificate.
memzero(&metadata, sizeof(metadata));
metadata.lcso = OPTIGA_META_LCS_OPERATIONAL;
METADATA_SET_LOCKED(metadata);
metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_ALWAYS;
metadata.execute = OPTIGA_META_ACCESS_ALWAYS;
@ -210,7 +217,7 @@ void optiga_lock(void) {
// Set metadata for device private key.
memzero(&metadata, sizeof(metadata));
metadata.lcso = OPTIGA_META_LCS_OPERATIONAL;
METADATA_SET_LOCKED(metadata);
metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_NEVER;
metadata.execute = ACCESS_PAIRED;
@ -221,7 +228,7 @@ void optiga_lock(void) {
// Set metadata for FIDO attestation private key.
memzero(&metadata, sizeof(metadata));
metadata.lcso = OPTIGA_META_LCS_OPERATIONAL;
METADATA_SET_LOCKED(metadata);
metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_NEVER;
metadata.execute = ACCESS_PAIRED;
@ -232,7 +239,7 @@ void optiga_lock(void) {
// Set metadata for pairing key.
memzero(&metadata, sizeof(metadata));
metadata.lcso = OPTIGA_META_LCS_OPERATIONAL;
METADATA_SET_LOCKED(metadata);
metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_NEVER;
metadata.execute = OPTIGA_META_ACCESS_ALWAYS;
@ -503,6 +510,14 @@ void keyfido_write(char *data) {
return;
}
// Set the data type of OID 0xE0E8 to trust anchor, so that we can use it to
// write the FIDO key.
memzero(&metadata, sizeof(metadata));
metadata.data_type = OPTIGA_META_VALUE(OPTIGA_DATA_TYPE_TA);
if (!set_metadata(OID_TRUST_ANCHOR, &metadata)) {
return;
}
// Write trust anchor certificate to OID 0xE0E8
ret = optiga_set_trust_anchor();
if (OPTIGA_SUCCESS != ret) {
@ -514,8 +529,8 @@ void keyfido_write(char *data) {
// 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 = (const optiga_metadata_item)OPTIGA_ACCESS_CONDITION(
OPTIGA_ACCESS_COND_INT, OID_TRUST_ANCHOR);
metadata.change =
OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_INT, OID_TRUST_ANCHOR);
metadata.version = OPTIGA_META_VERSION_DEFAULT;
if (!set_metadata(OID_KEY_FIDO, &metadata)) {
return;

@ -24,11 +24,11 @@
#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_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)

@ -45,7 +45,7 @@ void vcp_println(const char *fmt, ...) {
vcp_puts("\r\n", 2);
}
void vcp_println_hex(uint8_t *data, uint16_t len) {
void vcp_println_hex(const uint8_t *data, uint16_t len) {
for (int i = 0; i < len; i++) {
vcp_print("%02X", data[i]);
}

@ -28,7 +28,7 @@ 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);
void vcp_println_hex(const uint8_t *data, uint16_t len);
int get_from_hex(uint8_t *buf, uint16_t buf_len, const char *hex);
#endif

@ -58,10 +58,10 @@ static const uint8_t COUNTER_RESET[] = {0, 0, 0, 0, 0, 0, 0, PIN_MAX_TRIES};
// 100000 / PIN_STRETCH_ITERATIONS unlock operations.
static const uint8_t STRETCH_COUNTER_INIT[] = {0, 0, 0, 0, 0, 0x09, 0x27, 0xC0};
static const optiga_metadata_item TYPE_AUTOREF = {
(const uint8_t[]){OPTIGA_DATA_TYPE_AUTOREF}, 1};
static const optiga_metadata_item TYPE_PRESSEC = {
(const uint8_t[]){OPTIGA_DATA_TYPE_PRESSEC}, 1};
static const optiga_metadata_item TYPE_AUTOREF =
OPTIGA_META_VALUE(OPTIGA_DATA_TYPE_AUTOREF);
static const optiga_metadata_item TYPE_PRESSEC =
OPTIGA_META_VALUE(OPTIGA_DATA_TYPE_PRESSEC);
static const optiga_metadata_item ACCESS_STRETCHED_PIN =
OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_AUTO, OID_STRETCHED_PIN);
static const optiga_metadata_item ACCESS_PIN_SECRET =
@ -215,7 +215,8 @@ bool optiga_set_metadata(uint16_t oid, const optiga_metadata *metadata) {
}
}
// If the metadata aren't locked, then lock them.
#if PRODUCTION
// If the metadata aren't locked, then lock them in production builds.
optiga_metadata metadata_locked = {0};
metadata_locked.lcso = OPTIGA_META_LCS_OPERATIONAL;
if (!optiga_compare_metadata(&metadata_locked, &metadata_stored)) {
@ -231,6 +232,7 @@ bool optiga_set_metadata(uint16_t oid, const optiga_metadata *metadata) {
return false;
}
}
#endif
return true;
}

@ -37,19 +37,30 @@
static uint8_t tx_buffer[OPTIGA_MAX_APDU_SIZE] = {0};
static size_t tx_size = 0;
const optiga_metadata_item OPTIGA_META_LCS_OPERATIONAL = {
(const uint8_t *)"\x07", 1};
const optiga_metadata_item OPTIGA_META_ACCESS_ALWAYS = {
(const uint8_t[]){OPTIGA_ACCESS_COND_ALW}, 1};
const optiga_metadata_item OPTIGA_META_ACCESS_NEVER = {
(const uint8_t[]){OPTIGA_ACCESS_COND_NEV}, 1};
const optiga_metadata_item OPTIGA_META_KEY_USE_ENC = {
(const uint8_t[]){OPTIGA_KEY_USAGE_ENC}, 1};
const optiga_metadata_item OPTIGA_META_KEY_USE_KEYAGREE = {
(const uint8_t[]){OPTIGA_KEY_USAGE_KEYAGREE}, 1};
const optiga_metadata_item OPTIGA_META_LCS_OPERATIONAL =
OPTIGA_META_VALUE(OPTIGA_LCS_OP);
const optiga_metadata_item OPTIGA_META_ACCESS_ALWAYS =
OPTIGA_META_VALUE(OPTIGA_ACCESS_COND_ALW);
const optiga_metadata_item OPTIGA_META_ACCESS_NEVER =
OPTIGA_META_VALUE(OPTIGA_ACCESS_COND_NEV);
const optiga_metadata_item OPTIGA_META_KEY_USE_ENC =
OPTIGA_META_VALUE(OPTIGA_KEY_USAGE_ENC);
const optiga_metadata_item OPTIGA_META_KEY_USE_KEYAGREE =
OPTIGA_META_VALUE(OPTIGA_KEY_USAGE_KEYAGREE);
const optiga_metadata_item OPTIGA_META_VERSION_DEFAULT = {
(const uint8_t[]){0x00, 0x00}, 2};
#if PRODUCTION
#define OPTIGA_LOG(prefix, data, data_size)
#else
static optiga_log_hex_t log_hex = NULL;
void optiga_command_set_log_hex(optiga_log_hex_t f) { log_hex = f; }
#define OPTIGA_LOG(prefix, data, data_size) \
if (log_hex != NULL) { \
log_hex(prefix, data, data_size); \
}
#endif
static optiga_result process_output(uint8_t **out_data, size_t *out_size) {
// Check that there is no trailing output data in the response.
if (tx_size < 4 || (tx_buffer[2] << 8) + tx_buffer[3] != tx_size - 4) {
@ -58,11 +69,13 @@ static optiga_result process_output(uint8_t **out_data, size_t *out_size) {
// Check response status code.
if (tx_buffer[0] != 0) {
OPTIGA_LOG("FAILED", NULL, 0)
return OPTIGA_ERR_CMD;
}
*out_data = tx_buffer + 4;
*out_size = tx_size - 4;
OPTIGA_LOG("SUCCESS", *out_data, *out_size)
return OPTIGA_SUCCESS;
}
@ -270,6 +283,7 @@ optiga_result optiga_open_application(void) {
0x65, 0x6E, 0x41, 0x75, 0x74, 0x68, 0x41, 0x70, 0x70, 0x6C,
};
OPTIGA_LOG(__func__, OPEN_APP, sizeof(OPEN_APP))
optiga_result ret = optiga_execute_command(
OPEN_APP, sizeof(OPEN_APP), tx_buffer, sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -288,6 +302,7 @@ optiga_result optiga_get_error_code(uint8_t *error_code) {
write_uint16(&ptr, OPTIGA_OID_ERROR_CODE);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -311,6 +326,7 @@ optiga_result optiga_get_data_object(uint16_t oid, bool get_metadata,
write_uint16(&ptr, oid);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -342,6 +358,7 @@ optiga_result optiga_set_data_object(uint16_t oid, bool set_metadata,
memcpy(ptr, data, data_size);
}
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -404,6 +421,7 @@ optiga_result optiga_get_random(uint8_t *random, size_t random_size) {
write_uint16(&ptr, random_size);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -436,6 +454,7 @@ optiga_result optiga_encrypt_sym(optiga_sym_mode mode, uint16_t oid,
*(ptr++) = 0x01; // start and final data block
write_prefixed_data(&ptr, input, input_size);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret == OPTIGA_SUCCESS) {
@ -465,6 +484,7 @@ optiga_result optiga_set_auto_state(uint16_t nonce_oid, uint16_t key_oid,
*(ptr++) = 0x41; // pre-pending optional data tag
write_uint16(&ptr, 0);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
@ -491,6 +511,7 @@ optiga_result optiga_set_auto_state(uint16_t nonce_oid, uint16_t key_oid,
write_uint16(&ptr, SHA256_DIGEST_LENGTH);
hmac_sha256(key, key_size, nonce, sizeof(nonce), ptr);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer, sizeof(tx_buffer),
&tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -515,6 +536,7 @@ optiga_result optiga_clear_auto_state(uint16_t key_oid) {
*(ptr++) = 0x43; // verification value tag
write_uint16(&ptr, 0); // verification value length
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -554,6 +576,7 @@ optiga_result optiga_calc_sign(uint16_t oid, const uint8_t *digest,
write_uint16(&ptr, 2);
write_uint16(&ptr, oid);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -621,6 +644,7 @@ optiga_result optiga_verify_sign(optiga_curve curve, const uint8_t *public_key,
*(ptr++) = 0x06; // public key tag
write_prefixed_data(&ptr, public_key, public_key_size);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -653,6 +677,7 @@ optiga_result optiga_gen_key_pair(optiga_curve curve, optiga_key_usage usage,
write_uint16(&ptr, 1);
*(ptr++) = usage;
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -682,6 +707,7 @@ optiga_result optiga_gen_sym_key(optiga_aes algorithm, optiga_key_usage usage,
write_uint16(&ptr, 1);
*(ptr++) = usage;
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -726,6 +752,7 @@ optiga_result optiga_calc_ssec(optiga_curve curve, uint16_t oid,
*(ptr++) = 0x07; // export tag
write_uint16(&ptr, 0);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -779,6 +806,7 @@ optiga_result optiga_derive_key(optiga_key_derivation deriv, uint16_t oid,
*(ptr++) = 0x07; // export tag
write_uint16(&ptr, 0);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret == OPTIGA_SUCCESS) {
@ -907,6 +935,7 @@ optiga_result optiga_set_priv_key(uint16_t oid, const uint8_t priv_key[32]) {
return OPTIGA_ERR_PROCESS;
}
OPTIGA_LOG(__func__, sop_cmd1, sizeof(sop_cmd1))
ret = optiga_execute_command(sop_cmd1, sizeof(sop_cmd1), tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -920,6 +949,7 @@ optiga_result optiga_set_priv_key(uint16_t oid, const uint8_t priv_key[32]) {
return ret;
}
OPTIGA_LOG(__func__, sop_cmd2, sizeof(sop_cmd2))
ret = optiga_execute_command(sop_cmd2, sizeof(sop_cmd2), tx_buffer,
sizeof(tx_buffer), &tx_size);
memzero(sop_cmd2, sizeof(sop_cmd2));

@ -134,11 +134,11 @@ static uint8_t sec_chan_buffer[OPTIGA_MAX_APDU_SIZE + SEC_CHAN_OVERHEAD_SIZE] =
{0};
static size_t sec_chan_size = 0;
#ifdef NDEBUG
#if PRODUCTION
#define OPTIGA_LOG(prefix, data, data_size)
#else
static optiga_log_hex_t log_hex = NULL;
void optiga_set_log_hex(optiga_log_hex_t f) { log_hex = f; }
void optiga_transport_set_log_hex(optiga_log_hex_t f) { log_hex = f; }
#define OPTIGA_LOG(prefix, data, data_size) \
if (log_hex != NULL) { \
static uint8_t prev_data[4]; \
@ -184,7 +184,7 @@ optiga_result optiga_init(void) {
}
static optiga_result optiga_i2c_write(const uint8_t *data, uint16_t data_size) {
OPTIGA_LOG(">>> ", data, data_size)
OPTIGA_LOG(">>>", data, data_size)
for (int try_count = 0; try_count <= I2C_MAX_RETRY_COUNT; ++try_count) {
if (try_count != 0) {
@ -205,7 +205,7 @@ static optiga_result optiga_i2c_read(uint8_t *buffer, uint16_t buffer_size) {
HAL_Delay(1);
if (HAL_OK == i2c_receive(OPTIGA_I2C_INSTANCE, OPTIGA_ADDRESS, buffer,
buffer_size, I2C_TIMEOUT)) {
OPTIGA_LOG("<<< ", buffer, buffer_size)
OPTIGA_LOG("<<<", buffer, buffer_size)
return OPTIGA_SUCCESS;
}
}

@ -105,6 +105,14 @@ typedef enum {
OPTIGA_ACCESS_COND_NEV = 0xFF, // Never.
} optiga_access_cond;
// Life cycle status.
typedef enum {
OPTIGA_LCS_CR = 0x01, // Creation state.
OPTIGA_LCS_IN = 0x03, // Initialization state.
OPTIGA_LCS_OP = 0x07, // Operational state.
OPTIGA_LCS_TE = 0x0f, // Termination state.
} optiga_lcs;
typedef struct {
const uint8_t *ptr;
uint16_t len;
@ -132,8 +140,14 @@ typedef struct {
#define OPTIGA_RANDOM_MAX_SIZE 256
#define OPTIGA_MAX_CERT_SIZE 1728
#define OPTIGA_ACCESS_CONDITION(ac_id, oid) \
{ (const uint8_t[]){ac_id, oid >> 8, oid & 0xff}, 3 }
#define OPTIGA_ACCESS_CONDITION(ac_id, oid) \
(const optiga_metadata_item) { \
(const uint8_t[]){ac_id, oid >> 8, oid & 0xff}, 3 \
}
// Single-byte value of optiga_metadata_item.
#define OPTIGA_META_VALUE(val) \
(const optiga_metadata_item) { (const uint8_t[]){val}, 1 }
// Commonly used data object access conditions.
extern const optiga_metadata_item OPTIGA_META_LCS_OPERATIONAL;
@ -192,4 +206,9 @@ optiga_result optiga_derive_key(optiga_key_derivation deriv, uint16_t oid,
size_t key_size);
optiga_result optiga_set_trust_anchor(void);
optiga_result optiga_set_priv_key(uint16_t oid, const uint8_t priv_key[32]);
#if !PRODUCTION
void optiga_command_set_log_hex(optiga_log_hex_t f);
#endif
#endif

@ -34,4 +34,9 @@ typedef enum _optiga_result {
OPTIGA_ERR_CMD, // Command error. See error code data object 0xF1C2.
} optiga_result;
#if !PRODUCTION
typedef void (*optiga_log_hex_t)(const char *prefix, const uint8_t *data,
size_t data_size);
#endif
#endif

@ -44,10 +44,8 @@ optiga_result optiga_resync(void);
optiga_result optiga_soft_reset(void);
optiga_result optiga_set_data_reg_len(size_t size);
#ifndef NDEBUG
typedef void (*optiga_log_hex_t)(const char *prefix, const uint8_t *data,
size_t data_size);
void optiga_set_log_hex(optiga_log_hex_t f);
#if !PRODUCTION
void optiga_transport_set_log_hex(optiga_log_hex_t f);
#endif
#endif

@ -0,0 +1,208 @@
from __future__ import annotations
import os
import secrets
from dataclasses import dataclass
from typing import Any
from typing_extensions import Self
import click
import requests
import serial
SERVER_TOKEN = ""
SERVER_URL = "http://localhost:8000/provision"
@dataclass
class ProvisioningResult:
device_cert: bytes
fido_privkey: bytes
fido_cert: bytes
production: bool
@classmethod
def from_json(cls, json: dict[str, Any]) -> Self:
return cls(
device_cert=bytes.fromhex(json["device_cert"]),
fido_privkey=bytes.fromhex(json["fido_privkey"]),
fido_cert=bytes.fromhex(json["fido_cert"]),
production=json["production"],
)
def write(self, connection: Connection) -> None:
connection.command("CERTDEV WRITE", self.device_cert)
cert_dev = connection.command("CERTDEV READ")
if cert_dev != self.device_cert:
print("Device certificate mismatch")
print("Expected:", self.device_cert)
print("Got: ", cert_dev)
assert cert_dev == self.device_cert
connection.command("CERTFIDO WRITE", self.fido_cert)
cert_fido = connection.command("CERTFIDO READ")
assert cert_fido == self.fido_cert
connection.command("KEYFIDO WRITE", self.fido_privkey)
key_fido = connection.command("KEYFIDO READ")
assert key_fido is not None
assert key_fido in self.fido_cert
@dataclass
class DeviceInfo:
optiga_id: bytes
cpu_id: bytes
device_cert: bytes
@classmethod
def read(cls, connection: Connection) -> Self:
cpu_id = connection.command("CPUID READ")
optiga_id = connection.command("OPTIGAID READ")
cert_bytes = connection.command("CERTINF READ")
assert optiga_id is not None
assert cpu_id is not None
assert cert_bytes is not None
return cls(optiga_id, cpu_id, cert_bytes)
class ProdtestException(Exception):
def __init__(self, text: str) -> None:
super().__init__(text)
self.text = text
class Connection:
def __init__(self, path: str = "/dev/ttyACM0") -> None:
self.connection = serial.Serial(path, 115200, timeout=5)
def readline(self) -> bytes:
line = self.connection.readline().strip()
line_str = line.decode()
if len(line_str) > 100:
line_str = line_str[:100] + "..."
print("<<<", line_str)
return line
def writeline(self, data: bytes) -> None:
data_str = data.decode()
if len(data_str) > 100:
print(">>>", data_str[:100] + "...")
else:
print(">>>", data_str)
for byte in data:
if byte < 32 or byte > 126:
print("!!!", byte, "is not printable")
continue
self.connection.write(bytes([byte]))
echo = self.connection.read(1)
assert echo[0] == byte
self.connection.write(b"\r")
assert self.connection.read(2) == b"\r\n"
# self.connection.write(data + b"\r")
# echo = self.connection.read(len(data) + 2)
# print(len(echo), len(data) + 2)
# assert echo[:-2] == data
# assert echo[-2:] == b"\r\n"
def command(self, cmd: str, *args: Any) -> bytes | None:
cmd_line = cmd
for arg in args:
if isinstance(arg, bytes):
cmd_line += " " + arg.hex()
else:
cmd_line += " " + str(arg)
self.writeline(cmd_line.encode())
res = self.readline()
if res.startswith(b"ERROR"):
error_text = res[len(b"ERROR ") :].decode()
raise ProdtestException(error_text)
if not res.startswith(b"OK"):
raise ProdtestException("Unexpected response: " + res.decode())
res_arg = res[len(b"OK ") :]
if not res_arg:
return None
try:
return bytes.fromhex(res_arg.decode())
except ValueError:
return res_arg
def provision_request(
device: DeviceInfo, url: str, verify: bool = True
) -> ProvisioningResult:
request = {
"tester_id": SERVER_TOKEN,
"run_id": secrets.token_hex(16),
"optiga_id": device.optiga_id.hex(),
"cpu_id": device.cpu_id.hex(),
"cert": device.device_cert.hex(),
"model": "T2B1",
}
resp = requests.post(url, json=request, verify=verify)
if resp.status_code == 400:
print("Server returned error:", resp.text)
resp.raise_for_status()
resp_json = resp.json()
return ProvisioningResult.from_json(resp_json)
@click.group()
def cli() -> None:
pass
@cli.command()
def identify() -> None:
connection = Connection()
connection.command("PING")
DeviceInfo.read(connection)
@cli.command()
@click.option("--wipe", is_flag=True, help="Wipe the device")
def lock(wipe) -> None:
connection = Connection()
connection.command("PING")
connection.command("LOCK")
if wipe:
connection.command("WIPE")
@cli.command()
@click.option("-u", "--url", default=SERVER_URL, help="Server URL")
@click.option("-d", "--device", default="/dev/ttyACM0", help="Device path")
@click.option(
"--no-verify", is_flag=True, help="Disable server certificate verification"
)
@click.option(
"--lock/--no-lock", default=True, help="Lock the device after provisioning"
)
def provision(url, device, no_verify, lock) -> None:
global SERVER_TOKEN
SERVER_TOKEN = os.environ.get("SERVER_TOKEN")
if SERVER_TOKEN is None:
raise click.ClickException("SERVER_TOKEN environment variable is not set")
connection = Connection(device)
# test the connection
connection.command("PING")
# grab CPUID, OPTIGAID and device certificate
device = DeviceInfo.read(connection)
# call the provisioning server
result = provision_request(device, url, not no_verify)
# write provisioning result to the device
result.write(connection)
if lock:
connection.command("LOCK")
connection.command("WIPE")
if __name__ == "__main__":
cli()
Loading…
Cancel
Save