From 08afde5a90011d901882b038852219ac3b22369d Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Wed, 27 Nov 2024 11:29:30 +0100 Subject: [PATCH 1/2] securechip: add initial Optiga implementation This commit implements the securechip.h interface for the Optiga Trust M V3, with an interface and configuration roughly corresponding to how we use the ATECC608 secure chip: - A KDF key that is internally generated and cannot be read and written from the host MCU - A KDF key that is generated on the host - A monotonic counter attached to the first KDF key which limits the maxmium number of uses of the key over the lifetime of the device - Attestation key that is internally generated and used to sign attestation challenges The factory setup configures the metadata of each object we use, setting the state to Operational. After this, metadata cannot be changed, and the access conditions apply as specified. Shielded communication encrypts the communication with the chip and is active and enforced through the metadata access configs. It roughly corresponds to IO protection in the ATECC608. In the ATECC608, we additionally authorize each command with the authorization_key, another pre-shared secret. The Optiga offers the same functionality, but we don't use it to authorize all commands, as it is redundant to using the shielded communication in terms of enabling the host MCU to execute commands. Co-Authored-By: Niklas --- src/CMakeLists.txt | 11 + src/atecc/atecc.c | 20 +- src/atecc/atecc.h | 88 +- src/memory/memory_shared.c | 11 +- src/memory/memory_shared.h | 2 +- src/optiga/optiga.c | 1275 +++++++++++++++++ src/optiga/optiga.h | 44 + src/optiga/pal/README.bitbox02.md | 1 + src/optiga/pal/README.md | 275 ++++ src/optiga/pal/pal.c | 52 + src/optiga/pal/pal_gpio.c | 66 + src/optiga/pal/pal_i2c.c | 313 ++++ src/optiga/pal/pal_ifx_i2c_config.c | 90 ++ src/optiga/pal/pal_logger.c | 95 ++ src/optiga/pal/pal_os_datastore.c | 82 ++ src/optiga/pal/pal_os_event.c | 110 ++ src/optiga/pal/pal_os_lock.c | 88 ++ src/optiga/pal/pal_os_memory.c | 23 + src/optiga/pal/pal_os_timer.c | 88 ++ src/rust/Cargo.lock | 1 + src/rust/bitbox02-rust-c/Cargo.toml | 3 +- src/rust/bitbox02-rust-c/src/der.rs | 79 + src/rust/bitbox02-rust-c/src/lib.rs | 3 + .../bitbox02-rust/src/hww/api/device_info.rs | 1 + src/securechip/securechip.c | 24 +- src/securechip/securechip.h | 38 +- test/simulator/framework/mock_securechip.c | 27 +- test/unit-test/framework/mock_securechip.c | 27 +- 28 files changed, 2776 insertions(+), 161 deletions(-) create mode 100644 src/optiga/optiga.c create mode 100644 src/optiga/optiga.h create mode 100644 src/optiga/pal/README.bitbox02.md create mode 100644 src/optiga/pal/README.md create mode 100644 src/optiga/pal/pal.c create mode 100644 src/optiga/pal/pal_gpio.c create mode 100644 src/optiga/pal/pal_i2c.c create mode 100644 src/optiga/pal/pal_ifx_i2c_config.c create mode 100644 src/optiga/pal/pal_logger.c create mode 100644 src/optiga/pal/pal_os_datastore.c create mode 100644 src/optiga/pal/pal_os_event.c create mode 100644 src/optiga/pal/pal_os_lock.c create mode 100644 src/optiga/pal/pal_os_memory.c create mode 100644 src/optiga/pal/pal_os_timer.c create mode 100644 src/rust/bitbox02-rust-c/src/der.rs 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.c b/src/atecc/atecc.c index 7ffef9591..db026f039 100644 --- a/src/atecc/atecc.c +++ b/src/atecc/atecc.c @@ -234,7 +234,7 @@ static ATCA_STATUS _lock_slot(atecc_slot_t slot) static ATCA_STATUS _factory_setup(void) { if (_interface_functions == NULL) { - return (ATCA_STATUS)ATECC_ERR_IFS; + return (ATCA_STATUS)SC_ERR_IFS; } bool is_config_locked = false; ATCA_STATUS result = atcab_is_locked(LOCK_ZONE_CONFIG, &is_config_locked); @@ -336,14 +336,14 @@ static int _verify_config(void) return result; } if (!is_locked) { - return ATECC_ERR_ZONE_UNLOCKED_CONFIG; + return SC_ATECC_ERR_ZONE_UNLOCKED_CONFIG; } result = atcab_is_locked(LOCK_ZONE_DATA, &is_locked); if (result != ATCA_SUCCESS) { return result; } if (!is_locked) { - return ATECC_ERR_ZONE_UNLOCKED_DATA; + return SC_ATECC_ERR_ZONE_UNLOCKED_DATA; } bool same_config = false; @@ -352,7 +352,7 @@ static int _verify_config(void) return result; } if (!same_config) { - return ATECC_ERR_CONFIG_MISMATCH; + return SC_ERR_CONFIG_MISMATCH; } // Check that the slots are individually locked. @@ -361,21 +361,21 @@ static int _verify_config(void) return result; } if (!is_locked) { - return ATECC_ERR_SLOT_UNLOCKED_IO; + return SC_ATECC_ERR_SLOT_UNLOCKED_IO; } result = atcab_is_slot_locked(ATECC_SLOT_AUTHKEY, &is_locked); if (result != ATCA_SUCCESS) { return result; } if (!is_locked) { - return ATECC_ERR_SLOT_UNLOCKED_AUTH; + return SC_ATECC_ERR_SLOT_UNLOCKED_AUTH; } result = atcab_is_slot_locked(ATECC_SLOT_ENCRYPTION_KEY, &is_locked); if (result != ATCA_SUCCESS) { return result; } if (!is_locked) { - return ATECC_ERR_SLOT_UNLOCKED_ENC; + return SC_ATECC_ERR_SLOT_UNLOCKED_ENC; } return ATCA_SUCCESS; } @@ -383,7 +383,7 @@ static int _verify_config(void) int atecc_setup(const securechip_interface_functions_t* ifs) { if (ifs == NULL) { - return ATECC_ERR_IFS; + return SC_ERR_IFS; } _interface_functions = ifs; ATCA_STATUS result = atcab_init(&cfg); @@ -527,10 +527,10 @@ bool atecc_update_keys(void) static int _atecc_kdf(atecc_slot_t slot, const uint8_t* msg, size_t len, uint8_t* kdf_out) { if (len > 127 || (slot != ATECC_SLOT_ROLLKEY && slot != ATECC_SLOT_KDF)) { - return ATECC_ERR_INVALID_ARGS; + return SC_ERR_INVALID_ARGS; } if (msg == kdf_out) { - return ATECC_ERR_INVALID_ARGS; + return SC_ERR_INVALID_ARGS; } ATCA_STATUS result = _authorize_key(); diff --git a/src/atecc/atecc.h b/src/atecc/atecc.h index 45e52fe1c..7566ff048 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 functions. */ + #include "compiler_util.h" #include "securechip/securechip.h" #include @@ -22,105 +25,20 @@ #include #include -typedef enum { - ATECC_ERR_ZONE_UNLOCKED_CONFIG = -1, - ATECC_ERR_ZONE_UNLOCKED_DATA = -2, - ATECC_ERR_CONFIG_MISMATCH = -3, - ATECC_ERR_SLOT_UNLOCKED_IO = -4, - ATECC_ERR_SLOT_UNLOCKED_AUTH = -5, - ATECC_ERR_SLOT_UNLOCKED_ENC = -6, - ATECC_ERR_IFS = -7, - ATECC_ERR_INVALID_ARGS = -8, -} atecc_error_t; - -/** - * Initializes the cryptoauthlib communication, by providing a custom i2c chip - * communication interface/bridge to cryptoauthlib. On first call, the chip - * is configured and locked. - * @param[in] ifs Interface functions. - * @return values of `atecc_error_t` if negative, values of `ATCA_STATUS` if positive, 0 on - * success. - */ USE_RESULT int atecc_setup(const securechip_interface_functions_t* ifs); - -/** - * Updates the two KDF keys (rollkey and kdf key). The previous keys are lost - * and cannot be restored. Calling this function does not increment the - * monotonic counter Counter0. - * @return true on success. - */ USE_RESULT bool atecc_update_keys(void); - -/** - * Perform HMAC using the key in KDF slot with the input msg. - * @param[in] msg Use this msg as input - * @param[in] len Must be <= 127. - * @param[out] kdf_out Must have size 32. Result of the kdf will be stored here. - * Cannot be the same as `msg`. - * @return values of `atecc_error_t` if negative, values of `ATCA_STATUS` if positive, 0 on - */ USE_RESULT int atecc_kdf(const uint8_t* msg, size_t len, uint8_t* kdf_out); - -/** - * Perform KDF using the key in rollkey slot with the input msg. - * Calling this function increments the - * monotonic counter Counter0. - * @param[in] msg Use this msg as input - * @param[in] len Must be <= 127. - * @param[out] kdf_out Must have size 32. Result of the kdf will be stored here. - * Cannot be the same as `msg`. - * @return values of `securechip_error_t` if negative, values of `ATCA_STATUS` if positive, 0 on - */ USE_RESULT int atecc_kdf_rollkey(const uint8_t* msg, size_t len, uint8_t* kdf_out); - -/** - * Generates a new attestation device key and outputs the public key. - * @param[out] pubkey_out - */ USE_RESULT bool atecc_gen_attestation_key(uint8_t* pubkey_out); - -/** - * @param[in] msg 32 byte message to sign. - * @param[out] signature_out must be 64 bytes. R/S P256 signature. - */ USE_RESULT bool atecc_attestation_sign(const uint8_t* challenge, uint8_t* signature_out); - -/** - * Retrieves the number of remaining possible counter increments (max value - Counter0). - * The counter is increment when using `atecc_kdf()` (see its docstring). - * @param[out] remaining_out current value of the monotonic counter. - * @return false if there was a communication error with the SC. - */ USE_RESULT bool atecc_monotonic_increments_remaining(uint32_t* remaining_out); - -/** - * @param[out] rand_out must be 32 bytes. - */ USE_RESULT bool atecc_random(uint8_t* rand_out); - #if APP_U2F == 1 || FACTORYSETUP == 1 -/** - * Set the u2f counter to `counter`. Should only be used for initialization. - * @param[in] counter Value to set counter to - * @return True if success - */ USE_RESULT bool atecc_u2f_counter_set(uint32_t counter); #endif - #if APP_U2F == 1 -/** - * Monotonically increase the U2F counter and return the current value - * @param[out] counter Next counter value - * @return True if success - */ USE_RESULT bool atecc_u2f_counter_inc(uint32_t* counter); #endif - -/** - * Output the atecc model. - * @param[out] model_out atecc model - * @return True if success - */ USE_RESULT bool atecc_model(securechip_model_t* model_out); #endif 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..dd9898b1b --- /dev/null +++ b/src/optiga/optiga.c @@ -0,0 +1,1275 @@ +// 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) + +// Maximum size of metadata. See "Metadata Update Identifier": +// https://github.com/Infineon/optiga-trust-m-overview/blob/98b2b9c178f0391b1ab26b52082899704dab688a/docs/OPTIGA%E2%84%A2%20Trust%20M%20Solution%20Reference%20Manual.md#linka946a953_def2_41cf_850a_74fb7899fe11 +// Two extra bytes for the `0x20 ` header bytes. +#define METADATA_MAX_SIZE (44 + 2) + +// 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 threshold `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 + +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 + 16, + // 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 counter when LcsO < op. + 0xD0, + 0x03, + 0xE1, + 0xFC, + LCSO_STATE_OPERATIONAL, + // Allow reads, enforce shielded connection. + 0xD1, + 0x03, + 0x20, + 0xE1, + 0x40, + // 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) +{ + _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) +{ + _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) +{ + _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) +{ + _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) +{ + _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) +{ + _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) +{ + _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) +{ + _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) +{ + _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) +{ + _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) +{ + _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) +{ + _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 || FACTORY_DURING_PROD == 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. +// data_len_out holds the buffer size as an in-param and the actual size as an out-param. +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 tlvs_len = metadata[1]; + if ((size_t)(tlvs_len + 2) > metadata_len) { + // Malformed metadata: declared size exceeds buffer length + return false; + } + + const uint8_t* tlv_first = &metadata[2]; + + size_t offset = 0; + + while (offset + 2 <= tlvs_len) { // Ensure at least are available + uint8_t current_tag = tlv_first[offset]; + uint8_t size = tlv_first[offset + 1]; + + if (offset + 2 + size > tlvs_len) { + // Malformed TLV: size exceeds remaining length + return false; + } + + if (current_tag == tag) { + // Found the tag, copy data to output + if (size > *data_len_out) { + return false; + } + memcpy(data_out, &tlv_first[offset + 2], size); + *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[METADATA_MAX_SIZE] = {0}; + size_t tag_data_len = sizeof(tag_data); + 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[METADATA_MAX_SIZE] = {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 SC_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 SC_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 SC_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 SC_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[METADATA_MAX_SIZE] = {0}; + size_t expected_tag_data_len; + uint8_t actual_tag_data[METADATA_MAX_SIZE] = {0}; + size_t actual_tag_data_len; + + for (size_t i = 0; i < check_tags_len; i++) { + actual_tag_data_len = sizeof(actual_tag_data); + expected_tag_data_len = sizeof(expected_tag_data); + uint8_t tag = check_tags[i]; + if (!_read_metadata_tag( + expected_metadata, + expected_metadata_len, + tag, + expected_tag_data, + &expected_tag_data_len)) { + return SC_OPTIGA_ERR_UNEXPECTED_METADATA; + } + if (!_read_metadata_tag( + actual_metadata, actual_metadata_len, tag, actual_tag_data, &actual_tag_data_len)) { + return SC_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 SC_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 SC_ERR_CONFIG_MISMATCH; + } + if (_util->protection_level != OPTIGA_COMMS_FULL_PROTECTION) { + util_log("util protection level expected to be FULL"); + return SC_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 SC_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 SC_OPTIGA_ERR_CREATE; + } + + _crypt = optiga_crypt_create(OPTIGA_INSTANCE_ID_0, _optiga_lib_callback, NULL); + if (NULL == _crypt) { + return SC_OPTIGA_ERR_CREATE; + } + + return _verify_config(); +} + +bool optiga_update_keys(void) +{ + 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 SC_ERR_INVALID_ARGS; + } + + 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 SC_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 SC_ERR_INVALID_ARGS; + } + 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 SC_OPTIGA_ERR_UNEXPECTED_LEN; + } + rust_sha256(mac_out, mac_out_len, kdf_out); + return 0; +} + +bool optiga_gen_attestation_key(uint8_t* pubkey_out) +{ + 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) +{ + 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..19545ccb2 --- /dev/null +++ b/src/optiga/optiga.h @@ -0,0 +1,44 @@ +// 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 functions. */ + +#include "compiler_util.h" +#include "securechip/securechip.h" +#include +#include +#include +#include + +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 5c15e6ce2..8cef5d1db 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..4925a82ee 100644 --- a/src/securechip/securechip.h +++ b/src/securechip/securechip.h @@ -23,14 +23,23 @@ #include typedef enum { - SC_ERR_ZONE_UNLOCKED_CONFIG = -1, - SC_ERR_ZONE_UNLOCKED_DATA = -2, + // Errors common to any securechip implementation + SC_ERR_IFS = -1, + SC_ERR_INVALID_ARGS = -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, + + // Errors specific to the ATECC + SC_ATECC_ERR_ZONE_UNLOCKED_CONFIG = -100, + SC_ATECC_ERR_ZONE_UNLOCKED_DATA = -101, + SC_ATECC_ERR_SLOT_UNLOCKED_IO = -103, + SC_ATECC_ERR_SLOT_UNLOCKED_AUTH = -104, + SC_ATECC_ERR_SLOT_UNLOCKED_ENC = -105, + + // Errors specific to the Optiga + SC_OPTIGA_ERR_CREATE = -201, + SC_OPTIGA_ERR_UNEXPECTED_METADATA = -204, + SC_OPTIGA_ERR_PAL = -205, + SC_OPTIGA_ERR_UNEXPECTED_LEN = -206, } securechip_error_t; typedef struct { @@ -61,15 +70,15 @@ 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 0 on success. Values of `securechip_error_t` if negative. If positive, values of + * `ATCA_STATUS` for ATECC, values of optiga_lib_return_codes.h for Optiga. */ 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 +89,8 @@ 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 0 on success. Values of `securechip_error_t` if negative. If positive, values of + * `ATCA_STATUS` for ATECC, values of optiga_lib_return_codes.h for Optiga. */ USE_RESULT int securechip_kdf(const uint8_t* msg, size_t len, uint8_t* kdf_out); @@ -91,7 +101,8 @@ 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 0 on success. Values of `securechip_error_t` if negative. If positive, values of + * `ATCA_STATUS` for ATECC, values of optiga_lib_return_codes.h for Optiga. */ USE_RESULT int securechip_kdf_rollkey(const uint8_t* msg, size_t len, uint8_t* kdf_out); @@ -108,7 +119,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 +152,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..208459ebc 100644 --- a/test/simulator/framework/mock_securechip.c +++ b/test/simulator/framework/mock_securechip.c @@ -22,11 +22,6 @@ #include #include -typedef enum { - SECURECHIP_SLOT_ROLLKEY = 3, - SECURECHIP_SLOT_KDF = 4, -} securechip_slot_t; - static uint32_t _u2f_counter; bool securechip_update_keys(void) @@ -44,29 +39,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..ca1c98a42 100644 --- a/test/unit-test/framework/mock_securechip.c +++ b/test/unit-test/framework/mock_securechip.c @@ -23,11 +23,6 @@ #include #include -typedef enum { - SECURECHIP_SLOT_ROLLKEY = 3, - SECURECHIP_SLOT_KDF = 4, -} securechip_slot_t; - static uint32_t _u2f_counter; bool securechip_update_keys(void) @@ -45,29 +40,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) From ebeb45887ac2878815a1523ee70d6ffd1d01dcb1 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Tue, 17 Dec 2024 09:23:19 +0100 Subject: [PATCH 2/2] optiga: implement critical section To disable interrupts when processing Optiga commands. --- src/optiga/pal/pal_os_lock.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/optiga/pal/pal_os_lock.c b/src/optiga/pal/pal_os_lock.c index 7715ab830..73d98a1f3 100644 --- a/src/optiga/pal/pal_os_lock.c +++ b/src/optiga/pal/pal_os_lock.c @@ -37,6 +37,8 @@ #include "pal_os_lock.h" +#include + void pal_os_lock_create(pal_os_lock_t* p_lock, uint8_t lock_type) { p_lock->type = lock_type; @@ -73,14 +75,16 @@ void pal_os_lock_release(pal_os_lock_t* p_lock) } } +static volatile hal_atomic_t _atomic; + void pal_os_lock_enter_critical_section(void) { - // For safety critical systems it is recommended to implement a critical section entry + atomic_enter_critical(&_atomic); } void pal_os_lock_exit_critical_section(void) { - // For safety critical systems it is recommended to implement a critical section exit + atomic_leave_critical(&_atomic); } /**