spiffe-rs 0.1.0

Rust port of spiffe-go with SPIFFE IDs, bundles, SVIDs, Workload API client, federation helpers, and rustls-based SPIFFE TLS utilities.
Documentation
use crate::bundle::x509bundle;
use crate::spiffeid;
use crate::spiffeid::ID;
use crate::svid::x509svid;
use rustls::client::{ServerCertVerified, ServerCertVerifier};
use rustls::server::{ClientCertVerified, ClientCertVerifier};
use rustls::{Certificate, ClientConfig, Error as RustlsError, PrivateKey, RootCertStore, ServerConfig};
use std::sync::Arc;

pub type Authorizer =
    Arc<dyn Fn(&ID, &[Vec<Vec<u8>>]) -> std::result::Result<(), super::Error> + Send + Sync>;

#[derive(Clone, Default)]
pub struct Trace {
    pub get_certificate: Option<Arc<dyn Fn(GetCertificateInfo) + Send + Sync>>,
    pub got_certificate: Option<Arc<dyn Fn(GotCertificateInfo) + Send + Sync>>,
}

#[derive(Clone, Default)]
pub struct GetCertificateInfo;

#[derive(Clone, Default)]
pub struct GotCertificateInfo {
    pub cert: Option<Certificate>,
    pub err: Option<String>,
}

#[derive(Clone, Default)]
pub struct TlsOption {
    trace: std::option::Option<Trace>,
}

impl TlsOption {
    pub fn with_trace(trace: Trace) -> Self {
        Self {
            trace: std::option::Option::Some(trace),
        }
    }
}

#[derive(Clone)]
pub struct WebCert {
    pub certs: Vec<Certificate>,
    pub key: PrivateKey,
}

pub fn authorize_any() -> Authorizer {
    adapt_matcher(spiffeid::match_any())
}

pub fn authorize_id(allowed: ID) -> Authorizer {
    adapt_matcher(spiffeid::match_id(allowed))
}

pub fn authorize_one_of(allowed: &[ID]) -> Authorizer {
    adapt_matcher(spiffeid::match_one_of(allowed))
}

pub fn authorize_member_of(allowed: spiffeid::TrustDomain) -> Authorizer {
    adapt_matcher(spiffeid::match_member_of(allowed))
}

pub fn adapt_matcher(matcher: spiffeid::Matcher) -> Authorizer {
    Arc::new(move |actual, _chains| {
        matcher(actual).map_err(|err| super::wrap_error(err))
    })
}

pub fn tls_client_config(
    bundle_source: Arc<dyn x509bundle::Source + Send + Sync>,
    authorizer: Authorizer,
) -> super::Result<ClientConfig> {
    let verifier = Arc::new(SpiffeServerVerifier::new(bundle_source, authorizer));
    Ok(ClientConfig::builder()
        .with_safe_defaults()
        .with_custom_certificate_verifier(verifier)
        .with_no_client_auth())
}

pub fn mtls_client_config(
    svid_source: &dyn x509svid::Source,
    bundle_source: Arc<dyn x509bundle::Source + Send + Sync>,
    authorizer: Authorizer,
) -> super::Result<ClientConfig> {
    mtls_client_config_with_options(svid_source, bundle_source, authorizer, &[])
}

pub fn mtls_client_config_with_options(
    svid_source: &dyn x509svid::Source,
    bundle_source: Arc<dyn x509bundle::Source + Send + Sync>,
    authorizer: Authorizer,
    opts: &[TlsOption],
) -> super::Result<ClientConfig> {
    let (certs, key) = svid_to_rustls(svid_source, trace_from_options(opts))?;
    let verifier = Arc::new(SpiffeServerVerifier::new(bundle_source, authorizer));
    ClientConfig::builder()
        .with_safe_defaults()
        .with_custom_certificate_verifier(verifier)
        .with_client_auth_cert(certs, key)
        .map_err(|err| super::wrap_error(format!("unable to set client auth cert: {}", err)))
}

pub fn mtls_server_config(
    svid_source: &dyn x509svid::Source,
    bundle_source: Arc<dyn x509bundle::Source + Send + Sync>,
    authorizer: Authorizer,
) -> super::Result<ServerConfig> {
    mtls_server_config_with_options(svid_source, bundle_source, authorizer, &[])
}

pub fn mtls_server_config_with_options(
    svid_source: &dyn x509svid::Source,
    bundle_source: Arc<dyn x509bundle::Source + Send + Sync>,
    authorizer: Authorizer,
    opts: &[TlsOption],
) -> super::Result<ServerConfig> {
    let (certs, key) = svid_to_rustls(svid_source, trace_from_options(opts))?;
    let verifier = Arc::new(SpiffeClientVerifier::new(bundle_source, authorizer));
    ServerConfig::builder()
        .with_safe_defaults()
        .with_client_cert_verifier(verifier)
        .with_single_cert(certs, key)
        .map_err(|err| super::wrap_error(format!("unable to set server cert: {}", err)))
}

pub fn tls_server_config(svid_source: &dyn x509svid::Source) -> super::Result<ServerConfig> {
    tls_server_config_with_options(svid_source, &[])
}

pub fn tls_server_config_with_options(
    svid_source: &dyn x509svid::Source,
    opts: &[TlsOption],
) -> super::Result<ServerConfig> {
    let (certs, key) = svid_to_rustls(svid_source, trace_from_options(opts))?;
    ServerConfig::builder()
        .with_safe_defaults()
        .with_no_client_auth()
        .with_single_cert(certs, key)
        .map_err(|err| super::wrap_error(format!("unable to set server cert: {}", err)))
}

pub fn mtls_web_client_config(
    svid_source: &dyn x509svid::Source,
    roots: std::option::Option<RootCertStore>,
) -> super::Result<ClientConfig> {
    mtls_web_client_config_with_options(svid_source, roots, &[])
}

pub fn mtls_web_client_config_with_options(
    svid_source: &dyn x509svid::Source,
    roots: std::option::Option<RootCertStore>,
    opts: &[TlsOption],
) -> super::Result<ClientConfig> {
    let (certs, key) = svid_to_rustls(svid_source, trace_from_options(opts))?;
    let mut config = ClientConfig::builder()
        .with_safe_defaults()
        .with_root_certificates(roots.unwrap_or_else(system_roots))
        .with_client_auth_cert(certs, key)
        .map_err(|err| super::wrap_error(format!("unable to set client auth cert: {}", err)))?;
    config.alpn_protocols.clear();
    Ok(config)
}

pub fn mtls_web_server_config(
    cert: WebCert,
    bundle_source: Arc<dyn x509bundle::Source + Send + Sync>,
    authorizer: Authorizer,
) -> super::Result<ServerConfig> {
    let verifier = Arc::new(SpiffeClientVerifier::new(bundle_source, authorizer));
    ServerConfig::builder()
        .with_safe_defaults()
        .with_client_cert_verifier(verifier)
        .with_single_cert(cert.certs, cert.key)
        .map_err(|err| super::wrap_error(format!("unable to set server cert: {}", err)))
}

pub fn webpki_client_config(roots: std::option::Option<RootCertStore>) -> super::Result<ClientConfig> {
    let mut config = ClientConfig::builder()
        .with_safe_defaults()
        .with_root_certificates(roots.unwrap_or_else(system_roots))
        .with_no_client_auth();
    config.alpn_protocols.clear();
    Ok(config)
}

fn svid_to_rustls(
    svid_source: &dyn x509svid::Source,
    trace: std::option::Option<&Trace>,
) -> super::Result<(Vec<Certificate>, PrivateKey)> {
    if let Some(trace) = trace {
        if let Some(get) = &trace.get_certificate {
            get(GetCertificateInfo);
        }
    }
    let svid = match svid_source.get_x509_svid() {
        Ok(svid) => svid,
        Err(err) => {
            if let Some(trace) = trace {
                if let Some(got) = &trace.got_certificate {
                    got(GotCertificateInfo {
                        cert: None,
                        err: Some(err.to_string()),
                    });
                }
            }
            return Err(super::wrap_error(err));
        }
    };
    if svid.certificates.is_empty() {
        return Err(super::wrap_error("empty X509-SVID"));
    }
    let certs = svid
        .certificates
        .iter()
        .map(|cert| Certificate(cert.clone()))
        .collect::<Vec<_>>();
    let key = PrivateKey(svid.private_key.clone());
    if let Some(trace) = trace {
        if let Some(got) = &trace.got_certificate {
            got(GotCertificateInfo {
                cert: certs.first().cloned(),
                err: None,
            });
        }
    }
    Ok((certs, key))
}

fn trace_from_options(opts: &[TlsOption]) -> std::option::Option<&Trace> {
    opts.iter().rev().find_map(|opt| opt.trace.as_ref())
}

fn system_roots() -> RootCertStore {
    let mut roots = RootCertStore::empty();
    if let Ok(store) = rustls_native_certs::load_native_certs() {
        for cert in store {
            let _ = roots.add(&Certificate(cert.as_ref().to_vec()));
        }
    }
    roots
}

struct SpiffeServerVerifier {
    bundle_source: Arc<dyn x509bundle::Source + Send + Sync>,
    authorizer: Authorizer,
}

impl SpiffeServerVerifier {
    fn new(
        bundle_source: Arc<dyn x509bundle::Source + Send + Sync>,
        authorizer: Authorizer,
    ) -> Self {
        Self {
            bundle_source,
            authorizer,
        }
    }

    fn verify_peer(
        &self,
        end_entity: &Certificate,
        intermediates: &[Certificate],
    ) -> Result<ServerCertVerified, RustlsError> {
        let raw = raw_chain(end_entity, intermediates);
        let (id, chains) = x509svid::parse_and_verify(&raw, self.bundle_source.as_ref(), &[])
            .map_err(|err| RustlsError::General(format!("spiffe verification failed: {}", err)))?;
        (self.authorizer)(&id, &chains)
            .map_err(|err| RustlsError::General(err.to_string()))?;
        Ok(ServerCertVerified::assertion())
    }
}

impl ServerCertVerifier for SpiffeServerVerifier {
    fn verify_server_cert(
        &self,
        end_entity: &Certificate,
        intermediates: &[Certificate],
        _server_name: &rustls::ServerName,
        _scts: &mut dyn Iterator<Item = &[u8]>,
        _ocsp: &[u8],
        _now: std::time::SystemTime,
    ) -> Result<ServerCertVerified, RustlsError> {
        self.verify_peer(end_entity, intermediates)
    }
}

struct SpiffeClientVerifier {
    bundle_source: Arc<dyn x509bundle::Source + Send + Sync>,
    authorizer: Authorizer,
}

impl SpiffeClientVerifier {
    fn new(
        bundle_source: Arc<dyn x509bundle::Source + Send + Sync>,
        authorizer: Authorizer,
    ) -> Self {
        Self {
            bundle_source,
            authorizer,
        }
    }

    fn verify_peer(
        &self,
        end_entity: &Certificate,
        intermediates: &[Certificate],
    ) -> Result<ClientCertVerified, RustlsError> {
        let raw = raw_chain(end_entity, intermediates);
        let (id, chains) = x509svid::parse_and_verify(&raw, self.bundle_source.as_ref(), &[])
            .map_err(|err| RustlsError::General(format!("spiffe verification failed: {}", err)))?;
        (self.authorizer)(&id, &chains)
            .map_err(|err| RustlsError::General(err.to_string()))?;
        Ok(ClientCertVerified::assertion())
    }
}

impl ClientCertVerifier for SpiffeClientVerifier {
    fn client_auth_root_subjects(&self) -> &[rustls::DistinguishedName] {
        &[]
    }

    fn verify_client_cert(
        &self,
        end_entity: &Certificate,
        intermediates: &[Certificate],
        _now: std::time::SystemTime,
    ) -> Result<ClientCertVerified, RustlsError> {
        self.verify_peer(end_entity, intermediates)
    }
}

fn raw_chain(end_entity: &Certificate, intermediates: &[Certificate]) -> Vec<Vec<u8>> {
    let mut raw = Vec::with_capacity(1 + intermediates.len());
    raw.push(end_entity.0.clone());
    for cert in intermediates {
        raw.push(cert.0.clone());
    }
    raw
}