Simulate pending for all supported ops in async TLS example

pull/11231/head
David Garske 2026-09-02 14:33:12 -07:00
parent 1ed9f2c02c
commit 58445f6372
5 changed files with 122 additions and 24 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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 */