diff --git a/doc/dox_comments/header_files/iotsafe.h b/doc/dox_comments/header_files/iotsafe.h index 31d97838efa..b16ebeb0eb5 100644 --- a/doc/dox_comments/header_files/iotsafe.h +++ b/doc/dox_comments/header_files/iotsafe.h @@ -6,6 +6,12 @@ \return 0 on success \return WC_HW_E on hardware error + This function loads the IoT-Safe applet and must be called from a single + thread, before the context is used concurrently from multiple threads. + Once initialized, individual APDU transactions are serialized internally + in multi-threaded builds, so concurrent TLS sessions can safely share the + same IoT-Safe context. + _Example_ \code WOLFSSL_CTX *ctx; @@ -124,6 +130,8 @@ int wolfSSL_iotsafe_on_ex(WOLFSSL *ssl, byte *privkey_id, usually associated to a read event of a UART channel communicating with the modem. The read callback associated is global and changes for all the contexts that use IoT-safe support at the same time. + This function must be called from a single thread, during initialization, + before any IoT-Safe operation is performed. \param rf Read callback associated to a UART read event. The callback function takes two arguments (buf, len) and return the number of characters read, up to len. When a newline is encountered, the callback should return the number of characters received @@ -149,7 +157,9 @@ void wolfIoTSafe_SetCSIM_read_cb(wolfSSL_IOTSafe_CSIM_read_cb rf); usually associated to a write event on a UART channel communicating with the modem. The write callback associated is global and changes for all the contexts that use IoT-safe support at the same time. - \param rf Write callback associated to a UART write event. The callback function takes + This function must be called from a single thread, during initialization, + before any IoT-Safe operation is performed. + \param wf Write callback associated to a UART write event. The callback function takes two arguments (buf, len) and return the number of characters written, up to len. _Example_ diff --git a/wolfcrypt/src/port/iotsafe/iotsafe.c b/wolfcrypt/src/port/iotsafe/iotsafe.c index 16ebc4ee683..36484c3a786 100644 --- a/wolfcrypt/src/port/iotsafe/iotsafe.c +++ b/wolfcrypt/src/port/iotsafe/iotsafe.c @@ -76,6 +76,31 @@ static wolfSSL_IOTSafe_CSIM_write_cb csim_write_cb = NULL; static char csim_read_buf[MAXBUF]; static char csim_cmd[IOTSAFE_CMDSIZE_MAX]; +/* All APDU transactions run against a single shared CSIM channel + * (one modem/SIM), so they must be serialized. In multi-threaded + * builds the port keeps a mutex for that purpose; in single-threaded + * builds the lock helpers are no-ops. The mutex is created by + * iotsafe_init(), which must be called from a single thread (see + * wolfSSL_CTX_iotsafe_enable()) before concurrent use of the port. + */ +#if !defined(SINGLE_THREADED) +static wolfSSL_Mutex iotsafe_mutex; +#endif + +static void iotsafe_lock(void) +{ +#if !defined(SINGLE_THREADED) + wc_LockMutex(&iotsafe_mutex); +#endif +} + +static void iotsafe_unlock(void) +{ +#if !defined(SINGLE_THREADED) + wc_UnLockMutex(&iotsafe_mutex); +#endif +} + /* APDU layer: I/O */ static int csim_read(char *buf, int len) { @@ -414,13 +439,23 @@ static int iotsafe_init(void) "AT+CSIM=24,\"01A4040007A0000005590010\"\r\n"; int ret; +#if !defined(SINGLE_THREADED) + ret = wc_InitMutex(&iotsafe_mutex); + if (ret != 0) + return ret; +#endif + do { ret = expect_ok("ATE0\r\n", 6); if (ret == 0) ret = expect_tok(NULL, 0, NULL, NULL); } while (ret == 0); - if (ret < 0) + if (ret < 0) { +#if !defined(SINGLE_THREADED) + wc_FreeMutex(&iotsafe_mutex); +#endif return ret; + } WOLFSSL_MSG("ATE0 OK!"); if (expect_csim_response(atcmd_load_applet_str, @@ -430,15 +465,29 @@ static int iotsafe_init(void) } else { WOLFSSL_MSG("IoT Safe Applet INIT OK"); } - if (expect_tok(NULL, 0, NULL, NULL) < 0) + if (expect_tok(NULL, 0, NULL, NULL) < 0) { +#if !defined(SINGLE_THREADED) + wc_FreeMutex(&iotsafe_mutex); +#endif return -1; + } wolfIoT_initialized++; return 0; } +/* Ensure the port is initialized (mutex + applet). First use must + * happen from a single thread; see wolfSSL_CTX_iotsafe_enable(). + * Returns 0 on success, < 0 if initialization failed. */ +static int iotsafe_ensure_init(void) +{ + if (!wolfIoT_initialized) + return iotsafe_init(); + return 0; +} + /* internal: Read File content into a buffer */ -static int iotsafe_readfile(uint8_t *file_id, uint16_t file_id_sz, +static int iotsafe_readfile_locked(uint8_t *file_id, uint16_t file_id_sz, unsigned char *content, int max_size) { char *resp; @@ -522,6 +571,20 @@ static int iotsafe_readfile(uint8_t *file_id, uint16_t file_id_sz, return off; } +/* Serialized entry point: holds iotsafe_mutex across the APDU + * transaction. */ +static int iotsafe_readfile(uint8_t *file_id, uint16_t file_id_sz, + unsigned char *content, int max_size) +{ + int ret; + if (iotsafe_ensure_init() != 0) + return WC_HW_E; + iotsafe_lock(); + ret = iotsafe_readfile_locked(file_id, file_id_sz, content, max_size); + iotsafe_unlock(); + return ret; +} + static int iotsafe_getrandom(unsigned char* output, unsigned long sz) { char *resp = NULL; @@ -532,11 +595,10 @@ static int iotsafe_getrandom(unsigned char* output, unsigned long sz) if (sz == 0 || sz > 255) { return BAD_FUNC_ARG; } - if (!wolfIoT_initialized) { - if (iotsafe_init() < 0) { - return WC_HW_E; - } - } + if (iotsafe_ensure_init() != 0) + return WC_HW_E; + + iotsafe_lock(); iotsafe_cmd_start(csim_cmd, IOTSAFE_CLASS, IOTSAFE_INS_GETRANDOM,0,0); bytes_to_hex(&len, csim_cmd + AT_CSIM_CMD_SIZE + AT_CMD_LC_POS, 1); @@ -559,6 +621,7 @@ static int iotsafe_getrandom(unsigned char* output, unsigned long sz) for (i = 0; i < IOTSAFE_MAX_RETRIES; i++) { (void)expect_tok(NULL, 0, NULL, NULL); } + iotsafe_unlock(); return ret; } @@ -623,8 +686,8 @@ static int iotsafe_parse_public_key(char* resp, int len, ecc_key *key) * the operation is successful and the public key was found in the * command response and copied to the `key` structure, if not NULL. */ -static int iotsafe_gen_keypair(byte *wr_slot, unsigned long id_size, - ecc_key *key) +static int iotsafe_gen_keypair_locked(byte *wr_slot, unsigned long id_size, + ecc_key *key) { char *resp; int ret = WC_NO_ERR_TRACE(WC_HW_E); @@ -653,7 +716,21 @@ static int iotsafe_gen_keypair(byte *wr_slot, unsigned long id_size, return ret; } -static int iotsafe_get_public_key(byte *pubkey_id, unsigned long id_size, +/* Serialized entry point: holds iotsafe_mutex across the APDU + * transaction. */ +static int iotsafe_gen_keypair(byte *wr_slot, unsigned long id_size, + ecc_key *key) +{ + int ret; + if (iotsafe_ensure_init() != 0) + return WC_HW_E; + iotsafe_lock(); + ret = iotsafe_gen_keypair_locked(wr_slot, id_size, key); + iotsafe_unlock(); + return ret; +} + +static int iotsafe_get_public_key_locked(byte *pubkey_id, unsigned long id_size, ecc_key *key) { int ret; @@ -672,8 +749,22 @@ static int iotsafe_get_public_key(byte *pubkey_id, unsigned long id_size, return iotsafe_parse_public_key(resp, ret, key); } +/* Serialized entry point: holds iotsafe_mutex across the APDU + * transaction. */ +static int iotsafe_get_public_key(byte *pubkey_id, unsigned long id_size, + ecc_key *key) +{ + int ret; + if (iotsafe_ensure_init() != 0) + return WC_HW_E; + iotsafe_lock(); + ret = iotsafe_get_public_key_locked(pubkey_id, id_size, key); + iotsafe_unlock(); + return ret; +} + #define PUT_PK_SID 0x02 -static int iotsafe_put_public_key(byte *pubkey_id, unsigned long id_size, +static int iotsafe_put_public_key_locked(byte *pubkey_id, unsigned long id_size, ecc_key *key) { char *resp; @@ -749,8 +840,22 @@ static int iotsafe_put_public_key(byte *pubkey_id, unsigned long id_size, } return ret; } + +/* Serialized entry point: holds iotsafe_mutex across the APDU + * transaction. */ +static int iotsafe_put_public_key(byte *pubkey_id, unsigned long id_size, + ecc_key *key) +{ + int ret; + if (iotsafe_ensure_init() != 0) + return WC_HW_E; + iotsafe_lock(); + ret = iotsafe_put_public_key_locked(pubkey_id, id_size, key); + iotsafe_unlock(); + return ret; +} #ifdef HAVE_HKDF -static int iotsafe_hkdf_extract(byte* prk, const byte* salt, word32 saltLen, +static int iotsafe_hkdf_extract_locked(byte* prk, const byte* salt, word32 saltLen, byte* ikm, word32 ikmLen, int digest) { int ret; @@ -821,9 +926,23 @@ static int iotsafe_hkdf_extract(byte* prk, const byte* salt, word32 saltLen, } return ret; } + +/* Serialized entry point: holds iotsafe_mutex across the APDU + * transaction. */ +static int iotsafe_hkdf_extract(byte* prk, const byte* salt, word32 saltLen, + byte* ikm, word32 ikmLen, int digest) +{ + int ret; + if (iotsafe_ensure_init() != 0) + return WC_HW_E; + iotsafe_lock(); + ret = iotsafe_hkdf_extract_locked(prk, salt, saltLen, ikm, ikmLen, digest); + iotsafe_unlock(); + return ret; +} #endif -static int iotsafe_sign_hash(byte *privkey_idx, uint16_t id_size, +static int iotsafe_sign_hash_locked(byte *privkey_idx, uint16_t id_size, uint16_t hash_algo, uint8_t sign_algo, const byte *hash, word32 hashLen, byte *signature, word32 *sigLen) { @@ -938,7 +1057,23 @@ static int iotsafe_sign_hash(byte *privkey_idx, uint16_t id_size, return ret; } -static int iotsafe_verify_hash(byte *pubkey_idx, uint16_t id_size, +/* Serialized entry point: holds iotsafe_mutex across the APDU + * transaction. */ +static int iotsafe_sign_hash(byte *privkey_idx, uint16_t id_size, + uint16_t hash_algo, uint8_t sign_algo, const byte *hash, word32 hashLen, + byte *signature, word32 *sigLen) +{ + int ret; + if (iotsafe_ensure_init() != 0) + return WC_HW_E; + iotsafe_lock(); + ret = iotsafe_sign_hash_locked(privkey_idx, id_size, hash_algo, sign_algo, + hash, hashLen, signature, sigLen); + iotsafe_unlock(); + return ret; +} + +static int iotsafe_verify_hash_locked(byte *pubkey_idx, uint16_t id_size, uint16_t hash_algo, uint8_t sign_algo, const byte *hash, word32 hashLen, const byte *sig, word32 sigLen, @@ -1032,6 +1167,24 @@ static int iotsafe_verify_hash(byte *pubkey_idx, uint16_t id_size, return ret; } +/* Serialized entry point: holds iotsafe_mutex across the APDU + * transaction. */ +static int iotsafe_verify_hash(byte *pubkey_idx, uint16_t id_size, + uint16_t hash_algo, uint8_t sign_algo, + const byte *hash, word32 hashLen, + const byte *sig, word32 sigLen, + int *result) +{ + int ret; + if (iotsafe_ensure_init() != 0) + return WC_HW_E; + iotsafe_lock(); + ret = iotsafe_verify_hash_locked(pubkey_idx, id_size, hash_algo, sign_algo, + hash, hashLen, sig, sigLen, result); + iotsafe_unlock(); + return ret; +} + /* * Callbacks for IoT-Safe ECC/ECDH functions @@ -1285,6 +1438,13 @@ static int wolfIoT_ecc_shared_secret(WOLFSSL* ssl, struct ecc_key* otherKey, return BAD_FUNC_ARG; } + /* The APDU path below requires the port to be initialized (mutex + + * applet). Fail early, before allocating, if it is not. */ + if (iotsafe->enabled && iotsafe_ensure_init() != 0) { + WOLFSSL_MSG("IOTSAFE: not initialized"); + return WC_HW_E; + } + WOLFSSL_MSG("IOTSAFE: Called wolfIoT_ecc_shared_secret"); #ifdef DEBUG_IOTSAFE @@ -1305,17 +1465,22 @@ static int wolfIoT_ecc_shared_secret(WOLFSSL* ssl, struct ecc_key* otherKey, keypair_slot = (byte *)(&iotsafe->ecdh_keypair_slot); pubkey_idx = (byte *)(&iotsafe->peer_pubkey_slot); + /* Serialize the APDU transactions of this ECDH operation */ + iotsafe_lock(); + /* TLS v1.3 calls key gen already, so don't do it here */ if (wolfSSL_GetVersion(ssl) < WOLFSSL_TLSV1_3) { WOLFSSL_MSG("Generating ECDH key pair"); - ret = iotsafe_gen_keypair(keypair_slot, id_size, tmpKey); + /* iotsafe_mutex is already held: call the locked impls */ + ret = iotsafe_gen_keypair_locked(keypair_slot, id_size, tmpKey); if (ret < 0) { WOLFSSL_MSG("Error generating IoT-safe key pair"); } if (ret == 0) { WOLFSSL_MSG("Public key not yet retrieved, using GetPublic"); /* Importing generated public key */ - ret = iotsafe_get_public_key(keypair_slot, id_size, tmpKey); + ret = iotsafe_get_public_key_locked(keypair_slot, id_size, + tmpKey); if (ret < 0) { WOLFSSL_MSG("Error retrieving public key via GetPublic"); ret = WC_HW_E; @@ -1337,7 +1502,7 @@ static int wolfIoT_ecc_shared_secret(WOLFSSL* ssl, struct ecc_key* otherKey, if (ret == 0) { /* Store received public key from other endpoint in applet */ - ret = iotsafe_put_public_key(pubkey_idx, id_size, otherKey); + ret = iotsafe_put_public_key_locked(pubkey_idx, id_size, otherKey); if (ret < 0) { WOLFSSL_MSG("IoT-SAFE: Error in PutPublic"); } @@ -1378,6 +1543,7 @@ static int wolfIoT_ecc_shared_secret(WOLFSSL* ssl, struct ecc_key* otherKey, ret = 0; } } + iotsafe_unlock(); } else { ecc_key* privKey = NULL; ecc_key* pubKey = NULL;