iscp-rs 1.1.2

iSCPv2 Client Library
Documentation
use std::sync::Arc;

use rustls::pki_types::pem::PemObject;
use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};

use super::TransportError;

#[derive(Debug)]
struct SkipServerVerification(Arc<rustls::crypto::CryptoProvider>);

impl SkipServerVerification {
    fn new() -> Arc<Self> {
        Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider())))
    }
}

impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
    fn verify_server_cert(
        &self,
        _end_entity: &CertificateDer<'_>,
        _intermediates: &[CertificateDer<'_>],
        _server_name: &ServerName<'_>,
        _ocsp: &[u8],
        _now: UnixTime,
    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
        Ok(rustls::client::danger::ServerCertVerified::assertion())
    }

    fn verify_tls12_signature(
        &self,
        message: &[u8],
        cert: &CertificateDer<'_>,
        dss: &rustls::DigitallySignedStruct,
    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
        rustls::crypto::verify_tls12_signature(
            message,
            cert,
            dss,
            &self.0.signature_verification_algorithms,
        )
    }

    fn verify_tls13_signature(
        &self,
        message: &[u8],
        cert: &CertificateDer<'_>,
        dss: &rustls::DigitallySignedStruct,
    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
        rustls::crypto::verify_tls13_signature(
            message,
            cert,
            dss,
            &self.0.signature_verification_algorithms,
        )
    }

    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
        self.0.signature_verification_algorithms.supported_schemes()
    }
}

/// 証明書データ
#[derive(Clone, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum Certificate {
    Pem(Vec<u8>),
    Der(Vec<u8>),
}

impl Certificate {
    fn into_rustls_cert(self) -> Result<Vec<CertificateDer<'static>>, TransportError> {
        let res = match self {
            Certificate::Pem(pem) => {
                let certs: Vec<_> = CertificateDer::pem_slice_iter(&pem)
                    .filter_map(|result| match result {
                        Ok(cert) => Some(cert),
                        Err(e) => {
                            log::error!("{e}");
                            None
                        }
                    })
                    .collect();
                if certs.is_empty() {
                    return Err(TransportError::from_msg("invalid pem data"));
                }
                certs
            }
            Certificate::Der(der) => vec![CertificateDer::from_slice(&der).into_owned()],
        };
        Ok(res)
    }

    fn into_rustls_key(self) -> Result<PrivateKeyDer<'static>, TransportError> {
        let res = match self {
            Certificate::Pem(pem) => PrivateKeyDer::from_pem_slice(&pem)
                .map_err(|_| TransportError::from_msg("no key found"))?,
            Certificate::Der(der) => {
                PrivateKeyDer::try_from(der).map_err(TransportError::from_msg)?
            }
        };
        Ok(res)
    }
}

pub fn rustls_config_client(
    skip_server_verification: bool,
    client_auth_cert_and_key: Option<(Certificate, Certificate)>,
) -> Result<Option<rustls::ClientConfig>, TransportError> {
    let client_auth_cert_and_key = if let Some((cert, key)) = client_auth_cert_and_key {
        Some((cert.into_rustls_cert()?, key.into_rustls_key()?))
    } else {
        None
    };

    let provider = Arc::new(rustls::crypto::ring::default_provider());

    let client_config = if skip_server_verification {
        // Skip server verification
        let builder = rustls::ClientConfig::builder_with_provider(Arc::clone(&provider))
            .with_safe_default_protocol_versions()
            .map_err(TransportError::new)?
            .dangerous()
            .with_custom_certificate_verifier(SkipServerVerification::new());
        if let Some((client_auth_cert, client_auth_key)) = client_auth_cert_and_key {
            builder
                .with_client_auth_cert(client_auth_cert, client_auth_key)
                .map_err(TransportError::new)?
        } else {
            builder.with_no_client_auth()
        }
    } else if let Some((client_auth_cert, client_auth_key)) = client_auth_cert_and_key {
        // Use system native root certs and use client auth
        let mut roots = rustls::RootCertStore::empty();
        let certs = load_native_certs().map_err(TransportError::new)?;
        for cert in certs.into_iter() {
            roots.add(cert).map_err(TransportError::new)?;
        }
        rustls::ClientConfig::builder_with_provider(provider)
            .with_safe_default_protocol_versions()
            .map_err(TransportError::new)?
            .with_root_certificates(roots)
            .with_client_auth_cert(client_auth_cert, client_auth_key)
            .map_err(TransportError::new)?
    } else {
        // Use default tls config
        return Ok(None);
    };

    Ok(Some(client_config))
}

pub fn load_native_certs() -> Result<Vec<CertificateDer<'static>>, TransportError> {
    let rustls_native_certs::CertificateResult { certs, errors, .. } =
        rustls_native_certs::load_native_certs();

    for e in errors {
        log::warn!("loat native cert error: {e}");
    }

    if certs.is_empty() {
        return Err(TransportError::from_msg(
            "cannot get any native certificate",
        ));
    }
    Ok(certs)
}