Skip to content
Closed
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
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ pin-project-lite = "0.2"
#
rustls-pki-types = { version = "1", default-features = false }
rustls = { version = "0.23.40", default-features = false, features = ["std", "tls12", "brotli"] }
ring = "0.17.14"
aws-lc-rs = { version = "1.18.1", default-features = false }
tokio-rustls = { version = "0.26", default-features = false, features = ["tls12"] }
quinn = { version = "0.11", default-features = false, features = ["runtime-tokio"] }
quinn-proto = { version = "0.11", default-features = false }
Expand Down
5 changes: 3 additions & 2 deletions lib/vey-daemon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ tokio = { workspace = true, features = ["net", "io-util", "signal", "macros"] }
tokio-util = { workspace = true, features = ["compat"] }
http = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }
quinn = { workspace = true, optional = true, features = ["runtime-tokio", "ring"] }
quinn = { workspace = true, optional = true, features = ["runtime-tokio"] }
quinn-proto = { workspace = true, optional = true }
snmalloc = { workspace = true, optional = true }
vey-compat.workspace = true
vey-types = { workspace = true, features = ["async-log", "acl-rule"] }
vey-rustls-provider = { workspace = true, optional = true }
vey-stdlog.workspace = true
vey-syslog = { workspace = true, features = ["yaml"] }
vey-fluentd = { workspace = true, optional = true, features = ["yaml"] }
Expand Down Expand Up @@ -67,5 +68,5 @@ snmalloc = ["dep:snmalloc"]
ebpf = ["dep:vey-reuseport"]
event-log = ["dep:vey-fluentd"]
register = ["vey-yaml/http", "dep:http", "dep:serde_json", "dep:vey-http"]
quic = ["dep:quinn", "dep:quinn-proto", "vey-types/quinn"]
quic = ["dep:quinn", "dep:quinn-proto", "vey-types/quinn", "dep:vey-rustls-provider", "vey-rustls-provider/quic"]
openssl-async-job = ["vey-runtime/openssl-async-job"]
3 changes: 2 additions & 1 deletion lib/vey-daemon/src/listen/quic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ where
let socket = vey_socket::udp::new_std_bind_listen(&self.listen_config)?;
let listen_addr = socket.local_addr()?;

let mut endpoint_config = EndpointConfig::default();
let mut endpoint_config =
EndpointConfig::new(Arc::new(vey_rustls_provider::ResetKey::new()));
if let Some(payload_size) = self.endpoint_config.udp_payload_size()
&& let Err(e) = endpoint_config.max_udp_payload_size(payload_size)
{
Expand Down
5 changes: 3 additions & 2 deletions lib/vey-hickory-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ tokio = { workspace = true, features = ["net", "time"] }
rustls.workspace = true
rustls-pki-types.workspace = true
tokio-rustls.workspace = true
quinn = { workspace = true, optional = true, features = ["rustls"] }
quinn = { workspace = true, optional = true }
vey-rustls-provider = { workspace = true, optional = true }
h3 = { workspace = true, optional = true }
h3-quinn = { workspace = true, optional = true }
vey-socket.workspace = true

[features]
default = []
quic = ["dep:quinn", "dep:h3", "dep:h3-quinn"]
quic = ["dep:quinn", "dep:h3", "dep:h3-quinn", "dep:vey-rustls-provider", "vey-rustls-provider/quic"]
3 changes: 2 additions & 1 deletion lib/vey-hickory-client/src/connect/quinn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use hickory_net::NetError;
use quinn::crypto::rustls::QuicClientConfig;
use quinn::{Connection, Endpoint, EndpointConfig, TokioRuntime};
use rustls::ClientConfig;
use vey_rustls_provider::ResetKey;

use vey_socket::UdpConnectInfo;

Expand All @@ -21,7 +22,7 @@ pub(crate) async fn quic_connect(
) -> Result<Connection, NetError> {
let sock = connect_info.udp_connect()?;

let endpoint_config = EndpointConfig::default(); // TODO set max payload size
let endpoint_config = EndpointConfig::new(Arc::new(ResetKey::new())); // TODO set max payload size
let mut endpoint = Endpoint::new(endpoint_config, None, sock, Arc::new(TokioRuntime))?;

if tls_config.alpn_protocols.is_empty() {
Expand Down
11 changes: 8 additions & 3 deletions lib/vey-rustls-provider/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ rust-version.workspace = true
[dependencies]
anyhow.workspace = true
rustls.workspace = true
aws-lc-rs = { workspace = true, optional = true }
quinn = { workspace = true, optional = true }
rand = { workspace = true, optional = true }
ring = { workspace = true, optional = true }

[features]
rustls-ring = ["rustls/ring"]
rustls-aws-lc = ["rustls/aws-lc-rs"]
rustls-aws-lc-fips = ["rustls/fips"]
quic = ["dep:quinn", "dep:rand"]
rustls-ring = ["rustls/ring", "dep:ring"]
rustls-aws-lc = ["rustls/aws-lc-rs", "dep:aws-lc-rs", "aws-lc-rs/alloc"]
rustls-aws-lc-fips = ["rustls/fips", "dep:aws-lc-rs", "aws-lc-rs/alloc", "aws-lc-rs/fips"]
5 changes: 5 additions & 0 deletions lib/vey-rustls-provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

use anyhow::anyhow;

#[cfg(feature = "quic")]
mod quic;
#[cfg(feature = "quic")]
pub use quic::{HandshakeTokenKey, ResetKey};

const PROVIDER_NAME: Option<&str> = option_env!("VEY_RUSTLS_PROVIDER");

pub fn install_default() -> anyhow::Result<()> {
Expand Down
25 changes: 25 additions & 0 deletions lib/vey-rustls-provider/src/quic/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: 2026 VEY-OSS Developers.
*/

#[cfg(not(any(
feature = "rustls-ring",
feature = "rustls-aws-lc",
feature = "rustls-aws-lc-fips"
)))]
compile_error!("quic crypto requires rustls-ring, rustls-aws-lc, or rustls-aws-lc-fips");

#[cfg(any(
feature = "rustls-ring",
feature = "rustls-aws-lc",
feature = "rustls-aws-lc-fips"
))]
mod provider;

#[cfg(any(
feature = "rustls-ring",
feature = "rustls-aws-lc",
feature = "rustls-aws-lc-fips"
))]
pub use provider::{HandshakeTokenKey, ResetKey};
117 changes: 117 additions & 0 deletions lib/vey-rustls-provider/src/quic/provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: 2026 VEY-OSS Developers.
*/

use quinn::crypto::{AeadKey, CryptoError, HmacKey};
use rand::Rng;

cfg_select! {
any(feature = "rustls-aws-lc", feature = "rustls-aws-lc-fips") => {
use aws_lc_rs::{aead, hkdf, hmac};
}
feature = "rustls-ring" => {
use ring::{aead, hkdf, hmac};
}
}

/// HMAC-SHA256 key for QUIC stateless reset, from the active crypto library.
pub struct ResetKey(hmac::Key);

impl ResetKey {
pub fn new() -> Self {

Check failure on line 22 in lib/vey-rustls-provider/src/quic/provider.rs

View workflow job for this annotation

GitHub Actions / Clippy

you should consider adding a `Default` implementation for `ResetKey`

Check failure on line 22 in lib/vey-rustls-provider/src/quic/provider.rs

View workflow job for this annotation

GitHub Actions / Build (i686-unknown-linux-gnu, gcc-i686-linux-gnu)

you should consider adding a `Default` implementation for `ResetKey`

Check failure on line 22 in lib/vey-rustls-provider/src/quic/provider.rs

View workflow job for this annotation

GitHub Actions / Build

you should consider adding a `Default` implementation for `ResetKey`

Check failure on line 22 in lib/vey-rustls-provider/src/quic/provider.rs

View workflow job for this annotation

GitHub Actions / Build with OpenSSL Async Job (vey-gateway)

you should consider adding a `Default` implementation for `ResetKey`

Check failure on line 22 in lib/vey-rustls-provider/src/quic/provider.rs

View workflow job for this annotation

GitHub Actions / Build

you should consider adding a `Default` implementation for `ResetKey`
let mut raw = [0u8; 64];
rand::rng().fill_bytes(&mut raw);
Self(hmac::Key::new(hmac::HMAC_SHA256, &raw))
}
}

impl HmacKey for ResetKey {
fn sign(&self, data: &[u8], signature_out: &mut [u8]) {
signature_out.copy_from_slice(hmac::sign(&self.0, data).as_ref());
}

fn signature_len(&self) -> usize {
32
}

fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError> {
hmac::verify(&self.0, data, signature).map_err(|_| CryptoError)
}
}

/// HKDF-SHA256 key for QUIC handshake tokens, from the active crypto library.
pub struct HandshakeTokenKey(hkdf::Prk);

impl HandshakeTokenKey {
pub fn new() -> Self {

Check failure on line 47 in lib/vey-rustls-provider/src/quic/provider.rs

View workflow job for this annotation

GitHub Actions / Clippy

you should consider adding a `Default` implementation for `HandshakeTokenKey`

Check failure on line 47 in lib/vey-rustls-provider/src/quic/provider.rs

View workflow job for this annotation

GitHub Actions / Build (i686-unknown-linux-gnu, gcc-i686-linux-gnu)

you should consider adding a `Default` implementation for `HandshakeTokenKey`

Check failure on line 47 in lib/vey-rustls-provider/src/quic/provider.rs

View workflow job for this annotation

GitHub Actions / Build

you should consider adding a `Default` implementation for `HandshakeTokenKey`

Check failure on line 47 in lib/vey-rustls-provider/src/quic/provider.rs

View workflow job for this annotation

GitHub Actions / Build with OpenSSL Async Job (vey-gateway)

you should consider adding a `Default` implementation for `HandshakeTokenKey`

Check failure on line 47 in lib/vey-rustls-provider/src/quic/provider.rs

View workflow job for this annotation

GitHub Actions / Build

you should consider adding a `Default` implementation for `HandshakeTokenKey`
let mut master_key = [0u8; 64];
rand::rng().fill_bytes(&mut master_key);
Self(hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key))
}
}

impl quinn::crypto::HandshakeTokenKey for HandshakeTokenKey {
fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey> {
let mut key_buffer = [0u8; 32];
let info = [random_bytes];
let okm = self
.0
.expand(&info, hkdf::HKDF_SHA256)
.expect("token key length");
okm.fill(&mut key_buffer).expect("token key expand");

let key = aead::UnboundKey::new(&aead::AES_256_GCM, &key_buffer).expect("AES-256-GCM");
Box::new(TokenAead(aead::LessSafeKey::new(key)))
}
}

struct TokenAead(aead::LessSafeKey);

impl AeadKey for TokenAead {
fn seal(&self, data: &mut Vec<u8>, additional_data: &[u8]) -> Result<(), CryptoError> {
let aad = aead::Aad::from(additional_data);
let zero_nonce = aead::Nonce::assume_unique_for_key([0u8; 12]);
self.0
.seal_in_place_append_tag(zero_nonce, aad, data)
.map_err(|_| CryptoError)
}

fn open<'a>(
&self,
data: &'a mut [u8],
additional_data: &[u8],
) -> Result<&'a mut [u8], CryptoError> {
let aad = aead::Aad::from(additional_data);
let zero_nonce = aead::Nonce::assume_unique_for_key([0u8; 12]);
self.0
.open_in_place(zero_nonce, aad, data)
.map_err(|_| CryptoError)
}
}

#[cfg(test)]
mod tests {
use quinn::crypto::HandshakeTokenKey as _;
use quinn::crypto::HmacKey;

use super::*;

#[test]
fn reset_key_roundtrip() {
let key = ResetKey::new();
let mut signature = [0u8; 32];
key.sign(b"stateless-reset", &mut signature);
key.verify(b"stateless-reset", &signature).unwrap();
assert!(key.verify(b"other", &signature).is_err());
}

#[test]
fn token_aead_roundtrip() {
let key = HandshakeTokenKey::new().aead_from_hkdf(b"random");
let mut sealed = b"token".to_vec();
key.seal(&mut sealed, b"aad").unwrap();
let opened = key.open(&mut sealed, b"aad").unwrap();
assert_eq!(opened, b"token");
}
}
4 changes: 2 additions & 2 deletions vey-bench/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ url.workspace = true
h2.workspace = true
h3 = { workspace = true, optional = true }
h3-quinn = { workspace = true, optional = true }
quinn = { workspace = true, optional = true, features = ["rustls"] }
quinn = { workspace = true, optional = true }
bytes.workspace = true
futures-util.workspace = true
atomic-waker.workspace = true
Expand Down Expand Up @@ -72,7 +72,7 @@ vendored-mimalloc = ["mimalloc", "vey-mimalloc?/vendored"]
secure-vendored-mimalloc = ["mimalloc", "vey-mimalloc?/secure"]
snmalloc = ["dep:snmalloc"]
secure-snmalloc = ["snmalloc", "snmalloc?/check"]
quic = ["vey-types/quic", "vey-socks/quic", "vey-io-ext/quic", "vey-hickory-client/quic", "dep:quinn", "dep:h3", "dep:h3-quinn"]
quic = ["vey-types/quic", "vey-socks/quic", "vey-io-ext/quic", "vey-hickory-client/quic", "vey-rustls-provider/quic", "dep:quinn", "dep:h3", "dep:h3-quinn"]
rustls-ring = ["vey-rustls-provider/rustls-ring", "vey-types/rustls-ring", "quinn?/rustls-ring"]
rustls-aws-lc = ["vey-rustls-provider/rustls-aws-lc", "vey-types/rustls-aws-lc", "quinn?/rustls-aws-lc-rs"]
rustls-aws-lc-fips = ["vey-rustls-provider/rustls-aws-lc-fips", "vey-types/rustls-aws-lc-fips", "quinn?/rustls-aws-lc-rs-fips"]
Expand Down
18 changes: 14 additions & 4 deletions vey-bench/src/module/http/connection/h3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,13 @@ impl H3ConnectArgs {
limit.max_south_bytes,
stats.clone(),
);
Endpoint::new(Default::default(), None, socket, Arc::new(runtime))
.map_err(|e| anyhow!("failed to create quic endpoint: {e}"))
Endpoint::new(
quinn::EndpointConfig::new(Arc::new(vey_rustls_provider::ResetKey::new())),
None,
socket,
Arc::new(runtime),
)
.map_err(|e| anyhow!("failed to create quic endpoint: {e}"))
} else {
let socket = self.socket.udp_std_socket_to(quic_peer)?;
socket
Expand All @@ -151,8 +156,13 @@ impl H3ConnectArgs {
limit.max_south_bytes,
stats.clone(),
);
Endpoint::new(Default::default(), None, socket, Arc::new(runtime))
.map_err(|e| anyhow!("failed to create quic endpoint: {e}"))
Endpoint::new(
quinn::EndpointConfig::new(Arc::new(vey_rustls_provider::ResetKey::new())),
None,
socket,
Arc::new(runtime),
)
.map_err(|e| anyhow!("failed to create quic endpoint: {e}"))
}
}

Expand Down
2 changes: 1 addition & 1 deletion vey-gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ openssl-sys.workspace = true # for openssl variant detection
openssl-probe = { workspace = true, optional = true }
rustls.workspace = true
rustls-pki-types.workspace = true
quinn = { workspace = true, optional = true, features = ["rustls"] }
quinn = { workspace = true, optional = true }
tokio-rustls.workspace = true
jiff.workspace = true
uuid.workspace = true
Expand Down
9 changes: 7 additions & 2 deletions vey-gateway/src/backend/keyless_quic/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,13 @@ impl KeylessQuicUpstreamConnector {
.connect(peer)
.map_err(|e| anyhow!("failed to connect local udp socket to {peer}: {e}"))?;

let endpoint = Endpoint::new(Default::default(), None, socket, Arc::new(TokioRuntime))
.map_err(|e| anyhow!("failed to create quic endpoint: {e}"))?;
let endpoint = Endpoint::new(
quinn::EndpointConfig::new(Arc::new(vey_rustls_provider::ResetKey::new())),
None,
socket,
Arc::new(TokioRuntime),
)
.map_err(|e| anyhow!("failed to create quic endpoint: {e}"))?;

let mut client_config = ClientConfig::new(self.tls_client.driver.clone());
client_config.transport_config(self.quic_transport.clone());
Expand Down
5 changes: 4 additions & 1 deletion vey-gateway/src/serve/plain_quic_port/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ impl PlainQuicPort {
let tls_server = config
.tls_server
.build_quic_with_ticketer(tls_rolling_ticketer.clone())?;
Ok(quinn::ServerConfig::with_crypto(tls_server.driver))
Ok(quinn::ServerConfig::new(
tls_server.driver,
Arc::new(vey_rustls_provider::HandshakeTokenKey::new()),
))
}

pub(crate) fn prepare_initial(
Expand Down
2 changes: 1 addition & 1 deletion vey-proxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ zeroize.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread", "rt", "signal", "sync", "time", "io-util", "net", "fs"] }
tokio-rustls.workspace = true
rustls.workspace = true
quinn = { workspace = true, optional = true, features = ["rustls"] }
quinn = { workspace = true, optional = true }
openssl.workspace = true
openssl-sys.workspace = true # for openssl variant detection
openssl-probe = { workspace = true, optional = true }
Expand Down
9 changes: 7 additions & 2 deletions vey-proxy/src/audit/detour/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,13 @@ impl StreamDetourConnector {
.connect(peer)
.map_err(|e| anyhow!("failed to connect local udp socket to {peer}: {e}"))?;

let endpoint = Endpoint::new(Default::default(), None, socket, Arc::new(TokioRuntime))
.map_err(|e| anyhow!("failed to create quic endpoint: {e}"))?;
let endpoint = Endpoint::new(
quinn::EndpointConfig::new(Arc::new(vey_rustls_provider::ResetKey::new())),
None,
socket,
Arc::new(TokioRuntime),
)
.map_err(|e| anyhow!("failed to create quic endpoint: {e}"))?;

let mut client_config = ClientConfig::new(self.tls_client.driver.clone());
client_config.transport_config(self.quic_transport.clone());
Expand Down
5 changes: 4 additions & 1 deletion vey-proxy/src/serve/plain_quic_port/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ impl PlainQuicPort {
let tls_server = config
.tls_server
.build_quic_with_ticketer(tls_rolling_ticketer.clone())?;
Ok(quinn::ServerConfig::with_crypto(tls_server.driver))
Ok(quinn::ServerConfig::new(
tls_server.driver,
Arc::new(vey_rustls_provider::HandshakeTokenKey::new()),
))
}

pub(crate) fn prepare_initial(
Expand Down
Loading