Merge pull request #11231 from dgarske/cryptocb_async_tls13

Extend TLS 1.3 async support to HKDF, HMAC, AES-GCM, RNG and X25519/Ed25519
pull/11427/head
Sean Parkinson 2026-09-10 14:39:16 +10:00 committed by GitHub
commit 252c3b1aa3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1880 additions and 345 deletions

View File

@ -71,7 +71,7 @@ jobs:
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",

View File

@ -11900,8 +11900,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" &&
@ -11912,7 +11913,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

View File

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

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

View File

@ -9520,6 +9520,11 @@ void FreeAsyncCtx(WOLFSSL* ssl, byte freeAsync)
}
#endif
if (freeAsync) {
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLFSSL_TLS13)
/* Teardown only: a suspended record build must keep its
* resume marker across handler-tail cleanups. */
ssl->options.buildArgs13Set = 0;
#endif
XFREE(ssl->async, ssl->heap, DYNAMIC_TYPE_ASYNC);
ssl->async = NULL;
}
@ -9824,6 +9829,10 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl)
#ifdef WOLFSSL_ASYNC_IO
/* Cleanup async */
FreeAsyncCtx(ssl, 1);
#endif
#if defined(WOLFSSL_ASYNC_REINVOKE) && defined(WOLFSSL_TLS13) && \
!defined(NO_HMAC)
Tls13FreeHsHmac(ssl);
#endif
if (ssl->options.weOwnRng) {
wc_FreeRng(ssl->rng);
@ -19294,6 +19303,12 @@ exit_ppc:
return ret;
}
/* TLS 1.3 replays skip the sanity check that re-sets got_certificate;
* restore on completion or Finished reports out-of-order. */
if (ret == 0 && IsAtLeastTLSv1_3(ssl->version) &&
ssl->msgsReceived.got_certificate == 0) {
ssl->msgsReceived.got_certificate = 1;
}
#endif /* WOLFSSL_ASYNC_CRYPT || WOLFSSL_NONBLOCK_OCSP */
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
@ -25250,6 +25265,26 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
return ssl->error;
}
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_ASYNC_CRYPT)
/* Finish a TLS 1.3 key schedule the last handshake message left pending
* before any further record is read or decrypted: the derives install
* the very keys that record needs. See DoTls13MsgDerives(). */
if (ssl->options.tls1_3 && ssl->kdfMsgStep > 0) {
ret = DoTls13MsgDerives(ssl, ssl->kdfMsgType);
if (ret != 0) {
if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) {
WOLFSSL_ERROR(ret);
}
return ret;
}
/* The pend is resolved; leaving ssl->error set would make the
* next message skip its sanity check and got_* marking. */
if (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E)) {
ssl->error = 0;
}
}
#endif
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_ASYNC_CRYPT)
/* process any pending DTLS messages - this flow can happen with async */
if (ssl->dtls_rx_msg_list != NULL) {
@ -25942,6 +25977,24 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
ssl->buffers.inputBuffer.buffer,
&ssl->buffers.inputBuffer.idx,
ssl->curStartIdx + ssl->curSize);
#if defined(WOLFSSL_ASYNC_CRYPT)
/* A post-handler key-schedule pend consumed the
* message but not the record; finish it here so
* the retry reads the next record. */
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) &&
ssl->kdfMsgStep > 0) {
ssl->options.processReply = doProcessInit;
if ((ssl->buffers.inputBuffer.idx -
ssl->curStartIdx) < ssl->curSize) {
ssl->options.processReply =
runProcessingOneMessage;
}
else if (IsEncryptionOn(ssl, 0)) {
ssl->buffers.inputBuffer.idx +=
ssl->keys.padSz;
}
}
#endif
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
/* Post-handshake auth resumes through
* wolfSSL_negotiate() instead of reprocessing this
@ -25951,8 +26004,12 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
* the trailing MAC is read as the next record header
* and fails with VERSION_ERROR. Mirrors the end of
* record block below: resume inside the record when
* content is left, else skip the padding. */
* content is left, else skip the padding. Skipped for
* a key-schedule pend (kdfMsgStep != 0): the block
* above already finished the record, and running this
* one too would skip the padding twice. */
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) &&
ssl->kdfMsgStep == TLS13_MSG_KDF_NONE &&
ssl->options.processReply == doProcessInit) {
if ((ssl->buffers.inputBuffer.idx -
ssl->curStartIdx) < ssl->curSize) {

View File

@ -5712,6 +5712,31 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out,
ssl->options.onlyPskDheKe = ssl->ctx->onlyPskDheKe;
#endif
#endif
/* An abandoned handshake can leave a key-schedule or record-build
* resume marker set; a reused object must not resume into the new
* handshake. */
ssl->kdfDeriveStep = TLS13_SEND_KDF_NONE;
ssl->kdfMsgStep = TLS13_MSG_KDF_NONE;
ssl->kdfMsgType = 0;
#if defined(WOLFSSL_ASYNC_REINVOKE) && !defined(NO_HMAC)
Tls13FreeHsHmac(ssl);
#endif
#ifdef WOLFSSL_ASYNC_CRYPT
ssl->options.buildArgs13Set = 0;
/* An abandoned handshake can leave a mid-flight handler resume
* state and a queued key-schedule event behind; a reused object
* must start fresh. */
ssl->options.asyncState = TLS_ASYNC_BEGIN;
if (ssl->asyncDev == &ssl->kdfAsyncDev) {
if (ssl->kdfAsyncDev.event.state == WOLF_EVENT_STATE_PENDING &&
ssl->ctx != NULL) {
(void)wolfEventQueue_Remove(&ssl->ctx->event_queue,
&ssl->kdfAsyncDev.event);
}
XMEMSET(&ssl->kdfAsyncDev.event, 0, sizeof(WOLF_EVENT));
ssl->asyncDev = NULL;
}
#endif
#endif
#ifdef HAVE_SESSION_TICKET
#ifdef WOLFSSL_TLS13

155
src/tls.c
View File

@ -8479,7 +8479,7 @@ static int TLSX_KeyShare_GenX25519Key(WOLFSSL *ssl, KeyShareEntry* kse)
return MEMORY_E;
}
/* Make an Curve25519 key. */
/* Initialize the Curve25519 key. */
ret = wc_curve25519_init_ex((curve25519_key*)kse->key, ssl->heap,
ssl->devId);
if (ret == 0) {
@ -8508,28 +8508,32 @@ static int TLSX_KeyShare_GenX25519Key(WOLFSSL *ssl, KeyShareEntry* kse)
}
#endif /* WC_X25519_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
WC_ASYNC_ENABLE_X25519 */
if (ret == 0) {
#ifdef WOLFSSL_STATIC_EPHEMERAL
ret = wolfSSL_StaticEphemeralKeyLoad(ssl, WC_PK_TYPE_CURVE25519, kse->key);
if (ret != 0) /* on failure, fallback to local key generation */
#endif
{
#ifdef WOLFSSL_ASYNC_CRYPT
/* initialize event */
ret = wolfSSL_AsyncInit(ssl, &key->asyncDev,
WC_ASYNC_FLAG_NONE);
if (ret != 0)
return ret;
#endif
ret = wc_curve25519_make_key(ssl->rng, CURVE25519_KEYSIZE, key);
}
/* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
return wolfSSL_AsyncPush(ssl, &key->asyncDev);
}
#endif /* WOLFSSL_ASYNC_CRYPT */
/* Outside the allocation guard: a WC_PENDING_E retry must regenerate,
* not export an ungenerated key. pubKeyLen marks a completed export on
* every backend; pubSet stops the SW-async retry re-arming forever. */
if (ret == 0 && key != NULL && kse->pubKeyLen == 0 && !key->pubSet) {
#ifdef WOLFSSL_STATIC_EPHEMERAL
ret = wolfSSL_StaticEphemeralKeyLoad(ssl, WC_PK_TYPE_CURVE25519,
kse->key);
if (ret != 0) /* on failure, fallback to local key generation */
#endif
{
#ifdef WOLFSSL_ASYNC_CRYPT
/* initialize event */
ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_NONE);
if (ret != 0)
return ret;
#endif
ret = wc_curve25519_make_key(ssl->rng, CURVE25519_KEYSIZE, key);
/* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
return wolfSSL_AsyncPush(ssl, &key->asyncDev);
}
#endif /* WOLFSSL_ASYNC_CRYPT */
}
}
@ -8759,6 +8763,10 @@ static int TLSX_KeyShare_GenEccKey(WOLFSSL *ssl, KeyShareEntry* kse)
/* Initialize an ECC key struct for the ephemeral key */
ret = wc_ecc_init_ex((ecc_key*)kse->key, ssl->heap, ssl->devId);
if (ret == 0) {
/* setting eccKey means okay to call wc_ecc_free */
eccKey = (ecc_key*)kse->key;
}
#if defined(WC_ECC_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
defined(WC_ASYNC_ENABLE_ECC)
@ -8781,49 +8789,44 @@ static int TLSX_KeyShare_GenEccKey(WOLFSSL *ssl, KeyShareEntry* kse)
}
#endif /* WC_ECC_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
WC_ASYNC_ENABLE_ECC */
}
if (ret == 0) {
kse->keyLen = keySize;
kse->pubKeyLen = keySize * 2 + 1;
/* Outside the allocation guard: a WC_PENDING_E retry must regenerate,
* not export an ungenerated key. The key type marks completion;
* kse->pubKey covers backends that never touch the ecc_key (TSIP). */
if (ret == 0 && eccKey != NULL) {
/* Outside the generation guard below: the export alloc reads
* pubKeyLen even when generation is skipped. */
kse->keyLen = keySize;
kse->pubKeyLen = keySize * 2 + 1;
}
#if defined(WOLFSSL_RENESAS_TSIP_TLS)
ret = tsip_Tls13GenEccKeyPair(ssl, kse);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
if (ret == 0 && eccKey != NULL && kse->pubKey == NULL &&
eccKey->type != ECC_PRIVATEKEY &&
eccKey->type != ECC_PRIVATEKEY_ONLY) {
#if defined(WOLFSSL_RENESAS_TSIP_TLS)
ret = tsip_Tls13GenEccKeyPair(ssl, kse);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
return ret;
}
#endif
#ifdef WOLFSSL_STATIC_EPHEMERAL
ret = wolfSSL_StaticEphemeralKeyLoad(ssl, WC_PK_TYPE_ECDH, kse->key);
if (ret != 0 || eccKey->dp->id != curveId)
#endif
{
/* set curve info for EccMakeKey "peer" info */
ret = wc_ecc_set_curve(eccKey, (int)kse->keyLen, curveId);
if (ret == 0) {
/* Generate ephemeral ECC key; a crypto callback retry
* re-enters here, x963 export follows below. */
ret = EccMakeKey(ssl, eccKey, eccKey);
}
#ifdef WOLFSSL_ASYNC_CRYPT
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
return ret;
}
#endif
/* setting eccKey means okay to call wc_ecc_free */
eccKey = (ecc_key*)kse->key;
#ifdef WOLFSSL_STATIC_EPHEMERAL
ret = wolfSSL_StaticEphemeralKeyLoad(ssl, WC_PK_TYPE_ECDH, kse->key);
if (ret != 0 || eccKey->dp->id != curveId)
#endif
{
/* set curve info for EccMakeKey "peer" info */
ret = wc_ecc_set_curve(eccKey, (int)kse->keyLen, curveId);
if (ret == 0) {
#ifdef WOLFSSL_ASYNC_CRYPT
/* Detect when private key generation is done */
if (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E) &&
eccKey->type == ECC_PRIVATEKEY) {
ret = 0; /* ECC Key Generation is done */
}
else
#endif
{
/* Generate ephemeral ECC key */
/* For async this is called once and when event is done, the
* provided buffers in key be populated.
* Final processing is x963 key export below. */
ret = EccMakeKey(ssl, eccKey, eccKey);
}
}
#ifdef WOLFSSL_ASYNC_CRYPT
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
return ret;
#endif
}
}
}
@ -10661,7 +10664,8 @@ static int TLSX_KeyShare_Process(WOLFSSL* ssl, KeyShareEntry* keyShareEntry)
WOLFSSL_BUFFER(ssl->arrays->preMasterSecret, ssl->arrays->preMasterSz);
}
#endif
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) || \
defined(WOLFSSL_ASYNC_CRYPT)
keyShareEntry->derived = (ret == 0);
#endif
#ifdef WOLFSSL_ASYNC_CRYPT
@ -12278,6 +12282,27 @@ int TLSX_KeyShare_DeriveSecret(WOLFSSL *ssl)
TLSX* extension;
KeyShareEntry* list = NULL;
/* Find the KeyShare extension if it exists. */
extension = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE);
if (extension != NULL)
list = (KeyShareEntry*)extension->data;
if (list == NULL) {
/* Unreachable once the handshake reached this accept state
* (TLSX_KeyShare_Setup installed the extension), so no async event
* can be stranded by returning before the pop below. */
return KEY_SHARE_ERROR;
}
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) || \
defined(WOLFSSL_ASYNC_CRYPT)
/* Already derived: a later pend's retry re-enters here with the peer
* key freed. Checked before the pop so the later operation's queued
* event is not stolen. */
if (list->derived)
return 0;
#endif
#ifdef WOLFSSL_ASYNC_CRYPT
ret = wolfSSL_AsyncPop(ssl, NULL);
/* Check for error */
@ -12286,14 +12311,6 @@ int TLSX_KeyShare_DeriveSecret(WOLFSSL *ssl)
}
#endif
/* Find the KeyShare extension if it exists. */
extension = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE);
if (extension != NULL)
list = (KeyShareEntry*)extension->data;
if (list == NULL)
return KEY_SHARE_ERROR;
/* Calculate secret. */
ret = TLSX_KeyShare_Process(ssl, list);

File diff suppressed because it is too large Load Diff

View File

@ -11241,3 +11241,374 @@ int test_tls13_is_init_finished_want_write(void)
#endif
return EXPECT_RESULT();
}
#if defined(WOLFSSL_TLS13) && defined(WOLF_CRYPTO_CB) && \
defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLFSSL_ASYNC_REINVOKE) && \
defined(HAVE_ECC) && defined(HAVE_SUPPORTED_CURVES) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
#define TEST_TLS13_CB_PEND_DEVID_C 1
#define TEST_TLS13_CB_PEND_DEVID_S 2
#define TEST_TLS13_CB_PEND_JOBS 64
/* Which class of operation the callback pends. */
enum TestTls13PendTarget {
TEST_TLS13_PEND_AESGCM,
TEST_TLS13_PEND_ECC_KEYGEN,
TEST_TLS13_PEND_ECDSA_SIGN,
TEST_TLS13_PEND_ECDSA_VERIFY,
TEST_TLS13_PEND_KDF,
TEST_TLS13_PEND_HMAC
};
typedef struct TestTls13PendCtx {
/* Job table keyed by request: first sight queues and pends, the
* identical re-invocation completes (falls through to software). */
unsigned long jobs[TEST_TLS13_CB_PEND_JOBS];
int jobCount;
int target; /* enum TestTls13PendTarget */
int pended; /* WC_PENDING_E results issued; asserted non-zero */
int seen; /* matching requests observed; asserted non-zero */
} TestTls13PendCtx;
static int TestTls13PendMatches(int target, wc_CryptoInfo* info)
{
int match = 0;
switch (target) {
#ifndef WOLF_CRYPTO_CB_ASYNC_POLL
/* With WOLF_CRYPTO_CB_ASYNC_POLL the record ciphers follow the
* poll-completion contract instead of re-invocation; that model is
* covered by tests/api/test_async.c. */
case TEST_TLS13_PEND_AESGCM:
match = (info->algo_type == WC_ALGO_TYPE_CIPHER) &&
(info->cipher.type == WC_CIPHER_AES_GCM);
break;
#endif
case TEST_TLS13_PEND_ECC_KEYGEN:
match = (info->algo_type == WC_ALGO_TYPE_PK) &&
(info->pk.type == WC_PK_TYPE_EC_KEYGEN);
break;
case TEST_TLS13_PEND_ECDSA_SIGN:
match = (info->algo_type == WC_ALGO_TYPE_PK) &&
(info->pk.type == WC_PK_TYPE_ECDSA_SIGN);
break;
case TEST_TLS13_PEND_ECDSA_VERIFY:
match = (info->algo_type == WC_ALGO_TYPE_PK) &&
(info->pk.type == WC_PK_TYPE_ECDSA_VERIFY);
break;
case TEST_TLS13_PEND_KDF:
match = (info->algo_type == WC_ALGO_TYPE_KDF);
break;
case TEST_TLS13_PEND_HMAC:
match = (info->algo_type == WC_ALGO_TYPE_HMAC);
break;
default:
break;
}
return match;
}
/* Request fingerprint over the whole info struct: op class, sizes, pointers
* and inline content distinguish interleaved requests. */
static unsigned long TestTls13PendHash(wc_CryptoInfo* info)
{
unsigned long h = 5381;
const unsigned char* b = (const unsigned char*)info;
size_t i;
for (i = 0; i < sizeof(*info); i++)
h = h * 33 + b[i];
return h;
}
static int TestTls13PendCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
{
TestTls13PendCtx* c = (TestTls13PendCtx*)ctx;
unsigned long h;
int i;
(void)devIdArg;
if (info == NULL || c == NULL)
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
#if defined(HAVE_HKDF) && !defined(NO_HMAC) && !defined(HAVE_SELFTEST) && \
(!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0))
if (c->target == TEST_TLS13_PEND_HMAC &&
info->algo_type == WC_ALGO_TYPE_KDF) {
/* Serve the key schedule synchronously in software; falling back
* with the devId set would route its internal HMACs back here and
* those cannot resume (see wolfcrypt/src/hmac.c). Only the
* TLS-layer transcript HMACs are left to pend. */
if (info->kdf.type == WC_KDF_TYPE_HKDF_EXTRACT) {
return wc_HKDF_Extract_ex(info->kdf.hkdf_extract.hashType,
info->kdf.hkdf_extract.salt, info->kdf.hkdf_extract.saltSz,
info->kdf.hkdf_extract.inKey, info->kdf.hkdf_extract.inKeySz,
info->kdf.hkdf_extract.out, NULL, INVALID_DEVID);
}
if (info->kdf.type == WC_KDF_TYPE_HKDF_EXPAND) {
return wc_HKDF_Expand_ex(info->kdf.hkdf_expand.hashType,
info->kdf.hkdf_expand.inKey, info->kdf.hkdf_expand.inKeySz,
info->kdf.hkdf_expand.info, info->kdf.hkdf_expand.infoSz,
info->kdf.hkdf_expand.out, info->kdf.hkdf_expand.outSz,
NULL, INVALID_DEVID);
}
if (info->kdf.type == WC_KDF_TYPE_HKDF) {
return wc_HKDF_ex(info->kdf.hkdf.hashType,
info->kdf.hkdf.inKey, info->kdf.hkdf.inKeySz,
info->kdf.hkdf.salt, info->kdf.hkdf.saltSz,
info->kdf.hkdf.info, info->kdf.hkdf.infoSz,
info->kdf.hkdf.out, info->kdf.hkdf.outSz,
NULL, INVALID_DEVID);
}
}
#endif
if (!TestTls13PendMatches(c->target, info))
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
c->seen++;
h = TestTls13PendHash(info);
for (i = 0; i < c->jobCount; i++) {
if (c->jobs[i] == h) {
/* Re-invocation of a pended request: complete it. */
c->jobs[i] = c->jobs[--c->jobCount];
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
}
}
if (c->jobCount < TEST_TLS13_CB_PEND_JOBS) {
c->jobs[c->jobCount++] = h;
c->pended++;
return WC_PENDING_E;
}
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
}
/* wolfSSL_write()/wolfSSL_read() can return WC_PENDING_E just as the
* handshake does; drive them with the poll-and-retry loop the application is
* expected to use. Returns the byte count, or the error. */
static int TestTls13PendWrite(WOLFSSL* ssl, const char* buf, int sz)
{
int ret;
int err;
int rounds = 0;
do {
ret = wolfSSL_write(ssl, buf, sz);
if (ret > 0)
break;
err = wolfSSL_get_error(ssl, ret);
if (err == WC_NO_ERR_TRACE(WC_PENDING_E)) {
if (wolfSSL_AsyncPoll(ssl, WOLF_POLL_FLAG_CHECK_HW) < 0)
return -1;
}
else if (err != WOLFSSL_ERROR_WANT_READ &&
err != WOLFSSL_ERROR_WANT_WRITE) {
return ret;
}
} while (++rounds < 100);
return ret;
}
static int TestTls13PendRead(WOLFSSL* ssl, char* buf, int sz)
{
int ret;
int err;
int rounds = 0;
do {
ret = wolfSSL_read(ssl, buf, sz);
if (ret > 0)
break;
err = wolfSSL_get_error(ssl, ret);
if (err == WC_NO_ERR_TRACE(WC_PENDING_E)) {
if (wolfSSL_AsyncPoll(ssl, WOLF_POLL_FLAG_CHECK_HW) < 0)
return -1;
}
else if (err != WOLFSSL_ERROR_WANT_READ &&
err != WOLFSSL_ERROR_WANT_WRITE) {
return ret;
}
} while (++rounds < 100);
return ret;
}
/* One TLS 1.3 handshake with the given operation class pending on both sides,
* then application data both ways so records queued after the handshake are
* parsed by the peer as well. */
static int test_tls13_cryptocb_pend_one(int target, int mutual)
{
EXPECT_DECLS;
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
TestTls13PendCtx cliCtx;
TestTls13PendCtx srvCtx;
struct test_memio_ctx memio;
const char msg[] = "hello over TLS 1.3";
char buf[64];
XMEMSET(&cliCtx, 0, sizeof(cliCtx));
XMEMSET(&srvCtx, 0, sizeof(srvCtx));
XMEMSET(&memio, 0, sizeof(memio));
cliCtx.target = target;
srvCtx.target = target;
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_TLS13_CB_PEND_DEVID_C,
TestTls13PendCb, &cliCtx), 0);
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_TLS13_CB_PEND_DEVID_S,
TestTls13PendCb, &srvCtx), 0);
/* devId set on the CTX before credentials and SSL objects so all of
* it inherits the devId. ECC credentials: an RSA key under a devId
* would be treated as device-held. */
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
ExpectIntEQ(wolfSSL_CTX_SetDevId(ctx_c, TEST_TLS13_CB_PEND_DEVID_C),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_SetDevId(ctx_s, TEST_TLS13_CB_PEND_DEVID_S),
WOLFSSL_SUCCESS);
#ifdef HAVE_AESGCM
if (target == TEST_TLS13_PEND_AESGCM) {
/* Pin the AEAD so the pend assertion cannot depend on suite
* preference ordering. */
ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx_c,
"TLS13-AES128-GCM-SHA256"), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx_s,
"TLS13-AES128-GCM-SHA256"), WOLFSSL_SUCCESS);
}
#endif
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c,
"./certs/ca-ecc-cert.pem", 0), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_s,
"./certs/server-ecc.pem"), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, "./certs/ecc-key.pem",
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
if (mutual) {
/* Mutual auth: pending verifies of the client's chain and CV
* cover the received-marker restores. */
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_c,
"./certs/client-ecc-cert.pem"), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c,
"./certs/ecc-client-key.pem", WOLFSSL_FILETYPE_PEM),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s,
"./certs/client-ecc-cert.pem", 0), WOLFSSL_SUCCESS);
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_PEER |
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
}
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
/* Pin the group so every run negotiates the same way; the default offer
* can trigger HelloRetryRequest or a PQC key share, which are separate
* scenarios from the pend-resume paths this test covers. */
if (EXPECT_SUCCESS()) {
int groups[1];
groups[0] = WOLFSSL_ECC_SECP256R1;
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_groups(ssl_s, groups, 1), WOLFSSL_SUCCESS);
}
/* The shared memio transport; its TLS path serves a byte stream, so
* the re-read patterns of pending crypto operations cannot desync it. */
if (EXPECT_SUCCESS()) {
wolfSSL_SSLSetIORecv(ssl_c, test_memio_read_cb);
wolfSSL_SSLSetIOSend(ssl_c, test_memio_write_cb);
wolfSSL_SSLSetIORecv(ssl_s, test_memio_read_cb);
wolfSSL_SSLSetIOSend(ssl_s, test_memio_write_cb);
wolfSSL_SetIOReadCtx(ssl_c, &memio);
wolfSSL_SetIOWriteCtx(ssl_c, &memio);
wolfSSL_SetIOReadCtx(ssl_s, &memio);
wolfSSL_SetIOWriteCtx(ssl_s, &memio);
}
/* Generous rounds: pending every KDF request costs one poll-and-retry
* round trip per operation, and a TLS 1.3 handshake with session tickets
* runs a couple of hundred of them. */
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 600, NULL), 0);
/* Assert per-side pends so the test cannot pass without exercising a
* resume path; the client signs nothing unless mutual. */
ExpectIntGT(srvCtx.seen, 0);
ExpectIntGT(srvCtx.pended, 0);
if (mutual || target != TEST_TLS13_PEND_ECDSA_SIGN) {
ExpectIntGT(cliCtx.seen, 0);
ExpectIntGT(cliCtx.pended, 0);
}
ExpectIntEQ(TestTls13PendWrite(ssl_c, msg, (int)sizeof(msg)),
(int)sizeof(msg));
XMEMSET(buf, 0, sizeof(buf));
ExpectIntEQ(TestTls13PendRead(ssl_s, buf, (int)sizeof(buf)),
(int)sizeof(msg));
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
ExpectIntEQ(TestTls13PendWrite(ssl_s, msg, (int)sizeof(msg)),
(int)sizeof(msg));
XMEMSET(buf, 0, sizeof(buf));
ExpectIntEQ(TestTls13PendRead(ssl_c, buf, (int)sizeof(buf)),
(int)sizeof(msg));
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
wc_CryptoCb_UnRegisterDevice(TEST_TLS13_CB_PEND_DEVID_C);
wc_CryptoCb_UnRegisterDevice(TEST_TLS13_CB_PEND_DEVID_S);
return EXPECT_RESULT();
}
#endif /* guards */
/* Drive TLS 1.3 handshakes and application data with the poll-and-retry
* loop while a crypto callback pends each matching request. */
int test_tls13_cryptocb_async(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_TLS13) && defined(WOLF_CRYPTO_CB) && \
defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLFSSL_ASYNC_REINVOKE) && \
defined(HAVE_ECC) && defined(HAVE_SUPPORTED_CURVES) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
#if defined(HAVE_AESGCM) && !defined(WOLF_CRYPTO_CB_ASYNC_POLL)
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_AESGCM, 0),
TEST_SUCCESS);
#endif
#ifdef HAVE_ECC
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_ECC_KEYGEN, 0),
TEST_SUCCESS);
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_ECDSA_SIGN, 0),
TEST_SUCCESS);
/* Mutual auth with pending verifies: regression for the msgsReceived
* marker restores (a pended Certificate/CertificateVerify replay used
* to leave its marker clear and fail Finished with OUT_OF_ORDER_E). */
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_ECDSA_VERIFY, 1),
TEST_SUCCESS);
#endif
#if defined(HAVE_HKDF) && !defined(NO_HMAC)
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_KDF, 0),
TEST_SUCCESS);
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_KDF, 1),
TEST_SUCCESS);
#if !defined(HAVE_SELFTEST) && \
(!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0))
/* Transcript HMACs (Finished verify_data) pending on both sides. */
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_HMAC, 0),
TEST_SUCCESS);
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_HMAC, 1),
TEST_SUCCESS);
#endif
#endif
#endif
return EXPECT_RESULT();
}

View File

@ -132,6 +132,7 @@ int test_tls13_pqc_hybrid_async_server(void);
int test_tls13_pha_status_request(void);
int test_tls13_x25519_keyshare_masks_reserved_bit(void);
int test_tls13_is_init_finished_want_write(void);
int test_tls13_cryptocb_async(void);
#define TEST_TLS13_DECLS \
TEST_DECL_GROUP("tls13", test_tls13_apis), \
@ -241,6 +242,7 @@ int test_tls13_is_init_finished_want_write(void);
TEST_DECL_GROUP("tls13", test_tls13_pqc_hybrid_async_server), \
TEST_DECL_GROUP("tls13", test_tls13_pha_status_request), \
TEST_DECL_GROUP("tls13", test_tls13_x25519_keyshare_masks_reserved_bit), \
TEST_DECL_GROUP("tls13", test_tls13_is_init_finished_want_write)
TEST_DECL_GROUP("tls13", test_tls13_is_init_finished_want_write), \
TEST_DECL_GROUP("tls13", test_tls13_cryptocb_async)
#endif /* WOLFCRYPT_TEST_TLS13_H */

View File

@ -129,7 +129,44 @@ int test_memio_read_cb(WOLFSSL *ssl, char *data, int sz, void *ctx)
if (*len == 0 || *msg_pos >= *msg_count)
return WOLFSSL_CBIO_ERR_WANT_READ;
/* Calculate how much we can read from current message */
if (!is_dtls) {
/* TLS is a byte stream: serve across message boundaries so
* pending-crypto re-read patterns cannot desync the slots. */
int rem;
read_sz = *len;
if (read_sz > sz)
read_sz = sz;
XMEMCPY(data, buf, (size_t)read_sz);
XMEMMOVE(buf, buf + read_sz, (size_t)(*len - read_sz));
*len -= read_sz;
rem = read_sz;
while (rem > 0 && *msg_pos < *msg_count) {
if (msg_sizes[*msg_pos] > rem) {
msg_sizes[*msg_pos] -= rem;
rem = 0;
}
else {
rem -= msg_sizes[*msg_pos];
msg_sizes[*msg_pos] = 0;
(*msg_pos)++;
}
}
if (rem != 0) {
/* Slot accounting desynced from the byte count; fail loudly. */
return WOLFSSL_CBIO_ERR_GENERAL;
}
if (*msg_pos >= *msg_count && *len == 0) {
*msg_pos = 0;
*msg_count = 0;
}
return read_sz;
}
/* DTLS: datagram boundaries matter, serve one message at a time. */
read_sz = msg_sizes[*msg_pos];
if (read_sz > sz)
read_sz = sz;

View File

@ -77,8 +77,9 @@ Crypto Callback Build Options:
#if defined(WOLFSSL_ASYNC_CRYPT) && !defined(WOLF_CRYPTO_CB_ASYNC_POLL) && \
!defined(WOLFSSL_ASYNC_CRYPT_SW) && !defined(HAVE_INTEL_QA) && \
!defined(HAVE_CAVIUM) && !defined(WOLF_CRYPTO_CB_ASYNC_NO_WARN)
#warning "crypto callbacks with async crypt may not work for TLS. Define \
WOLF_CRYPTO_CB_ASYNC_POLL to enable it, or WOLF_CRYPTO_CB_ASYNC_NO_WARN to \
#warning "crypto callbacks with async crypt cannot complete TLS 1.2 \
record ciphers (TLS 1.3 resumes them by re-invoking the callback). Define \
WOLF_CRYPTO_CB_ASYNC_POLL to enable them, or WOLF_CRYPTO_CB_ASYNC_NO_WARN to \
silence."
#endif

View File

@ -1774,6 +1774,26 @@ int wolfSSL_GetHmacMaxSize(void)
}
#ifdef HAVE_HKDF
/* Wait out an async HMAC sub-op: the HKDF loops cannot resume
* mid-chain. QAT/Cavium only (not covered by CI); a crypto callback
* pending must instead propagate so the caller can re-invoke. */
#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_HMAC) && \
(defined(HAVE_INTEL_QA) || defined(HAVE_CAVIUM))
/* QAT/Cavium only: WOLFSSL_ASYNC_REINVOKE is never defined for these
* backends (see internal.h), so an HKDF sub-op can only pend on the
* hardware device, never on a re-invokable crypto callback. Wait
* unconditionally: the HKDF loops cannot resume mid-chain. */
#define HKDF_HMAC_WAIT(ret, hmac) \
do { \
(ret) = wc_AsyncWait((ret), &(hmac)->asyncDev, \
WC_ASYNC_FLAG_NONE); \
} while (0)
#else
/* No HMAC device to wait on, or the pending must reach the caller
* (crypto callback re-invocation); hmac is unevaluated. */
#define HKDF_HMAC_WAIT(ret, hmac) WC_DO_NOTHING
#endif
/* HMAC-KDF-Extract.
* RFC 5869 - HMAC-based Extract-and-Expand Key Derivation Function (HKDF).
*
@ -1799,7 +1819,9 @@ int wolfSSL_GetHmacMaxSize(void)
}
#ifdef WOLF_CRYPTO_CB
/* Try crypto callback first */
/* Try crypto callback first. Only CRYPTOCB_UNAVAILABLE falls back
* to software. WC_PENDING_E is returned as-is: the caller polls,
* re-invoking with identical arguments until it clears. */
if (devId != INVALID_DEVID) {
ret = wc_CryptoCb_Hkdf_Extract(type, salt, saltSz, inKey, inKeySz,
out, devId);
@ -1832,10 +1854,14 @@ int wolfSSL_GetHmacMaxSize(void)
#else
ret = wc_HmacSetKey(myHmac, type, localSalt, saltSz);
#endif
if (ret == 0)
if (ret == 0) {
ret = wc_HmacUpdate(myHmac, inKey, inKeySz);
if (ret == 0)
HKDF_HMAC_WAIT(ret, myHmac);
}
if (ret == 0) {
ret = wc_HmacFinal(myHmac, out);
HKDF_HMAC_WAIT(ret, myHmac);
}
wc_HmacFree(myHmac);
}
WC_FREE_VAR_EX(myHmac, NULL, DYNAMIC_TYPE_HMAC);
@ -1891,7 +1917,8 @@ int wolfSSL_GetHmacMaxSize(void)
return BAD_FUNC_ARG;
#ifdef WOLF_CRYPTO_CB
/* Try crypto callback first for complete operation */
/* Try crypto callback first. WC_PENDING_E is returned to the
* caller to poll, as in wc_HKDF_Extract_ex(). */
if (devId != INVALID_DEVID) {
ret = wc_CryptoCb_Hkdf_Expand(type, inKey, inKeySz, info, infoSz,
out, outSz, devId);
@ -1927,15 +1954,19 @@ int wolfSSL_GetHmacMaxSize(void)
if (ret != 0)
break;
ret = wc_HmacUpdate(myHmac, tmp, tmpSz);
HKDF_HMAC_WAIT(ret, myHmac);
if (ret != 0)
break;
ret = wc_HmacUpdate(myHmac, info, infoSz);
HKDF_HMAC_WAIT(ret, myHmac);
if (ret != 0)
break;
ret = wc_HmacUpdate(myHmac, &n, 1);
HKDF_HMAC_WAIT(ret, myHmac);
if (ret != 0)
break;
ret = wc_HmacFinal(myHmac, tmp);
HKDF_HMAC_WAIT(ret, myHmac);
if (ret != 0)
break;
@ -1988,7 +2019,8 @@ int wolfSSL_GetHmacMaxSize(void)
(void)devId; /* suppress unused parameter warning */
#ifdef WOLF_CRYPTO_CB
/* Try crypto callback first for complete operation */
/* Try crypto callback first. WC_PENDING_E is returned to the
* caller to poll, as in wc_HKDF_Extract_ex(). */
if (devId != INVALID_DEVID) {
ret = wc_CryptoCb_Hkdf(type, inKey, inKeySz, salt, saltSz, info,
infoSz, out, outSz, devId);
@ -2006,6 +2038,8 @@ int wolfSSL_GetHmacMaxSize(void)
XMEMSET(prk, 0, WC_MAX_DIGEST_SIZE);
wc_MemZero_Add("wc_HKDF_ex prk", prk, WC_MAX_DIGEST_SIZE);
#endif
/* Restartable, not resumable: the retry redoes extract, so a
* device that pends must serve the repeat from its result. */
ret = wc_HKDF_Extract_ex(type, salt, saltSz, inKey, inKeySz, prk, heap,
devId);
if (ret == 0) {
@ -2027,6 +2061,8 @@ int wolfSSL_GetHmacMaxSize(void)
outSz, NULL, INVALID_DEVID);
}
#undef HKDF_HMAC_WAIT
#endif /* HAVE_HKDF */
#endif /* NO_HMAC */

View File

@ -81415,6 +81415,10 @@ typedef struct {
#if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD)
int rsaPssVerifyCount; /* RSA-PSS verify callback invocations */
#endif
#if defined(HAVE_HKDF) && !defined(NO_HMAC)
int hkdfPendArm; /* pend the next this-many HKDF callback calls */
int hkdfPendCount; /* pends issued; test asserts non-zero */
#endif
} myCryptoDevCtx;
#ifdef WOLF_CRYPTO_CB_ONLY_RSA
@ -85119,6 +85123,17 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
#endif /* WOLFSSL_CMAC && !(NO_AES) && WOLFSSL_AES_DIRECT */
else if (info->algo_type == WC_ALGO_TYPE_KDF) {
#if defined(HAVE_HKDF) && !defined(NO_HMAC)
/* Simulate a device that queues the request and completes it on a
* later call, so the caller has to poll. */
if (myCtx->hkdfPendArm > 0 &&
(info->kdf.type == WC_KDF_TYPE_HKDF ||
info->kdf.type == WC_KDF_TYPE_HKDF_EXTRACT ||
info->kdf.type == WC_KDF_TYPE_HKDF_EXPAND)) {
myCtx->hkdfPendArm--;
myCtx->hkdfPendCount++;
return WC_PENDING_E;
}
if (info->kdf.type == WC_KDF_TYPE_HKDF) {
/* Redirect to software implementation for testing */
#if !defined(HAVE_SELFTEST) && \
@ -85432,6 +85447,122 @@ static wc_test_ret_t shake_cb_copy_free_test(myCryptoDevCtx* myCtx,
}
#endif /* WOLFSSL_SHA3 && SHAKE && (CB_COPY || CB_FREE) */
#if defined(HAVE_HKDF) && !defined(NO_HMAC) && \
!defined(NO_SHA256) && !defined(HAVE_SELFTEST) && \
(!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0)) && \
!defined(WC_TEST_NO_CRYPTOCB_SW_TEST)
/* Bound retries so a broken contract fails instead of spinning. */
#define HKDF_CB_MAX_POLL 16
/* Drive the HKDF crypto callbacks against a device that pends first: the
* caller re-invokes with identical arguments until WC_PENDING_E clears.
* Vectors are RFC 5869 appendix A.1 (test case 1, SHA-256). */
static wc_test_ret_t hkdf_cryptocb_async_test(myCryptoDevCtx* ctx)
{
wc_test_ret_t ret = 0;
int rc;
int polls;
byte prk[WC_SHA256_DIGEST_SIZE];
byte okm[42];
WOLFSSL_SMALL_STACK_STATIC const byte ikm[22] = {
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b };
WOLFSSL_SMALL_STACK_STATIC const byte salt[13] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c };
WOLFSSL_SMALL_STACK_STATIC const byte info[10] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9 };
WOLFSSL_SMALL_STACK_STATIC const byte expectedPrk[WC_SHA256_DIGEST_SIZE]
= {
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5 };
WOLFSSL_SMALL_STACK_STATIC const byte expected[42] = {
0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a,
0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a,
0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c,
0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf,
0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18,
0x58, 0x65 };
/* Three pends, so four passes: proves the caller loops, not retries
* exactly once. */
ctx->hkdfPendArm = 3;
ctx->hkdfPendCount = 0;
polls = 0;
do {
rc = wc_HKDF_Extract_ex(WC_SHA256, salt, (word32)sizeof(salt),
ikm, (word32)sizeof(ikm), prk,
HEAP_HINT, devId);
polls++;
} while (rc == WC_NO_ERR_TRACE(WC_PENDING_E) && polls < HKDF_CB_MAX_POLL);
if (rc != 0)
ret = WC_TEST_RET_ENC_EC(rc);
else if (polls != 4)
ret = WC_TEST_RET_ENC_NC;
else if (XMEMCMP(prk, expectedPrk, sizeof(prk)) != 0)
ret = WC_TEST_RET_ENC_NC;
if (ret != 0)
goto exit_hkdf_async;
/* Expand the PRK, pending three times as well. */
ctx->hkdfPendArm = 3;
ctx->hkdfPendCount = 0;
polls = 0;
do {
rc = wc_HKDF_Expand_ex(WC_SHA256, prk, (word32)sizeof(prk),
info, (word32)sizeof(info), okm,
(word32)sizeof(okm), HEAP_HINT, devId);
polls++;
} while (rc == WC_NO_ERR_TRACE(WC_PENDING_E) && polls < HKDF_CB_MAX_POLL);
if (rc != 0)
ret = WC_TEST_RET_ENC_EC(rc);
else if (polls != 4)
ret = WC_TEST_RET_ENC_NC;
else if (XMEMCMP(okm, expected, sizeof(okm)) != 0)
ret = WC_TEST_RET_ENC_NC;
if (ret != 0)
goto exit_hkdf_async;
/* Same vector through the one-shot wc_HKDF_ex(). */
XMEMSET(okm, 0, sizeof(okm));
ctx->hkdfPendArm = 1;
ctx->hkdfPendCount = 0;
polls = 0;
do {
rc = wc_HKDF_ex(WC_SHA256, ikm, (word32)sizeof(ikm),
salt, (word32)sizeof(salt),
info, (word32)sizeof(info),
okm, (word32)sizeof(okm), HEAP_HINT, devId);
polls++;
} while (rc == WC_NO_ERR_TRACE(WC_PENDING_E) && polls < HKDF_CB_MAX_POLL);
if (rc != 0)
ret = WC_TEST_RET_ENC_EC(rc);
else if (polls != 2)
ret = WC_TEST_RET_ENC_NC;
else if (XMEMCMP(okm, expected, sizeof(okm)) != 0)
ret = WC_TEST_RET_ENC_NC;
/* Counter is reset per leg, so an earlier leg cannot satisfy this. */
else if (ctx->hkdfPendCount == 0)
ret = WC_TEST_RET_ENC_NC;
exit_hkdf_async:
/* Disarm on every path, or a failing leg would leave the simulated
* device injecting WC_PENDING_E into later HKDF requests. */
ctx->hkdfPendArm = 0;
return ret;
}
#undef HKDF_CB_MAX_POLL
#endif /* HAVE_HKDF && !NO_HMAC && !NO_SHA256 && !HAVE_SELFTEST && ... */
#if !defined(WC_TEST_NO_CRYPTOCB_SW_TEST)
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void)
{
@ -85465,6 +85596,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void)
#if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD)
myCtx.rsaPssVerifyCount = 0;
#endif
#if defined(HAVE_HKDF) && !defined(NO_HMAC)
/* myCtx is uninitialized stack: a garbage arm would inject
* WC_PENDING_E into callers that are not polling. */
myCtx.hkdfPendArm = 0;
myCtx.hkdfPendCount = 0;
#endif
/* set devId to something other than INVALID_DEVID */
devId = 1;
@ -85945,6 +86082,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void)
#if defined(HAVE_HKDF) && !defined(NO_HMAC)
if (ret == 0)
ret = hkdf_test();
#if !defined(NO_SHA256) && !defined(HAVE_SELFTEST) && \
(!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0))
if (ret == 0)
ret = hkdf_cryptocb_async_test(&myCtx);
#endif
#endif
#if defined(HAVE_CMAC_KDF)
if (ret == 0)

View File

@ -2373,6 +2373,19 @@ WOLFSSL_LOCAL int ChachaAEADDecrypt(WOLFSSL* ssl, byte* plain, const byte* input
#ifdef WOLFSSL_TLS13
WOLFSSL_LOCAL int DecryptTls13(WOLFSSL* ssl, byte* output, const byte* input,
word16 sz, const byte* aad, word16 aadSz);
WOLFSSL_LOCAL int DoTls13MsgDerives(WOLFSSL* ssl, byte type);
/* A crypto/PK callback pending is finished by re-invoking the provider:
* wolfSSL_AsyncPoll() never runs a callback. Exported so tests compile in
* only where a callback pend is resumable. */
#if defined(WOLFSSL_ASYNC_CRYPT) && \
(defined(WOLF_CRYPTO_CB) || defined(HAVE_PK_CALLBACKS)) && \
!defined(WOLFSSL_ASYNC_CRYPT_SW) && !defined(HAVE_INTEL_QA) && \
!defined(HAVE_CAVIUM)
#define WOLFSSL_ASYNC_REINVOKE
#endif
#if defined(WOLFSSL_ASYNC_REINVOKE) && !defined(NO_HMAC)
WOLFSSL_LOCAL void Tls13FreeHsHmac(WOLFSSL* ssl);
#endif
WOLFSSL_LOCAL int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input,
word32* inOutIdx, byte type,
word32 size, word32 totalSz);
@ -3959,6 +3972,12 @@ typedef struct KeyShareEntry {
#endif
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
word16 session; /* NamedGroup that was in session */
#endif
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) || \
defined(WOLFSSL_ASYNC_CRYPT)
/* Also under WOLFSSL_ASYNC_CRYPT: a pending operation retried on the
* same accept state re-enters the derive with the peer key freed, and
* this is the marker that stops the re-derive. */
word16 derived; /* preMaster has been derived */
#endif
#ifdef WOLFSSL_ASYNC_CRYPT
@ -3978,7 +3997,7 @@ WOLFSSL_LOCAL int TLSX_KeyShare_Choose(const WOLFSSL *ssl, TLSX* extensions,
byte* searched);
WOLFSSL_LOCAL int TLSX_KeyShare_Setup(WOLFSSL *ssl, KeyShareEntry* clientKSE);
WOLFSSL_LOCAL int TLSX_KeyShare_Establish(WOLFSSL* ssl, int* doHelloRetry);
WOLFSSL_LOCAL int TLSX_KeyShare_DeriveSecret(WOLFSSL* sclientKSEclientKSEsl);
WOLFSSL_LOCAL int TLSX_KeyShare_DeriveSecret(WOLFSSL* ssl);
WOLFSSL_LOCAL int TLSX_KeyShare_Parse(WOLFSSL* ssl, const byte* input,
word16 length, byte msgType);
WOLFSSL_LOCAL int TLSX_KeyShare_Parse_ClientHello(const WOLFSSL* ssl,
@ -5577,6 +5596,19 @@ struct Options {
#ifdef WOLFSSL_ASYNC_CRYPT
word16 buildArgsSet:1; /* buildArgs are set and need to
* be free'd */
#ifdef WOLFSSL_TLS13
word16 buildArgs13Set:1; /* a TLS 1.3 record build is in
* progress and must resume,
* not restart */
word16 chHashInput:1; /* current ClientHello already
* hashed into the transcript;
* a PSK-binder pend must not
* re-hash it on resume */
word16 asyncReplayMsg:1; /* the next TLS 1.3 handshake
* message is a replay of one
* whose handler pended; skip
* its sanity check once */
#endif
#endif
#ifdef WOLFSSL_DTLS13
word16 dtls13SendMoreAcks:1; /* Send more acks during the
@ -6176,8 +6208,8 @@ typedef struct HS_Hashes {
} HS_Hashes;
#ifndef WOLFSSL_NO_TLS12
/* Persistable BuildMessage arguments */
#if !defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13)
/* Persistable BuildMessage/BuildTls13Message arguments */
typedef struct BuildMsgArgs {
word32 digestSz;
word32 sz;
@ -6197,8 +6229,11 @@ typedef struct BuildMsgArgs {
typedef void (*FreeArgsCb)(struct WOLFSSL* ssl, void* pArgs);
struct WOLFSSL_ASYNC {
#if defined(WOLFSSL_ASYNC_CRYPT) && !defined(WOLFSSL_NO_TLS12)
BuildMsgArgs buildArgs; /* holder for current BuildMessage args */
#if defined(WOLFSSL_ASYNC_CRYPT) && \
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
/* Record builder resume args, shared by BuildMessage() and
* BuildTls13Message(): a connection runs only one of them. */
BuildMsgArgs buildArgs;
#endif
FreeArgsCb freeArgs; /* function pointer to cleanup args */
#ifdef WC_NO_PTR_INT_CAST
@ -6447,6 +6482,56 @@ enum ConnectionIdUsage {
(ssl)->ctx->suites))
/* wolfSSL ssl type */
/* TLS 1.3 key-schedule resume steps (kdfMsgStep/kdfDeriveStep). A value is
* recorded after its named operation completes ("step <= X" = X not done);
* 0 = sequence not entered or finished. Values repeat across sequences. */
/* Receive side (kdfMsgStep), driven by DoTls13MsgDerives(). */
enum Tls13KdfMsgStep {
TLS13_MSG_KDF_NONE = 0,
/* client processing server_hello */
TLS13_MSG_KDF_SH_ENTERED = 1,
TLS13_MSG_KDF_SH_EARLY_SECRET = 2,
TLS13_MSG_KDF_SH_HS_SECRET = 3,
TLS13_MSG_KDF_SH_HS_KEYS = 4,
TLS13_MSG_KDF_SH_KEYS_SET = 5,
TLS13_MSG_KDF_SH_DTLS_EPOCH = 6,
/* client processing finished */
TLS13_MSG_KDF_FIN_ENTERED = 1,
TLS13_MSG_KDF_FIN_MASTER_SECRET = 2,
TLS13_MSG_KDF_FIN_QUIC_EARLY_KEYS = 3,
TLS13_MSG_KDF_FIN_TRAFFIC_KEYS = 4,
TLS13_MSG_KDF_FIN_TRAFFIC_DONE = 5,
TLS13_MSG_KDF_FIN_KEYS_SET = 6,
/* server processing finished (resumption secret for tickets) */
TLS13_MSG_KDF_SFIN_ENTERED = 1,
TLS13_MSG_KDF_SFIN_RESUMPTION_SECRET = 2
};
/* Send side (kdfDeriveStep), inside the senders themselves. */
enum Tls13KdfSendStep {
TLS13_SEND_KDF_NONE = 0,
/* SendTls13EncryptedExtensions() */
TLS13_SEND_KDF_EE_HS_SECRET = 1,
TLS13_SEND_KDF_EE_HS_KEYS = 2,
TLS13_SEND_KDF_EE_ENC_KEYS_SET = 3,
TLS13_SEND_KDF_EE_KEYS_SET = 4,
TLS13_SEND_KDF_EE_DTLS_EPOCH = 5,
/* SendTls13Finished() */
TLS13_SEND_KDF_FIN_ENTERED = 1,
TLS13_SEND_KDF_FIN_MASTER_SECRET = 2,
TLS13_SEND_KDF_FIN_ENC_TRAFFIC_KEYS = 3,
TLS13_SEND_KDF_FIN_TRAFFIC_KEYS = 4,
TLS13_SEND_KDF_FIN_ENC_KEYS_SET = 5,
TLS13_SEND_KDF_FIN_DTLS_TRAFFIC_EPOCH = 6,
TLS13_SEND_KDF_FIN_EARLY_ENC_KEYS = 7,
TLS13_SEND_KDF_FIN_EARLY_KEYS_SET = 8,
TLS13_SEND_KDF_FIN_RESUMPTION_SECRET = 9,
TLS13_SEND_KDF_FIN_DTLS_EPOCH_SET = 10
};
struct WOLFSSL {
WOLFSSL_CTX* ctx;
#if defined(WOLFSSL_HAPROXY)
@ -7028,6 +7113,27 @@ struct WOLFSSL {
* ciphers; 0 means uncached and is never a valid AEAD overhead. EtM does
* not apply to AEAD. */
word32 recordSzOverhead;
#ifdef WOLFSSL_ASYNC_CRYPT
/* Async device for the TLS 1.3 key schedule: HKDF has no key object
* to carry one. Event bookkeeping only, for the callback re-invoke
* path (never wolfAsync_DevCtxInit'd, no hardware context). */
WC_ASYNC_DEV kdfAsyncDev;
#endif
/* Key-schedule resume steps: completed derives must not re-run (e.g.
* the extract is in place over preMasterSecret). Unconditional so the
* schedule needs no ifdefs; without async they stay 0. */
byte kdfDeriveStep; /* enum Tls13KdfSendStep (send side) */
byte kdfMsgStep; /* enum Tls13KdfMsgStep (receive side) */
byte kdfMsgType; /* handshake type kdfMsgStep belongs to */
#if defined(WOLFSSL_ASYNC_REINVOKE) && defined(WOLFSSL_TLS13) && \
!defined(NO_HMAC)
/* Transcript HMAC (Finished verify_data, PSK binders) held across a
* WC_PENDING_E so the retry re-invokes the same object and arguments,
* bound to its output buffer. */
Hmac* hsHmac;
byte* hsHmacOut;
byte hsHmacStep;
#endif
};
#if defined(WOLFSSL_SYS_CRYPTO_POLICY)

View File

@ -4056,9 +4056,8 @@
#error WOLF_CRYPTO_CB_ASYNC_POLL requires bulk cipher async support
#endif
/* Crypto callbacks are the only async backend and cannot finish a pending
* bulk cipher op: the record layer has already advanced past the crypto
* call, and without poll routing nothing refills the output buffer. */
/* Callback-only async cannot finish a pending TLS 1.2 bulk cipher op
* (TLS 1.3 resumes them by re-invoking the callback). */
#if defined(WOLF_CRYPTO_CB) && !defined(WOLF_CRYPTO_CB_ASYNC_POLL) && \
!defined(WOLFSSL_ASYNC_CRYPT_SW) && !defined(HAVE_INTEL_QA) && \
!defined(HAVE_CAVIUM)