From e4fb8b409ed0610c0db4e64a13ef65e11ad651e4 Mon Sep 17 00:00:00 2001 From: peg Date: Tue, 6 Oct 2026 09:42:28 +0200 Subject: [PATCH 1/2] Refactor to expose helpers for self-signed rustls config in attested-tls crate --- .github/workflows/test.yml | 7 ++- Cargo.lock | 2 - README.md | 3 ++ crates/attested-tls-proxy/Cargo.toml | 4 +- crates/attested-tls-proxy/TCP_TUNNEL.md | 2 +- crates/attested-tls-proxy/src/cli/http.rs | 2 +- crates/attested-tls-proxy/src/cli/pem.rs | 4 +- .../attested-tls-proxy/src/cli/tcp_tunnel.rs | 4 +- .../src/http/attested_get.rs | 3 +- crates/attested-tls-proxy/src/http/mod.rs | 2 +- crates/attested-tls-proxy/src/lib.rs | 2 - .../attested-tls-proxy/src/tcp_tunnel/mod.rs | 2 +- .../tests/http/attested_get_redirect.rs | 4 +- .../attested-tls-proxy/tests/http/target.rs | 3 +- .../tests/tcp_tunnel/common.rs | 2 +- .../tests/tcp_tunnel/tunnel.rs | 4 +- crates/attested-tls/Cargo.toml | 11 ++--- crates/attested-tls/README.md | 9 ++++ crates/attested-tls/src/lib.rs | 48 +++---------------- .../src/self_signed.rs | 26 ++++------ .../src/tls.rs | 24 +++++++--- 21 files changed, 74 insertions(+), 94 deletions(-) rename crates/{attested-tls-proxy => attested-tls}/src/self_signed.rs (93%) rename crates/{attested-tls-proxy => attested-tls}/src/tls.rs (71%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 09d73f5..669abe5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,8 +35,11 @@ jobs: - name: Check formatting run: cargo fmt --all -- --check + - name: Check attested-tls with default features + run: cargo check -p attested-tls --locked + - name: Run cargo clippy - run: cargo clippy --workspace --features azure -- -D warnings + run: cargo clippy --workspace --features azure,attested-tls/ws,attested-tls/rpc -- -D warnings - name: Run cargo test - run: cargo test --workspace --features azure --all-targets -- --test-threads=1 + run: cargo test --workspace --features azure,attested-tls/ws,attested-tls/rpc --all-targets -- --test-threads=1 diff --git a/Cargo.lock b/Cargo.lock index f307a3a..04ef9fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -702,8 +702,6 @@ dependencies = [ "tower-http", "tracing", "tracing-subscriber", - "webpki-roots 1.0.4", - "x509-parser", ] [[package]] diff --git a/README.md b/README.md index 3ee62bf..1ff4a65 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,9 @@ Omit `--features azure` on systems without the TPM dependencies described below. To install from a local checkout, use `cargo install --path crates/attested-tls-proxy --locked`. Docker and Compose commands also run from the repository root. +The `attested-tls` library's `ws` and `rpc` features are opt-in. To include their +tests, add `attested-tls/ws,attested-tls/rpc` to the test command's feature list. + ## Dependencies and feature flags The `azure` feature, for Microsoft Azure attestation requires [tpm2](https://tpm2-software.github.io) to be installed. On Debian-based systems this is provided by [`libtss2-dev`](https://packages.debian.org/trixie/libtss2-dev), and on nix `tpm2-tss`. This dependency is currently not packaged for MacOS, meaning currently it is not possible to compile or run with the `azure` feature on MacOS. diff --git a/crates/attested-tls-proxy/Cargo.toml b/crates/attested-tls-proxy/Cargo.toml index b3351ab..bfa0302 100644 --- a/crates/attested-tls-proxy/Cargo.toml +++ b/crates/attested-tls-proxy/Cargo.toml @@ -12,7 +12,6 @@ keywords = ["attested-TLS", "CVM", "TDX"] attested-tls = { path = "../attested-tls", default-features = false } tokio = { workspace = true, features = ["full"] } tokio-rustls = { workspace = true, features = ["aws_lc_rs"] } -x509-parser = { workspace = true, features = ["verify"] } thiserror.workspace = true clap.workspace = true rustls-pemfile.workspace = true @@ -29,16 +28,15 @@ serde = "1.0.228" reqwest = { version = "0.13.4", default-features = false, features = [ "rustls-no-provider", ] } -webpki-roots.workspace = true tracing.workspace = true tracing-subscriber.workspace = true axum = "0.8.8" tower-http = { version = "0.6.7", features = ["fs"] } -rcgen.workspace = true pin-project-lite = "0.2.16" pccs = { git = "https://github.com/flashbots/attested-tls", branch = "main" } [dev-dependencies] +rcgen.workspace = true tempfile.workspace = true tdx-quote = { version = "0.0.5", features = ["mock"] } attested-tls = { path = "../attested-tls", features = ["test-helpers", "mock"] } diff --git a/crates/attested-tls-proxy/TCP_TUNNEL.md b/crates/attested-tls-proxy/TCP_TUNNEL.md index cde4abb..667bce1 100644 --- a/crates/attested-tls-proxy/TCP_TUNNEL.md +++ b/crates/attested-tls-proxy/TCP_TUNNEL.md @@ -164,7 +164,7 @@ client.serve_until(async { Dropping a `serve_until` future aborts its connection tasks. For graceful shutdown, resolve the supplied shutdown future and await completion instead. Embedding applications own runtime shutdown, including outstanding blocking attestation -work. The shared `attested_tls_proxy::tls` module provides TLS configuration helpers, including self-signed +work. The shared `attested_tls::tls` module provides TLS configuration helpers, including self-signed verification that retains client credentials. Both client constructors accept a `startup_check` boolean before `options`. diff --git a/crates/attested-tls-proxy/src/cli/http.rs b/crates/attested-tls-proxy/src/cli/http.rs index f26fec1..d390590 100644 --- a/crates/attested-tls-proxy/src/cli/http.rs +++ b/crates/attested-tls-proxy/src/cli/http.rs @@ -114,7 +114,7 @@ impl ClientArgs { let client_attestation_generator = AttestationGenerator::new_with_detection(client_attestation_type, dev_dummy_dcap)?; - let client_tls_config = attested_tls_proxy::tls::client_config( + let client_tls_config = attested_tls::tls::client_config( tls_cert_and_chain.as_ref(), remote_tls_cert, allow_self_signed, diff --git a/crates/attested-tls-proxy/src/cli/pem.rs b/crates/attested-tls-proxy/src/cli/pem.rs index 274d6c2..343284a 100644 --- a/crates/attested-tls-proxy/src/cli/pem.rs +++ b/crates/attested-tls-proxy/src/cli/pem.rs @@ -18,9 +18,7 @@ pub(super) fn load_tls_cert_and_key_server( return Err(anyhow!("Certificate chain provided but no private key")); } tracing::warn!("No TLS ceritifcate provided - generating self-signed"); - Ok(attested_tls_proxy::self_signed::generate_self_signed_cert( - ip, - )?) + Ok(attested_tls::self_signed::generate_self_signed_cert(ip)?) } } diff --git a/crates/attested-tls-proxy/src/cli/tcp_tunnel.rs b/crates/attested-tls-proxy/src/cli/tcp_tunnel.rs index c12d108..13d0d06 100644 --- a/crates/attested-tls-proxy/src/cli/tcp_tunnel.rs +++ b/crates/attested-tls-proxy/src/cli/tcp_tunnel.rs @@ -1,11 +1,11 @@ use anyhow::{anyhow, ensure}; +use attested_tls::self_signed::generate_self_signed_cert; +use attested_tls::tls; use attested_tls::{ TlsCertAndKey, attestation::{AttestationGenerator, AttestationVerifier}, }; -use attested_tls_proxy::self_signed::generate_self_signed_cert; use attested_tls_proxy::tcp_tunnel::{TunnelClient, TunnelOptions, TunnelServer}; -use attested_tls_proxy::tls; use clap::Args; use std::{ net::SocketAddr, diff --git a/crates/attested-tls-proxy/src/http/attested_get.rs b/crates/attested-tls-proxy/src/http/attested_get.rs index 46e864d..03a2b33 100644 --- a/crates/attested-tls-proxy/src/http/attested_get.rs +++ b/crates/attested-tls-proxy/src/http/attested_get.rs @@ -32,7 +32,8 @@ pub async fn attested_get( remote_certificate: Option>, allow_self_signed: bool, ) -> Result { - let client_config = crate::tls::client_config(None, remote_certificate, allow_self_signed)?; + let client_config = + attested_tls::tls::client_config(None, remote_certificate, allow_self_signed)?; let proxy_client = ProxyClient::new_with_tls_config( client_config, "127.0.0.1:0".to_string(), diff --git a/crates/attested-tls-proxy/src/http/mod.rs b/crates/attested-tls-proxy/src/http/mod.rs index 298dc05..0c856d0 100644 --- a/crates/attested-tls-proxy/src/http/mod.rs +++ b/crates/attested-tls-proxy/src/http/mod.rs @@ -3,7 +3,7 @@ use crate::target::{InvalidTarget, normalize_target}; pub mod attested_get; pub mod file_server; pub mod health_check; -use crate::tls; +use attested_tls::tls; pub use attested_tls; pub use attested_tls::attestation; diff --git a/crates/attested-tls-proxy/src/lib.rs b/crates/attested-tls-proxy/src/lib.rs index 20a0172..4ffce83 100644 --- a/crates/attested-tls-proxy/src/lib.rs +++ b/crates/attested-tls-proxy/src/lib.rs @@ -1,9 +1,7 @@ //! HTTP and TCP proxies over attested TLS. pub mod http; -pub mod self_signed; mod target; pub mod tcp_tunnel; -pub mod tls; pub use target::InvalidTarget; // Preserve the original HTTP proxy API at the crate root. diff --git a/crates/attested-tls-proxy/src/tcp_tunnel/mod.rs b/crates/attested-tls-proxy/src/tcp_tunnel/mod.rs index 030bc7a..5f1fd78 100644 --- a/crates/attested-tls-proxy/src/tcp_tunnel/mod.rs +++ b/crates/attested-tls-proxy/src/tcp_tunnel/mod.rs @@ -5,7 +5,7 @@ //! the calling application. //! //! Assumes a Rustls crypto provider is already installed. -use crate::tls; +use attested_tls::tls; use crate::target::{InvalidTarget, normalize_target}; diff --git a/crates/attested-tls-proxy/tests/http/attested_get_redirect.rs b/crates/attested-tls-proxy/tests/http/attested_get_redirect.rs index 9d30e53..a15438b 100644 --- a/crates/attested-tls-proxy/tests/http/attested_get_redirect.rs +++ b/crates/attested-tls-proxy/tests/http/attested_get_redirect.rs @@ -1,8 +1,8 @@ use std::time::Duration; +use attested_tls::self_signed::generate_self_signed_cert; use attested_tls_proxy::{ - AttestationGenerator, ProxyServer, attestation::AttestationVerifier, - attested_get::attested_get, self_signed::generate_self_signed_cert, + AttestationGenerator, ProxyServer, attestation::AttestationVerifier, attested_get::attested_get, }; use axum::{Router, routing::get}; use tokio::{net::TcpListener, process::Command, time::timeout}; diff --git a/crates/attested-tls-proxy/tests/http/target.rs b/crates/attested-tls-proxy/tests/http/target.rs index 9a98503..43a147b 100644 --- a/crates/attested-tls-proxy/tests/http/target.rs +++ b/crates/attested-tls-proxy/tests/http/target.rs @@ -1,8 +1,9 @@ use std::time::Duration; +use attested_tls::self_signed::generate_self_signed_cert; use attested_tls_proxy::{ AttestationGenerator, ProxyClient, ProxyError, ProxyServer, attestation::AttestationVerifier, - attested_get::attested_get, self_signed::generate_self_signed_cert, + attested_get::attested_get, }; use axum::{Router, http::HeaderMap, routing::get}; use tokio::{net::TcpListener, time::timeout}; diff --git a/crates/attested-tls-proxy/tests/tcp_tunnel/common.rs b/crates/attested-tls-proxy/tests/tcp_tunnel/common.rs index cd73492..af657b0 100644 --- a/crates/attested-tls-proxy/tests/tcp_tunnel/common.rs +++ b/crates/attested-tls-proxy/tests/tcp_tunnel/common.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] use attested_tls::attestation::{AttestationGenerator, AttestationVerifier}; -use attested_tls_proxy::self_signed::generate_self_signed_cert; +use attested_tls::self_signed::generate_self_signed_cert; use attested_tls_proxy::tcp_tunnel::{TunnelClient, TunnelError, TunnelOptions, TunnelServer}; use std::{future::Future, net::SocketAddr, time::Duration}; use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle}; diff --git a/crates/attested-tls-proxy/tests/tcp_tunnel/tunnel.rs b/crates/attested-tls-proxy/tests/tcp_tunnel/tunnel.rs index 70710f8..86a20a4 100644 --- a/crates/attested-tls-proxy/tests/tcp_tunnel/tunnel.rs +++ b/crates/attested-tls-proxy/tests/tcp_tunnel/tunnel.rs @@ -1,5 +1,5 @@ -use attested_tls_proxy::self_signed::generate_self_signed_cert; -use attested_tls_proxy::tls; +use attested_tls::self_signed::generate_self_signed_cert; +use attested_tls::tls; use super::common::*; use attested_tls::{ diff --git a/crates/attested-tls/Cargo.toml b/crates/attested-tls/Cargo.toml index 9b4f237..61bef64 100644 --- a/crates/attested-tls/Cargo.toml +++ b/crates/attested-tls/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["attested-TLS", "CVM", "TDX"] tokio = { workspace = true, features = ["full"] } tokio-rustls.workspace = true sha2 = "0.10.9" -x509-parser.workspace = true +x509-parser = { workspace = true, features = ["verify"] } thiserror.workspace = true webpki-roots.workspace = true http.workspace = true @@ -34,16 +34,15 @@ hyper-util = { workspace = true, features = ["tokio"], optional = true } bytes = { workspace = true, optional = true } http-body-util = { workspace = true, optional = true } -# Used by test helpers -rcgen = { workspace = true, optional = true } +# Used for self-signed certificates and test helpers +rcgen.workspace = true [dev-dependencies] -rcgen.workspace = true tempfile.workspace = true attestation = { git = "https://github.com/flashbots/attested-tls", branch = "main", features = ["mock"] } [features] -default = ["ws", "rpc"] +default = [] # Adds support for Microsoft Azure attestation generation and verification azure = ["attestation/azure"] @@ -65,6 +64,6 @@ rpc = [ ] # Exposes helper functions for testing - do not enable in production as this allows dangerous configuration -test-helpers = ["rcgen", "attestation/mock"] +test-helpers = ["attestation/mock"] mock = ["attestation/mock"] diff --git a/crates/attested-tls/README.md b/crates/attested-tls/README.md index 6abd7a7..916d608 100644 --- a/crates/attested-tls/README.md +++ b/crates/attested-tls/README.md @@ -14,6 +14,15 @@ It uses session binding through exported key material from the TLS session. This Attestation may be provided by either the server, or the client, or both. +## Optional features + +No features are enabled by default. Enable `ws` for the `websockets` module or +`rpc` for the `attested_rpc` module in your Cargo dependency: + +```toml +attested-tls = { path = "../attested-tls", features = ["ws", "rpc"] } +``` + ## Protocol Specification A TLS 1.3 handshake is made between server and client. The protocol name `flashbots-ratls/1` is included in ALPN. Future versions of the protocol may add additional protocol names which increment the number given after the slash, but backwards compatibility will be provided through also specifying `flashbots-ratls/1`. diff --git a/crates/attested-tls/src/lib.rs b/crates/attested-tls/src/lib.rs index 05e0050..08e605f 100644 --- a/crates/attested-tls/src/lib.rs +++ b/crates/attested-tls/src/lib.rs @@ -1,4 +1,6 @@ //! Attested TLS protocol server and client +pub mod self_signed; +pub mod tls; #[cfg(feature = "ws")] pub mod websockets; @@ -17,17 +19,13 @@ use attestation::{ use parity_scale_codec::{Decode, Encode}; use sha2::{Digest, Sha256}; use thiserror::Error; -use tokio_rustls::rustls::{ - self, - server::{VerifierBuilderError, WebPkiClientVerifier}, -}; +use tokio_rustls::rustls::{self, server::VerifierBuilderError}; use x509_parser::parse_x509_certificate; use std::num::TryFromIntError; use std::sync::Arc; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::task::JoinError; -use tokio_rustls::rustls::RootCertStore; use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName}; use tokio_rustls::{ TlsAcceptor, TlsConnector, @@ -82,19 +80,7 @@ impl AttestedTlsServer { attestation_verifier: AttestationVerifier, client_auth: bool, ) -> Result { - let server_config = if client_auth { - let root_store = - RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); - let verifier = WebPkiClientVerifier::builder(Arc::new(root_store)).build()?; - - ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) - .with_client_cert_verifier(verifier) - .with_single_cert(cert_and_key.cert_chain.clone(), cert_and_key.key)? - } else { - ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) - .with_no_client_auth() - .with_single_cert(cert_and_key.cert_chain.clone(), cert_and_key.key)? - }; + let server_config = tls::server_config(&cert_and_key, client_auth)?; Self::new_with_tls_config( cert_and_key.cert_chain, @@ -258,29 +244,7 @@ impl AttestedTlsClient { attestation_verifier: AttestationVerifier, remote_certificate: Option>, ) -> Result { - // If a remote CA cert was given, use it as the root store, otherwise use webpki_roots - let root_store = match remote_certificate { - Some(remote_certificate) => { - let mut root_store = RootCertStore::empty(); - root_store.add(remote_certificate)?; - root_store - } - None => RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()), - }; - - // Setup TLS client configuration, with or without client auth - let client_config = if let Some(ref cert_and_key) = cert_and_key { - ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) - .with_root_certificates(root_store) - .with_client_auth_cert( - cert_and_key.cert_chain.clone(), - cert_and_key.key.clone_key(), - )? - } else { - ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) - .with_root_certificates(root_store) - .with_no_client_auth() - }; + let client_config = tls::client_config(cert_and_key.as_ref(), remote_certificate, false)?; Self::new_with_tls_config( client_config, @@ -516,6 +480,8 @@ pub enum AttestedTlsError { Rustls(#[from] tokio_rustls::rustls::Error), #[error("Verifier builder: {0}")] VerifierBuilder(#[from] VerifierBuilderError), + #[error("Certificate generation: {0}")] + CertificateGeneration(#[from] rcgen::Error), #[error("IO: {0}")] Io(#[from] std::io::Error), #[error("Attestation: {0}")] diff --git a/crates/attested-tls-proxy/src/self_signed.rs b/crates/attested-tls/src/self_signed.rs similarity index 93% rename from crates/attested-tls-proxy/src/self_signed.rs rename to crates/attested-tls/src/self_signed.rs index b4f0b15..7ab33e2 100644 --- a/crates/attested-tls-proxy/src/self_signed.rs +++ b/crates/attested-tls/src/self_signed.rs @@ -6,9 +6,9 @@ use tokio_rustls::rustls::{ }; use x509_parser::prelude::{FromDer, X509Certificate}; -use crate::attested_tls::{AttestedTlsError, TlsCertAndKey}; +use crate::{AttestedTlsError, TlsCertAndKey}; -/// Generate a self signed certifcate +/// Generate a self-signed certificate pub fn generate_self_signed_cert(ip_address: IpAddr) -> Result { let keypair = rcgen::KeyPair::generate()?; let mut params = rcgen::CertificateParams::default(); @@ -192,10 +192,9 @@ impl rustls::server::danger::ClientCertVerifier for SkipClientVerification { mod tests { use super::*; use crate::{ - AttestationGenerator, + AttestationGenerator, AttestedTlsClient, AttestedTlsServer, attestation::{AttestationType, AttestationVerifier}, - attested_tls::{AttestedTlsClient, AttestedTlsServer}, - http::test_helpers::{generate_certificate_chain, generate_tls_config}, + test_helpers::{generate_certificate_chain, generate_tls_config}, }; use tokio::net::TcpListener; use tokio_rustls::rustls::pki_types::ServerName; @@ -204,18 +203,12 @@ mod tests { async fn self_signed_server_attestation() { let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); - let cert_and_key = generate_self_signed_cert("127.0.0.1".parse().unwrap()).unwrap(); - - let server_config = rustls::ServerConfig::builder() - .with_no_client_auth() - .with_single_cert( - cert_and_key.cert_chain.clone().to_vec(), - cert_and_key.key.clone_key(), - ) - .unwrap(); + let (server_config, cert_chain) = + crate::tls::self_signed_server_config("127.0.0.1".parse().unwrap(), false).unwrap(); + let server_certificate = cert_chain[0].clone(); let server = AttestedTlsServer::new_with_tls_config( - cert_and_key.cert_chain, + cert_chain, server_config.into(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), @@ -231,7 +224,8 @@ mod tests { server.handle_connection(tcp_stream).await.unwrap(); }); - let client_config = crate::tls::client_config(None, None, true).unwrap(); + let client_config = + crate::tls::client_config(None, Some(server_certificate), false).unwrap(); let client = AttestedTlsClient::new_with_tls_config( client_config.into(), diff --git a/crates/attested-tls-proxy/src/tls.rs b/crates/attested-tls/src/tls.rs similarity index 71% rename from crates/attested-tls-proxy/src/tls.rs rename to crates/attested-tls/src/tls.rs index 940cad9..6cbc581 100644 --- a/crates/attested-tls-proxy/src/tls.rs +++ b/crates/attested-tls/src/tls.rs @@ -1,8 +1,8 @@ -//! Shared TLS 1.3 configuration for HTTP proxies and TCP tunnels. -use std::sync::Arc; +//! TLS 1.3 configuration helpers for attested TLS. +use std::{net::IpAddr, sync::Arc}; -use crate::self_signed::SkipServerVerification; -use attested_tls::{AttestedTlsError, TlsCertAndKey}; +use crate::self_signed::{SkipServerVerification, generate_self_signed_cert}; +use crate::{AttestedTlsError, TlsCertAndKey}; use tokio_rustls::rustls::{ self, ClientConfig, RootCertStore, ServerConfig, pki_types::CertificateDer, server::WebPkiClientVerifier, @@ -40,8 +40,8 @@ pub fn client_config( } /// Build a TLS 1.3 server configuration. Optional client certificate authentication -/// uses public roots. For private client CAs, supply a custom ServerConfig through -/// the HTTP proxy or TCP tunnel's `new_with_tls_config` constructor. +/// uses public roots. For private client CAs, supply a custom ServerConfig to +/// [`crate::AttestedTlsServer::new_with_tls_config`]. pub fn server_config( identity: &TlsCertAndKey, client_auth: bool, @@ -55,3 +55,15 @@ pub fn server_config( }; Ok(builder.with_single_cert(identity.cert_chain.clone(), identity.key.clone_key())?) } + +/// Generate a self-signed certificate for the given IP address and build a TLS 1.3 +/// server configuration. Returns the configuration and its certificate chain. +/// Client authentication uses the same public roots as [`server_config`]. +pub fn self_signed_server_config( + ip_address: IpAddr, + client_auth: bool, +) -> Result<(ServerConfig, Vec>), AttestedTlsError> { + let identity = generate_self_signed_cert(ip_address)?; + let config = server_config(&identity, client_auth)?; + Ok((config, identity.cert_chain)) +} From 3ceae11c9e1979decee466e8593a4d71ae81e729 Mon Sep 17 00:00:00 2001 From: peg Date: Tue, 6 Oct 2026 10:06:18 +0200 Subject: [PATCH 2/2] Add example demonstrating setting up http2 handler over attested tls with self-signed cert --- crates/attested-tls/Cargo.toml | 5 ++ .../examples/self_signed_http2.rs | 84 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 crates/attested-tls/examples/self_signed_http2.rs diff --git a/crates/attested-tls/Cargo.toml b/crates/attested-tls/Cargo.toml index 61bef64..31d467e 100644 --- a/crates/attested-tls/Cargo.toml +++ b/crates/attested-tls/Cargo.toml @@ -38,6 +38,11 @@ http-body-util = { workspace = true, optional = true } rcgen.workspace = true [dev-dependencies] +bytes.workspace = true +http-body-util.workspace = true +hyper = { workspace = true, features = ["client", "server", "http2"] } +hyper-util = { workspace = true, features = ["tokio"] } +tokio-rustls = { workspace = true, features = ["aws_lc_rs"] } tempfile.workspace = true attestation = { git = "https://github.com/flashbots/attested-tls", branch = "main", features = ["mock"] } diff --git a/crates/attested-tls/examples/self_signed_http2.rs b/crates/attested-tls/examples/self_signed_http2.rs new file mode 100644 index 0000000..6186f42 --- /dev/null +++ b/crates/attested-tls/examples/self_signed_http2.rs @@ -0,0 +1,84 @@ +//! Serve HTTP/2 after a TLS handshake and server attestation exchange. +//! +//! Run with `cargo run -p attested-tls --example self_signed_http2`. +//! This example uses mock attestation through the crate's development dependencies. +//! Applications using real attestation must build without `attestation/mock`. + +use std::convert::Infallible; + +use attested_tls::{ + AttestedTlsServer, + attestation::{AttestationGenerator, AttestationVerifier}, + tls::self_signed_server_config, +}; +use bytes::Bytes; +use http_body_util::Full; +use hyper::{Request, Response, body::Incoming, service::service_fn}; +use hyper_util::rt::{TokioExecutor, TokioIo}; +use tokio::net::{TcpListener, TcpStream}; +use tokio_rustls::rustls; + +type Error = Box; + +#[tokio::main] +async fn main() -> Result<(), Error> { + rustls::crypto::aws_lc_rs::default_provider() + .install_default() + .map_err(|_| "could not install the TLS crypto provider")?; + + let listener = TcpListener::bind("127.0.0.1:8443").await?; + let address = listener.local_addr()?; + + // Generate self-signed cert and rustls server config + let (mut config, cert_chain) = self_signed_server_config(address.ip(), false)?; + + // Supply the application protocol as http2. AttestedTlsServer maps it to + // `flashbots-ratls/1+h2` and also advertises its bare protocol fallback. + config.alpn_protocols = vec![b"h2".to_vec()]; + + let server = AttestedTlsServer::new_with_tls_config( + cert_chain, + config, + AttestationGenerator::detect()?, + // This server attests itself and expects no client attestation. + AttestationVerifier::expect_none(), + )?; + + println!("Listening on {address}"); + + loop { + let (socket, peer) = listener.accept().await?; + let server = server.clone(); + tokio::spawn(async move { + if let Err(error) = serve_connection(server, socket).await { + eprintln!("Connection from {peer} failed: {error}"); + } + }); + } +} + +// Handle an incoming TCP connection +async fn serve_connection(server: AttestedTlsServer, socket: TcpStream) -> Result<(), Error> { + // This completes both TLS and attestation before returning the stream + let (stream, _client_measurements, _client_attestation_type) = + server.handle_connection(socket).await?; + + // Enforce http2 + if stream.get_ref().1.alpn_protocol() != Some(b"flashbots-ratls/1+h2".as_slice()) { + return Err("client did not negotiate HTTP/2".into()); + } + + // Serve http2 connection + hyper::server::conn::http2::Builder::new(TokioExecutor::new()) + .serve_connection(TokioIo::new(stream), service_fn(handle_request)) + .await?; + Ok(()) +} + +// Respond with a hello message +async fn handle_request(request: Request) -> Result>, Infallible> { + println!("{} {}", request.method(), request.uri()); + Ok(Response::new(Full::new(Bytes::from_static( + b"Hello over attested TLS and HTTP/2!\n", + )))) +}