use crate::{
crypto::SignaturePublicKey,
extension::ExtensionType,
external_client::{ExternalClient, ExternalClientConfig},
group::{
mls_rules::{DefaultMlsRules, MlsRules},
proposal::ProposalType,
},
identity::CredentialType,
protocol_version::ProtocolVersion,
tree_kem::Capabilities,
CryptoProvider, Sealed,
};
use std::{
collections::HashMap,
fmt::{self, Debug},
};
pub type ExternalBaseConfig = Config<Missing, DefaultMlsRules, Missing>;
#[derive(Debug)]
pub struct ExternalClientBuilder<C>(C);
impl Default for ExternalClientBuilder<ExternalBaseConfig> {
fn default() -> Self {
Self::new()
}
}
impl ExternalClientBuilder<ExternalBaseConfig> {
pub fn new() -> Self {
Self(Config(ConfigInner {
settings: Default::default(),
identity_provider: Missing,
mls_rules: DefaultMlsRules::new(),
crypto_provider: Missing,
signing_data: None,
}))
}
}
impl<C: IntoConfig> ExternalClientBuilder<C> {
pub fn extension_type(
self,
type_: ExtensionType,
) -> ExternalClientBuilder<IntoConfigOutput<C>> {
self.extension_types(Some(type_))
}
pub fn extension_types<I>(self, types: I) -> ExternalClientBuilder<IntoConfigOutput<C>>
where
I: IntoIterator<Item = ExtensionType>,
{
let mut c = self.0.into_config();
c.0.settings.extension_types.extend(types);
ExternalClientBuilder(c)
}
pub fn custom_proposal_type(
self,
type_: ProposalType,
) -> ExternalClientBuilder<IntoConfigOutput<C>> {
self.custom_proposal_types(Some(type_))
}
pub fn custom_proposal_types<I>(self, types: I) -> ExternalClientBuilder<IntoConfigOutput<C>>
where
I: IntoIterator<Item = ProposalType>,
{
let mut c = self.0.into_config();
c.0.settings.custom_proposal_types.extend(types);
ExternalClientBuilder(c)
}
pub fn protocol_version(
self,
version: ProtocolVersion,
) -> ExternalClientBuilder<IntoConfigOutput<C>> {
self.protocol_versions(Some(version))
}
pub fn protocol_versions<I>(self, versions: I) -> ExternalClientBuilder<IntoConfigOutput<C>>
where
I: IntoIterator<Item = ProtocolVersion>,
{
let mut c = self.0.into_config();
c.0.settings.protocol_versions.extend(versions);
ExternalClientBuilder(c)
}
pub fn external_signing_key(
self,
id: Vec<u8>,
key: SignaturePublicKey,
) -> ExternalClientBuilder<IntoConfigOutput<C>> {
let mut c = self.0.into_config();
c.0.settings.external_signing_keys.insert(id, key);
ExternalClientBuilder(c)
}
pub fn max_epoch_jitter(self, max_jitter: u64) -> ExternalClientBuilder<IntoConfigOutput<C>> {
let mut c = self.0.into_config();
c.0.settings.max_epoch_jitter = Some(max_jitter);
ExternalClientBuilder(c)
}
pub fn cache_proposals(
self,
cache_proposals: bool,
) -> ExternalClientBuilder<IntoConfigOutput<C>> {
let mut c = self.0.into_config();
c.0.settings.cache_proposals = cache_proposals;
ExternalClientBuilder(c)
}
pub fn identity_provider<I>(
self,
identity_provider: I,
) -> ExternalClientBuilder<WithIdentityProvider<I, C>>
where
I: IdentityProvider,
{
let Config(c) = self.0.into_config();
ExternalClientBuilder(Config(ConfigInner {
settings: c.settings,
identity_provider,
mls_rules: c.mls_rules,
crypto_provider: c.crypto_provider,
signing_data: c.signing_data,
}))
}
pub fn crypto_provider<Cp>(
self,
crypto_provider: Cp,
) -> ExternalClientBuilder<WithCryptoProvider<Cp, C>>
where
Cp: CryptoProvider,
{
let Config(c) = self.0.into_config();
ExternalClientBuilder(Config(ConfigInner {
settings: c.settings,
identity_provider: c.identity_provider,
mls_rules: c.mls_rules,
crypto_provider,
signing_data: c.signing_data,
}))
}
pub fn mls_rules<Pr>(self, mls_rules: Pr) -> ExternalClientBuilder<WithMlsRules<Pr, C>>
where
Pr: MlsRules,
{
let Config(c) = self.0.into_config();
ExternalClientBuilder(Config(ConfigInner {
settings: c.settings,
identity_provider: c.identity_provider,
mls_rules,
crypto_provider: c.crypto_provider,
signing_data: c.signing_data,
}))
}
pub fn signer(
self,
signer: SignatureSecretKey,
signing_identity: SigningIdentity,
) -> ExternalClientBuilder<IntoConfigOutput<C>> {
let mut c = self.0.into_config();
c.0.signing_data = Some((signer, signing_identity));
ExternalClientBuilder(c)
}
}
impl<C: IntoConfig> ExternalClientBuilder<C>
where
C::IdentityProvider: IdentityProvider + Clone,
C::MlsRules: MlsRules + Clone,
C::CryptoProvider: CryptoProvider + Clone,
{
pub(crate) fn build_config(self) -> IntoConfigOutput<C> {
let mut c = self.0.into_config();
if c.0.settings.protocol_versions.is_empty() {
c.0.settings.protocol_versions = ProtocolVersion::all().collect();
}
c
}
pub fn build(self) -> ExternalClient<IntoConfigOutput<C>> {
let mut c = self.build_config();
let signing_data = c.0.signing_data.take();
ExternalClient::new(c, signing_data)
}
}
#[derive(Debug)]
pub struct Missing;
pub type WithIdentityProvider<I, C> =
Config<I, <C as IntoConfig>::MlsRules, <C as IntoConfig>::CryptoProvider>;
pub type WithMlsRules<Pr, C> =
Config<<C as IntoConfig>::IdentityProvider, Pr, <C as IntoConfig>::CryptoProvider>;
pub type WithCryptoProvider<Cp, C> =
Config<<C as IntoConfig>::IdentityProvider, <C as IntoConfig>::MlsRules, Cp>;
pub type IntoConfigOutput<C> = Config<
<C as IntoConfig>::IdentityProvider,
<C as IntoConfig>::MlsRules,
<C as IntoConfig>::CryptoProvider,
>;
impl<Ip, Pr, Cp> ExternalClientConfig for ConfigInner<Ip, Pr, Cp>
where
Ip: IdentityProvider + Clone,
Pr: MlsRules + Clone,
Cp: CryptoProvider + Clone,
{
type IdentityProvider = Ip;
type MlsRules = Pr;
type CryptoProvider = Cp;
fn supported_extensions(&self) -> Vec<ExtensionType> {
self.settings.extension_types.clone()
}
fn supported_protocol_versions(&self) -> Vec<ProtocolVersion> {
self.settings.protocol_versions.clone()
}
fn identity_provider(&self) -> Self::IdentityProvider {
self.identity_provider.clone()
}
fn crypto_provider(&self) -> Self::CryptoProvider {
self.crypto_provider.clone()
}
fn external_signing_key(&self, external_key_id: &[u8]) -> Option<SignaturePublicKey> {
self.settings
.external_signing_keys
.get(external_key_id)
.cloned()
}
fn mls_rules(&self) -> Self::MlsRules {
self.mls_rules.clone()
}
fn max_epoch_jitter(&self) -> Option<u64> {
self.settings.max_epoch_jitter
}
fn cache_proposals(&self) -> bool {
self.settings.cache_proposals
}
fn supported_custom_proposals(&self) -> Vec<ProposalType> {
self.settings.custom_proposal_types.clone()
}
}
impl<Ip, Mpf, Cp> Sealed for Config<Ip, Mpf, Cp> {}
impl<Ip, Pr, Cp> MlsConfig for Config<Ip, Pr, Cp>
where
Ip: IdentityProvider + Clone,
Pr: MlsRules + Clone,
Cp: CryptoProvider + Clone,
{
type Output = ConfigInner<Ip, Pr, Cp>;
fn get(&self) -> &Self::Output {
&self.0
}
}
pub trait MlsConfig: Send + Sync + Clone + Sealed {
#[doc(hidden)]
type Output: ExternalClientConfig;
#[doc(hidden)]
fn get(&self) -> &Self::Output;
}
impl<T: MlsConfig> ExternalClientConfig for T {
type IdentityProvider = <T::Output as ExternalClientConfig>::IdentityProvider;
type MlsRules = <T::Output as ExternalClientConfig>::MlsRules;
type CryptoProvider = <T::Output as ExternalClientConfig>::CryptoProvider;
fn supported_extensions(&self) -> Vec<ExtensionType> {
self.get().supported_extensions()
}
fn supported_protocol_versions(&self) -> Vec<ProtocolVersion> {
self.get().supported_protocol_versions()
}
fn supported_custom_proposals(&self) -> Vec<ProposalType> {
self.get().supported_custom_proposals()
}
fn identity_provider(&self) -> Self::IdentityProvider {
self.get().identity_provider()
}
fn crypto_provider(&self) -> Self::CryptoProvider {
self.get().crypto_provider()
}
fn external_signing_key(&self, external_key_id: &[u8]) -> Option<SignaturePublicKey> {
self.get().external_signing_key(external_key_id)
}
fn mls_rules(&self) -> Self::MlsRules {
self.get().mls_rules()
}
fn cache_proposals(&self) -> bool {
self.get().cache_proposals()
}
fn max_epoch_jitter(&self) -> Option<u64> {
self.get().max_epoch_jitter()
}
fn capabilities(&self) -> Capabilities {
self.get().capabilities()
}
fn version_supported(&self, version: ProtocolVersion) -> bool {
self.get().version_supported(version)
}
fn supported_credentials(&self) -> Vec<CredentialType> {
self.get().supported_credentials()
}
}
#[derive(Clone)]
pub(crate) struct Settings {
pub(crate) extension_types: Vec<ExtensionType>,
pub(crate) custom_proposal_types: Vec<ProposalType>,
pub(crate) protocol_versions: Vec<ProtocolVersion>,
pub(crate) external_signing_keys: HashMap<Vec<u8>, SignaturePublicKey>,
pub(crate) max_epoch_jitter: Option<u64>,
pub(crate) cache_proposals: bool,
}
impl Debug for Settings {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Settings")
.field("extension_types", &self.extension_types)
.field("custom_proposal_types", &self.custom_proposal_types)
.field("protocol_versions", &self.protocol_versions)
.field(
"external_signing_keys",
&mls_rs_core::debug::pretty_with(|f| {
f.debug_map()
.entries(
self.external_signing_keys
.iter()
.map(|(k, v)| (mls_rs_core::debug::pretty_bytes(k), v)),
)
.finish()
}),
)
.field("max_epoch_jitter", &self.max_epoch_jitter)
.field("cache_proposals", &self.cache_proposals)
.finish()
}
}
impl Default for Settings {
fn default() -> Self {
Self {
cache_proposals: true,
extension_types: vec![],
protocol_versions: vec![],
external_signing_keys: Default::default(),
max_epoch_jitter: None,
custom_proposal_types: vec![],
}
}
}
mod private {
use mls_rs_core::{crypto::SignatureSecretKey, identity::SigningIdentity};
use super::{IntoConfigOutput, Settings};
#[derive(Clone, Debug)]
pub struct Config<Ip, Pr, Cp>(pub(crate) ConfigInner<Ip, Pr, Cp>);
#[derive(Clone, Debug)]
pub struct ConfigInner<Ip, Mpf, Cp> {
pub(crate) settings: Settings,
pub(crate) identity_provider: Ip,
pub(crate) mls_rules: Mpf,
pub(crate) crypto_provider: Cp,
pub(crate) signing_data: Option<(SignatureSecretKey, SigningIdentity)>,
}
pub trait IntoConfig {
type IdentityProvider;
type MlsRules;
type CryptoProvider;
fn into_config(self) -> IntoConfigOutput<Self>;
}
impl<Ip, Pr, Cp> IntoConfig for Config<Ip, Pr, Cp> {
type IdentityProvider = Ip;
type MlsRules = Pr;
type CryptoProvider = Cp;
fn into_config(self) -> Self {
self
}
}
}
use mls_rs_core::{
crypto::SignatureSecretKey,
identity::{IdentityProvider, SigningIdentity},
};
use private::{Config, ConfigInner, IntoConfig};
#[cfg(test)]
pub(crate) mod test_utils {
use crate::{
cipher_suite::CipherSuite, crypto::test_utils::TestCryptoProvider,
identity::basic::BasicIdentityProvider,
};
use super::{
ExternalBaseConfig, ExternalClientBuilder, WithCryptoProvider, WithIdentityProvider,
};
pub type TestExternalClientConfig = WithIdentityProvider<
BasicIdentityProvider,
WithCryptoProvider<TestCryptoProvider, ExternalBaseConfig>,
>;
pub type TestExternalClientBuilder = ExternalClientBuilder<TestExternalClientConfig>;
impl TestExternalClientBuilder {
pub fn new_for_test() -> Self {
ExternalClientBuilder::new()
.crypto_provider(TestCryptoProvider::default())
.identity_provider(BasicIdentityProvider::new())
}
pub fn new_for_test_disabling_cipher_suite(cipher_suite: CipherSuite) -> Self {
let crypto_provider = TestCryptoProvider::with_enabled_cipher_suites(
TestCryptoProvider::all_supported_cipher_suites()
.into_iter()
.filter(|cs| cs != &cipher_suite)
.collect(),
);
ExternalClientBuilder::new()
.crypto_provider(crypto_provider)
.identity_provider(BasicIdentityProvider::new())
}
}
}