core_crypto/mls/credential/
x509.rs1#[cfg(test)]
2use crate::test_utils::x509::X509Certificate;
3#[cfg(test)]
4use mls_crypto_provider::PkiKeypair;
5#[cfg(test)]
6use x509_cert::der::Encode;
7
8use super::{Error, Result};
9use openmls_traits::types::SignatureScheme;
10use wire_e2e_identity::prelude::{HashAlgorithm, WireIdentityReader};
11use zeroize::Zeroize;
12
13use crate::{RecursiveError, e2e_identity::id::WireQualifiedClientId, prelude::ClientId};
14
15#[derive(Debug, Clone, Zeroize)]
16#[zeroize(drop)]
17pub struct CertificatePrivateKey {
18 pub(crate) value: Vec<u8>,
19 #[zeroize(skip)]
20 pub(crate) signature_scheme: SignatureScheme,
21}
22
23impl CertificatePrivateKey {
24 pub(crate) fn into_parts(mut self) -> (Vec<u8>, SignatureScheme) {
25 (std::mem::take(&mut self.value), self.signature_scheme)
26 }
27}
28
29#[derive(Debug, Clone)]
33pub struct CertificateBundle {
34 pub certificate_chain: Vec<Vec<u8>>,
37 pub private_key: CertificatePrivateKey,
39}
40
41impl CertificateBundle {
42 pub fn get_client_id(&self) -> Result<ClientId> {
44 let leaf = self.certificate_chain.first().ok_or(Error::InvalidIdentity)?;
45
46 let hash_alg = match self.private_key.signature_scheme {
47 SignatureScheme::ECDSA_SECP256R1_SHA256 | SignatureScheme::ED25519 => HashAlgorithm::SHA256,
48 SignatureScheme::ECDSA_SECP384R1_SHA384 => HashAlgorithm::SHA384,
49 SignatureScheme::ED448 | SignatureScheme::ECDSA_SECP521R1_SHA512 => HashAlgorithm::SHA512,
50 };
51
52 let identity = leaf
53 .extract_identity(None, hash_alg)
54 .map_err(|_| Error::InvalidIdentity)?;
55 let client_id = identity
56 .client_id
57 .parse::<WireQualifiedClientId>()
58 .map_err(RecursiveError::e2e_identity("parsing wire qualified client id"))?;
59 Ok(client_id.into())
60 }
61
62 pub fn get_created_at(&self) -> Result<u64> {
64 let leaf = self.certificate_chain.first().ok_or(Error::InvalidIdentity)?;
65 leaf.extract_created_at().map_err(|_| Error::InvalidIdentity)
66 }
67}
68
69#[cfg(test)]
70fn new_rand_client(domain: Option<String>) -> (String, String) {
71 let rand_str = |n: usize| {
72 use rand::distributions::{Alphanumeric, DistString as _};
73 Alphanumeric.sample_string(&mut rand::thread_rng(), n)
74 };
75 let user_id = uuid::Uuid::new_v4().to_string();
76 let domain = domain.unwrap_or_else(|| format!("{}.com", rand_str(6)));
77 let client_id = wire_e2e_identity::prelude::E2eiClientId::try_new(user_id, rand::random::<u64>(), &domain)
78 .unwrap()
79 .to_qualified();
80 (client_id, domain)
81}
82
83#[cfg(test)]
84impl CertificateBundle {
85 #![allow(missing_docs)]
87
88 pub fn rand(name: &ClientId, signer: &crate::test_utils::x509::X509Certificate) -> Self {
90 let handle = format!("{name}_wire");
94 let display_name = format!("{name} Smith");
95 Self::new(&handle, &display_name, None, None, signer)
96 }
97
98 pub fn new(
100 handle: &str,
101 display_name: &str,
102 client_id: Option<&crate::e2e_identity::id::QualifiedE2eiClientId>,
103 cert_keypair: Option<PkiKeypair>,
104 signer: &crate::test_utils::x509::X509Certificate,
105 ) -> Self {
106 Self::new_with_expiration(handle, display_name, client_id, cert_keypair, signer, None)
107 }
108
109 pub fn new_with_expiration(
110 handle: &str,
111 display_name: &str,
112 client_id: Option<&crate::e2e_identity::id::QualifiedE2eiClientId>,
113 cert_keypair: Option<PkiKeypair>,
114 signer: &crate::test_utils::x509::X509Certificate,
115 expiration: Option<std::time::Duration>,
116 ) -> Self {
117 let domain = "world.com";
121 let (client_id, domain) = client_id
122 .map(|cid| {
123 let cid = String::from_utf8(cid.to_vec()).unwrap();
124 (cid, domain.to_string())
125 })
126 .unwrap_or_else(|| new_rand_client(Some(domain.to_string())));
127
128 let mut cert_params = crate::test_utils::x509::CertificateParams {
129 domain: domain.into(),
130 common_name: Some(display_name.to_string()),
131 handle: Some(handle.to_string()),
132 client_id: Some(client_id.to_string()),
133 cert_keypair,
134 ..Default::default()
135 };
136
137 if let Some(expiration) = expiration {
138 cert_params.expiration = expiration;
139 }
140
141 let cert = signer.create_and_sign_end_identity(cert_params);
142 Self::from_certificate_and_issuer(&cert, signer)
143 }
144
145 pub fn new_with_default_values(
146 signer: &crate::test_utils::x509::X509Certificate,
147 expiration: Option<std::time::Duration>,
148 ) -> Self {
149 Self::new_with_expiration("alice_wire@world.com", "Alice Smith", None, None, signer, expiration)
150 }
151
152 pub fn from_self_signed_certificate(cert: &X509Certificate) -> Self {
153 Self::from_certificate_and_issuer(cert, cert)
154 }
155
156 pub fn from_certificate_and_issuer(cert: &X509Certificate, issuer: &X509Certificate) -> Self {
157 Self {
158 certificate_chain: vec![cert.certificate.to_der().unwrap(), issuer.certificate.to_der().unwrap()],
159 private_key: CertificatePrivateKey {
160 value: cert.pki_keypair.signing_key_bytes(),
161 signature_scheme: cert.signature_scheme,
162 },
163 }
164 }
165
166 pub fn rand_identifier(
167 name: &str,
168 signers: &[&crate::test_utils::x509::X509Certificate],
169 ) -> crate::prelude::ClientIdentifier {
170 crate::prelude::ClientIdentifier::X509(
171 signers
172 .iter()
173 .map(|signer| (signer.signature_scheme, Self::rand(&name.into(), signer)))
174 .collect::<std::collections::HashMap<_, _>>(),
175 )
176 }
177}