use openmls_traits::{signatures::Signer, storage::StorageProvider as StorageProviderTrait};
use super::{builder::MlsGroupBuilder, *};
use crate::{
credentials::CredentialWithKey,
group::{
core_group::create_commit_params::CreateCommitParams,
errors::{ExternalCommitError, WelcomeError},
},
messages::{
group_info::{GroupInfo, VerifiableGroupInfo},
Welcome,
},
schedule::psk::store::ResumptionPskStore,
storage::OpenMlsProvider,
treesync::RatchetTreeIn,
};
impl MlsGroup {
pub fn builder() -> MlsGroupBuilder {
MlsGroupBuilder::new()
}
pub fn new<Provider: OpenMlsProvider>(
provider: &Provider,
signer: &impl Signer,
mls_group_create_config: &MlsGroupCreateConfig,
credential_with_key: CredentialWithKey,
) -> Result<Self, NewGroupError<Provider::StorageError>> {
MlsGroupBuilder::new().build_internal(
provider,
signer,
credential_with_key,
Some(mls_group_create_config.clone()),
)
}
pub fn new_with_group_id<Provider: OpenMlsProvider>(
provider: &Provider,
signer: &impl Signer,
mls_group_create_config: &MlsGroupCreateConfig,
group_id: GroupId,
credential_with_key: CredentialWithKey,
) -> Result<Self, NewGroupError<Provider::StorageError>> {
MlsGroupBuilder::new()
.with_group_id(group_id)
.build_internal(
provider,
signer,
credential_with_key,
Some(mls_group_create_config.clone()),
)
}
pub fn join_by_external_commit<Provider: OpenMlsProvider>(
provider: &Provider,
signer: &impl Signer,
ratchet_tree: Option<RatchetTreeIn>,
verifiable_group_info: VerifiableGroupInfo,
mls_group_config: &MlsGroupJoinConfig,
aad: &[u8],
credential_with_key: CredentialWithKey,
) -> Result<(Self, MlsMessageOut, Option<GroupInfo>), ExternalCommitError<Provider::StorageError>>
{
let framing_parameters = FramingParameters::new(aad, WireFormat::PublicMessage);
let proposal_store = ProposalStore::new();
let params = CreateCommitParams::builder()
.framing_parameters(framing_parameters)
.proposal_store(&proposal_store)
.credential_with_key(credential_with_key)
.build();
let (mut group, create_commit_result) = CoreGroup::join_by_external_commit(
provider,
signer,
params,
ratchet_tree,
verifiable_group_info,
)?;
group.set_max_past_epochs(mls_group_config.max_past_epochs);
let mls_group = MlsGroup {
mls_group_config: mls_group_config.clone(),
group,
proposal_store: ProposalStore::new(),
own_leaf_nodes: vec![],
aad: vec![],
group_state: MlsGroupState::PendingCommit(Box::new(PendingCommitState::External(
create_commit_result.staged_commit,
))),
};
provider
.storage()
.write_mls_join_config(mls_group.group_id(), &mls_group.mls_group_config)
.map_err(ExternalCommitError::StorageError)?;
provider
.storage()
.write_group_state(mls_group.group_id(), &mls_group.group_state)
.map_err(ExternalCommitError::StorageError)?;
mls_group
.group
.store(provider.storage())
.map_err(ExternalCommitError::StorageError)?;
let public_message: PublicMessage = create_commit_result.commit.into();
Ok((
mls_group,
public_message.into(),
create_commit_result.group_info,
))
}
}
fn transpose_err_opt<T, E>(v: Result<Option<T>, E>) -> Option<Result<T, E>> {
match v {
Ok(Some(v)) => Some(Ok(v)),
Ok(None) => None,
Err(err) => Some(Err(err)),
}
}
impl StagedWelcome {
pub fn new_from_welcome<Provider: OpenMlsProvider>(
provider: &Provider,
mls_group_config: &MlsGroupJoinConfig,
welcome: Welcome,
ratchet_tree: Option<RatchetTreeIn>,
) -> Result<Self, WelcomeError<Provider::StorageError>> {
let resumption_psk_store =
ResumptionPskStore::new(mls_group_config.number_of_resumption_psks);
let key_package_bundle: KeyPackageBundle = welcome
.secrets()
.iter()
.find_map(|egs| {
let hash_ref = egs.new_member();
transpose_err_opt(
provider
.storage()
.key_package(&hash_ref)
.map_err(WelcomeError::StorageError),
)
})
.ok_or(WelcomeError::NoMatchingKeyPackage)??;
if !key_package_bundle.key_package().last_resort() {
provider
.storage()
.delete_key_package(&key_package_bundle.key_package.hash_ref(provider.crypto())?)
.map_err(WelcomeError::StorageError)?;
} else {
log::debug!("Key package has last resort extension, not deleting");
}
let group = StagedCoreWelcome::new_from_welcome(
welcome,
ratchet_tree,
key_package_bundle,
provider,
resumption_psk_store,
)?;
let staged_welcome = StagedWelcome {
mls_group_config: mls_group_config.clone(),
group,
};
Ok(staged_welcome)
}
pub fn welcome_sender_index(&self) -> LeafNodeIndex {
self.group.welcome_sender_index()
}
pub fn welcome_sender(&self) -> Result<&LeafNode, LibraryError> {
self.group.welcome_sender()
}
pub fn into_group<Provider: OpenMlsProvider>(
self,
provider: &Provider,
) -> Result<MlsGroup, WelcomeError<Provider::StorageError>> {
let mut group = self.group.into_core_group(provider)?;
group.set_max_past_epochs(self.mls_group_config.max_past_epochs);
let mls_group = MlsGroup {
mls_group_config: self.mls_group_config,
group,
proposal_store: ProposalStore::new(),
own_leaf_nodes: vec![],
aad: vec![],
group_state: MlsGroupState::Operational,
};
provider
.storage()
.write_mls_join_config(mls_group.group_id(), &mls_group.mls_group_config)
.map_err(WelcomeError::StorageError)?;
provider
.storage()
.write_group_state(mls_group.group_id(), &MlsGroupState::Operational)
.map_err(WelcomeError::StorageError)?;
Ok(mls_group)
}
}