From 35b463cf2bec23e4e302df6e8ed04838c20c2a1a Mon Sep 17 00:00:00 2001 From: stormshield-gt Date: Wed, 27 Nov 2024 20:06:23 +0100 Subject: [PATCH] make post quantum test run in the CI --- .github/workflows/rust.yml | 6 +- Cargo.toml | 1 + quinn/Cargo.toml | 5 ++ quinn/tests/many_connections.rs | 10 ++- quinn/tests/post_quantum.rs | 123 ++++++++++++++++++++++++++++++++ 5 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 quinn/tests/post_quantum.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 552fae963..5d44effa6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -133,10 +133,10 @@ jobs: - uses: Swatinem/rust-cache@v2 # Prevent feature unification from selecting *ring* as the crypto provider - run: RUST_BACKTRACE=1 cargo test --manifest-path quinn-proto/Cargo.toml --no-default-features --features rustls-aws-lc-rs - - run: RUST_BACKTRACE=1 cargo test --manifest-path quinn/Cargo.toml --no-default-features --features rustls-aws-lc-rs,runtime-tokio + - run: RUST_BACKTRACE=1 cargo test --manifest-path quinn/Cargo.toml --no-default-features --features rustls-aws-lc-rs,runtime-tokio,__rustls-post-quantum-test # FIPS - run: RUST_BACKTRACE=1 cargo test --manifest-path quinn-proto/Cargo.toml --no-default-features --features rustls-aws-lc-rs-fips - - run: RUST_BACKTRACE=1 cargo test --manifest-path quinn/Cargo.toml --no-default-features --features rustls-aws-lc-rs-fips,runtime-tokio + - run: RUST_BACKTRACE=1 cargo test --manifest-path quinn/Cargo.toml --no-default-features --features rustls-aws-lc-rs-fips,__rustls-post-quantum-test,runtime-tokio wasm_test: name: test wasm32-unknown-unknown @@ -283,7 +283,7 @@ jobs: env: RUSTFLAGS: -Dwarnings # skip FIPS features outside of Linux - SKIP_FEATURES: ${{ matrix.os != 'ubuntu-latest' && 'rustls-aws-lc-rs-fips,aws-lc-rs-fips' || '' }} + SKIP_FEATURES: ${{ matrix.os != 'ubuntu-latest' && 'rustls-aws-lc-rs-fips,aws-lc-rs-fips,__rustls-post-quantum-test' || '' }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable diff --git a/Cargo.toml b/Cargo.toml index b78d81eaa..80db06bb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ rustls = { version = "0.23.5", default-features = false, features = ["std"] } rustls-pemfile = "2" rustls-platform-verifier = "0.4" rustls-pki-types = "1.7" +rustls-post-quantum = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1" slab = "0.4.6" diff --git a/quinn/Cargo.toml b/quinn/Cargo.toml index ab918b79c..3865e930d 100644 --- a/quinn/Cargo.toml +++ b/quinn/Cargo.toml @@ -53,6 +53,7 @@ rustc-hash = { workspace = true } pin-project-lite = { workspace = true } proto = { package = "quinn-proto", path = "../quinn-proto", version = "0.11.7", default-features = false } rustls = { workspace = true, optional = true } +rustls-post-quantum = { workspace = true, optional = true } smol = { workspace = true, optional = true } socket2 = { workspace = true } thiserror = { workspace = true } @@ -94,6 +95,10 @@ required-features = ["rustls-ring"] name = "connection" required-features = ["rustls-ring"] +[[test]] +name = "post_quantum" +required-features = ["__rustls-post-quantum-test"] + [[bench]] name = "bench" harness = false diff --git a/quinn/tests/many_connections.rs b/quinn/tests/many_connections.rs index 5083487a6..45185c3e9 100644 --- a/quinn/tests/many_connections.rs +++ b/quinn/tests/many_connections.rs @@ -19,12 +19,10 @@ struct Shared { #[test] #[ignore] fn connect_n_nodes_to_1_and_send_1mb_data() { - tracing::subscriber::set_global_default( - tracing_subscriber::FmtSubscriber::builder() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .finish(), - ) - .unwrap(); + let _ = tracing_subscriber::FmtSubscriber::builder() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .with_test_writer() + .try_init(); let runtime = Builder::new_current_thread().enable_all().build().unwrap(); let _guard = runtime.enter(); diff --git a/quinn/tests/post_quantum.rs b/quinn/tests/post_quantum.rs new file mode 100644 index 000000000..abc5644db --- /dev/null +++ b/quinn/tests/post_quantum.rs @@ -0,0 +1,123 @@ +#![cfg(feature = "rustls-aws-lc-rs")] + +use std::{ + error::Error, + net::{Ipv4Addr, SocketAddr}, + sync::Arc, +}; + +use rustls::{ + pki_types::{CertificateDer, PrivatePkcs8KeyDer}, + NamedGroup, +}; +use tracing::info; + +use quinn::{ + crypto::rustls::{HandshakeData, QuicClientConfig, QuicServerConfig}, + Endpoint, +}; + +#[tokio::test] +async fn post_quantum_key_worst_case_header() { + check_post_quantum_key_exchange(1274, 8081).await; +} + +#[tokio::test] +async fn post_quantum_key_exchange_large_mtu() { + check_post_quantum_key_exchange(1433, 8082).await; +} + +async fn check_post_quantum_key_exchange(min_mtu: u16, server_port: u16) { + let _ = tracing_subscriber::FmtSubscriber::builder() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .with_test_writer() + .try_init(); + + let server_addr = SocketAddr::from((Ipv4Addr::LOCALHOST, server_port)); + + let (endpoint, server_cert) = make_server_endpoint(server_addr, min_mtu).unwrap(); + // accept a single connection + tokio::spawn(async move { + let incoming_conn = endpoint.accept().await.unwrap(); + let conn = incoming_conn.await.unwrap(); + info!( + "[server] connection accepted: addr={}", + conn.remote_address() + ); + assert_eq!( + conn.handshake_data() + .unwrap() + .downcast::() + .unwrap() + .negotiated_key_exchange_group, + X25519_KYBER768_DRAFT00 + ) + // Dropping all handles associated with a connection implicitly closes it + }); + + let endpoint = + make_client_endpoint(SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0)), server_cert).unwrap(); + // connect to server + let connection = endpoint + .connect(server_addr, "localhost") + .unwrap() + .await + .unwrap(); + info!("[client] connected: addr={}", connection.remote_address()); + + // Waiting for a stream will complete with an error when the server closes the connection + let _ = connection.accept_uni().await; + + // Make sure the server has a chance to clean up + endpoint.wait_idle().await; +} + +fn make_client_endpoint( + bind_addr: SocketAddr, + server_cert: CertificateDer<'static>, +) -> Result> { + let mut certs = rustls::RootCertStore::empty(); + certs.add(server_cert)?; + let rustls_config = + rustls::ClientConfig::builder_with_provider(Arc::new(rustls_post_quantum::provider())) + .with_safe_default_protocol_versions() + .unwrap() + .with_root_certificates(certs) + .with_no_client_auth(); + + let client_cfg = + quinn::ClientConfig::new(Arc::new(QuicClientConfig::try_from(rustls_config).unwrap())); + let mut endpoint = Endpoint::client(bind_addr)?; + endpoint.set_default_client_config(client_cfg); + Ok(endpoint) +} + +fn make_server_endpoint( + bind_addr: SocketAddr, + min_mtu: u16, +) -> Result<(Endpoint, CertificateDer<'static>), Box> { + let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap(); + let server_cert = CertificateDer::from(cert.cert); + let priv_key = PrivatePkcs8KeyDer::from(cert.key_pair.serialize_der()); + let mut server_config = quinn::ServerConfig::with_crypto(Arc::new( + QuicServerConfig::try_from( + rustls::ServerConfig::builder_with_provider(Arc::new(rustls_post_quantum::provider())) + .with_safe_default_protocol_versions() + .unwrap() + .with_no_client_auth() + .with_single_cert(vec![server_cert.clone()], priv_key.into()) + .unwrap(), + ) + .unwrap(), + )); + + let transport_config = Arc::get_mut(&mut server_config.transport).unwrap(); + transport_config.max_concurrent_uni_streams(0_u8.into()); + transport_config.min_mtu(min_mtu); + + let endpoint = Endpoint::server(server_config, bind_addr)?; + Ok((endpoint, server_cert)) +} + +/// +const X25519_KYBER768_DRAFT00: NamedGroup = NamedGroup::Unknown(0x06399);