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