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
14 changes: 14 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# wolfSSL Release (unreleased)

## Enhancements

* TLS 1.3: zero traffic key staging buffers in `SetKeysSide()` once a
CryptoCB callback has imported the AES key into a Secure Element
(`aes->devCtx != NULL`). Clears `keys->{client,server}_write_key`
on the provisioned side(s) after cipher init succeeds. The static
IV buffers (`keys->{client,server}_write_IV`,
`keys->aead_{enc,dec}_imp_IV`) are intentionally left intact because
`BuildTls13Nonce()` reads them on every AEAD record to construct the
per-record nonce. Scoped to TLS 1.3, non-DTLS, non-QUIC; requires
`WOLF_CRYPTO_CB` and `WOLF_CRYPTO_CB_AES_SETKEY`.

# wolfSSL Release 5.9.1 (Apr. 8, 2026)

Release 5.9.1 has been developed according to wolfSSL's development and QA
Expand Down
69 changes: 69 additions & 0 deletions src/keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -3591,6 +3591,75 @@ int SetKeysSide(WOLFSSL* ssl, enum encrypt_side side)
ssl->heap, ssl->devId, ssl->rng, ssl->options.tls1_3);
}

/* Zero the TLS-layer staging key buffers once the CryptoCB callback
* has imported the key into a Secure Element.
*
* Convention: after a successful wc_AesSetKey / wc_AesGcmSetKey where
* the CryptoCB handled the key import, the callback leaves
* aes->devCtx != NULL and the software key schedule (aes->key,
* aes->devKey, aes->gcm.H / aes->gcm.M0) is NOT populated. The TLS
* layer may therefore destroy its staging copy of the traffic key.
*
* Only the key buffers (client_write_key / server_write_key) are
* zeroed. The static IVs (client_write_IV / server_write_IV) and
* the AEAD implicit-IV copies (aead_{enc,dec}_imp_IV) are NOT
* zeroed: BuildTls13Nonce() in tls13.c reads keys->aead_*_imp_IV on
* every AEAD record to construct the per-record nonce
* (nonce = static_iv XOR seq_num, RFC 8446 Section 5.3). Zeroing
* them would break the record path or, if applied symmetrically on
* both peers, silently degenerate the nonce to the bare sequence
* number and break interop with any unpatched peer. The static_iv
* is not a confidentiality-critical secret in the same sense as
* the traffic key; losing it does not compromise plaintext.
*
* Scope:
* - TLS 1.3 only. TLS 1.2 additionally reads
* keys->{client,server}_write_key for rehandshake/secure
* renegotiation flows.
* - Non-DTLS. Dtls13EpochCopyKeys (called from Dtls13NewEpoch)
* references keys->*_write_key for epoch switching; DTLS 1.3
* needs separate analysis.
* - Non-QUIC. QUIC traffic secrets live outside these buffers
* but the interaction with stack-installed QUIC handlers has
* not been audited; exclude until it is.
*
* When called with ENCRYPT_SIDE_ONLY or DECRYPT_SIDE_ONLY, only the
* buffer consumed by this call is zeroed; the complementary buffer
* is written in a later SetKeysSide() from its own DeriveTls13Keys()
* and StoreKeys() pair (StoreKeys gates on PROVISION_CLIENT /
* PROVISION_SERVER so only the provisioned side is written).
*
* Ordering: this block must run AFTER SetKeys() (so offload has
* happened) and BEFORE Dtls13SetRecordNumberKeys() /
* wolfSSL_quic_keys_active() below, in case a future refactor in
* either starts reading keys->*_write_key. The DTLS and QUIC gates
* in this block mean neither currently executes on the same ssl,
* but keep the order explicit. */
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_AES_SETKEY)
if (ret == 0 && ssl->options.tls1_3 && !ssl->options.dtls
&& !WOLFSSL_IS_QUIC(ssl)) {
int encOffloaded = (wc_encrypt != NULL && wc_encrypt->aes != NULL &&
wc_encrypt->aes->devCtx != NULL);
int decOffloaded = (wc_decrypt != NULL && wc_decrypt->aes != NULL &&
wc_decrypt->aes->devCtx != NULL);

if (encOffloaded || decOffloaded) {
if (ssl->options.side == WOLFSSL_CLIENT_END) {
if (encOffloaded)
ForceZero(keys->client_write_key, ssl->specs.key_size);
if (decOffloaded)
ForceZero(keys->server_write_key, ssl->specs.key_size);
}
else {
if (encOffloaded)
ForceZero(keys->server_write_key, ssl->specs.key_size);
if (decOffloaded)
ForceZero(keys->client_write_key, ssl->specs.key_size);
}
}
}
#endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_AES_SETKEY */

#ifdef WOLFSSL_DTLS13
if (ret == 0 && ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version))
ret = Dtls13SetRecordNumberKeys(ssl, side);
Expand Down
Loading
Loading