Skip to content

Commit 9d46b57

Browse files
authored
Merge pull request #10246 from sameehj/aes-gcm-fix
Zero TLS 1.3 traffic keys after AES SE offload
2 parents b573823 + ba51fbd commit 9d46b57

4 files changed

Lines changed: 560 additions & 59 deletions

File tree

ChangeLog.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# wolfSSL Release (unreleased)
2+
3+
## Enhancements
4+
5+
* TLS 1.3: zero traffic key staging buffers in `SetKeysSide()` once a
6+
CryptoCB callback has imported the AES key into a Secure Element
7+
(`aes->devCtx != NULL`). Clears `keys->{client,server}_write_key`
8+
on the provisioned side(s) after cipher init succeeds. The static
9+
IV buffers (`keys->{client,server}_write_IV`,
10+
`keys->aead_{enc,dec}_imp_IV`) are intentionally left intact because
11+
`BuildTls13Nonce()` reads them on every AEAD record to construct the
12+
per-record nonce. Scoped to TLS 1.3, non-DTLS, non-QUIC; requires
13+
`WOLF_CRYPTO_CB` and `WOLF_CRYPTO_CB_AES_SETKEY`.
14+
115
# wolfSSL Release 5.9.1 (Apr. 8, 2026)
216

317
Release 5.9.1 has been developed according to wolfSSL's development and QA

src/keys.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3592,6 +3592,75 @@ int SetKeysSide(WOLFSSL* ssl, enum encrypt_side side)
35923592
ssl->heap, ssl->devId, ssl->rng, ssl->options.tls1_3);
35933593
}
35943594

3595+
/* Zero the TLS-layer staging key buffers once the CryptoCB callback
3596+
* has imported the key into a Secure Element.
3597+
*
3598+
* Convention: after a successful wc_AesSetKey / wc_AesGcmSetKey where
3599+
* the CryptoCB handled the key import, the callback leaves
3600+
* aes->devCtx != NULL and the software key schedule (aes->key,
3601+
* aes->devKey, aes->gcm.H / aes->gcm.M0) is NOT populated. The TLS
3602+
* layer may therefore destroy its staging copy of the traffic key.
3603+
*
3604+
* Only the key buffers (client_write_key / server_write_key) are
3605+
* zeroed. The static IVs (client_write_IV / server_write_IV) and
3606+
* the AEAD implicit-IV copies (aead_{enc,dec}_imp_IV) are NOT
3607+
* zeroed: BuildTls13Nonce() in tls13.c reads keys->aead_*_imp_IV on
3608+
* every AEAD record to construct the per-record nonce
3609+
* (nonce = static_iv XOR seq_num, RFC 8446 Section 5.3). Zeroing
3610+
* them would break the record path or, if applied symmetrically on
3611+
* both peers, silently degenerate the nonce to the bare sequence
3612+
* number and break interop with any unpatched peer. The static_iv
3613+
* is not a confidentiality-critical secret in the same sense as
3614+
* the traffic key; losing it does not compromise plaintext.
3615+
*
3616+
* Scope:
3617+
* - TLS 1.3 only. TLS 1.2 additionally reads
3618+
* keys->{client,server}_write_key for rehandshake/secure
3619+
* renegotiation flows.
3620+
* - Non-DTLS. Dtls13EpochCopyKeys (called from Dtls13NewEpoch)
3621+
* references keys->*_write_key for epoch switching; DTLS 1.3
3622+
* needs separate analysis.
3623+
* - Non-QUIC. QUIC traffic secrets live outside these buffers
3624+
* but the interaction with stack-installed QUIC handlers has
3625+
* not been audited; exclude until it is.
3626+
*
3627+
* When called with ENCRYPT_SIDE_ONLY or DECRYPT_SIDE_ONLY, only the
3628+
* buffer consumed by this call is zeroed; the complementary buffer
3629+
* is written in a later SetKeysSide() from its own DeriveTls13Keys()
3630+
* and StoreKeys() pair (StoreKeys gates on PROVISION_CLIENT /
3631+
* PROVISION_SERVER so only the provisioned side is written).
3632+
*
3633+
* Ordering: this block must run AFTER SetKeys() (so offload has
3634+
* happened) and BEFORE Dtls13SetRecordNumberKeys() /
3635+
* wolfSSL_quic_keys_active() below, in case a future refactor in
3636+
* either starts reading keys->*_write_key. The DTLS and QUIC gates
3637+
* in this block mean neither currently executes on the same ssl,
3638+
* but keep the order explicit. */
3639+
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_AES_SETKEY)
3640+
if (ret == 0 && ssl->options.tls1_3 && !ssl->options.dtls
3641+
&& !WOLFSSL_IS_QUIC(ssl)) {
3642+
int encOffloaded = (wc_encrypt != NULL && wc_encrypt->aes != NULL &&
3643+
wc_encrypt->aes->devCtx != NULL);
3644+
int decOffloaded = (wc_decrypt != NULL && wc_decrypt->aes != NULL &&
3645+
wc_decrypt->aes->devCtx != NULL);
3646+
3647+
if (encOffloaded || decOffloaded) {
3648+
if (ssl->options.side == WOLFSSL_CLIENT_END) {
3649+
if (encOffloaded)
3650+
ForceZero(keys->client_write_key, ssl->specs.key_size);
3651+
if (decOffloaded)
3652+
ForceZero(keys->server_write_key, ssl->specs.key_size);
3653+
}
3654+
else {
3655+
if (encOffloaded)
3656+
ForceZero(keys->server_write_key, ssl->specs.key_size);
3657+
if (decOffloaded)
3658+
ForceZero(keys->client_write_key, ssl->specs.key_size);
3659+
}
3660+
}
3661+
}
3662+
#endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_AES_SETKEY */
3663+
35953664
#ifdef WOLFSSL_DTLS13
35963665
if (ret == 0 && ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version))
35973666
ret = Dtls13SetRecordNumberKeys(ssl, side);

0 commit comments

Comments
 (0)