core_crypto/mls/credential/
ext.rs1use crate::prelude::{CryptoError, CryptoResult, DeviceStatus, MlsCiphersuite, MlsCredentialType, WireIdentity};
2use openmls::prelude::{Credential, CredentialType, CredentialWithKey};
3use openmls_traits::types::{HashType, SignatureScheme};
4use wire_e2e_identity::prelude::{compute_raw_key_thumbprint, HashAlgorithm, JwsAlgorithm};
5use x509_cert::{der::Decode, Certificate};
6
7#[allow(dead_code)]
8pub(crate) trait CredentialExt {
9 fn parse_leaf_cert(&self) -> CryptoResult<Option<Certificate>>;
10 fn get_type(&self) -> CryptoResult<MlsCredentialType>;
11 fn extract_identity(
12 &self,
13 cs: MlsCiphersuite,
14 env: Option<&wire_e2e_identity::prelude::x509::revocation::PkiEnvironment>,
15 ) -> CryptoResult<WireIdentity>;
16 fn extract_public_key(&self) -> CryptoResult<Option<Vec<u8>>>;
17 fn is_basic(&self) -> bool;
18}
19
20impl CredentialExt for CredentialWithKey {
21 fn parse_leaf_cert(&self) -> CryptoResult<Option<Certificate>> {
22 self.credential.parse_leaf_cert()
23 }
24
25 fn get_type(&self) -> CryptoResult<MlsCredentialType> {
26 self.credential.get_type()
27 }
28
29 fn extract_identity(
30 &self,
31 cs: MlsCiphersuite,
32 env: Option<&wire_e2e_identity::prelude::x509::revocation::PkiEnvironment>,
33 ) -> CryptoResult<WireIdentity> {
34 match self.credential.mls_credential() {
35 openmls::prelude::MlsCredentialType::X509(cert) => cert.extract_identity(cs, env),
36 openmls::prelude::MlsCredentialType::Basic(_) => {
37 let client_id = std::str::from_utf8(self.credential.identity())
40 .unwrap_or_default()
41 .to_string();
42
43 let thumbprint = compute_thumbprint(cs, self.signature_key.as_slice())?;
44
45 Ok(WireIdentity {
46 client_id,
47 credential_type: MlsCredentialType::Basic,
48 thumbprint,
49 status: DeviceStatus::Valid,
50 x509_identity: None,
51 })
52 }
53 }
54 }
55
56 fn extract_public_key(&self) -> CryptoResult<Option<Vec<u8>>> {
57 self.credential.extract_public_key()
58 }
59
60 fn is_basic(&self) -> bool {
61 self.credential.is_basic()
62 }
63}
64
65impl CredentialExt for Credential {
66 fn parse_leaf_cert(&self) -> CryptoResult<Option<Certificate>> {
67 match self.mls_credential() {
68 openmls::prelude::MlsCredentialType::X509(cert) => cert.parse_leaf_cert(),
69 _ => Ok(None),
70 }
71 }
72
73 fn get_type(&self) -> CryptoResult<MlsCredentialType> {
74 match self.credential_type() {
75 CredentialType::Basic => Ok(MlsCredentialType::Basic),
76 CredentialType::X509 => Ok(MlsCredentialType::X509),
77 CredentialType::Unknown(_) => Err(CryptoError::ImplementationError),
78 }
79 }
80
81 fn extract_identity(
82 &self,
83 _cs: MlsCiphersuite,
84 _env: Option<&wire_e2e_identity::prelude::x509::revocation::PkiEnvironment>,
85 ) -> CryptoResult<WireIdentity> {
86 Err(CryptoError::ImplementationError)
90 }
91
92 fn extract_public_key(&self) -> CryptoResult<Option<Vec<u8>>> {
93 match self.mls_credential() {
94 openmls::prelude::MlsCredentialType::X509(cert) => cert.extract_public_key(),
95 _ => Ok(None),
96 }
97 }
98
99 fn is_basic(&self) -> bool {
100 self.credential_type() == openmls::prelude::CredentialType::Basic
101 }
102}
103
104impl CredentialExt for openmls::prelude::Certificate {
105 fn parse_leaf_cert(&self) -> CryptoResult<Option<Certificate>> {
106 let leaf = self.certificates.first().ok_or(CryptoError::InvalidIdentity)?;
107 let leaf = Certificate::from_der(leaf.as_slice())?;
108 Ok(Some(leaf))
109 }
110
111 fn get_type(&self) -> CryptoResult<MlsCredentialType> {
112 Ok(MlsCredentialType::X509)
113 }
114
115 fn extract_identity(
116 &self,
117 cs: MlsCiphersuite,
118 env: Option<&wire_e2e_identity::prelude::x509::revocation::PkiEnvironment>,
119 ) -> CryptoResult<WireIdentity> {
120 let leaf = self.certificates.first().ok_or(CryptoError::InvalidIdentity)?;
121 let leaf = leaf.as_slice();
122 use wire_e2e_identity::prelude::WireIdentityReader as _;
123 let identity = leaf
124 .extract_identity(env, cs.e2ei_hash_alg())
125 .map_err(|_| CryptoError::InvalidIdentity)?;
126 let identity = WireIdentity::try_from((identity, leaf))?;
127 Ok(identity)
128 }
129
130 fn extract_public_key(&self) -> CryptoResult<Option<Vec<u8>>> {
131 let leaf = self.certificates.first().ok_or(CryptoError::InvalidIdentity)?;
132 use wire_e2e_identity::prelude::WireIdentityReader as _;
133 let pk = leaf
134 .as_slice()
135 .extract_public_key()
136 .map_err(|_| CryptoError::InvalidIdentity)?;
137 Ok(Some(pk))
138 }
139
140 fn is_basic(&self) -> bool {
141 false
142 }
143}
144
145fn compute_thumbprint(cs: MlsCiphersuite, raw_key: &[u8]) -> CryptoResult<String> {
146 let sign_alg = match cs.signature_algorithm() {
147 SignatureScheme::ED25519 => JwsAlgorithm::Ed25519,
148 SignatureScheme::ECDSA_SECP256R1_SHA256 => JwsAlgorithm::P256,
149 SignatureScheme::ECDSA_SECP384R1_SHA384 => JwsAlgorithm::P384,
150 SignatureScheme::ECDSA_SECP521R1_SHA512 => JwsAlgorithm::P521,
151 SignatureScheme::ED448 => return Err(CryptoError::Unsupported),
152 };
153 let hash_alg = match cs.hash_algorithm() {
154 HashType::Sha2_256 => HashAlgorithm::SHA256,
155 HashType::Sha2_384 => HashAlgorithm::SHA384,
156 HashType::Sha2_512 => HashAlgorithm::SHA512,
157 };
158
159 Ok(compute_raw_key_thumbprint(sign_alg, hash_alg, raw_key).unwrap_or_default())
161}