core_crypto/mls/credential/
x509.rs#[cfg(test)]
use crate::test_utils::x509::X509Certificate;
#[cfg(test)]
use mls_crypto_provider::PkiKeypair;
#[cfg(test)]
use x509_cert::der::Encode;
use super::{Error, Result};
use openmls_traits::types::SignatureScheme;
use wire_e2e_identity::prelude::{HashAlgorithm, WireIdentityReader};
use zeroize::Zeroize;
use crate::{RecursiveError, e2e_identity::id::WireQualifiedClientId, prelude::ClientId};
#[derive(Debug, Clone, Zeroize)]
#[zeroize(drop)]
pub struct CertificatePrivateKey {
    pub(crate) value: Vec<u8>,
    #[zeroize(skip)]
    pub(crate) signature_scheme: SignatureScheme,
}
impl CertificatePrivateKey {
    pub(crate) fn into_parts(mut self) -> (Vec<u8>, SignatureScheme) {
        (std::mem::take(&mut self.value), self.signature_scheme)
    }
}
#[derive(Debug, Clone)]
pub struct CertificateBundle {
    pub certificate_chain: Vec<Vec<u8>>,
    pub private_key: CertificatePrivateKey,
}
impl CertificateBundle {
    pub fn get_client_id(&self) -> Result<ClientId> {
        let leaf = self.certificate_chain.first().ok_or(Error::InvalidIdentity)?;
        let hash_alg = match self.private_key.signature_scheme {
            SignatureScheme::ECDSA_SECP256R1_SHA256 | SignatureScheme::ED25519 => HashAlgorithm::SHA256,
            SignatureScheme::ECDSA_SECP384R1_SHA384 => HashAlgorithm::SHA384,
            SignatureScheme::ED448 | SignatureScheme::ECDSA_SECP521R1_SHA512 => HashAlgorithm::SHA512,
        };
        let identity = leaf
            .extract_identity(None, hash_alg)
            .map_err(|_| Error::InvalidIdentity)?;
        let client_id = identity
            .client_id
            .parse::<WireQualifiedClientId>()
            .map_err(RecursiveError::e2e_identity("parsing wire qualified client id"))?;
        Ok(client_id.into())
    }
    pub fn get_created_at(&self) -> Result<u64> {
        let leaf = self.certificate_chain.first().ok_or(Error::InvalidIdentity)?;
        leaf.extract_created_at().map_err(|_| Error::InvalidIdentity)
    }
}
#[cfg(test)]
fn new_rand_client(domain: Option<String>) -> (String, String) {
    let rand_str = |n: usize| {
        use rand::distributions::{Alphanumeric, DistString as _};
        Alphanumeric.sample_string(&mut rand::thread_rng(), n)
    };
    let user_id = uuid::Uuid::new_v4().to_string();
    let domain = domain.unwrap_or_else(|| format!("{}.com", rand_str(6)));
    let client_id = wire_e2e_identity::prelude::E2eiClientId::try_new(user_id, rand::random::<u64>(), &domain)
        .unwrap()
        .to_qualified();
    (client_id, domain)
}
#[cfg(test)]
impl CertificateBundle {
    #![allow(missing_docs)]
    pub fn rand(name: &ClientId, signer: &crate::test_utils::x509::X509Certificate) -> Self {
        let handle = format!("{name}_wire");
        let display_name = format!("{name} Smith");
        Self::new(&handle, &display_name, None, None, signer)
    }
    pub fn new(
        handle: &str,
        display_name: &str,
        client_id: Option<&crate::e2e_identity::id::QualifiedE2eiClientId>,
        cert_keypair: Option<PkiKeypair>,
        signer: &crate::test_utils::x509::X509Certificate,
    ) -> Self {
        Self::new_with_expiration(handle, display_name, client_id, cert_keypair, signer, None)
    }
    pub fn new_with_expiration(
        handle: &str,
        display_name: &str,
        client_id: Option<&crate::e2e_identity::id::QualifiedE2eiClientId>,
        cert_keypair: Option<PkiKeypair>,
        signer: &crate::test_utils::x509::X509Certificate,
        expiration: Option<std::time::Duration>,
    ) -> Self {
        let domain = "world.com";
        let (client_id, domain) = client_id
            .map(|cid| {
                let cid = String::from_utf8(cid.to_vec()).unwrap();
                (cid, domain.to_string())
            })
            .unwrap_or_else(|| new_rand_client(Some(domain.to_string())));
        let mut cert_params = crate::test_utils::x509::CertificateParams {
            domain: domain.into(),
            common_name: Some(display_name.to_string()),
            handle: Some(handle.to_string()),
            client_id: Some(client_id.to_string()),
            cert_keypair,
            ..Default::default()
        };
        if let Some(expiration) = expiration {
            cert_params.expiration = expiration;
        }
        let cert = signer.create_and_sign_end_identity(cert_params);
        Self::from_certificate_and_issuer(&cert, signer)
    }
    pub fn new_with_default_values(
        signer: &crate::test_utils::x509::X509Certificate,
        expiration: Option<std::time::Duration>,
    ) -> Self {
        Self::new_with_expiration("alice_wire@world.com", "Alice Smith", None, None, signer, expiration)
    }
    pub fn from_self_signed_certificate(cert: &X509Certificate) -> Self {
        Self::from_certificate_and_issuer(cert, cert)
    }
    pub fn from_certificate_and_issuer(cert: &X509Certificate, issuer: &X509Certificate) -> Self {
        Self {
            certificate_chain: vec![cert.certificate.to_der().unwrap(), issuer.certificate.to_der().unwrap()],
            private_key: CertificatePrivateKey {
                value: cert.pki_keypair.signing_key_bytes(),
                signature_scheme: cert.signature_scheme,
            },
        }
    }
    pub fn rand_identifier(
        name: &str,
        signers: &[&crate::test_utils::x509::X509Certificate],
    ) -> crate::prelude::ClientIdentifier {
        crate::prelude::ClientIdentifier::X509(
            signers
                .iter()
                .map(|signer| (signer.signature_scheme, Self::rand(&name.into(), signer)))
                .collect::<std::collections::HashMap<_, _>>(),
        )
    }
}