1use std::io::{Read, Write};
28
29use openmls_traits::signatures::Signer;
30use serde::{Deserialize, Serialize};
31use tls_codec::{
32 Deserialize as TlsDeserializeTrait, DeserializeBytes, Error, Serialize as TlsSerializeTrait,
33 Size, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, VLBytes,
34};
35
36#[cfg(test)]
37mod tests;
38
39use crate::{ciphersuite::SignaturePublicKey, group::Member, treesync::LeafNode};
40use errors::*;
41
42#[cfg(doc)]
43use crate::group::MlsGroup;
44
45pub mod errors;
47
48#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
81#[cfg_attr(
82 feature = "0-8-1-storage-format",
83 derive(serde::Serialize, serde::Deserialize)
84)]
85#[cfg_attr(
86 not(feature = "0-8-1-storage-format"),
87 derive(
88 openmls_serialization_helpers::Serialize,
89 openmls_serialization_helpers::Deserialize,
90 )
91)]
92#[repr(u16)]
93pub enum CredentialType {
94 #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 0)]
95 Basic = 1,
97 #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 1)]
98 X509 = 2,
100 #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 3)]
101 Grease(u16),
103 #[cfg_attr(not(feature = "0-8-1-storage-format"), storage_tag = 2)]
104 Other(u16),
106}
107
108impl CredentialType {
109 pub fn is_grease(&self) -> bool {
114 matches!(self, CredentialType::Grease(v) if crate::grease::is_grease_value(*v))
115 }
116}
117
118impl Size for CredentialType {
119 fn tls_serialized_len(&self) -> usize {
120 2
121 }
122}
123
124impl TlsDeserializeTrait for CredentialType {
125 fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, Error>
126 where
127 Self: Sized,
128 {
129 let mut extension_type = [0u8; 2];
130 bytes.read_exact(&mut extension_type)?;
131
132 Ok(CredentialType::from(u16::from_be_bytes(extension_type)))
133 }
134}
135
136impl TlsSerializeTrait for CredentialType {
137 fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, Error> {
138 let value = u16::from(*self);
139 if CredentialType::from(value) != *self {
142 return Err(Error::EncodingError(format!(
143 "{self:?} is encoded as {value:#06x}, which decodes as a different credential type"
144 )));
145 }
146 writer.write_all(&value.to_be_bytes())?;
147
148 Ok(2)
149 }
150}
151
152impl DeserializeBytes for CredentialType {
153 fn tls_deserialize_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error>
154 where
155 Self: Sized,
156 {
157 let mut bytes_ref = bytes;
158 let credential_type = CredentialType::tls_deserialize(&mut bytes_ref)?;
159 Ok((credential_type, bytes_ref))
160 }
161}
162
163impl From<u16> for CredentialType {
164 fn from(value: u16) -> Self {
165 match value {
166 1 => CredentialType::Basic,
167 2 => CredentialType::X509,
168 other if crate::grease::is_grease_value(other) => CredentialType::Grease(other),
169 other => CredentialType::Other(other),
170 }
171 }
172}
173
174impl From<CredentialType> for u16 {
175 fn from(value: CredentialType) -> Self {
176 match value {
177 CredentialType::Basic => 1,
178 CredentialType::X509 => 2,
179 CredentialType::Grease(value) => value,
180 CredentialType::Other(other) => other,
181 }
182 }
183}
184
185#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
196pub struct Certificate {
197 cert_data: Vec<u8>,
198}
199
200#[derive(
230 Debug,
231 PartialEq,
232 Eq,
233 Clone,
234 Serialize,
235 Deserialize,
236 TlsSize,
237 TlsSerialize,
238 TlsDeserialize,
239 TlsDeserializeBytes,
240)]
241pub struct Credential {
242 credential_type: CredentialType,
243 serialized_credential_content: VLBytes,
244}
245
246impl Credential {
247 pub fn credential_type(&self) -> CredentialType {
249 self.credential_type
250 }
251
252 pub fn new(credential_type: CredentialType, serialized_credential: Vec<u8>) -> Self {
255 Self {
256 credential_type,
257 serialized_credential_content: serialized_credential.into(),
258 }
259 }
260
261 pub fn serialized_content(&self) -> &[u8] {
266 self.serialized_credential_content.as_slice()
267 }
268
269 pub fn deserialized<T: tls_codec::Size + tls_codec::Deserialize>(
271 &self,
272 ) -> Result<T, tls_codec::Error> {
273 T::tls_deserialize_exact(&self.serialized_credential_content)
274 }
275}
276
277#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
288pub struct BasicCredential {
289 identity: VLBytes,
290}
291
292impl BasicCredential {
293 pub fn new(identity: Vec<u8>) -> Self {
300 Self {
301 identity: identity.into(),
302 }
303 }
304
305 pub fn identity(&self) -> &[u8] {
307 self.identity.as_slice()
308 }
309}
310
311impl From<BasicCredential> for Credential {
312 fn from(credential: BasicCredential) -> Self {
313 Credential {
314 credential_type: CredentialType::Basic,
315 serialized_credential_content: credential.identity,
316 }
317 }
318}
319
320impl TryFrom<Credential> for BasicCredential {
321 type Error = BasicCredentialError;
322
323 fn try_from(credential: Credential) -> Result<Self, Self::Error> {
324 match credential.credential_type {
325 CredentialType::Basic => Ok(BasicCredential::new(
326 credential.serialized_credential_content.into(),
327 )),
328 _ => Err(errors::BasicCredentialError::WrongCredentialType),
329 }
330 }
331}
332
333#[derive(Debug, Clone)]
337pub struct NewSignerBundle<'a, S: Signer> {
338 pub signer: &'a S,
340 pub credential_with_key: CredentialWithKey,
342}
343
344#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
345pub struct CredentialWithKey {
347 pub credential: Credential,
349 pub signature_key: SignaturePublicKey,
351}
352
353impl From<&LeafNode> for CredentialWithKey {
354 fn from(leaf_node: &LeafNode) -> Self {
355 Self {
356 credential: leaf_node.credential().clone(),
357 signature_key: leaf_node.signature_key().clone(),
358 }
359 }
360}
361
362impl From<&Member> for CredentialWithKey {
363 fn from(member: &Member) -> Self {
364 Self {
365 credential: member.credential.clone(),
366 signature_key: member.signature_key.clone().into(),
367 }
368 }
369}
370
371#[cfg(test)]
372impl CredentialWithKey {
373 pub fn from_parts(credential: Credential, key: &[u8]) -> Self {
374 Self {
375 credential,
376 signature_key: key.into(),
377 }
378 }
379}
380
381#[cfg(any(test, feature = "test-utils"))]
382pub mod test_utils {
383 use openmls_basic_credential::SignatureKeyPair;
384 use openmls_traits::{types::SignatureScheme, OpenMlsProvider};
385
386 use super::{BasicCredential, CredentialWithKey};
387
388 #[openmls_traits::maybe_async]
396 pub async fn new_credential(
397 provider: &impl OpenMlsProvider,
398 identity: &[u8],
399 signature_scheme: SignatureScheme,
400 ) -> (CredentialWithKey, SignatureKeyPair) {
401 let credential = BasicCredential::new(identity.into());
402 let signature_keys = SignatureKeyPair::new(signature_scheme).unwrap();
403 signature_keys.store(provider.storage()).await.unwrap();
404
405 (
406 CredentialWithKey {
407 credential: credential.into(),
408 signature_key: signature_keys.public().into(),
409 },
410 signature_keys,
411 )
412 }
413}
414
415#[cfg(test)]
416mod unit_tests {
417 use tls_codec::{
418 DeserializeBytes, Serialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
419 };
420
421 use super::{BasicCredential, Credential, CredentialType};
422
423 #[test]
424 fn basic_credential_identity_and_codec() {
425 const IDENTITY: &str = "identity";
426 let basic_credential = BasicCredential::new(IDENTITY.into());
428 assert_eq!(basic_credential.identity(), IDENTITY.as_bytes());
429
430 let credential = Credential::from(basic_credential.clone());
432 let serialized = credential.tls_serialize_detached().unwrap();
433
434 let deserialized = Credential::tls_deserialize_exact_bytes(&serialized).unwrap();
435 assert_eq!(credential.credential_type(), deserialized.credential_type());
436 assert_eq!(
437 credential.serialized_content(),
438 deserialized.serialized_content()
439 );
440
441 let deserialized_basic_credential = BasicCredential::try_from(deserialized).unwrap();
442 assert_eq!(
443 deserialized_basic_credential.identity(),
444 IDENTITY.as_bytes()
445 );
446 assert_eq!(basic_credential, deserialized_basic_credential);
447 }
448
449 #[test]
451 fn custom_credential() {
452 #[derive(
453 Debug, Clone, PartialEq, Eq, TlsSize, TlsSerialize, TlsDeserialize, TlsDeserializeBytes,
454 )]
455 struct CustomCredential {
456 custom_field1: u32,
457 custom_field2: Vec<u8>,
458 custom_field3: Option<u8>,
459 }
460
461 let custom_credential = CustomCredential {
462 custom_field1: 42,
463 custom_field2: vec![1, 2, 3],
464 custom_field3: Some(2),
465 };
466
467 let credential = Credential::new(
468 CredentialType::Other(1234),
469 custom_credential.tls_serialize_detached().unwrap(),
470 );
471
472 let serialized = credential.tls_serialize_detached().unwrap();
473 let deserialized = Credential::tls_deserialize_exact_bytes(&serialized).unwrap();
474 assert_eq!(credential, deserialized);
475
476 let deserialized_custom_credential =
477 CustomCredential::tls_deserialize_exact_bytes(deserialized.serialized_content())
478 .unwrap();
479
480 assert_eq!(custom_credential, deserialized_custom_credential);
481 }
482}