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
2 changes: 1 addition & 1 deletion .github/workflows/async.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
name: Async Tests

# START OF COMMON SECTION
Expand Down Expand Up @@ -71,7 +71,7 @@
run: |
cat > "$RUNNER_TEMP/async-configs.json" <<'EOF'
[
{"comment": "The only entry that pairs the software async simulator with --enable-all. --enable-all turns on cryptocb, which stops configure.ac from auto-enabling the simulator, so the asynccrypt-all entries below define WOLFSSL_ASYNC_CRYPT but never actually return WC_PENDING_E. Without this one nothing exercises TLS 1.3 post-handshake auth or DTLS writes against a pending crypto op. The minutes value is a projection, not a CI measurement: this config takes 1.6 min locally where the asynccrypt-all entries below take 1.4 against their declared 3. Refresh it from the first real run.",
{"comment": "The only entry that pairs the software async simulator with --enable-all. --enable-all turns on cryptocb, which stops configure.ac from auto-enabling the simulator, so the asynccrypt-all entries below define WOLFSSL_ASYNC_CRYPT but their built-in devices never return WC_PENDING_E; pending is exercised there by the tests that register their own pending callback (hkdf_cryptocb_async_test() in wolfCrypt, test_tls13_cryptocb_async in the api suite). Without this one nothing exercises TLS 1.3 post-handshake auth or DTLS writes against a pending crypto op. The minutes value is a projection, not a CI measurement: this config takes 1.6 min locally where the asynccrypt-all entries below take 1.4 against their declared 3. Refresh it from the first real run.",
"name": "asynccrypt-sw-all-dtls13", "minutes": 3,
"configure": ["--enable-asynccrypt-sw", "--enable-all",
"--enable-dtls13",
Expand Down
7 changes: 4 additions & 3 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -11686,8 +11686,9 @@ then
fi
fi

# Crypto callbacks with async crypt may not work for TLS unless
# WOLF_CRYPTO_CB_ASYNC_POLL is defined. Report it once here and silence the
# Crypto callbacks with async crypt cannot complete TLS 1.2 record ciphers
# unless WOLF_CRYPTO_CB_ASYNC_POLL is defined (TLS 1.3 resumes them by
# re-invoking the callback). Report it once here and silence the
# source-level #warning. AC_MSG_NOTICE, not AC_MSG_WARN: the multi-test
# harness fails any scenario whose configure emits "configure: WARNING:".
if test "$ENABLED_ASYNCCRYPT" = "yes" && test "x$ENABLED_CRYPTOCB" != "xno" &&
Expand All @@ -11698,7 +11699,7 @@ then
*WOLF_CRYPTO_CB_ASYNC_POLL*)
;;
*)
AC_MSG_NOTICE([crypto callbacks with async crypt may not work for TLS. Define WOLF_CRYPTO_CB_ASYNC_POLL to enable it.])
AC_MSG_NOTICE([crypto callbacks with async crypt cannot complete TLS 1.2 record ciphers. Define WOLF_CRYPTO_CB_ASYNC_POLL to enable them.])
AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ASYNC_NO_WARN"
;;
esac
Expand Down
41 changes: 41 additions & 0 deletions doc/dox_comments/header_files/hmac.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,19 @@ int wc_HKDF_Extract(
\return HMAC_MIN_KEYLEN_E May be returned when using a FIPS implementation
and the key length specified is shorter than the minimum acceptable FIPS
standard
\return WC_PENDING_E May be returned in a WOLF_CRYPTO_CB build when the
registered crypto callback device has taken the request but not yet
finished it. The caller must re-invoke with identical arguments until the
result is no longer WC_PENDING_E; HKDF has no WC_ASYNC_DEV, so this is a
poll and not a wc_AsyncWait(). In a WOLFSSL_ASYNC_CRYPT build the TLS 1.3
key schedule resumes a pending HKDF request by re-invoking the callback
with identical arguments; without WOLFSSL_ASYNC_CRYPT a device also used
for TLS 1.3 must complete HKDF requests synchronously. wc_HKDF_ex()
follows the same contract and re-issues its extract step on every retry,
so a device that pends must serve a repeated identical request from its
completed result. There is no request handle: a device should key its
completion tracking on the output pointer plus the argument tuple, and
one that cannot correlate a retry that way must not pend.

\param type hash type to use for the HKDF. Valid types are: WC_MD5, WC_SHA,
WC_SHA256, WC_SHA384, WC_SHA512, WC_SHA3_224, WC_SHA3_256, WC_SHA3_384 or
Expand Down Expand Up @@ -357,6 +370,19 @@ int wc_HKDF_Expand(
\return HMAC_MIN_KEYLEN_E May be returned when using a FIPS implementation
and the key length specified is shorter than the minimum acceptable FIPS
standard
\return WC_PENDING_E May be returned in a WOLF_CRYPTO_CB build when the
registered crypto callback device has taken the request but not yet
finished it. The caller must re-invoke with identical arguments until the
result is no longer WC_PENDING_E; HKDF has no WC_ASYNC_DEV, so this is a
poll and not a wc_AsyncWait(). In a WOLFSSL_ASYNC_CRYPT build the TLS 1.3
key schedule resumes a pending HKDF request by re-invoking the callback
with identical arguments; without WOLFSSL_ASYNC_CRYPT a device also used
for TLS 1.3 must complete HKDF requests synchronously. wc_HKDF_ex()
follows the same contract and re-issues its extract step on every retry,
so a device that pends must serve a repeated identical request from its
completed result. There is no request handle: a device should key its
completion tracking on the output pointer plus the argument tuple, and
one that cannot correlate a retry that way must not pend.

\param type hash type to use for the HKDF. Valid types are: WC_MD5, WC_SHA,
WC_SHA256, WC_SHA384, WC_SHA512, WC_SHA3_224, WC_SHA3_256, WC_SHA3_384 or
Expand Down Expand Up @@ -459,6 +485,11 @@ int wc_Tls13_HKDF_Extract(
\return HMAC_MIN_KEYLEN_E May be returned when using a FIPS implementation
and the key length specified is shorter than the minimum acceptable FIPS
standard
\return WC_PENDING_E May be returned in a WOLF_CRYPTO_CB build when the
registered crypto callback device has taken the request but not yet
finished it; the caller re-invokes with identical arguments until the
result is no longer WC_PENDING_E. The TLS 1.3 key schedule does this in
WOLFSSL_ASYNC_CRYPT builds.

\param prk Generated pseudorandom key
\param salt Salt. May be NULL; saltLen is then ignored unless a crypto
Expand Down Expand Up @@ -510,6 +541,11 @@ int wc_Tls13_HKDF_Extract_ex(
\return HMAC_MIN_KEYLEN_E May be returned when using a FIPS implementation
and the key length specified is shorter than the minimum acceptable FIPS
standard
\return WC_PENDING_E May be returned in a WOLF_CRYPTO_CB build when the
registered crypto callback device has taken the request but not yet
finished it; the caller re-invokes with identical arguments until the
result is no longer WC_PENDING_E. The TLS 1.3 key schedule does this in
WOLFSSL_ASYNC_CRYPT builds.

\param okm Generated pseudorandom key - output key material.
\param okmLen Length of generated pseudorandom key - output key material.
Expand Down Expand Up @@ -591,6 +627,11 @@ int wc_Tls13_HKDF_Expand_Label(
\return HMAC_MIN_KEYLEN_E May be returned when using a FIPS implementation
and the key length specified is shorter than the minimum acceptable FIPS
standard
\return WC_PENDING_E May be returned in a WOLF_CRYPTO_CB build when the
registered crypto callback device has taken the request but not yet
finished it; the caller re-invokes with identical arguments until the
result is no longer WC_PENDING_E. The TLS 1.3 key schedule does this in
WOLFSSL_ASYNC_CRYPT builds.

\param okm Generated pseudorandom key - output key material.
\param okmLen Length of generated pseudorandom key - output key material.
Expand Down
11 changes: 9 additions & 2 deletions examples/async/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down Expand Up @@ -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

Expand Down
12 changes: 10 additions & 2 deletions examples/async/async_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down
12 changes: 10 additions & 2 deletions examples/async/async_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down
94 changes: 77 additions & 17 deletions examples/async/async_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,39 +162,99 @@ 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)
{
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 */
Expand Down
17 changes: 16 additions & 1 deletion examples/async/async_tls.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
Loading
Loading