1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use super::CredentialBundle;
use crate::{
    prelude::CryptoError,
    prelude::{CertificateBundle, Client, ClientId, CryptoResult},
};
use mls_crypto_provider::MlsCryptoProvider;
use openmls_traits::types::SignatureScheme;
use std::collections::{HashMap, HashSet};

/// Used by consumers to initializes a MLS client. Encompasses all the client types available.
/// Could be enriched later with Verifiable Presentations.
#[derive(Debug, Clone)]
pub enum ClientIdentifier {
    /// Basic keypair
    Basic(ClientId),
    /// X509 certificate
    X509(HashMap<SignatureScheme, CertificateBundle>),
}

impl ClientIdentifier {
    /// Extract the unique [ClientId] from an identifier. Use with parsimony as, in case of a x509
    /// certificate this leads to parsing the certificate
    pub fn get_id(&self) -> CryptoResult<std::borrow::Cow<ClientId>> {
        match self {
            ClientIdentifier::Basic(id) => Ok(std::borrow::Cow::Borrowed(id)),
            ClientIdentifier::X509(certs) => {
                // since ClientId has uniqueness constraints, it is the same for all certificates.
                // hence no need to compute it for every certificate then verify its uniqueness
                // that's not a getter's job
                let cert = certs.values().next().ok_or(CryptoError::ImplementationError)?;
                let id = cert.get_client_id()?;
                Ok(std::borrow::Cow::Owned(id))
            }
        }
    }

    /// Generate a new CredentialBundle (Credential + KeyPair) for each ciphersuite.
    /// This method does not persist them in the keystore !
    pub fn generate_credential_bundles(
        self,
        backend: &MlsCryptoProvider,
        signature_schemes: HashSet<SignatureScheme>,
    ) -> CryptoResult<Vec<(SignatureScheme, ClientId, CredentialBundle)>> {
        match self {
            ClientIdentifier::Basic(id) => signature_schemes.iter().try_fold(
                Vec::with_capacity(signature_schemes.len()),
                |mut acc, &sc| -> CryptoResult<_> {
                    let cb = Client::new_basic_credential_bundle(&id, sc, backend)?;
                    acc.push((sc, id.clone(), cb));
                    Ok(acc)
                },
            ),
            ClientIdentifier::X509(certs) => {
                let cap = certs.len();
                certs
                    .into_iter()
                    .try_fold(Vec::with_capacity(cap), |mut acc, (sc, cert)| -> CryptoResult<_> {
                        let id = cert.get_client_id()?;
                        let cb = Client::new_x509_credential_bundle(cert)?;
                        acc.push((sc, id, cb));
                        Ok(acc)
                    })
            }
        }
    }
}