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