drawbridge_server/auth/
tls.rs

1// SPDX-FileCopyrightText: 2022 Profian Inc. <opensource@profian.com>
2// SPDX-License-Identifier: Apache-2.0
3
4use std::io::BufRead;
5use std::ops::Deref;
6
7use anyhow::{bail, Context};
8use rustls::server::WebPkiClientVerifier;
9use rustls::{RootCertStore, ServerConfig};
10use rustls_pemfile::Item::{Pkcs1Key, Pkcs8Key, Sec1Key, X509Certificate};
11use rustls_pki_types::CertificateDer;
12use rustls_pki_types::PrivateKeyDer;
13
14#[derive(Clone, Copy, Debug)]
15#[repr(transparent)]
16pub struct TrustedCertificate;
17
18#[repr(transparent)]
19#[allow(missing_debug_implementations)]
20#[derive(Clone)]
21pub struct Config(ServerConfig);
22
23impl Deref for Config {
24    type Target = ServerConfig;
25
26    fn deref(&self) -> &Self::Target {
27        &self.0
28    }
29}
30
31impl From<Config> for ServerConfig {
32    fn from(conf: Config) -> Self {
33        conf.0
34    }
35}
36
37fn read_certificates(mut rd: impl BufRead) -> anyhow::Result<Vec<CertificateDer<'static>>> {
38    rustls_pemfile::read_all(&mut rd)
39        .map(|item| match item? {
40            X509Certificate(buf) => Ok(buf),
41            _ => bail!("unsupported certificate type"),
42        })
43        .collect()
44}
45
46impl Config {
47    pub fn read(
48        mut certs: impl BufRead,
49        mut key: impl BufRead,
50        mut cas: impl BufRead,
51    ) -> anyhow::Result<Self> {
52        let certs =
53            read_certificates(&mut certs).context("failed to read server certificate chain")?;
54        let key = {
55            if let Some(key) = rustls_pemfile::read_all(&mut key).next() {
56                match key? {
57                    Pkcs1Key(inner) => PrivateKeyDer::from(inner),
58                    Pkcs8Key(inner) => PrivateKeyDer::from(inner),
59                    Sec1Key(inner) => PrivateKeyDer::from(inner),
60                    _ => {
61                        bail!("Unexpected key type found");
62                    }
63                }
64            } else {
65                bail!("No key found")
66            }
67        };
68
69        let client_verifier = {
70            let mut roots = RootCertStore::empty();
71            read_certificates(&mut cas)
72                .context("failed to read CA certificates")?
73                .into_iter()
74                .try_for_each(|cert| roots.add(cert))
75                .context("failed to construct root certificate store")?;
76            // TODO: Allow client certificates signed by unknown CAs.
77            WebPkiClientVerifier::builder(roots.into())
78                .allow_unauthenticated()
79                .build()?
80        };
81
82        ServerConfig::builder()
83            .with_client_cert_verifier(client_verifier)
84            .with_single_cert(certs, key)
85            .context("invalid server certificate key")
86            .map(Self)
87    }
88}