cloudflare / cloudflare/quiche
CryptoFail due to Invalid Nonce, caused by Quiche AEAD algorithm selection
- Dominant language
- Rust
- Stars
- 11.8k
- Forks
- 1.1k
- Avg merge
- 21h 9m
- Merged PRs (30d)
- 6
Description
As part of the contributing guidelines I am creating this issue to start the conversation before pushing a PR with the fix, which I will include the fix in this issue as well.
I also looked for other conversations, I found this one from 2020, with no response: https://github.com/cloudflare/quiche/issues/524
In our QUIC stack, connections would complete handshake but then fail on early 1-RTT sends because quiche’s BoringSSL backend selected TLS13-specific AES-GCM AEAD entrypoints (EVP_aead_aes_*_gcm_tls13), which on the resolved boring backend rejected that runtime nonce/counter usage with INVALID_NONCE; this surfaced from quiche as CryptoFail, and in tokio-quiche appeared as send_on_path failure followed by local transport close code 1, causing reconnect churn. Switching quiche’s AES-GCM selection to the generic AEAD entrypoints (EVP_aead_aes_*_gcm) resolved the nonce failure and restored stable connections.
Findings:
- In tokio-quiche, that failure is observed in the worker send path at qconn.send_on_path(...), then it closes with transport InternalError (1) if no local error is already set , see source here:
https://github.com/cloudflare/quiche/blob/b30f9e76c32332aa35377dcb00f556626d47a841/tokio-quiche/src/quic/io/worker.rs#L562-L614
- The send path calls into quiche packet encryption (encrypt_pkt -> seal_with_u64_counter):
https://github.com/cloudflare/quiche/blob/b30f9e76c32332aa35377dcb00f556626d47a841/quiche/src/packet.rs#L711-L725
- In quiche’s BoringSSL backend, AEAD algorithm selection uses TLS13-specific AES-GCM entrypoints:
https://github.com/cloudflare/quiche/blob/b30f9e76c32332aa35377dcb00f556626d47a841/quiche/src/crypto/boringssl.rs#L23-L31
- The actual crypto failure is emitted from the seal call (EVP_AEAD_CTX_seal_scatter) which maps to CryptoFail on non-success:
https://github.com/cloudflare/quiche/blob/b30f9e76c32332aa35377dcb00f556626d47a841/quiche/src/crypto/boringssl.rs#L96-L140
I have tested this patch locally and it works, however, it could have some potential impacts on decisions later.
The fix is as follows:
https://github.com/cloudflare/quiche/blob/b30f9e76c32332aa35377dcb00f556626d47a841/quiche/src/crypto/boringssl.rs#L29-L40
Changes to:
```
`impl Algorithm {
fn get_evp_aead(self) -> *const EVP_AEAD {
match self {
Algorithm::AES128_GCM => unsafe { EVP_aead_aes_128_gcm() },
Algorithm::AES256_GCM => unsafe { EVP_aead_aes_256_gcm() },
Algorithm::ChaCha20_Poly1305 => unsafe {
EVP_aead_chacha20_poly1305()
},
}
}
}`
```
And:
https://github.com/cloudflare/quiche/blob/b30f9e76c32332aa35377dcb00f556626d47a841/quiche/src/crypto/boringssl.rs#L323-L328
Changes to:
```
`extern "C" {
fn EVP_aead_aes_128_gcm() -> *const EVP_AEAD;
fn EVP_aead_aes_256_gcm() -> *const EVP_AEAD;
fn EVP_aead_chacha20_poly1305() -> *const EVP_AEAD;`
```
PR: https://github.com/cloudflare/quiche/pull/2412
Contributor guide
Research direction
Start in quiche/src/crypto/boringssl.rs, then trace encryption through quiche/src/packet.rs and the send path in tokio-quiche/src/quic/io/worker.rs. Review the referenced AEAD selection and seal call, and validate that early 1-RTT sends no longer produce INVALID_NONCE or CryptoFail and that connections remain stable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cryptography, networking
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 32/100