diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 825de7993..63733cae1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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) diff --git a/src/atecc/atecc.h b/src/atecc/atecc.h index 45e52fe1c..0a5e75d41 100644 --- a/src/atecc/atecc.h +++ b/src/atecc/atecc.h @@ -15,6 +15,9 @@ #ifndef _ATECC_H_ #define _ATECC_H_ +/* ATECC implementation of the secure chip functions. */ +/* See securechip.h for the docstrings of the individual funtions. */ + #include "compiler_util.h" #include "securechip/securechip.h" #include @@ -33,94 +36,20 @@ typedef enum { 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 diff --git a/src/memory/memory_shared.c b/src/memory/memory_shared.c index 9488183e5..71e3874c1 100644 --- a/src/memory/memory_shared.c +++ b/src/memory/memory_shared.c @@ -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; + } } diff --git a/src/memory/memory_shared.h b/src/memory/memory_shared.h index 5e8f8f985..c1a91e6bb 100644 --- a/src/memory/memory_shared.h +++ b/src/memory/memory_shared.h @@ -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]; diff --git a/src/optiga/optiga.c b/src/optiga/optiga.c new file mode 100644 index 000000000..1270752fd --- /dev/null +++ b/src/optiga/optiga.c @@ -0,0 +1,1305 @@ +// Copyright 2024 Shift Crypto AG +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "optiga.h" + +#include "pal/pal_i2c.h" +#include "pal/pal_os_datastore.h" +#include "pal/pal_os_timer.h" + +#include +#include +#include +#include +#include +#include +#include + +// Set this to 1 for a more convenience during development. +// Factory setup will be performed in the normal firmware, which makes it easier to tinker with the +// chip setup and config. +// Must be 0 for the production firmware releases. +#define FACTORY_DURING_PROD 0 + +// Number of times the first kdf slot can be used. +// The maxmimum does not seem to be specified, so we use something a little below the endurance +// indication of 600000 updates. See Solution Reference Manual Figure 32. +#define MONOTONIC_COUNTER_MAX_USE (590000) + +// The Data Object IDs we use. + +// Stores a shared secret used for a shielded connection. Is is used to encrypt +// communications. Read/write disabled. +#define OID_PLATFORM_BINDING 0xE140 +// CMAC key slot, read/write prohibited. Used to perform KDF using CMAC. Key is regenerated at +// factory setup and with each device reset. Monotonic counter `OID_COUNTER` attached. +#define OID_AES_SYMKEY 0xE200 +// HMAC key slot, read prohibited, write allowed. 32 random bytes are written to it at factory setup +// and with each device reset. +#define OID_HMAC 0xF1D0 +// Arbitrary data object, stores up to 140 bytes. Used to store the U2F counter. +#define OID_ARBITRARY_DATA 0xF1D1 +// ECC slot used for creating the device attestation key and signing with it. Read/write +// disabled. The Key is internally generated at factory setup and used to sign the device +// attestation host challenge. +#define OID_ATTESTATION 0xE0F1 +// Monotonic counter, initialized at 0 and attached to `OID_AES_SYMKEY` - every CMAC generation +// increments the counter. When the threshld `MONOTONIC_COUNTER_MAX_USE` is reached, further CMAC +// computations return an error. +#define OID_COUNTER 0xE120 + +// See Solution Reference Manual Table 79 "Data structure arbitrary data object". +#define ARBITRARY_DATA_OBJECT_TYPE_3_MAX_SIZE 140 + +// Struct stored in the arbitrary data object. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpacked" +#pragma GCC diagnostic ignored "-Wattributes" +typedef union { + struct __attribute__((__packed__)) { + uint32_t u2f_counter; + } fields; + uint8_t bytes[ARBITRARY_DATA_OBJECT_TYPE_3_MAX_SIZE]; +} arbitrary_data_t; +#pragma GCC diagnostic pop + +#define ABORT_IF_NULL(ptr) \ + do { \ + if ((ptr) == 0) { \ + AbortAutoenter("Not initalized"); \ + } \ + } while (0) + +static optiga_util_t* _util; +static optiga_crypt_t* _crypt; + +static const securechip_interface_functions_t* _ifs = NULL; + +// Values of life cycle states. +// See Table "Life Cycle Status": +// https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#link05d4c12a_5c94_4a05_a05d_102c53684d3d +#define LCSO_STATE_CREATION (0x01) +#define LCSO_STATE_OPERATIONAL (0x07) + +#define TAG_LCSO 0xC0 + +// Set the object LcsO flag to Operational. After this, the metadata cannot be changed anymore. +// During development, set this to `LCSO_STATE_CREATION`. +#define FINAL_LCSO_STATE LCSO_STATE_OPERATIONAL + +static const uint8_t _platform_binding_metadata[] = { + // Metadata tag in the data object + 0x20, + // Number of bytes that follow + 17, + // Set LcsO. Refer to macro to see the value or some more notes. + 0xC0, + 0x01, + FINAL_LCSO_STATE, + // Change/Write access. This allows updating the binding secret when LcsO < op. + 0xD0, + 0x03, + 0xE1, + 0xFC, + LCSO_STATE_OPERATIONAL, + // Disallow reads + 0xD1, + 0x01, + 0xFF, + // Allow execute + 0xD3, + 0x01, + 0x00, + // Data object type set to PTFBIND (Platform binding secret) + 0xE8, + 0x01, + 0x22, +}; + +static const uint8_t _aes_symkey_metadata[] = { + // Metadata tag in the data object + 0x20, + // Number of bytes that follow + 21, + // Set LcsO. Refer to macro to see the value or some more notes. + 0xC0, + 0x01, + FINAL_LCSO_STATE, + // Set key usage to "Enc". + // See Table "Metadata associated with data and key objects" -> 0xE1 + // https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#link8051b344_ff66_4d6b_bcfd_d21bb87d05d4 + 0xE1, + 0x01, + 0x02, + // Allow writes - GenSymkey requires this to be able to write. + // However, writes from the host are forbidden. + // Table "Common key objects with TAG’s and AC‘s" - 0xE200: + // https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#linkf056a0f7_a31f_41c3_b1d9_f270a4fe0378 + // "The GetDataObject, and SetDataObject commands are not allowed for the data part of the key + // object even if the metadata state the access rights differently" + 0xD0, + 0x01, + 0x00, + // Disallow reads + 0xD1, + 0x01, + 0xFF, + // Attach execution to counter at 0xE120 and enforce shielded connection. + // See Table 'Access Condition Identifier and Operators" -> "Conf": + // https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#linkc15dfea4_2cc2_46ae_a53b_1e6ea9487f34 + 0xD3, + 0x07, + 0x40, + 0xE1, + 0x20, + // && + 0xFD, + // Enforce shielded connection. According to 4.4.1.7 "EncryptSym" shielded protection is + // enforced anyway, but better to be explicit. + 0x20, + 0xE1, + 0x40, +}; + +static const uint8_t _hmac_metadata[] = { + // Metadata tag in the data object + 0x20, + // Number of bytes that follow + 19, + // Set LcsO. Refer to macro to see the value or some more notes. + 0xC0, + 0x01, + FINAL_LCSO_STATE, + // Data object type: PRESSEC + // See table "Data Object Types": + // https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#linkaf9aa284_1397_4161_8761_8c44fbbfa69d + 0xE8, + 0x01, + 0x21, + // Allow writes, enforce shielded connection. + 0xD0, + 0x03, + 0x20, + 0xE1, + 0x40, + // Disallow reads + 0xD1, + 0x01, + 0xFF, + // Execute: enforce shielded connection. + // See Table 'Access Condition Identifier and Operators" -> "Conf": + // https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#linkc15dfea4_2cc2_46ae_a53b_1e6ea9487f34 + 0xD3, + 0x03, + 0x20, + 0xE1, + 0x40, +}; + +static const uint8_t _arbitrary_data_metadata[] = { + // Metadata tag in the data object + 0x20, + // Number of bytes that follow + 19, + // Set LcsO. Refer to macro to see the value or some more notes. + 0xC0, + 0x01, + FINAL_LCSO_STATE, + // Data object type: BSTR. + // See table "Data Object Types": + // https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#linkaf9aa284_1397_4161_8761_8c44fbbfa69d + 0xE8, + 0x01, + 0x00, + // Allow writes, enforce shielded connection. + 0xD0, + 0x03, + 0x20, + 0xE1, + 0x40, + // Allow reads, enforce shielded connection. + 0xD1, + 0x03, + 0x20, + 0xE1, + 0x40, + // Disallow exe + 0xD3, + 0x01, + 0xFF, +}; + +static const uint8_t _attestation_metadata[] = { + // Metadata tag in the data object + 0x20, + // Number of bytes that follow + 17, + // Set LcsO. Refer to macro to see the value or some more notes. + 0xC0, + 0x01, + FINAL_LCSO_STATE, + // Key usage associated with key container: Sign + // See table "Metadata associated with data and key objects": + // https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#link8051b344_ff66_4d6b_bcfd_d21bb87d05d4 + 0xE1, + 0x01, + 0x10, + // Allow writes - GenKeyPair requires this to be able to write. + // However, writes from the host are forbidden. + // Table "Common key objects with TAG’s and AC‘s" - 0xE0F1: + // https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#linkf056a0f7_a31f_41c3_b1d9_f270a4fe0378 + // "The GetDataObject, and SetDataObject commands are not allowed for the data part of the key + // object even if the metadata state the access rights differently" + 0xD0, + 0x01, + 0x00, + // Disallow reads + 0xD1, + 0x01, + 0xFF, + // Execute: enforce shielded connection. + // See Table 'Access Condition Identifier and Operators" -> "Conf": + // https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#linkc15dfea4_2cc2_46ae_a53b_1e6ea9487f34 + 0xD3, + 0x03, + 0x20, + 0xE1, + 0x40, +}; + +static const uint8_t _counter_metadata[] = { + // Metadata tag in the data object + 0x20, + // Number of bytes that follow + 14, + // Set LcsO. Refer to macro to see the value or some more notes. + 0xC0, + 0x01, + FINAL_LCSO_STATE, + // Change/Write access. This allows updating the binding secret when LcsO < op. + 0xD0, + 0x03, + 0xE1, + 0xFC, + LCSO_STATE_OPERATIONAL, + // Allow reads + 0xD1, + 0x01, + 0x00, + // Allow exe + 0xD3, + 0x01, + 0x00, +}; +// +// Sync wrappers around optiga util/crypt functions +// + +// The OPTIGA library is asynchronous and will schedule a callback when the command is done. The +// callback will set this shared variable to the result of the command. +static volatile optiga_lib_status_t _optiga_lib_status; + +static void _optiga_lib_callback(void* callback_ctx, optiga_lib_status_t event) +{ + (void)callback_ctx; + _optiga_lib_status = event; +} + +// Helper that is used in the main thread to busy wait for the callback to update the shared +// variable. +// It first checks the return status of the command, then busy waits, and then checks the +// asynchronous return status. +// Will return from caller if command failed. +// `return_status` will be updated with the actual return status +// Return statuses are documented in optiga_lib_return_codes.h +#define _WAIT(return_status, optiga_lib_status) \ + do { \ + if ((return_status) != OPTIGA_UTIL_SUCCESS) { \ + return (return_status); \ + } \ + while (OPTIGA_LIB_BUSY == (optiga_lib_status)) { \ + } \ + if (OPTIGA_LIB_SUCCESS != (optiga_lib_status)) { \ + return (optiga_lib_status); \ + } \ + (return_status) = (optiga_lib_status); \ + } while (0) + +static optiga_lib_status_t _optiga_util_read_data_sync( + optiga_util_t* me, + uint16_t optiga_oid, + uint16_t offset, + uint8_t* buffer, + uint16_t* length) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = optiga_util_read_data(me, optiga_oid, offset, buffer, length); + _WAIT(res, _optiga_lib_status); + return res; +} + +static optiga_lib_status_t _optiga_util_write_data_sync( + optiga_util_t* me, + uint16_t optiga_oid, + uint8_t write_type, + uint16_t offset, + const uint8_t* buffer, + uint16_t length) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = + optiga_util_write_data(me, optiga_oid, write_type, offset, buffer, length); + _WAIT(res, _optiga_lib_status); + return res; +} + +static optiga_lib_status_t _optiga_util_read_metadata_sync( + optiga_util_t* me, + uint16_t optiga_oid, + uint8_t* buffer, + uint16_t* length) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = optiga_util_read_metadata(me, optiga_oid, buffer, length); + _WAIT(res, _optiga_lib_status); + return res; +} + +#if FACTORYSETUP == 1 || FACTORY_DURING_PROD == 1 +static optiga_lib_status_t _optiga_util_write_metadata_sync( + optiga_util_t* me, + uint16_t optiga_oid, + const uint8_t* buffer, + uint8_t length) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = optiga_util_write_metadata(me, optiga_oid, buffer, length); + _WAIT(res, _optiga_lib_status); + return res; +} +#endif + +static optiga_lib_status_t _optiga_util_open_application_sync( + optiga_util_t* me, + bool_t perform_restore) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = optiga_util_open_application(me, perform_restore); + _WAIT(res, _optiga_lib_status); + return res; +} + +#if FACTORYSETUP == 1 || FACTORY_DURING_PROD == 1 +static optiga_lib_status_t _optiga_util_close_application_sync( + optiga_util_t* me, + bool_t perform_hibernate) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = optiga_util_close_application(me, perform_hibernate); + _WAIT(res, _optiga_lib_status); + return res; +} +#endif + +static optiga_lib_status_t _optiga_crypt_hmac_sync( + optiga_crypt_t* me, + optiga_hmac_type_t type, + uint16_t secret, + const uint8_t* input_data, + uint32_t input_data_length, + uint8_t* mac, + uint32_t* mac_length) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = + optiga_crypt_hmac(me, type, secret, input_data, input_data_length, mac, mac_length); + _WAIT(res, _optiga_lib_status); + return res; +} + +static optiga_lib_status_t _optiga_crypt_ecc_generate_keypair_sync( + optiga_crypt_t* me, + optiga_ecc_curve_t curve_id, + uint8_t key_usage, + bool_t export_private_key, + void* private_key, + uint8_t* public_key, + uint16_t* public_key_length) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = optiga_crypt_ecc_generate_keypair( + me, curve_id, key_usage, export_private_key, private_key, public_key, public_key_length); + _WAIT(res, _optiga_lib_status); + return res; +} + +static optiga_lib_status_t _optiga_crypt_ecdsa_sign_sync( + optiga_crypt_t* me, + const uint8_t* digest, + uint8_t digest_length, + optiga_key_id_t private_key, + uint8_t* signature, + uint16_t* signature_length) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = optiga_crypt_ecdsa_sign( + me, digest, digest_length, private_key, signature, signature_length); + _WAIT(res, _optiga_lib_status); + return res; +} + +static optiga_lib_status_t _optiga_crypt_symmetric_encrypt_sync( + optiga_crypt_t* me, + optiga_symmetric_encryption_mode_t encryption_mode, + optiga_key_id_t symmetric_key_oid, + const uint8_t* plain_data, + uint32_t plain_data_length, + const uint8_t* iv, + uint16_t iv_length, + const uint8_t* associated_data, + uint16_t associated_data_length, + uint8_t* encrypted_data, + uint32_t* encrypted_data_length) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = optiga_crypt_symmetric_encrypt( + me, + encryption_mode, + symmetric_key_oid, + plain_data, + plain_data_length, + iv, + iv_length, + associated_data, + associated_data_length, + encrypted_data, + encrypted_data_length); + _WAIT(res, _optiga_lib_status); + return res; +} + +static optiga_lib_status_t _optiga_crypt_random_sync( + optiga_crypt_t* me, + optiga_rng_type_t rng_type, + uint8_t* random_data, + uint16_t random_data_length) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = optiga_crypt_random(me, rng_type, random_data, random_data_length); + _WAIT(res, _optiga_lib_status); + return res; +} + +static optiga_lib_status_t _optiga_crypt_symmetric_generate_key_sync( + optiga_crypt_t* me, + optiga_symmetric_key_type_t key_type, + uint8_t key_usage, + bool_t export_symmetric_key, + void* symmetric_key) +{ + ABORT_IF_NULL(me); + + _optiga_lib_status = OPTIGA_LIB_BUSY; + optiga_lib_status_t res = optiga_crypt_symmetric_generate_key( + me, key_type, key_usage, export_symmetric_key, symmetric_key); + _WAIT(res, _optiga_lib_status); + return res; +} + +#if APP_U2F == 1 || FACTORYSETUP == 1 +static bool _read_arbitrary_data(arbitrary_data_t* data_out) +{ + memset(data_out->bytes, 0x00, sizeof(data_out->bytes)); + uint16_t len = sizeof(data_out->bytes); + optiga_lib_status_t res = + _optiga_util_read_data_sync(_util, OID_ARBITRARY_DATA, 0, data_out->bytes, &len); + if (res != OPTIGA_UTIL_SUCCESS) { + util_log("could not read arbitrary data: %x", res); + return false; + } + if (len != sizeof(data_out->bytes)) { + util_log( + "arbitrary data: expected to read size %d, but read %d. Data read: %s", + (int)sizeof(data_out->bytes), + (int)len, + util_dbg_hex(data_out->bytes, len)); + return false; + } + return true; +} +#endif + +#if APP_U2F == 1 || FACTORYSETUP == 1 +static int _write_arbitrary_data(const arbitrary_data_t* data) +{ + optiga_lib_status_t res = _optiga_util_write_data_sync( + _util, + OID_ARBITRARY_DATA, + OPTIGA_UTIL_ERASE_AND_WRITE, + 0, + &data->bytes[0], + sizeof(data->bytes)); + if (res != OPTIGA_LIB_SUCCESS) { + util_log("could not write arbitrary %x", res); + return res; + } + return 0; +} +#endif + +// In a metadata object (0x20 ...), +// extract tag data for a specific tag. +// Returns false if the metadata is invalid or the tag is not present. +static bool _read_metadata_tag( + const uint8_t* metadata, + size_t metadata_len, + uint8_t tag, + uint8_t* data_out, + size_t* data_len_out) +{ + if (metadata_len < 2 || metadata[0] != 0x20) { + // Metadata does not start with the expected tag or is too short + return false; + } + + uint8_t metadata_size = metadata[1]; + if ((size_t)(metadata_size + 2) > metadata_len) { + // Malformed metadata: declared size exceeds buffer length + return false; + } + + const uint8_t* tlv = &metadata[2]; + size_t tlv_len = metadata_size; + + size_t offset = 0; + + while (offset + 2 <= tlv_len) { // Ensure at least are available + uint8_t current_tag = tlv[offset]; + uint8_t size = tlv[offset + 1]; + + if (offset + 2 + size > tlv_len) { + // Malformed TLV: size exceeds remaining length + return false; + } + + if (current_tag == tag) { + // Found the tag, copy data to output + if (data_out) { + memcpy(data_out, &tlv[offset + 2], size); + } + if (data_len_out) { + *data_len_out = size; + } + return true; + } + + // Move to the next TLV + offset += 2 + size; + } + + // Tag not found + return false; +} + +#if FACTORYSETUP == 1 || FACTORY_DURING_PROD == 1 +// Read the LcsO status from a metadata object. Returns false if the metadata is invalid or LcsO is +// not present. +static bool _read_lcso(const uint8_t* metadata, size_t metadata_len, uint8_t* lcso_out) +{ + uint8_t tag_data[100] = {0}; + size_t tag_data_len = 0; + if (!_read_metadata_tag(metadata, metadata_len, TAG_LCSO, tag_data, &tag_data_len)) { + return false; + } + if (tag_data_len != 1) { + return false; + } + *lcso_out = tag_data[0]; + return true; +} + +static int _read_lcso_of_object(uint16_t optiga_oid, uint8_t* lcso_out, bool unprotected) +{ + uint8_t metadata[1000] = {0}; + uint16_t metadata_size = sizeof(metadata); + + if (unprotected) { + // Is reset to full protection after the metadata read command. + OPTIGA_UTIL_SET_COMMS_PROTECTION_LEVEL(_util, OPTIGA_COMMS_NO_PROTECTION); + } + optiga_lib_status_t res = + _optiga_util_read_metadata_sync(_util, optiga_oid, metadata, &metadata_size); + if (res != OPTIGA_LIB_SUCCESS) { + util_log("fail: read binding secret metadata: %x", res); + return res; + } + if (!_read_lcso(metadata, metadata_size, lcso_out)) { + return OPTIGA_ERR_UNEXPECTED_METADATA; + } + return 0; +} + +// Setup shielded communication. +// Writes the shared secret to the chip 0xE140 data object and sets the metadata. +// See solution reference manual 2.3.4 "Use case: Pair OPTIGA™ Trust M with host (pre-shared secret +// based)". +static int _setup_shielded_communication(void) +{ + const uint16_t oid = OID_PLATFORM_BINDING; + uint8_t lcso = 0; + optiga_lib_status_t res = _read_lcso_of_object(oid, &lcso, true); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + if (lcso >= LCSO_STATE_OPERATIONAL) { + util_log("shared secret already setup"); + return 0; + } + + util_log("setting up shielded communication"); + + uint8_t platform_binding_secret[32]; + uint16_t platform_binding_secret_size = sizeof(platform_binding_secret); + + pal_status_t pal_res = pal_os_datastore_read( + OPTIGA_PLATFORM_BINDING_SHARED_SECRET_ID, + platform_binding_secret, + &platform_binding_secret_size); + if (PAL_STATUS_SUCCESS != pal_res || + platform_binding_secret_size != sizeof(platform_binding_secret)) { + util_log("failed datastore read: %x", pal_res); + return OPTIGA_ERR_PAL; + } + + // We write the binding secret before updating the metadata, as the metadata update locks the + // slot. Shielded communication is disabled as it is not set up yet and not required for + // updating the platform binding object. + OPTIGA_UTIL_SET_COMMS_PROTECTION_LEVEL(_util, OPTIGA_COMMS_NO_PROTECTION); + res = _optiga_util_write_data_sync( + _util, + oid, + OPTIGA_UTIL_ERASE_AND_WRITE, + 0, + platform_binding_secret, + sizeof(platform_binding_secret)); + if (res != OPTIGA_LIB_SUCCESS) { + util_log("fail: write binding secret to chip: %x", res); + return res; + } + + // Shielded communication is disabled as it is not set up yet and not required for updating the + // platform binding object. + OPTIGA_UTIL_SET_COMMS_PROTECTION_LEVEL(_util, OPTIGA_COMMS_NO_PROTECTION); + res = _optiga_util_write_metadata_sync( + _util, oid, _platform_binding_metadata, sizeof(_platform_binding_metadata)); + if (res != OPTIGA_LIB_SUCCESS) { + util_log("fail: write metadata of platform binding: %x", res); + return res; + } + + return 0; +} + +static int _configure_object_aes_symkey(void) +{ + const uint16_t oid = OID_AES_SYMKEY; + + uint8_t lcso = 0; + optiga_lib_status_t res = _read_lcso_of_object(oid, &lcso, false); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + if (lcso >= LCSO_STATE_OPERATIONAL) { + util_log("_configure_object_aes_symkey: already setup"); + return 0; + } + util_log("_configure_object_aes_symkey: setting up"); + return _optiga_util_write_metadata_sync( + _util, oid, _aes_symkey_metadata, sizeof(_aes_symkey_metadata)); +} + +static int _configure_object_hmac(void) +{ + const uint16_t oid = OID_HMAC; + + uint8_t lcso = 0; + optiga_lib_status_t res = _read_lcso_of_object(oid, &lcso, false); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + if (lcso >= LCSO_STATE_OPERATIONAL) { + util_log("_configure_object_hmac: already setup"); + return 0; + } + util_log("_configure_object_hmac: setting up"); + return _optiga_util_write_metadata_sync(_util, oid, _hmac_metadata, sizeof(_hmac_metadata)); +} + +static int _configure_object_arbitrary_data(void) +{ + const uint16_t oid = OID_ARBITRARY_DATA; + + uint8_t lcso = 0; + optiga_lib_status_t res = _read_lcso_of_object(oid, &lcso, false); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + if (lcso >= LCSO_STATE_OPERATIONAL) { + util_log("_configure_object_arbitrary_data: already setup"); + return 0; + } + util_log("_configure_object_arbitrary_data: setting up"); + + res = _optiga_util_write_metadata_sync( + _util, oid, _arbitrary_data_metadata, sizeof(_arbitrary_data_metadata)); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + + // Initialize arbitrary data, all zeroes. + const arbitrary_data_t arbitrary_data = {0}; + int write_res = _write_arbitrary_data(&arbitrary_data); + if (write_res) { + util_log("could not initialize arbitrary data"); + return write_res; + } + return 0; +} + +static int _configure_object_counter(void) +{ + const uint16_t oid = OID_COUNTER; + + uint8_t lcso = 0; + optiga_lib_status_t res = _read_lcso_of_object(oid, &lcso, false); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + if (lcso >= LCSO_STATE_OPERATIONAL) { + util_log("_configure_object_counter: already setup"); + return 0; + } + util_log("_configure_object_counter: setting up"); + + // Configure the monotonic counter. + // Table "Common data structures" -> "Counter": + // https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#link24b48059_db81_40f5_8b65_7afca4918ab1 + // Bytes 0-3 are the initial counter value, set to 0. + // Bytes 4-7 are the threshold. + // Ints are encoded as uint32 big endian. + uint8_t counter_buf[8] = {0}; + optiga_common_set_uint32(&counter_buf[4], MONOTONIC_COUNTER_MAX_USE); + res = _optiga_util_write_data_sync( + _util, oid, OPTIGA_UTIL_ERASE_AND_WRITE, 0, counter_buf, sizeof(counter_buf)); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + + return _optiga_util_write_metadata_sync( + _util, oid, _counter_metadata, sizeof(_counter_metadata)); +} + +static int _configure_object_attestation(void) +{ + const uint16_t oid = OID_ATTESTATION; + + uint8_t lcso = 0; + optiga_lib_status_t res = _read_lcso_of_object(oid, &lcso, false); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + if (lcso >= LCSO_STATE_OPERATIONAL) { + util_log("_configure_attestation: already setup"); + return 0; + } + util_log("_configure_attestation: setting up"); + + return _optiga_util_write_metadata_sync( + _util, oid, _attestation_metadata, sizeof(_attestation_metadata)); +} + +static int _factory_write_config(void) +{ + int res_shielded = _setup_shielded_communication(); + if (res_shielded) { + return res_shielded; + } + + optiga_lib_status_t res; + + res = _configure_object_aes_symkey(); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + + res = _configure_object_hmac(); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + + res = _configure_object_arbitrary_data(); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + + res = _configure_object_counter(); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + + res = _configure_object_attestation(); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + + util_log("write config OK"); + + return 0; +} + +static int _factory_setup(void) +{ + optiga_lib_status_t res; + + _util = optiga_util_create(OPTIGA_INSTANCE_ID_0, _optiga_lib_callback, NULL); + if (NULL == _util) { + util_log("couldn't create optiga util"); + return OPTIGA_ERR_CREATE; + } + + _crypt = optiga_crypt_create(OPTIGA_INSTANCE_ID_0, _optiga_lib_callback, NULL); + if (NULL == _crypt) { + util_log("couldn't create optiga crypt"); + return OPTIGA_ERR_CREATE; + } + + OPTIGA_UTIL_SET_COMMS_PROTOCOL_VERSION(_util, OPTIGA_COMMS_PROTOCOL_VERSION_PRE_SHARED_SECRET); + OPTIGA_CRYPT_SET_COMMS_PROTOCOL_VERSION( + _crypt, OPTIGA_COMMS_PROTOCOL_VERSION_PRE_SHARED_SECRET); + + OPTIGA_UTIL_SET_COMMS_PROTECTION_LEVEL(_util, OPTIGA_COMMS_NO_PROTECTION); + res = _optiga_util_open_application_sync(_util, 0); + if (res != OPTIGA_LIB_SUCCESS) { + util_log("failed to open util application: %x", res); + return res; + } + + res = _factory_write_config(); + if (res) { + return res; + } + + res = _optiga_util_close_application_sync(_util, 0); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + + if (NULL != _crypt) { + optiga_crypt_destroy(_crypt); + _crypt = NULL; + } + + if (NULL != _util) { + optiga_util_destroy(_util); + _util = NULL; + } + + return 0; +} +#endif // FACTORYSETUP == 1 || FACTORY_DURING_PROD == 1 + +static int _verify_metadata( + uint16_t oid, + const uint8_t* expected_metadata, + size_t expected_metadata_len, + const uint8_t* check_tags, + size_t check_tags_len) +{ + uint8_t actual_metadata[1000] = {0}; + uint16_t actual_metadata_len = sizeof(actual_metadata); + + optiga_lib_status_t res = + _optiga_util_read_metadata_sync(_util, oid, actual_metadata, &actual_metadata_len); + if (res != OPTIGA_LIB_SUCCESS) { + util_log("fail: read binding secret metadata: %x", res); + return res; + } + + uint8_t expected_tag_data[100] = {0}; + size_t expected_tag_data_len = 0; + uint8_t actual_tag_data[100] = {0}; + size_t actual_tag_data_len = 0; + + for (size_t i = 0; i < check_tags_len; i++) { + uint8_t tag = check_tags[i]; + if (!_read_metadata_tag( + expected_metadata, + expected_metadata_len, + tag, + expected_tag_data, + &expected_tag_data_len)) { + return OPTIGA_ERR_UNEXPECTED_METADATA; + } + if (!_read_metadata_tag( + actual_metadata, actual_metadata_len, tag, actual_tag_data, &actual_tag_data_len)) { + return OPTIGA_ERR_UNEXPECTED_METADATA; + } + if (actual_tag_data_len != expected_tag_data_len || + !MEMEQ(actual_tag_data, expected_tag_data, actual_tag_data_len)) { + return OPTIGA_ERR_CONFIG_MISMATCH; + } + } + return 0; +} + +static int _verify_config(void) +{ + int res; + + OPTIGA_UTIL_SET_COMMS_PROTOCOL_VERSION(_util, OPTIGA_COMMS_PROTOCOL_VERSION_PRE_SHARED_SECRET); + OPTIGA_CRYPT_SET_COMMS_PROTOCOL_VERSION( + _crypt, OPTIGA_COMMS_PROTOCOL_VERSION_PRE_SHARED_SECRET); + + // Verify shielded connection is active. + if (_crypt->protection_level != OPTIGA_COMMS_FULL_PROTECTION) { + util_log("crypt protection level expected to be FULL"); + return OPTIGA_ERR_CONFIG_MISMATCH; + } + if (_util->protection_level != OPTIGA_COMMS_FULL_PROTECTION) { + util_log("util protection level expected to be FULL"); + return OPTIGA_ERR_CONFIG_MISMATCH; + } + + res = _optiga_util_open_application_sync(_util, 0); + if (res) { + return res; + } + + // Verify metadata tags are setup as expected. + + { + const uint8_t check_tags[] = {0xC0, 0xD0, 0xD1, 0xD3, 0xE8}; + res = _verify_metadata( + OID_PLATFORM_BINDING, + _platform_binding_metadata, + sizeof(_platform_binding_metadata), + check_tags, + sizeof(check_tags)); + if (res) { + return res; + } + } + { + const uint8_t check_tags[] = {0xC0, 0xE1, 0xD0, 0xD1, 0xD3}; + res = _verify_metadata( + OID_AES_SYMKEY, + _aes_symkey_metadata, + sizeof(_aes_symkey_metadata), + check_tags, + sizeof(check_tags)); + if (res) { + return res; + } + } + { + const uint8_t check_tags[] = {0xC0, 0xE8, 0xD0, 0xD1, 0xD3}; + res = _verify_metadata( + OID_HMAC, _hmac_metadata, sizeof(_hmac_metadata), check_tags, sizeof(check_tags)); + if (res) { + return res; + } + } + { + const uint8_t check_tags[] = {0xC0, 0xE8, 0xD0, 0xD1, 0xD3}; + res = _verify_metadata( + OID_ARBITRARY_DATA, + _arbitrary_data_metadata, + sizeof(_arbitrary_data_metadata), + check_tags, + sizeof(check_tags)); + if (res) { + return res; + } + } + { + const uint8_t check_tags[] = {0xC0, 0xE1, 0xD0, 0xD1, 0xD3}; + res = _verify_metadata( + OID_ATTESTATION, + _attestation_metadata, + sizeof(_attestation_metadata), + check_tags, + sizeof(check_tags)); + if (res) { + return res; + } + } + { + const uint8_t check_tags[] = {0xC0, 0xD0, 0xD1, 0xD3}; + res = _verify_metadata( + OID_COUNTER, + _counter_metadata, + sizeof(_counter_metadata), + check_tags, + sizeof(check_tags)); + if (res) { + return res; + } + } + return 0; +} + +int optiga_setup(const securechip_interface_functions_t* ifs) +{ + if (ifs == NULL) { + return OPTIGA_ERR_IFS; + } + _ifs = ifs; + + util_log("optiga_setup"); + + // A timer is used to provide the OPTIGA library with the ability to schedule work on the main + // event loop + pal_timer_init(); + +#if FACTORYSETUP == 1 || FACTORY_DURING_PROD == 1 + int res = _factory_setup(); + if (res) { + util_log("factory setup failed"); + return res; + } +#endif + + _util = optiga_util_create(OPTIGA_INSTANCE_ID_0, _optiga_lib_callback, NULL); + if (NULL == _util) { + return OPTIGA_ERR_CREATE; + } + + _crypt = optiga_crypt_create(OPTIGA_INSTANCE_ID_0, _optiga_lib_callback, NULL); + if (NULL == _crypt) { + return OPTIGA_ERR_CREATE; + } + + return _verify_config(); +} + +bool optiga_update_keys(void) +{ + ABORT_IF_NULL(_ifs); + ABORT_IF_NULL(_ifs->random_32_bytes); + + uint8_t new_key[32] = {0}; + _ifs->random_32_bytes(new_key); + + optiga_lib_status_t res = _optiga_util_write_data_sync( + _util, OID_HMAC, OPTIGA_UTIL_ERASE_AND_WRITE, 0x00, new_key, sizeof(new_key)); + if (res != OPTIGA_UTIL_SUCCESS) { + util_log("failed updating the hmac key: %x", res); + return false; + } + + optiga_key_id_t keyid = OPTIGA_KEY_ID_SECRET_BASED; + res = _optiga_crypt_symmetric_generate_key_sync( + _crypt, OPTIGA_SYMMETRIC_AES_256, OPTIGA_KEY_USAGE_ENCRYPTION, false, &keyid); + if (res != OPTIGA_UTIL_SUCCESS) { + util_log("failed updating the sym key: %x", res); + return false; + } + + return true; +} + +int optiga_kdf_external(const uint8_t* msg, size_t len, uint8_t* mac_out) +{ + if (len != 32) { + return OPTIGA_ERR_INVALID_ARGS; + } + + ABORT_IF_NULL(_crypt); + optiga_lib_status_t res; + // The equivalient of python `mac_out = hmac.new(key, msg[:len], hashlib.sha256).digest()` + + uint32_t mac_out_len = 32; + + res = _optiga_crypt_hmac_sync( + _crypt, OPTIGA_HMAC_SHA_256, OID_HMAC, msg, len, mac_out, &mac_out_len); + if (res != OPTIGA_LIB_SUCCESS) { + util_log("kdf fail err=%x", res); + return res; + } + if (mac_out_len != 32) { + return OPTIGA_ERR_UNEXPECTED_LEN; + } + + return 0; +} + +int optiga_kdf_internal(const uint8_t* msg, size_t len, uint8_t* kdf_out) +{ + if (len != 32) { + return OPTIGA_ERR_INVALID_ARGS; + } + ABORT_IF_NULL(_crypt); + optiga_lib_status_t res; + + uint8_t mac_out[16] = {0}; + uint32_t mac_out_len = sizeof(mac_out); + + res = _optiga_crypt_symmetric_encrypt_sync( + _crypt, + OPTIGA_SYMMETRIC_CMAC, + OID_AES_SYMKEY, + msg, + len, + NULL, + 0, + NULL, + 0, + mac_out, + &mac_out_len); + if (res != OPTIGA_LIB_SUCCESS) { + return res; + } + if (mac_out_len != sizeof(mac_out)) { + return OPTIGA_ERR_UNEXPECTED_LEN; + } + rust_sha256(mac_out, mac_out_len, kdf_out); + return 0; +} + +bool optiga_gen_attestation_key(uint8_t* pubkey_out) +{ + ABORT_IF_NULL(_crypt); + optiga_key_id_t slot = OPTIGA_KEY_ID_E0F1; + uint8_t pubkey_der[68] = {0}; + uint16_t pubkey_der_size = sizeof(pubkey_der); + optiga_lib_status_t res = _optiga_crypt_ecc_generate_keypair_sync( + _crypt, + OPTIGA_ECC_CURVE_NIST_P_256, + OPTIGA_KEY_USAGE_SIGN, + false, + (void*)&slot, + pubkey_der, + &pubkey_der_size); + if (res != OPTIGA_CRYPT_SUCCESS) { + util_log("gen keypair failed: %x", res); + return false; + } + // Parse DER "BIT STRING", see Solution Reference Manual 6.2.2, + // example for ECC NIST-P256. + // The 64 byte X/Y values are the last 64 bytes. + if (pubkey_der_size != 68 || !MEMEQ(pubkey_der, "\x03\x42\x00\x04", 4)) { + return false; + } + memcpy(pubkey_out, pubkey_der + 4, 64); + return true; +} + +bool optiga_attestation_sign(const uint8_t* challenge, uint8_t* signature_out) +{ + ABORT_IF_NULL(_crypt); + uint8_t sig_der[70] = {0}; + uint16_t sig_der_size = sizeof(sig_der); + optiga_lib_status_t res = _optiga_crypt_ecdsa_sign_sync( + _crypt, challenge, 32, OPTIGA_KEY_ID_E0F1, sig_der, &sig_der_size); + if (res != OPTIGA_CRYPT_SUCCESS) { + util_log("sign failed: %x", res); + return false; + } + // Parse signature, see Solution Reference Manual 6.2.2, + // example for ECC NIST-P256 signature. + // The R/S components are + return rust_der_parse_optiga_signature( + rust_util_bytes(sig_der, sig_der_size), rust_util_bytes_mut(signature_out, 64)); +} + +bool optiga_monotonic_increments_remaining(uint32_t* remaining_out) +{ + uint8_t buf[4] = {0}; + uint16_t size = sizeof(buf); + optiga_lib_status_t res = _optiga_util_read_data_sync(_util, OID_COUNTER, 0, buf, &size); + if (res != OPTIGA_LIB_SUCCESS) { + return false; + } + + uint32_t counter = optiga_common_get_uint32(buf); + if (counter > MONOTONIC_COUNTER_MAX_USE) { + Abort("optiga monotonic counter larget than max"); + } + *remaining_out = MONOTONIC_COUNTER_MAX_USE - counter; + return true; +} + +// rand_out must be 32 bytes +bool optiga_random(uint8_t* rand_out) +{ + optiga_lib_status_t res = _optiga_crypt_random_sync(_crypt, OPTIGA_RNG_TYPE_TRNG, rand_out, 32); + if (res != OPTIGA_CRYPT_SUCCESS) { + util_log("optiga_random failed: %x", res); + return false; + } + return true; +} + +#if APP_U2F == 1 || FACTORYSETUP == 1 +bool optiga_u2f_counter_set(uint32_t counter) +{ + arbitrary_data_t data = {0}; + if (!_read_arbitrary_data(&data)) { + return false; + } + data.fields.u2f_counter = counter; + return _write_arbitrary_data(&data) == 0; +} +#endif + +#if APP_U2F == 1 +bool optiga_u2f_counter_inc(uint32_t* counter) +{ + arbitrary_data_t data = {0}; + if (!_read_arbitrary_data(&data)) { + return false; + } + data.fields.u2f_counter += 1; + *counter = data.fields.u2f_counter; + return _write_arbitrary_data(&data); +} +#endif + +bool optiga_model(securechip_model_t* model_out) +{ + *model_out = OPTIGA_TRUST_M_V3; + return true; +} diff --git a/src/optiga/optiga.h b/src/optiga/optiga.h new file mode 100644 index 000000000..96d8bde62 --- /dev/null +++ b/src/optiga/optiga.h @@ -0,0 +1,54 @@ +// Copyright 2024 Shift Crypto AG +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _OPTIGA_H_ +#define _OPTIGA_H_ + +/* Optiga Trust M implementation of the secure chip functions. */ +/* See securechip.h for the docstrings of the individual funtions. */ + +#include "compiler_util.h" +#include "securechip/securechip.h" +#include +#include +#include +#include + +typedef enum { + OPTIGA_ERR_CREATE = -1, + OPTIGA_ERR_CONFIG_MISMATCH = -2, + OPTIGA_ERR_IFS = -3, + OPTIGA_ERR_UNEXPECTED_METADATA = -4, + OPTIGA_ERR_PAL = -5, + OPTIGA_ERR_UNEXPECTED_LEN = -6, + OPTIGA_ERR_INVALID_ARGS = -7, +} optiga_error_t; + +USE_RESULT int optiga_setup(const securechip_interface_functions_t* ifs); +USE_RESULT bool optiga_update_keys(void); +USE_RESULT int optiga_kdf_external(const uint8_t* msg, size_t len, uint8_t* mac_out); +USE_RESULT int optiga_kdf_internal(const uint8_t* msg, size_t len, uint8_t* mac_out); +USE_RESULT bool optiga_gen_attestation_key(uint8_t* pubkey_out); +USE_RESULT bool optiga_attestation_sign(const uint8_t* challenge, uint8_t* signature_out); +USE_RESULT bool optiga_monotonic_increments_remaining(uint32_t* remaining_out); +USE_RESULT bool optiga_random(uint8_t* rand_out); +#if APP_U2F == 1 || FACTORYSETUP == 1 +USE_RESULT bool optiga_u2f_counter_set(uint32_t counter); +#endif +#if APP_U2F == 1 +USE_RESULT bool optiga_u2f_counter_inc(uint32_t* counter); +#endif +USE_RESULT bool optiga_model(securechip_model_t* model_out); + +#endif // _OPTIGA_H_ diff --git a/src/optiga/pal/README.bitbox02.md b/src/optiga/pal/README.bitbox02.md new file mode 100644 index 000000000..3797bb8e6 --- /dev/null +++ b/src/optiga/pal/README.bitbox02.md @@ -0,0 +1 @@ +copied from extras/pal/NEW_PAL_TEMPLATE/ diff --git a/src/optiga/pal/README.md b/src/optiga/pal/README.md new file mode 100644 index 000000000..f86613d2a --- /dev/null +++ b/src/optiga/pal/README.md @@ -0,0 +1,275 @@ +# Porting Guide + +The implementation of Platform Abstraction Layer (PAL) needs to be updated in order to migrate to a new +target platform. + +The PAL reference code for the XMC4800 IoT connectivity kit is provided as part of package which can be used. +The implementation can be found in “/pal/xmc4800” and the header files are available in +“/optiga/include” with the required APIs used by upper layers. The header files are platform +agnostic and would not require any changes. The low level drivers used by PAL for XMC4800 are configured and +generated using DAVE™. + +## Communication with OPTIGA™ Trust M + +The hardware/platform resource configuration with respect to I2C master and GPIOs (Vdd and Reset) are to be +updated in pal_ifx_i2c_config.c. These configurations are used by the IFX I2C implementation to communicate +with OPTIGA™ Trust M. + +### Update I2C master platform specific context[e.g. (void)&i2c_master_0] + +```c +/* +* \brief PAL I2C configuration for OPTIGA +*/ +pal_i2c_t optiga_pal_i2c_context_0 = +{ + /// Pointer to I2C master platform specific context + (void)&i2c_master_0, + /// Slave address + 0x30, + /// Upper layer context + NULL, + /// Callback event handler + NULL +}; +``` + +### Update platform specific context for GPIOs (Vdd and Reset) [e.g. (void)&pin_3_4] +```c +/* +* \brief Vdd pin configuration for OPTIGA +*/ +pal_gpio_t optiga_vdd_0 = +{ + // Platform specific GPIO context for the pin used to toggle Vdd + (void*)&vdd_pin +}; + +/** +* \brief Reset pin configuration for OPTIGA +*/ + pal_gpio_t optiga_reset_0 = +{ + // Platform specific GPIO context for the pin used to toggle Reset + (void*)&reset_pin +}; +``` + +### Update PAL I2C APIs [pal_i2c.c] to communicate with OPTIGA™ Trust M + +The `pal_i2c` is expected to provide the APIs for I2C driver initialization, de-initialization, read, write and set +bitrate kind of operations + +1. `pal_i2c_init` +2. `pal_i2c_deinit` +3. `pal_i2c_read` +4. `pal_i2c_write` +5. `pal_i2c_set_bitrate` + +A few target platforms, the I2C master driver initialization (`pal_i2c_init`) is done during the platform start up. In +such an environment, there is no need to implement pal_i2c_init and `pal_i2c_deinit` functions. Otherwise, +these (`pal_i2c_init` & `pal_i2c_deinit`) functions must be implemented as per the upper layer expectations based +on the need. The details of these expectations are available in the [Host library API documentation (chm)](https://github.com/Infineon/optiga-trust-m/blob/master/documents/OPTIGA_Trust_M_V1_Host_Library_Documentation.chm). + +The reference implementation of PAL I2C based on XMC4800 IoT connectivity kit does not need to have the +platform I2C driver initialization explicitly done as part of pal_i2c_init as it is taken care by the DAVE™ library +initialization. Hence pal_i2c_init & pal_i2c_deinit are not implemented. + +In addition to the above specified APIs, the PAL I2C must handle the events from the low level I2C driver and +invoke the upper layer handlers registered with PAL I2C context for the respective transaction as shown in the +below example. + +```c +//I2C driver callback function when the transmit is completed successfully +void i2c_master_end_of_transmit_callback(void) +{ + invoke_upper_layer_callback(gp_pal_i2c_current_ctx, (uint8_t)PAL_I2C_EVENT_TX_SUCCESS); +} +``` + +In above example the I2C driver callback, when transmission is successful invokes the handler to inform the +result. + +### Update PAL GPIO [pal_gpio.c] to power on and reset the OPTIGA™ Trust M + +1. `pal_gpio_set_high` +2. `pal_gpio_set_low` + +### Update PAL Timer [pal_os_timer.c] to enable timer + +1. `pal_os_timer_get_time_in_milliseconds` +2. `pal_os_timer_delay_in_milliseconds` + +### Update Event management for the asynchronous interactions for IFX I2C [pal_os_event.c] + +1. `pal_os_event_register_callback_oneshot` +2. `pal_os_event_trigger_registered_callback` + +The `pal_os_event_register_callback_oneshot` function is expected to register the handler and context +provided as part of input parameters and triggers the timer for the requested time. The `p_pal_os_event` is an +event instance created using `pal_os_event_create`. + +```c +void pal_os_event_register_callback_oneshot(pal_os_event_t * p_pal_os_event, + register_callback callback, + void* callback_args, + uint32_t time_us) +{ + p_pal_os_event->callback_registered = callback; + p_pal_os_event->callback_ctx = callback_args; + //lint --e{534} suppress "Return value is not required to be checked" + TIMER_SetTimeInterval(&scheduler_timer, (time_us*100)); + TIMER_Start(&scheduler_timer); +} +``` + +The handler registered must be invoked once the timer has elapsed as shown in +`pal_os_event_trigger_registered_callback`. The `pal_os_event_trigger_registered_callback` is to be +registered with event timer interrupt to get trigerred when the timer expires. The `pal_os_event_0` is the +instance in the `pal_os_event` used store the registered callback and context. + +```c +void pal_os_event_trigger_registered_callback(void) +{ + register_callback callback; + + TIMER_ClearEvent(&scheduler_timer); + //lint --e{534} suppress "Return value is not required to be checked" + TIMER_Stop(&scheduler_timer); + TIMER_Clear(&scheduler_timer); + + if (pal_os_event_0.callback_registered) + { + Callback = pal_os_event_0.callback_registered; + callback ((void*)callback_ctx); + } +} +``` + +## Initalisation hints + +This is a code sample demonstrating initialization routine for the security chip, as well as a hibernation example +```c +#include "optiga/optiga_util.h" +#include "optiga/pal/pal_os_event.h" +#include "optiga/pal/pal.h" +#include "optiga/pal/pal_os_timer.h" +/** + * Callback when optiga_util_xxxx operation is completed asynchronously + */ +static volatile optiga_lib_status_t optiga_lib_status; +static void optiga_util_callback(void * context, optiga_lib_status_t return_status) +{ + optiga_lib_status = return_status; +} + +int32_t main(void) +{ + uint8_t return_value = 0; + + optiga_lib_status_t return_status; + + optiga_util_t * me_util; + + do + { + //Create an instance of optiga_util to open the application on OPTIGA. + me_util = optiga_util_create(0, optiga_util_callback, NULL); + + /** + * Open the application on OPTIGA which is a precondition to perform any other operations + * using optiga_util_open_application + */ + optiga_lib_status = OPTIGA_LIB_BUSY; + return_status = optiga_util_open_application(me_util, 0); + + if (OPTIGA_LIB_SUCCESS != return_status) + { + break; + } + while (optiga_lib_status == OPTIGA_LIB_BUSY) + { + //Wait until the optiga_util_open_application is completed + } + if (OPTIGA_LIB_SUCCESS != optiga_lib_status) + { + //optiga util open application failed + break; + } + /** + * Hibernate the application on OPTIGA + * using optiga_util_close_application + */ + optiga_lib_status = OPTIGA_LIB_BUSY; + return_status = optiga_util_close_application(me_util, 1); + + if (OPTIGA_LIB_SUCCESS != return_status) + { + break; + } + + while (optiga_lib_status == OPTIGA_LIB_BUSY) + { + //Wait until the optiga_util_close_application is completed + } + + if (OPTIGA_LIB_SUCCESS != optiga_lib_status) + { + //optiga util close application failed + break; + } + /** + * Restore the application on OPTIGA + * using optiga_util_open_application + */ + optiga_lib_status = OPTIGA_LIB_BUSY; + return_status = optiga_util_open_application(me_util, 1); + + if (OPTIGA_LIB_SUCCESS != return_status) + { + break; + } + while (OPTIGA_LIB_BUSY == optiga_lib_status) + { + //Wait until the optiga_util_open_application is completed + } + if (OPTIGA_LIB_SUCCESS != optiga_lib_status) + { + //optiga util open application failed + break; + } + + /* + Paste your code is here + */ + + /** + * Close the application on OPTIGA after all the operations are executed + * using optiga_util_close_application + */ + optiga_lib_status = OPTIGA_LIB_BUSY; + return_status = optiga_util_close_application(me_util, 0); + + if (OPTIGA_LIB_SUCCESS != return_status) + { + break; + } + + while (optiga_lib_status == OPTIGA_LIB_BUSY) + { + //Wait until the optiga_util_close_application is completed + } + + if (OPTIGA_LIB_SUCCESS != optiga_lib_status) + { + //optiga util close application failed + break; + } + + // destroy util and crypt instances + optiga_util_destroy(me_util); + }while (FALSE); + + return return_value; +} +``` diff --git a/src/optiga/pal/pal.c b/src/optiga/pal/pal.c new file mode 100644 index 000000000..851557386 --- /dev/null +++ b/src/optiga/pal/pal.c @@ -0,0 +1,52 @@ +/** + * \copyright + * MIT License + * + * Copyright (c) 2019 Infineon Technologies AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + * + * \endcopyright + * + * \author Infineon Technologies AG + * + * \file pal.c + * + * \brief This file implements the platform abstraction layer APIs. + * + * \ingroup grPAL + * + * @{ + */ + +#include "pal.h" + +pal_status_t pal_init(void) +{ + return PAL_STATUS_SUCCESS; +} + +pal_status_t pal_deinit(void) +{ + return PAL_STATUS_SUCCESS; +} + +/** + * @} + */ diff --git a/src/optiga/pal/pal_gpio.c b/src/optiga/pal/pal_gpio.c new file mode 100644 index 000000000..4fa6bfb2f --- /dev/null +++ b/src/optiga/pal/pal_gpio.c @@ -0,0 +1,66 @@ +/** + * \copyright + * MIT License + * + * Copyright (c) 2019 Infineon Technologies AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + * + * \endcopyright + * + * \author Infineon Technologies AG + * + * \file pal_gpio.c + * + * \brief This file implements the platform abstraction layer APIs for GPIO. + * + * \ingroup grPAL + * + * @{ + */ + +#include "pal_gpio.h" + +pal_status_t pal_gpio_init(const pal_gpio_t* p_gpio_context) +{ + (void)p_gpio_context; + return PAL_STATUS_SUCCESS; +} + +pal_status_t pal_gpio_deinit(const pal_gpio_t* p_gpio_context) +{ + (void)p_gpio_context; + return PAL_STATUS_SUCCESS; +} + +void pal_gpio_set_high(const pal_gpio_t* p_gpio_context) +{ + (void)p_gpio_context; + // We don't support setting vdd or rst pins +} + +void pal_gpio_set_low(const pal_gpio_t* p_gpio_context) +{ + (void)p_gpio_context; + // We don't support setting vdd or rst pins +} + +/** + * @} + */ diff --git a/src/optiga/pal/pal_i2c.c b/src/optiga/pal/pal_i2c.c new file mode 100644 index 000000000..f78cfd0cd --- /dev/null +++ b/src/optiga/pal/pal_i2c.c @@ -0,0 +1,313 @@ +/** + * \copyright + * MIT License + * + * Copyright (c) 2019 Infineon Technologies AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + * + * \endcopyright + * + * \author Infineon Technologies AG + * + * \file pal_i2c.c + * + * \brief This file implements the platform abstraction layer(pal) APIs for I2C. + * + * \ingroup grPAL + * + * @{ + */ + +#include "pal_i2c.h" +#include "hal_delay.h" +#include "hal_i2c_m_sync.h" +#include "pal_os_timer.h" +#include "util.h" +extern struct i2c_m_sync_desc I2C_0; + +#define PAL_I2C_MASTER_MAX_BITRATE (400U) + +static volatile uint32_t g_entry_count = 0; +static const pal_i2c_t* gp_pal_i2c_current_ctx; + +static pal_status_t pal_i2c_acquire(const void* p_i2c_context) +{ + // To avoid compiler errors/warnings. This context might be used by a target + // system to implement a proper mutex handling + (void)p_i2c_context; + + if (0 == g_entry_count) { + g_entry_count++; + if (1 == g_entry_count) { + return PAL_STATUS_SUCCESS; + } + } + return PAL_STATUS_FAILURE; +} + +static void pal_i2c_release(const void* p_i2c_context) +{ + // To avoid compiler errors/warnings. This context might be used by a target + // system to implement a proper mutex handling + (void)p_i2c_context; + + g_entry_count = 0; +} + +static void invoke_upper_layer_callback(const pal_i2c_t* p_pal_i2c_ctx, optiga_lib_status_t event) +{ + upper_layer_callback_t upper_layer_handler; + + // Casting a data pointer to a function pointer is not OK according to ISO C. However, everyone + // does it... +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + upper_layer_handler = (upper_layer_callback_t)p_pal_i2c_ctx->upper_layer_event_handler; +#pragma GCC diagnostic pop + + upper_layer_handler(p_pal_i2c_ctx->p_upper_layer_ctx, event); + + // Release I2C Bus + pal_i2c_release(p_pal_i2c_ctx->p_upper_layer_ctx); +} + +// !!!OPTIGA_LIB_PORTING_REQUIRED +// The next 5 functions are required only in case you have interrupt based i2c implementation +// void i2c_master_end_of_transmit_callback(void) +//{ +// invoke_upper_layer_callback(gp_pal_i2c_current_ctx, PAL_I2C_EVENT_SUCCESS); +//} +// +// void i2c_master_end_of_receive_callback(void) +//{ +// invoke_upper_layer_callback(gp_pal_i2c_current_ctx, PAL_I2C_EVENT_SUCCESS); +//} +// +// void i2c_master_error_detected_callback(void) +//{ +// invoke_upper_layer_callback(gp_pal_i2c_current_ctx, PAL_I2C_EVENT_ERROR); +//} +// +// void i2c_master_nack_received_callback(void) +//{ +// i2c_master_error_detected_callback(); +//} +// +// void i2c_master_arbitration_lost_callback(void) +//{ +// i2c_master_error_detected_callback(); +//} + +pal_status_t pal_i2c_init(const pal_i2c_t* p_i2c_context) +{ + (void)p_i2c_context; + return PAL_STATUS_SUCCESS; +} + +pal_status_t pal_i2c_deinit(const pal_i2c_t* p_i2c_context) +{ + (void)p_i2c_context; + return PAL_STATUS_SUCCESS; +} + +pal_status_t pal_i2c_write(const pal_i2c_t* p_i2c_context, uint8_t* p_data, uint16_t length) +{ + pal_status_t status = PAL_STATUS_FAILURE; + struct _i2c_m_msg packet; + uint8_t retries = 25U; + int32_t r; + + packet.addr = p_i2c_context->slave_address; + packet.len = (int32_t)length; + packet.buffer = p_data; + packet.flags = I2C_M_SEVEN | I2C_M_STOP; + + // Acquire the I2C bus before read/write + if (PAL_STATUS_SUCCESS == pal_i2c_acquire(p_i2c_context)) { + gp_pal_i2c_current_ctx = p_i2c_context; + + // Invoke the low level i2c master driver API to write to the bus + // !!!OPTIGA_LIB_PORTING_REQUIRED + do { + r = i2c_m_sync_transfer(p_i2c_context->p_i2c_hw_config, &packet); + delay_ms(2U); + } while (retries-- && r != I2C_OK); + + if (r != I2C_OK) { + // If I2C Master fails to invoke the write operation, invoke upper layer event handler + // with error. + + // Casting a data pointer to a function pointer is not OK according to ISO C. However, + // everyone does it... +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + ((upper_layer_callback_t)(p_i2c_context->upper_layer_event_handler))( + p_i2c_context->p_upper_layer_ctx, PAL_I2C_EVENT_ERROR); +#pragma GCC diagnostic pop + + // Release I2C Bus + pal_i2c_release(p_i2c_context); + } else { + // !!!OPTIGA_LIB_PORTING_REQUIRED + /** + * Infineon I2C Protocol is a polling based protocol, if foo_i2c_write will fail it will + * be reported to the upper layers by calling + * (p_i2c_context->upper_layer_event_handler))(p_i2c_context->p_upper_layer_ctx , + * PAL_I2C_EVENT_ERROR); If the function foo_i2c_write() will succedd then two options + * are possible + * 1. if foo_i2c_write() is interrupt based, then you need to configure interrupts in + * the function pal_i2c_init() so that on a succesfull transmit interrupt the callback + * i2c_master_end_of_transmit_callback(), in case of successfull receive + * i2c_master_end_of_receive_callback() callback in case of not acknowedged, arbitration + * lost, generic error i2c_master_nack_received_callback() or + * i2c_master_arbitration_lost_callback() + * 2. If foo_i2c_write() is a blocking function which will return either ok or failure + * after transmitting data you can handle this case directly here and call + * invoke_upper_layer_callback(gp_pal_i2c_current_ctx, PAL_I2C_EVENT_SUCCESS); + * + */ + invoke_upper_layer_callback(gp_pal_i2c_current_ctx, PAL_I2C_EVENT_SUCCESS); + status = PAL_STATUS_SUCCESS; + } + } else { + status = PAL_STATUS_I2C_BUSY; + // Casting a data pointer to a function pointer is not OK according to ISO C. However, + // everyone does it... +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + ((upper_layer_callback_t)(p_i2c_context->upper_layer_event_handler))( + p_i2c_context->p_upper_layer_ctx, PAL_I2C_EVENT_BUSY); +#pragma GCC diagnostic pop + } + return status; +} + +pal_status_t pal_i2c_read(const pal_i2c_t* p_i2c_context, uint8_t* p_data, uint16_t length) +{ + // int32_t start = pal_os_timer_get_time_in_milliseconds(); + pal_status_t status = PAL_STATUS_FAILURE; + struct _i2c_m_msg packet; + uint8_t retries = 25U; + int32_t r; + + packet.addr = p_i2c_context->slave_address; + packet.len = (int32_t)length; + packet.buffer = p_data; + packet.flags = I2C_M_SEVEN | I2C_M_RD | I2C_M_STOP; + + // Acquire the I2C bus before read/write + if (PAL_STATUS_SUCCESS == pal_i2c_acquire(p_i2c_context)) { + gp_pal_i2c_current_ctx = p_i2c_context; + + // Invoke the low level i2c master driver API to read from the bus + do { + r = i2c_m_sync_transfer(p_i2c_context->p_i2c_hw_config, &packet); + delay_ms(2U); + } while (retries-- && r != I2C_OK); + + if (r != I2C_OK) { + // If I2C Master fails to invoke the read operation, invoke upper layer event handler + // with error. + // Casting a data pointer to a function pointer is not OK according to ISO C. However, + // everyone does it... +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + ((upper_layer_callback_t)(p_i2c_context->upper_layer_event_handler))( + p_i2c_context->p_upper_layer_ctx, PAL_I2C_EVENT_ERROR); +#pragma GCC diagnostic pop + + // Release I2C Bus + pal_i2c_release(p_i2c_context); + } else { + // !!!OPTIGA_LIB_PORTING_REQUIRED + /** + * Similar to the foo_i2c_write() case you can directly call + * invoke_upper_layer_callback(gp_pal_i2c_current_ctx, PAL_I2C_EVENT_SUCCESS); + * if you have blocking (non-interrupt) i2c calls + */ + invoke_upper_layer_callback(gp_pal_i2c_current_ctx, PAL_I2C_EVENT_SUCCESS); + status = PAL_STATUS_SUCCESS; + } + } else { + status = PAL_STATUS_I2C_BUSY; + // Casting a data pointer to a function pointer is not OK according to ISO C. However, + // everyone does it... +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + ((upper_layer_callback_t)(p_i2c_context->upper_layer_event_handler))( + p_i2c_context->p_upper_layer_ctx, PAL_I2C_EVENT_BUSY); +#pragma GCC diagnostic pop + } + // uint32_t final_time = pal_os_timer_get_time_in_milliseconds() - start; + return status; +} + +pal_status_t pal_i2c_set_bitrate(const pal_i2c_t* p_i2c_context, uint16_t bitrate) +{ + pal_status_t return_status = PAL_STATUS_FAILURE; + optiga_lib_status_t event = PAL_I2C_EVENT_ERROR; + + // Acquire the I2C bus before setting the bitrate + if (PAL_STATUS_SUCCESS == pal_i2c_acquire(p_i2c_context)) { + // If the user provided bitrate is greater than the I2C master hardware maximum supported + // value, set the I2C master to its maximum supported value. + if (bitrate > PAL_I2C_MASTER_MAX_BITRATE) { + bitrate = PAL_I2C_MASTER_MAX_BITRATE; + } + + do { + if (0 != i2c_m_sync_disable(&I2C_0)) { + return_status = PAL_STATUS_FAILURE; + break; + } + + if (0 != i2c_m_sync_set_baudrate(p_i2c_context->p_i2c_hw_config, 0, bitrate)) { + return_status = PAL_STATUS_FAILURE; + break; + } + + if (0 != i2c_m_sync_enable(&I2C_0)) { + return_status = PAL_STATUS_FAILURE; + break; + } + event = PAL_I2C_EVENT_SUCCESS; + return_status = PAL_STATUS_SUCCESS; + } while (0); + } else { + return_status = PAL_STATUS_I2C_BUSY; + event = PAL_I2C_EVENT_BUSY; + } + if (0 != p_i2c_context->upper_layer_event_handler) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + ((upper_layer_callback_t)(p_i2c_context->upper_layer_event_handler))( + p_i2c_context->p_upper_layer_ctx, event); +#pragma GCC diagnostic pop + } + // Release I2C Bus if its acquired + if (PAL_STATUS_I2C_BUSY != return_status) { + pal_i2c_release(p_i2c_context); + } + return return_status; +} + +/** + * @} + */ diff --git a/src/optiga/pal/pal_ifx_i2c_config.c b/src/optiga/pal/pal_ifx_i2c_config.c new file mode 100644 index 000000000..e75043ef0 --- /dev/null +++ b/src/optiga/pal/pal_ifx_i2c_config.c @@ -0,0 +1,90 @@ +/** + * \copyright + * MIT License + * + * Copyright (c) 2019 Infineon Technologies AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + * + * \endcopyright + * + * \author Infineon Technologies AG + * + * \file pal_ifx_i2c_config.c + * + * \brief This file implements platform abstraction layer configurations for ifx i2c protocol. + * + * \ingroup grPAL + * + * @{ + */ + +#include "driver_init.h" +#include "ifx_i2c_config.h" +#include "pal_gpio.h" +#include "pal_i2c.h" + +// !!!OPTIGA_LIB_PORTING_REQUIRED +// typedef struct locl_i2c_struct_to_descroibe_master +//{ +// // you parameters to control the master instance +// // See other implementation to get intuition on how to implement this part +//}local_i2c_struct_to_descroibe_master_t; +// +// local_i2c_struct_to_descroibe_master_t i2c_master_0; + +/** + * \brief PAL I2C configuration for OPTIGA. + */ +pal_i2c_t optiga_pal_i2c_context_0 = { + /// Pointer to I2C master platform specific context + (void*)&I2C_0, + /// Upper layer context + NULL, + /// Callback event handler + NULL, + /// Slave address + 0x30, +}; + +/** + * \brief PAL vdd pin configuration for OPTIGA. + * NC on bitbox02 + */ +pal_gpio_t optiga_vdd_0 = { + // !!!OPTIGA_LIB_PORTING_REQUIRED + // Platform specific GPIO context for the pin used to toggle Vdd. + // You should have vdd_pin define in your system, + // alternativly you can put here raw GPIO number, but without the & sign + 0}; + +/** + * \brief PAL reset pin configuration for OPTIGA. + * NC on bitbox02 + */ +pal_gpio_t optiga_reset_0 = { + // !!!OPTIGA_LIB_PORTING_REQUIRED + // Platform specific GPIO context for the pin used to toggle Reset. + // You should have reset_pin define in your system, + // alternativly you can put here raw GPIO number, but without the & sign + 0}; + +/** + * @} + */ diff --git a/src/optiga/pal/pal_logger.c b/src/optiga/pal/pal_logger.c new file mode 100644 index 000000000..01854098e --- /dev/null +++ b/src/optiga/pal/pal_logger.c @@ -0,0 +1,95 @@ +/** + * \copyright + * MIT License + * + * Copyright (c) 2019 Infineon Technologies AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + * + * \endcopyright + * + * \author Infineon Technologies AG + * + * \file pal_logger.c + * + * \brief This file provides the prototypes declarations for pal logger. + * + * \ingroup grPAL + * + * @{ + */ +#include "pal_logger.h" +#include "util.h" + +pal_logger_t logger_console = { + .logger_config_ptr = NULL, + .logger_rx_flag = 0, + .logger_tx_flag = 1, +}; + +pal_status_t pal_logger_init(void* p_logger_context) +{ + pal_status_t return_status = PAL_STATUS_FAILURE; + pal_logger_t* p_log_context = p_logger_context; + (void)p_log_context; + + do { + // !!!OPTIGA_LIB_PORTING_REQUIRED + } while (0); + return return_status; +} + +pal_status_t pal_logger_deinit(void* p_logger_context) +{ + pal_status_t return_status = PAL_STATUS_FAILURE; + pal_logger_t* p_log_context = p_logger_context; + (void)p_log_context; + + do { + // !!!OPTIGA_LIB_PORTING_REQUIRED + } while (0); + return return_status; +} + +pal_status_t pal_logger_write( + void* p_logger_context, + const uint8_t* p_log_data, + uint32_t log_data_length) +{ + (void)p_logger_context; + char s[10000]; + snprintf(s, sizeof(s), "%s", p_log_data); + s[log_data_length] = 0; + util_log("%s", s); + return PAL_STATUS_SUCCESS; +} + +// NOLINTBEGIN(readability-non-const-parameter) +pal_status_t pal_logger_read(void* p_logger_context, uint8_t* p_log_data, uint32_t log_data_length) +{ + (void)p_logger_context; + (void)p_log_data; + (void)log_data_length; + return PAL_STATUS_FAILURE; +} +// NOLINTEND(readability-non-const-parameter) + +/** + * @} + */ diff --git a/src/optiga/pal/pal_os_datastore.c b/src/optiga/pal/pal_os_datastore.c new file mode 100644 index 000000000..26207e4ce --- /dev/null +++ b/src/optiga/pal/pal_os_datastore.c @@ -0,0 +1,82 @@ +/** + * \copyright + * MIT License + * + * Copyright (c) 2020 Infineon Technologies AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + * + * \endcopyright + * + * \author Infineon Technologies AG + * + * \file pal_os_datastore.c + * + * \brief This file implements the platform abstraction layer APIs for data store. + * + * \ingroup grPAL + * + * @{ + */ + +#include "pal_os_datastore.h" +#include "memory/memory.h" +#include + +/// @cond hidden + +// While the binding shared secret is read using pal_os_datastore_read() by the optiga library, +// we are suppoesd to store it manually using pal_os_datastore_write() during factory setup. +// However, we use the memory io protection key for that, which is initialized during +// memory_setup(). +pal_status_t pal_os_datastore_write(uint16_t datastore_id, const uint8_t* p_buffer, uint16_t length) +{ + (void)p_buffer; + (void)length; + util_log("pal_datastore_write, id=%d", datastore_id); + return PAL_STATUS_FAILURE; +} + +pal_status_t pal_os_datastore_read( + uint16_t datastore_id, + uint8_t* p_buffer, + uint16_t* p_buffer_length) +{ + pal_status_t return_status = PAL_STATUS_FAILURE; + util_log("pal_datastore_read, id=%d", datastore_id); + + switch (datastore_id) { + case OPTIGA_PLATFORM_BINDING_SHARED_SECRET_ID: { + memory_get_io_protection_key(p_buffer); + *p_buffer_length = 32; + return_status = PAL_STATUS_SUCCESS; + break; + } + default: { + *p_buffer_length = 0; + break; + } + } + + return return_status; +} +/// @endcond +/** + * @} + */ diff --git a/src/optiga/pal/pal_os_event.c b/src/optiga/pal/pal_os_event.c new file mode 100644 index 000000000..e16bf484e --- /dev/null +++ b/src/optiga/pal/pal_os_event.c @@ -0,0 +1,110 @@ +/** + * \copyright + * MIT License + * + * Copyright (c) 2019 Infineon Technologies AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + * + * \endcopyright + * + * \author Infineon Technologies AG + * + * \file pal_os_event.c + * + * \brief This file implements the platform abstraction layer APIs for os event/scheduler. + * + * \ingroup grPAL + * + * @{ + */ + +#include "pal_os_event.h" +#include "hal_timer.h" +#include "util.h" +extern struct timer_descriptor TIMER_0; + +static pal_os_event_t pal_os_event_0 = {0}; + +void pal_os_event_start( + pal_os_event_t* p_pal_os_event, + register_callback callback, + void* callback_args) +{ + if (0 == p_pal_os_event->is_event_triggered) { + p_pal_os_event->is_event_triggered = TRUE; + pal_os_event_register_callback_oneshot(p_pal_os_event, callback, callback_args, 1000); + } +} + +void pal_os_event_stop(pal_os_event_t* p_pal_os_event) +{ + p_pal_os_event->is_event_triggered = 0; +} + +pal_os_event_t* pal_os_event_create(register_callback callback, void* callback_args) +{ + if ((NULL != callback) && (NULL != callback_args)) { + pal_os_event_start(&pal_os_event_0, callback, callback_args); + } + return (&pal_os_event_0); +} + +static struct timer_task scheduler; + +void pal_os_event_trigger_registered_callback(void) +{ + // traceln("%s: called", __func__); + register_callback callback; + + if (pal_os_event_0.callback_registered) { + callback = pal_os_event_0.callback_registered; + callback(pal_os_event_0.callback_ctx); + } +} + +static void _timer_cb(const struct timer_task* const timer_task) +{ + (void)timer_task; + pal_os_event_trigger_registered_callback(); +} + +void pal_os_event_register_callback_oneshot( + pal_os_event_t* p_pal_os_event, + register_callback callback, + void* callback_args, + uint32_t time_us) +{ + p_pal_os_event->callback_registered = callback; + p_pal_os_event->callback_ctx = callback_args; + + scheduler.interval = (time_us + 99) / 100; + scheduler.cb = _timer_cb; + scheduler.mode = TIMER_TASK_ONE_SHOT; + timer_add_task(&TIMER_0, &scheduler); +} + +void pal_os_event_destroy(pal_os_event_t* pal_os_event) +{ + (void)pal_os_event; +} + +/** + * @} + */ diff --git a/src/optiga/pal/pal_os_lock.c b/src/optiga/pal/pal_os_lock.c new file mode 100644 index 000000000..7715ab830 --- /dev/null +++ b/src/optiga/pal/pal_os_lock.c @@ -0,0 +1,88 @@ +/** + * \copyright + * MIT License + * + * Copyright (c) 2019 Infineon Technologies AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + * + * \endcopyright + * + * \author Infineon Technologies AG + * + * \file pal_os_lock.c + * + * \brief This file implements the platform abstraction layer APIs for os locks (e.g. semaphore). + * + * \ingroup grPAL + * + * @{ + */ + +#include "pal_os_lock.h" + +void pal_os_lock_create(pal_os_lock_t* p_lock, uint8_t lock_type) +{ + p_lock->type = lock_type; + p_lock->lock = 0; +} + +void pal_os_lock_destroy(pal_os_lock_t* p_lock) +{ + (void)p_lock; +} + +pal_status_t pal_os_lock_acquire(pal_os_lock_t* p_lock) +{ + pal_status_t return_status = PAL_STATUS_FAILURE; + + // Below is a sample shared resource acquire mechanism + // it doesn't provide a guarantee against a deadlock + if (!(p_lock->lock)) { + p_lock->lock++; + if (1 != p_lock->lock) { + p_lock->lock--; + } + return_status = PAL_STATUS_SUCCESS; + } + return return_status; +} + +void pal_os_lock_release(pal_os_lock_t* p_lock) +{ + // Below is a sample shared resource acquire mechanism + // it doesn't provide a guarantee against a deadlock + if (0 != p_lock->lock) { + p_lock->lock--; + } +} + +void pal_os_lock_enter_critical_section(void) +{ + // For safety critical systems it is recommended to implement a critical section entry +} + +void pal_os_lock_exit_critical_section(void) +{ + // For safety critical systems it is recommended to implement a critical section exit +} + +/** + * @} + */ diff --git a/src/optiga/pal/pal_os_memory.c b/src/optiga/pal/pal_os_memory.c new file mode 100644 index 000000000..267d31ee1 --- /dev/null +++ b/src/optiga/pal/pal_os_memory.c @@ -0,0 +1,23 @@ +#include "pal_os_memory.h" +#include "util.h" +#include + +void* pal_os_calloc(uint32_t number_of_blocks, uint32_t block_size) +{ + return calloc(number_of_blocks, block_size); +} + +void pal_os_free(void* block) +{ + free(block); +} + +void pal_os_memcpy(void* p_destination, const void* p_source, uint32_t size) +{ + memcpy(p_destination, p_source, size); +} + +void pal_os_memset(void* p_buffer, uint32_t value, uint32_t size) +{ + memset(p_buffer, value, size); +} diff --git a/src/optiga/pal/pal_os_timer.c b/src/optiga/pal/pal_os_timer.c new file mode 100644 index 000000000..97424d30f --- /dev/null +++ b/src/optiga/pal/pal_os_timer.c @@ -0,0 +1,88 @@ +/** + * \copyright + * MIT License + * + * Copyright (c) 2019 Infineon Technologies AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + * + * \endcopyright + * + * \author Infineon Technologies AG + * + * \file pal_os_timer.c + * + * \brief This file implements the platform abstraction layer APIs for timer. + * + * \ingroup grPAL + * + * @{ + */ + +#include "pal_os_timer.h" +#include "hal_delay.h" +#include "hal_timer.h" +#include "hpl_time_measure.h" +#include "util.h" +extern struct timer_descriptor TIMER_0; + +static volatile uint32_t g_ms_count = 0; +static struct timer_task scheduler; + +uint32_t pal_os_timer_get_time_in_microseconds(void) +{ + static uint32_t count = 0; + // The implementation must ensure that every invocation of this API returns a unique + // value. + return g_ms_count * 1000 + (count++); +} + +uint32_t pal_os_timer_get_time_in_milliseconds(void) +{ + return g_ms_count; +} + +void pal_os_timer_delay_in_milliseconds(uint16_t milliseconds) +{ + delay_ms(milliseconds); +} + +static void _timer_cb(const struct timer_task* const timer_task) +{ + (void)timer_task; + g_ms_count++; +} + +pal_status_t pal_timer_init(void) +{ + scheduler.interval = 1; + scheduler.cb = _timer_cb; + scheduler.mode = TIMER_TASK_REPEAT; + timer_add_task(&TIMER_0, &scheduler); + return PAL_STATUS_SUCCESS; +} + +pal_status_t pal_timer_deinit(void) +{ + timer_remove_task(&TIMER_0, &scheduler); + return PAL_STATUS_SUCCESS; +} +/** + * @} + */ diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 43f841733..974a1909c 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -136,6 +136,7 @@ dependencies = [ "bitbox02-noise", "bitbox02-rust", "bitcoin", + "der", "hex", "p256", "sha2", diff --git a/src/rust/bitbox02-rust-c/Cargo.toml b/src/rust/bitbox02-rust-c/Cargo.toml index d996e979d..b96068ff5 100644 --- a/src/rust/bitbox02-rust-c/Cargo.toml +++ b/src/rust/bitbox02-rust-c/Cargo.toml @@ -29,6 +29,7 @@ bitbox02 = { path = "../bitbox02", optional = true } bitbox02-noise = { path = "../bitbox02-noise", optional = true } util = { path = "../util" } p256 = { version = "0.13.2", default-features = false, features = ["arithmetic", "ecdsa"], optional = true } +der = { version = "0.7.9", default-features = false, optional = true } hex = { workspace = true } sha2 = { workspace = true, optional = true } sha3 = { workspace = true, optional = true } @@ -63,7 +64,7 @@ target-c-unit-tests = [ platform-bitbox02 = [] bootloader = [] -firmware = ["bitbox02-rust", "bitbox02", "bitbox02-noise", "sha2", "p256"] +firmware = ["bitbox02-rust", "bitbox02", "bitbox02-noise", "sha2", "p256", "der"] # Only to be enabled in Rust unit tests. testing = ["bitbox02-rust/testing", "bitbox02/testing"] diff --git a/src/rust/bitbox02-rust-c/src/der.rs b/src/rust/bitbox02-rust-c/src/der.rs new file mode 100644 index 000000000..ba8d745d4 --- /dev/null +++ b/src/rust/bitbox02-rust-c/src/der.rs @@ -0,0 +1,79 @@ +// Copyright 2024 Shift Crypto AG +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use der::asn1::UintRef; +use der::{Decode, SliceReader}; + +fn parse_int256(decoder: &mut SliceReader) -> Result<[u8; 32], ()> { + let int = UintRef::decode(decoder).map_err(|_| ())?; + let int_bytes = int.as_bytes(); + if int_bytes.len() > 32 { + return Err(()); + } + let mut array = [0u8; 32]; + let start_index = 32 - int_bytes.len(); + array[start_index..].copy_from_slice(int_bytes); + Ok(array) +} + +fn parse_two_int256s(data: &[u8]) -> Result<([u8; 32], [u8; 32]), ()> { + let mut decoder = SliceReader::new(data).map_err(|_| ())?; + + let first = parse_int256(&mut decoder)?; + let second = parse_int256(&mut decoder)?; + + Ok((first, second)) +} + +/// Parse a ECC signature as returned by the Optiga Trust M. +/// See Solution Reference Manual 6.2.2, example for ECC NIST-P256 signature. +/// https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/pdf/OPTIGA_Trust_M_Datasheet_v3.70.pdf +/// The input is the DER encoding of the signature R/S values encoded as two DER "INGEGER". +/// It's the same encoding as a regular DER-signature, but without the 0x30 sequence header. +/// sig_compact_out must be 64 bytes and will contain the R/S values (each 32 bytes). +#[no_mangle] +pub extern "C" fn rust_der_parse_optiga_signature( + sig_der: crate::util::Bytes, + mut sig_compact_out: crate::util::BytesMut, +) -> bool { + match parse_two_int256s(sig_der.as_ref()) { + Ok((first, second)) => { + sig_compact_out.as_mut()[..32].copy_from_slice(&first); + sig_compact_out.as_mut()[32..].copy_from_slice(&second); + true + } + Err(_) => false, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_rust_der_parse_optiga_signature() { + let sig_der = b"\x02\x02\x12\x34\x02\x03\x00\xab\xcd"; + let mut sig_compact = [0u8; 64]; + assert!(rust_der_parse_optiga_signature( + unsafe { crate::util::rust_util_bytes(sig_der.as_ptr(), sig_der.len()) }, + unsafe { + crate::util::rust_util_bytes_mut(sig_compact.as_mut_ptr(), sig_compact.len()) + }, + )); + assert_eq!( + hex::encode(sig_compact), + "0000000000000000000000000000000000000000000000000000000000001234000000000000000000000000000000000000000000000000000000000000abcd", + ); + } +} diff --git a/src/rust/bitbox02-rust-c/src/lib.rs b/src/rust/bitbox02-rust-c/src/lib.rs index 094a41a26..e1d24a668 100644 --- a/src/rust/bitbox02-rust-c/src/lib.rs +++ b/src/rust/bitbox02-rust-c/src/lib.rs @@ -37,6 +37,9 @@ mod sha2; #[cfg(feature = "firmware")] mod workflow; +#[cfg(feature = "firmware")] +mod der; + // Whenever execution reaches somewhere it isn't supposed to rust code will "panic". Our panic // handler will print the available information on the screen and over RTT. If we compile with // `panic=abort` this code will never get executed. diff --git a/src/rust/bitbox02-rust/src/hww/api/device_info.rs b/src/rust/bitbox02-rust/src/hww/api/device_info.rs index 41f00227f..b2fa68b7f 100644 --- a/src/rust/bitbox02-rust/src/hww/api/device_info.rs +++ b/src/rust/bitbox02-rust/src/hww/api/device_info.rs @@ -29,6 +29,7 @@ pub fn process() -> Result { securechip_model: match securechip::model()? { securechip::Model::ATECC_ATECC608A => "ATECC608A".into(), securechip::Model::ATECC_ATECC608B => "ATECC608B".into(), + securechip::Model::OPTIGA_TRUST_M_V3 => "OPTIGA_TRUST_M_V3".into(), }, })) } diff --git a/src/securechip/securechip.c b/src/securechip/securechip.c index 110cafba3..91463a857 100644 --- a/src/securechip/securechip.c +++ b/src/securechip/securechip.c @@ -13,9 +13,11 @@ // limitations under the License. #include "securechip.h" -#include "atecc/atecc.h" -#include "hardfault.h" -#include "memory/memory_shared.h" + +#include +#include +#include +#include typedef struct { int (*setup)(const securechip_interface_functions_t* fns); @@ -42,7 +44,21 @@ bool securechip_init(void) { switch (memory_get_securechip_type()) { case MEMORY_SECURECHIP_TYPE_OPTIGA: - Abort("Not implemented"); + _fns.setup = optiga_setup; + _fns.update_keys = optiga_update_keys; + _fns.kdf = optiga_kdf_external; + _fns.kdf_rollkey = optiga_kdf_internal; + _fns.gen_attestation_key = optiga_gen_attestation_key; + _fns.attestation_sign = optiga_attestation_sign; + _fns.monotonic_increments_remaining = optiga_monotonic_increments_remaining; + _fns.random = optiga_random; +#if APP_U2F == 1 || FACTORYSETUP == 1 + _fns.u2f_counter_set = optiga_u2f_counter_set; +#endif +#if APP_U2F == 1 + _fns.u2f_counter_inc = optiga_u2f_counter_inc; +#endif + _fns.model = optiga_model; break; case MEMORY_SECURECHIP_TYPE_ATECC: default: diff --git a/src/securechip/securechip.h b/src/securechip/securechip.h index ca26ae05c..523120b61 100644 --- a/src/securechip/securechip.h +++ b/src/securechip/securechip.h @@ -22,17 +22,6 @@ #include #include -typedef enum { - SC_ERR_ZONE_UNLOCKED_CONFIG = -1, - SC_ERR_ZONE_UNLOCKED_DATA = -2, - SC_ERR_CONFIG_MISMATCH = -3, - SC_ERR_SLOT_UNLOCKED_IO = -4, - SC_ERR_SLOT_UNLOCKED_AUTH = -5, - SC_ERR_SLOT_UNLOCKED_ENC = -6, - SC_ERR_IFS = -7, - SC_ERR_INVALID_ARGS = -8, -} securechip_error_t; - typedef struct { /** * @param[out] key_out must be of size 32 @@ -61,15 +50,16 @@ USE_RESULT bool securechip_init(void); * communication interface/bridge to cryptoauthlib. On first call, the chip * is configured and locked. * @param[in] ifs Interface functions. - * @return values of `securechip_error_t` if negative, values of `ATCA_STATUS` if positive, 0 on - * success. + * @return For ATECC: values of `atecc_error_t` if negative, values of `ATCA_STATUS` if positive, 0 + * on success. For Optiga: valus of `optiga_error_t` if negative, values of + * optiga_lib_return_codes.h if positive, 0 on success. */ USE_RESULT int securechip_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. + * monotonic counter. * @return true on success. */ USE_RESULT bool securechip_update_keys(void); @@ -80,7 +70,9 @@ USE_RESULT bool securechip_update_keys(void); * @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 + * @return For ATECC: values of `atecc_error_t` if negative, values of `ATCA_STATUS` if positive, 0 + * on success. For Optiga: valus of `optiga_error_t` if negative, values of + * optiga_lib_return_codes.h if positive, 0 on success. */ USE_RESULT int securechip_kdf(const uint8_t* msg, size_t len, uint8_t* kdf_out); @@ -91,7 +83,9 @@ USE_RESULT int securechip_kdf(const uint8_t* msg, size_t len, uint8_t* kdf_out); * @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 + * @return For ATECC: values of `atecc_error_t` if negative, values of `ATCA_STATUS` if positive, 0 + * on success. For Optiga: valus of `optiga_error_t` if negative, values of + * optiga_lib_return_codes.h if positive, 0 on success. */ USE_RESULT int securechip_kdf_rollkey(const uint8_t* msg, size_t len, uint8_t* kdf_out); @@ -108,7 +102,7 @@ USE_RESULT bool securechip_gen_attestation_key(uint8_t* pubkey_out); USE_RESULT bool securechip_attestation_sign(const uint8_t* challenge, uint8_t* signature_out); /** - * Retrieves the number of remaining possible counter increments (max value - Counter0). + * Retrieves the number of remaining possible counter increments (max value - Counter). * The counter is increment when using `securechip_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. @@ -141,6 +135,7 @@ USE_RESULT bool securechip_u2f_counter_inc(uint32_t* counter); typedef enum { ATECC_ATECC608A, ATECC_ATECC608B, + OPTIGA_TRUST_M_V3, } securechip_model_t; /** diff --git a/test/simulator/framework/mock_securechip.c b/test/simulator/framework/mock_securechip.c index 42bebb063..0aac36a04 100644 --- a/test/simulator/framework/mock_securechip.c +++ b/test/simulator/framework/mock_securechip.c @@ -44,29 +44,15 @@ static const uint8_t _kdfkey[32] = "\xd2\xe1\xe6\xb1\x8b\x6c\x6b\x08\x43\x3e\xdb\xc1\xd1\x68\xc1\xa0\x04\x37\x74\xa4\x22\x18\x77" "\xe7\x9e\xd5\x66\x84\xbe\x5a\xc0\x1b"; -static int _securechip_kdf(securechip_slot_t slot, const uint8_t* msg, size_t len, uint8_t* kdf_out) -{ - const uint8_t* key; - switch (slot) { - case SECURECHIP_SLOT_ROLLKEY: - key = _rollkey; - break; - case SECURECHIP_SLOT_KDF: - key = _kdfkey; - break; - default: - return SC_ERR_INVALID_ARGS; - } - wally_hmac_sha256(key, 32, msg, len, kdf_out, 32); - return 0; -} int securechip_kdf(const uint8_t* msg, size_t len, uint8_t* kdf_out) { - return _securechip_kdf(SECURECHIP_SLOT_KDF, msg, len, kdf_out); + wally_hmac_sha256(_kdfkey, 32, msg, len, kdf_out, 32); + return 0; } int securechip_kdf_rollkey(const uint8_t* msg, size_t len, uint8_t* kdf_out) { - return _securechip_kdf(SECURECHIP_SLOT_ROLLKEY, msg, len, kdf_out); + wally_hmac_sha256(_rollkey, 32, msg, len, kdf_out, 32); + return 0; } bool securechip_u2f_counter_set(uint32_t counter) diff --git a/test/unit-test/framework/mock_securechip.c b/test/unit-test/framework/mock_securechip.c index f0fbf3e47..e088e8b97 100644 --- a/test/unit-test/framework/mock_securechip.c +++ b/test/unit-test/framework/mock_securechip.c @@ -45,29 +45,15 @@ static const uint8_t _kdfkey[32] = "\xd2\xe1\xe6\xb1\x8b\x6c\x6b\x08\x43\x3e\xdb\xc1\xd1\x68\xc1\xa0\x04\x37\x74\xa4\x22\x18\x77" "\xe7\x9e\xd5\x66\x84\xbe\x5a\xc0\x1b"; -static int _securechip_kdf(securechip_slot_t slot, const uint8_t* msg, size_t len, uint8_t* kdf_out) -{ - const uint8_t* key; - switch (slot) { - case SECURECHIP_SLOT_ROLLKEY: - key = _rollkey; - break; - case SECURECHIP_SLOT_KDF: - key = _kdfkey; - break; - default: - return SC_ERR_INVALID_ARGS; - } - wally_hmac_sha256(key, 32, msg, len, kdf_out, 32); - return 0; -} int securechip_kdf(const uint8_t* msg, size_t len, uint8_t* kdf_out) { - return _securechip_kdf(SECURECHIP_SLOT_KDF, msg, len, kdf_out); + wally_hmac_sha256(_kdfkey, 32, msg, len, kdf_out, 32); + return 0; } int securechip_kdf_rollkey(const uint8_t* msg, size_t len, uint8_t* kdf_out) { - return _securechip_kdf(SECURECHIP_SLOT_ROLLKEY, msg, len, kdf_out); + wally_hmac_sha256(_rollkey, 32, msg, len, kdf_out, 32); + return 0; } bool securechip_u2f_counter_set(uint32_t counter)