cloudflare / cloudflare/quiche

CryptoFail due to Invalid Nonce, caused by Quiche AEAD algorithm selection

Open
#2,411 3 comments 2 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.