Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion doc/dox_comments/header_files/iotsafe.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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_
Expand Down
202 changes: 184 additions & 18 deletions wolfcrypt/src/port/iotsafe/iotsafe.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Comment thread
danielinux marked this conversation as resolved.

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,
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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");
}
Expand Down Expand Up @@ -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;
Expand Down
Loading