selium_server/
quic.rs

1//! Much of this code was borrowed with many thanks from the Quinn project:
2//! `<https://github.com/quinn-rs/quinn/blob/main/quinn/examples/server.rs>`
3
4use anyhow::{bail, Context, Result};
5use quinn::{Connection, IdleTimeout, ServerConfig};
6use rustls::server::AllowAnyAuthenticatedClient;
7use rustls::{Certificate, PrivateKey, RootCertStore};
8use rustls_pemfile::{certs, pkcs8_private_keys, rsa_private_keys};
9use std::{fs, path::Path, sync::Arc};
10
11const ALPN_QUIC_HTTP: &[&[u8]] = &[b"hq-29"];
12
13#[derive(Default)]
14pub struct ConfigOptions {
15    pub keylog: bool,
16    pub stateless_retry: bool,
17    pub max_idle_timeout: IdleTimeout,
18}
19
20pub fn server_config(
21    root_store: RootCertStore,
22    certs: Vec<Certificate>,
23    key: PrivateKey,
24    options: ConfigOptions,
25) -> Result<ServerConfig> {
26    let client_cert_verifier = Arc::new(AllowAnyAuthenticatedClient::new(root_store));
27
28    let mut server_crypto = rustls::ServerConfig::builder()
29        .with_safe_defaults()
30        .with_client_cert_verifier(client_cert_verifier)
31        .with_single_cert(certs, key)?;
32
33    server_crypto.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect();
34    if options.keylog {
35        server_crypto.key_log = Arc::new(rustls::KeyLogFile::new());
36    }
37
38    let mut server_config = ServerConfig::with_crypto(Arc::new(server_crypto));
39    let transport_config = Arc::get_mut(&mut server_config.transport).unwrap();
40    transport_config.max_concurrent_uni_streams(0_u8.into());
41    transport_config.max_idle_timeout(Some(options.max_idle_timeout));
42    if options.stateless_retry {
43        server_config.use_retry(true);
44    }
45
46    Ok(server_config)
47}
48
49fn load_key<T: AsRef<Path>>(path: T) -> Result<PrivateKey> {
50    let path = path.as_ref();
51    let key = fs::read(path).context("failed to read private key")?;
52    let key = if path.extension().map_or(false, |x| x == "der") {
53        PrivateKey(key)
54    } else {
55        let pkcs8 = pkcs8_private_keys(&mut &*key).context("malformed PKCS #8 private key")?;
56        match pkcs8.into_iter().next() {
57            Some(x) => PrivateKey(x),
58            None => {
59                let rsa = rsa_private_keys(&mut &*key).context("malformed PKCS #1 private key")?;
60                match rsa.into_iter().next() {
61                    Some(x) => PrivateKey(x),
62                    None => {
63                        bail!("no private keys found");
64                    }
65                }
66            }
67        }
68    };
69
70    Ok(key)
71}
72
73fn load_certs<T: AsRef<Path>>(path: T) -> Result<Vec<Certificate>> {
74    let path = path.as_ref();
75    let cert_chain = fs::read(path).context("failed to read certificate chain")?;
76
77    let cert_chain = if path.extension().map_or(false, |x| x == "der") {
78        vec![Certificate(cert_chain)]
79    } else {
80        certs(&mut &*cert_chain)
81            .context("invalid PEM-encoded certificate")?
82            .into_iter()
83            .map(Certificate)
84            .collect()
85    };
86
87    Ok(cert_chain)
88}
89
90pub fn read_certs<T: AsRef<Path>>(
91    cert_path: T,
92    key_path: T,
93) -> Result<(Vec<Certificate>, PrivateKey)> {
94    let certs = load_certs(cert_path)?;
95    let key = load_key(key_path)?;
96    Ok((certs, key))
97}
98
99pub fn load_root_store<T: AsRef<Path>>(ca_file: T) -> Result<RootCertStore> {
100    let ca_file = ca_file.as_ref();
101    let mut store = RootCertStore::empty();
102    let certs = load_certs(ca_file)?;
103    store.add_parsable_certificates(&certs);
104
105    if store.is_empty() {
106        bail!("No valid certs found in file {ca_file:?}");
107    }
108
109    Ok(store)
110}
111
112pub fn get_pubkey_from_connection(connection: &Connection) -> Result<Vec<u8>> {
113    let peer_identity = connection
114        .peer_identity()
115        .context("Unable to read peer identity")?;
116
117    let certs = peer_identity
118        .downcast_ref::<Vec<Certificate>>()
119        .context("Unable to read cert")?;
120
121    Ok(certs
122        .first()
123        .context("Failed to get first certificate")?
124        .0
125        .clone())
126}