diff --git a/examples/async/README.md b/examples/async/README.md index 6412ffa500..d59a5f9ff6 100644 --- a/examples/async/README.md +++ b/examples/async/README.md @@ -29,7 +29,14 @@ make -C examples/async ASYNC_MODE=sw ### Crypto Callback Mode Uses `WOLF_CRYPTO_CB` with the `AsyncTlsCryptoCb` callback that simulates hardware -crypto delays by returning `WC_PENDING_E` for a configurable number of iterations: +crypto delays by returning `WC_PENDING_E` for a configurable number of iterations. +The simulated device keeps a job table keyed by the request, like a hardware +crypto manager: a request pends `TEST_PEND_COUNT` times (default 2) and the +next re-invocation with identical arguments completes it. On TLS 1.3 every supported +operation class pends (HKDF, AES-GCM, ECC/X25519 key generation and shared +secret, ECDSA/Ed25519 sign and verify), including mutual authentication. On +TLS 1.2 (`--tls12`) only the RSA and ECDSA signing set pends; the TLS 1.2 state +machines do not resume the other classes. ``` make -C examples/async ASYNC_MODE=cryptocb ``` @@ -68,7 +75,7 @@ Define `NET_USER_HEADER` to include your network shim and provide the ## Asynchronous Cryptography Design -When a cryptographic call is handed off to hardware it return `WC_PENDING_E` up to caller. Then it can keep calling until the operation completes. For some platforms it is required to call `wolfSSL_AsyncPoll`. At the TLS layer a "devId" (Device ID) must be set using `wolfSSL_CTX_SetDevId` to indicate desire to offload cryptography. +When a cryptographic call is handed off to hardware, `WC_PENDING_E` is returned up to the caller, which keeps calling until the operation completes. For some platforms it is required to call `wolfSSL_AsyncPoll`. At the TLS layer a "devId" (Device ID) must be set using `wolfSSL_CTX_SetDevId` to indicate the desire to offload cryptography. For further design details please see: https://github.com/wolfSSL/wolfAsyncCrypt#design diff --git a/examples/async/async_client.c b/examples/async/async_client.c index 510857ebdf..070d858933 100644 --- a/examples/async/async_client.c +++ b/examples/async/async_client.c @@ -252,8 +252,11 @@ int client_async_test(int argc, char** argv) AsyncTlsCryptoCbCtx cryptoCbCtx; #endif #ifdef WOLFSSL_STATIC_MEMORY - static byte memory[300000]; - static byte memoryIO[34500]; + /* Sized for a TLS 1.3 mutual-auth handshake with every supported + * operation class pending: suspended verifies during mutual auth raise + * the bucket high-water mark well above the synchronous footprint. */ + static byte memory[800000]; + static byte memoryIO[64000]; #if !defined(WOLFSSL_STATIC_MEMORY_LEAN) WOLFSSL_MEM_CONN_STATS ssl_stats; #endif @@ -304,6 +307,7 @@ int client_async_test(int argc, char** argv) if (devId == INVALID_DEVID) devId = 1; XMEMSET(&cryptoCbCtx, 0, sizeof(cryptoCbCtx)); + cryptoCbCtx.tls12 = tls12; if (wc_CryptoCb_RegisterDevice(devId, AsyncTlsCryptoCb, &cryptoCbCtx) != 0) { fprintf(stderr, "ERROR: wc_CryptoCb_RegisterDevice failed\n"); goto out; @@ -567,6 +571,10 @@ int client_async_test(int argc, char** argv) #ifdef WOLFSSL_DEBUG_NONBLOCK printf("WANT_READ/WRITE count: %d\n", wouldblock_count); printf("WC_PENDING_E count: %d\n", pending_count); +#ifdef WOLF_CRYPTO_CB + printf("Device WC_PENDING_E returns: %d (table-full completions: %d)\n", + cryptoCbCtx.pendingCount, cryptoCbCtx.jobFullCount); +#endif #endif ret = 0; diff --git a/examples/async/async_server.c b/examples/async/async_server.c index 9d745fbeea..d4bcd22741 100644 --- a/examples/async/async_server.c +++ b/examples/async/async_server.c @@ -212,8 +212,11 @@ int server_async_test(int argc, char** argv) AsyncTlsCryptoCbCtx cryptoCbCtx; #endif #ifdef WOLFSSL_STATIC_MEMORY - static byte memory[300000]; - static byte memoryIO[34500]; + /* Sized for a TLS 1.3 mutual-auth handshake with every supported + * operation class pending: suspended verifies during mutual auth raise + * the bucket high-water mark well above the synchronous footprint. */ + static byte memory[800000]; + static byte memoryIO[64000]; #if !defined(WOLFSSL_STATIC_MEMORY_LEAN) WOLFSSL_MEM_CONN_STATS ssl_stats; #endif @@ -311,6 +314,7 @@ int server_async_test(int argc, char** argv) if (devId == INVALID_DEVID) devId = 1; XMEMSET(&cryptoCbCtx, 0, sizeof(cryptoCbCtx)); + cryptoCbCtx.tls12 = tls12; if (wc_CryptoCb_RegisterDevice(devId, AsyncTlsCryptoCb, &cryptoCbCtx) != 0) { fprintf(stderr, "ERROR: wc_CryptoCb_RegisterDevice failed\n"); goto exit; @@ -666,6 +670,10 @@ int server_async_test(int argc, char** argv) #ifdef WOLFSSL_DEBUG_NONBLOCK printf("WANT_READ/WRITE count: %d\n", wouldblock_count); printf("WC_PENDING_E count: %d\n", pending_count); +#ifdef WOLF_CRYPTO_CB + printf("Device WC_PENDING_E returns: %d (table-full completions: %d)\n", + cryptoCbCtx.pendingCount, cryptoCbCtx.jobFullCount); +#endif #endif ret = 0; diff --git a/examples/async/async_tls.c b/examples/async/async_tls.c index 2a4ee7fab8..da7da42864 100644 --- a/examples/async/async_tls.c +++ b/examples/async/async_tls.c @@ -162,6 +162,78 @@ int posix_getdevrandom(unsigned char *out, unsigned int sz) #define TEST_PEND_COUNT 2 #endif +#ifdef WOLFSSL_ASYNC_CRYPT +/* Return 1 to simulate WC_PENDING_E. A request (hash of wc_CryptoInfo) + * pends TEST_PEND_COUNT times and completes on re-invocation, like a + * hardware crypto manager job table. A full table completes requests + * synchronously (jobFullCount records the degradation). */ +static int AsyncTlsCryptoCbPend(AsyncTlsCryptoCbCtx* myCtx, + wc_CryptoInfo* info) +{ + unsigned long h = 5381; + const unsigned char* b = (const unsigned char*)info; + size_t i; + int simulate = 0; + + /* TLS 1.3 resumes every class below; TLS 1.2 only retries the + * signing set, so restrict when the app selected TLS 1.2. */ + if (myCtx->tls12) { + if (info->algo_type == WC_ALGO_TYPE_PK) { + simulate = (info->pk.type == WC_PK_TYPE_RSA || + info->pk.type == WC_PK_TYPE_ECDSA_SIGN); + } + } + else if (info->algo_type == WC_ALGO_TYPE_PK) { + simulate = (info->pk.type == WC_PK_TYPE_RSA || + info->pk.type == WC_PK_TYPE_EC_KEYGEN || + info->pk.type == WC_PK_TYPE_ECDSA_SIGN || + info->pk.type == WC_PK_TYPE_ECDSA_VERIFY || + info->pk.type == WC_PK_TYPE_ECDH || + info->pk.type == WC_PK_TYPE_CURVE25519_KEYGEN || + info->pk.type == WC_PK_TYPE_CURVE25519 || + info->pk.type == WC_PK_TYPE_ED25519_SIGN || + info->pk.type == WC_PK_TYPE_ED25519_VERIFY); + } + else if (info->algo_type == WC_ALGO_TYPE_KDF) { + simulate = 1; /* TLS 1.3 HKDF key schedule */ + } + else if (info->algo_type == WC_ALGO_TYPE_CIPHER) { + simulate = (info->cipher.type == WC_CIPHER_AES_GCM); + } + if (!simulate) + return 0; + + for (i = 0; i < sizeof(*info); i++) + h = (h * 33) + b[i]; + + for (i = 0; i < (size_t)myCtx->jobCount; i++) { + if (myCtx->jobHash[i] == h) { + myCtx->jobTries[i]++; + if (myCtx->jobTries[i] <= TEST_PEND_COUNT) { + myCtx->pendingCount++; + return 1; /* still pending */ + } + /* complete: remove job and run the operation below */ + myCtx->jobCount--; + myCtx->jobHash[i] = myCtx->jobHash[myCtx->jobCount]; + myCtx->jobTries[i] = myCtx->jobTries[myCtx->jobCount]; + return 0; + } + } + if (myCtx->jobCount >= ASYNC_TLS_PEND_JOBS) { + /* Full (non-identical retries strand entries): complete + * synchronously and count the degradation. */ + myCtx->jobFullCount++; + return 0; + } + myCtx->jobHash[myCtx->jobCount] = h; + myCtx->jobTries[myCtx->jobCount] = 1; + myCtx->jobCount++; + myCtx->pendingCount++; + return 1; +} +#endif /* WOLFSSL_ASYNC_CRYPT */ + /* Example crypto dev callback function that calls software version */ /* This is where you would plug-in calls to your own hardware crypto */ int AsyncTlsCryptoCb(int devIdArg, wc_CryptoInfo* info, void* ctx) @@ -169,32 +241,20 @@ int AsyncTlsCryptoCb(int devIdArg, wc_CryptoInfo* info, void* ctx) int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); /* bypass HW by default */ AsyncTlsCryptoCbCtx* myCtx = (AsyncTlsCryptoCbCtx*)ctx; - if (info == NULL) + if (info == NULL || myCtx == NULL) return BAD_FUNC_ARG; #ifdef DEBUG_CRYPTOCB wc_CryptoCb_InfoString(info); #endif - if (info->algo_type == WC_ALGO_TYPE_PK) { #ifdef WOLFSSL_ASYNC_CRYPT - /* Simulate async pending for RSA and ECC signing operations. - * This matches a typical hardware crypto scenario (e.g., TPM) where - * only signing is offloaded to hardware. Keygen, verify, and ECDH - * are performed synchronously in software. - * Note: WOLFSSL_ASYNC_CRYPT + WOLF_CRYPTO_CB pending simulation - * requires operations whose TLS state machines properly handle retry - * via wolfSSL_AsyncPop. ECC keygen in TLSX_KeyShare_GenEccKey does - * not support this because the keygen call is inside the key - * allocation guard (kse->key == NULL) which is skipped on retry. */ - if (info->pk.type == WC_PK_TYPE_RSA || - info->pk.type == WC_PK_TYPE_ECDSA_SIGN) - { - if (myCtx->pendingCount++ < TEST_PEND_COUNT) return WC_PENDING_E; - myCtx->pendingCount = 0; - } + if (AsyncTlsCryptoCbPend(myCtx, info)) { + return WC_PENDING_E; + } #endif + if (info->algo_type == WC_ALGO_TYPE_PK) { #ifndef NO_RSA if (info->pk.type == WC_PK_TYPE_RSA) { /* set devId to invalid, so software is used */ diff --git a/examples/async/async_tls.h b/examples/async/async_tls.h index 52814639da..1dc43220a8 100644 --- a/examples/async/async_tls.h +++ b/examples/async/async_tls.h @@ -46,8 +46,23 @@ typedef struct wc_CryptoInfo wc_CryptoInfo; #ifdef WOLF_CRYPTO_CB /* Example custom context for crypto callback */ +/* Max simultaneous simulated pending requests (device job table) */ +#ifndef ASYNC_TLS_PEND_JOBS +#define ASYNC_TLS_PEND_JOBS 64 +#endif typedef struct { - int pendingCount; /* track pending tries test count */ + int pendingCount; /* total WC_PENDING_E returns (statistic) */ + /* Simulated device job table. A pended request is identified by a + * hash of its wc_CryptoInfo so the re-invocation with identical + * arguments can be matched and completed. */ + unsigned long jobHash[ASYNC_TLS_PEND_JOBS]; + int jobTries[ASYNC_TLS_PEND_JOBS]; + int jobCount; + int jobFullCount; /* requests completed synchronously: table full */ + /* Set by the application when TLS 1.2 was selected: restricts the + * simulated pending to the operations the TLS 1.2 state machines can + * retry. TLS 1.3 (0, the default) pends every supported class. */ + int tls12; } AsyncTlsCryptoCbCtx; int AsyncTlsCryptoCb(int devIdArg, wc_CryptoInfo* info, void* ctx); #endif /* WOLF_CRYPTO_CB */