Skip to content

Commit

Permalink
securechip: add initial Optiga implementation
Browse files Browse the repository at this point in the history
This commit implements the securechip.h interface for the Optiga Trust
M V3, with an interface and configuration roughly corresponding to how
we use the ATECC608 secure chip:

- A KDF key that is internally generated and cannot be read and
written from the host MCU
- A KDF key that is generated on the host
- A monotonic counter attached to the first KDF key which limits the
  maxmium number of uses of the key over the lifetime of the device
- Attestation key that is internally generated and used to sign
  attestation challenges

The factory setup configures the metadata of each object we use,
setting the state to Operational. After this, metadata cannot be
changed, and the access conditions apply as specified.

Shielded communication encrypts the communication with the chip and is
active and enforced through the metadata access configs. It roughly
corresponds to IO protection in the ATECC608.

In the ATECC608, we additionally authorize each command with the
authorization_key, another pre-shared secret. The Optiga offers the
same functionality, but we don't use it to authorize all commands, as
it is redundant to using the shielded communication in terms of
enabling the host MCU to execute commands.

Co-Authored-By: Niklas <[email protected]>
  • Loading branch information
benma and NickeZ committed Jan 7, 2025
1 parent cda3c57 commit 08afde5
Show file tree
Hide file tree
Showing 28 changed files with 2,776 additions and 161 deletions.
11 changes: 11 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ set(PLATFORM-BITBOX02-SOURCES ${PLATFORM-BITBOX02-SOURCES} PARENT_SCOPE)
set(SECURECHIP-SOURCES
${CMAKE_SOURCE_DIR}/src/atecc/atecc.c
${CMAKE_SOURCE_DIR}/src/securechip/securechip.c
${CMAKE_SOURCE_DIR}/src/optiga/pal/pal.c
${CMAKE_SOURCE_DIR}/src/optiga/pal/pal_gpio.c
${CMAKE_SOURCE_DIR}/src/optiga/pal/pal_i2c.c
${CMAKE_SOURCE_DIR}/src/optiga/pal/pal_ifx_i2c_config.c
${CMAKE_SOURCE_DIR}/src/optiga/pal/pal_logger.c
${CMAKE_SOURCE_DIR}/src/optiga/pal/pal_os_datastore.c
${CMAKE_SOURCE_DIR}/src/optiga/pal/pal_os_event.c
${CMAKE_SOURCE_DIR}/src/optiga/pal/pal_os_lock.c
${CMAKE_SOURCE_DIR}/src/optiga/pal/pal_os_timer.c
${CMAKE_SOURCE_DIR}/src/optiga/pal/pal_os_memory.c
${CMAKE_SOURCE_DIR}/src/optiga/optiga.c
)
set(SECURECHIP-SOURCES ${SECURECHIP-SOURCES} PARENT_SCOPE)

Expand Down
20 changes: 10 additions & 10 deletions src/atecc/atecc.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ static ATCA_STATUS _lock_slot(atecc_slot_t slot)
static ATCA_STATUS _factory_setup(void)
{
if (_interface_functions == NULL) {
return (ATCA_STATUS)ATECC_ERR_IFS;
return (ATCA_STATUS)SC_ERR_IFS;
}
bool is_config_locked = false;
ATCA_STATUS result = atcab_is_locked(LOCK_ZONE_CONFIG, &is_config_locked);
Expand Down Expand Up @@ -336,14 +336,14 @@ static int _verify_config(void)
return result;
}
if (!is_locked) {
return ATECC_ERR_ZONE_UNLOCKED_CONFIG;
return SC_ATECC_ERR_ZONE_UNLOCKED_CONFIG;
}
result = atcab_is_locked(LOCK_ZONE_DATA, &is_locked);
if (result != ATCA_SUCCESS) {
return result;
}
if (!is_locked) {
return ATECC_ERR_ZONE_UNLOCKED_DATA;
return SC_ATECC_ERR_ZONE_UNLOCKED_DATA;
}

bool same_config = false;
Expand All @@ -352,7 +352,7 @@ static int _verify_config(void)
return result;
}
if (!same_config) {
return ATECC_ERR_CONFIG_MISMATCH;
return SC_ERR_CONFIG_MISMATCH;
}

// Check that the slots are individually locked.
Expand All @@ -361,29 +361,29 @@ static int _verify_config(void)
return result;
}
if (!is_locked) {
return ATECC_ERR_SLOT_UNLOCKED_IO;
return SC_ATECC_ERR_SLOT_UNLOCKED_IO;
}
result = atcab_is_slot_locked(ATECC_SLOT_AUTHKEY, &is_locked);
if (result != ATCA_SUCCESS) {
return result;
}
if (!is_locked) {
return ATECC_ERR_SLOT_UNLOCKED_AUTH;
return SC_ATECC_ERR_SLOT_UNLOCKED_AUTH;
}
result = atcab_is_slot_locked(ATECC_SLOT_ENCRYPTION_KEY, &is_locked);
if (result != ATCA_SUCCESS) {
return result;
}
if (!is_locked) {
return ATECC_ERR_SLOT_UNLOCKED_ENC;
return SC_ATECC_ERR_SLOT_UNLOCKED_ENC;
}
return ATCA_SUCCESS;
}

int atecc_setup(const securechip_interface_functions_t* ifs)
{
if (ifs == NULL) {
return ATECC_ERR_IFS;
return SC_ERR_IFS;
}
_interface_functions = ifs;
ATCA_STATUS result = atcab_init(&cfg);
Expand Down Expand Up @@ -527,10 +527,10 @@ bool atecc_update_keys(void)
static int _atecc_kdf(atecc_slot_t slot, const uint8_t* msg, size_t len, uint8_t* kdf_out)
{
if (len > 127 || (slot != ATECC_SLOT_ROLLKEY && slot != ATECC_SLOT_KDF)) {
return ATECC_ERR_INVALID_ARGS;
return SC_ERR_INVALID_ARGS;
}
if (msg == kdf_out) {
return ATECC_ERR_INVALID_ARGS;
return SC_ERR_INVALID_ARGS;
}

ATCA_STATUS result = _authorize_key();
Expand Down
88 changes: 3 additions & 85 deletions src/atecc/atecc.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,112 +15,30 @@
#ifndef _ATECC_H_
#define _ATECC_H_

/* ATECC implementation of the secure chip functions. */
/* See securechip.h for the docstrings of the individual functions. */

#include "compiler_util.h"
#include "securechip/securechip.h"
#include <platform/platform_config.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

typedef enum {
ATECC_ERR_ZONE_UNLOCKED_CONFIG = -1,
ATECC_ERR_ZONE_UNLOCKED_DATA = -2,
ATECC_ERR_CONFIG_MISMATCH = -3,
ATECC_ERR_SLOT_UNLOCKED_IO = -4,
ATECC_ERR_SLOT_UNLOCKED_AUTH = -5,
ATECC_ERR_SLOT_UNLOCKED_ENC = -6,
ATECC_ERR_IFS = -7,
ATECC_ERR_INVALID_ARGS = -8,
} atecc_error_t;

/**
* Initializes the cryptoauthlib communication, by providing a custom i2c chip
* communication interface/bridge to cryptoauthlib. On first call, the chip
* is configured and locked.
* @param[in] ifs Interface functions.
* @return values of `atecc_error_t` if negative, values of `ATCA_STATUS` if positive, 0 on
* success.
*/
USE_RESULT int atecc_setup(const securechip_interface_functions_t* ifs);

/**
* Updates the two KDF keys (rollkey and kdf key). The previous keys are lost
* and cannot be restored. Calling this function does not increment the
* monotonic counter Counter0.
* @return true on success.
*/
USE_RESULT bool atecc_update_keys(void);

/**
* Perform HMAC using the key in KDF slot with the input msg.
* @param[in] msg Use this msg as input
* @param[in] len Must be <= 127.
* @param[out] kdf_out Must have size 32. Result of the kdf will be stored here.
* Cannot be the same as `msg`.
* @return values of `atecc_error_t` if negative, values of `ATCA_STATUS` if positive, 0 on
*/
USE_RESULT int atecc_kdf(const uint8_t* msg, size_t len, uint8_t* kdf_out);

/**
* Perform KDF using the key in rollkey slot with the input msg.
* Calling this function increments the
* monotonic counter Counter0.
* @param[in] msg Use this msg as input
* @param[in] len Must be <= 127.
* @param[out] kdf_out Must have size 32. Result of the kdf will be stored here.
* Cannot be the same as `msg`.
* @return values of `securechip_error_t` if negative, values of `ATCA_STATUS` if positive, 0 on
*/
USE_RESULT int atecc_kdf_rollkey(const uint8_t* msg, size_t len, uint8_t* kdf_out);

/**
* Generates a new attestation device key and outputs the public key.
* @param[out] pubkey_out
*/
USE_RESULT bool atecc_gen_attestation_key(uint8_t* pubkey_out);

/**
* @param[in] msg 32 byte message to sign.
* @param[out] signature_out must be 64 bytes. R/S P256 signature.
*/
USE_RESULT bool atecc_attestation_sign(const uint8_t* challenge, uint8_t* signature_out);

/**
* Retrieves the number of remaining possible counter increments (max value - Counter0).
* The counter is increment when using `atecc_kdf()` (see its docstring).
* @param[out] remaining_out current value of the monotonic counter.
* @return false if there was a communication error with the SC.
*/
USE_RESULT bool atecc_monotonic_increments_remaining(uint32_t* remaining_out);

/**
* @param[out] rand_out must be 32 bytes.
*/
USE_RESULT bool atecc_random(uint8_t* rand_out);

#if APP_U2F == 1 || FACTORYSETUP == 1
/**
* Set the u2f counter to `counter`. Should only be used for initialization.
* @param[in] counter Value to set counter to
* @return True if success
*/
USE_RESULT bool atecc_u2f_counter_set(uint32_t counter);
#endif

#if APP_U2F == 1
/**
* Monotonically increase the U2F counter and return the current value
* @param[out] counter Next counter value
* @return True if success
*/
USE_RESULT bool atecc_u2f_counter_inc(uint32_t* counter);
#endif

/**
* Output the atecc model.
* @param[out] model_out atecc model
* @return True if success
*/
USE_RESULT bool atecc_model(securechip_model_t* model_out);

#endif
11 changes: 10 additions & 1 deletion src/memory/memory_shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,14 @@ uint8_t memory_get_screen_type(void)

uint8_t memory_get_securechip_type(void)
{
return MEMORY_SECURECHIP_TYPE_ATECC;
chunk_shared_t chunk = {0};
memory_read_shared_bootdata(&chunk);
uint8_t securechip_type = chunk.fields.securechip_type;
util_zero(&chunk, sizeof(chunk));
switch (securechip_type) {
case MEMORY_SECURECHIP_TYPE_OPTIGA:
return securechip_type;
default:
return MEMORY_SECURECHIP_TYPE_ATECC;
}
}
2 changes: 1 addition & 1 deletion src/memory/memory_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ typedef union {
uint8_t auto_enter;
uint8_t upside_down;
uint8_t screen_type;
uint8_t securechip_type;
// Following are used by firmware only
uint8_t reserved[1];
uint8_t io_protection_key_split[32];
uint8_t authorization_key_split[32];
uint8_t encryption_key_split[32];
Expand Down
Loading

0 comments on commit 08afde5

Please sign in to comment.