use openmls::{
credentials::test_utils::new_credential,
messages::group_info::VerifiableGroupInfo,
prelude::{tls_codec::*, *},
treesync::LeafNodeParameters,
};
use openmls_basic_credential::SignatureKeyPair;
use openmls_test::openmls_test;
fn create_alice_group(
ciphersuite: Ciphersuite,
provider: &impl openmls::storage::OpenMlsProvider,
use_ratchet_tree_extension: bool,
) -> (MlsGroup, CredentialWithKey, SignatureKeyPair) {
let group_config = MlsGroupCreateConfig::builder()
.use_ratchet_tree_extension(use_ratchet_tree_extension)
.ciphersuite(ciphersuite)
.build();
let (credential_with_key, signature_keys) =
new_credential(provider, b"Alice", ciphersuite.signature_algorithm());
let group = MlsGroup::new(
provider,
&signature_keys,
&group_config,
credential_with_key.clone(),
)
.expect("An unexpected error occurred.");
(group, credential_with_key, signature_keys)
}
#[openmls_test]
fn test_external_commit() {
let alice_provider = &Provider::default();
let bob_provider = &Provider::default();
let (alice_group, _, alice_signer) = create_alice_group(ciphersuite, alice_provider, false);
let verifiable_group_info = {
let group_info = alice_group
.export_group_info(alice_provider.crypto(), &alice_signer, true)
.unwrap();
let serialized_group_info = group_info.tls_serialize_detached().unwrap();
let mls_message_in =
MlsMessageIn::tls_deserialize(&mut serialized_group_info.as_slice()).unwrap();
mls_message_in.into_verifiable_group_info().unwrap()
};
let verifiable_group_info_broken = {
let group_info = alice_group
.export_group_info(alice_provider.crypto(), &alice_signer, true)
.unwrap();
let serialized_group_info = {
let mut tmp = group_info.tls_serialize_detached().unwrap();
let last = tmp.len().checked_sub(1).unwrap();
tmp[last] ^= 1;
tmp
};
let mls_message_in =
MlsMessageIn::tls_deserialize(&mut serialized_group_info.as_slice()).unwrap();
mls_message_in.into_verifiable_group_info().unwrap()
};
{
let (bob_credential, bob_signature_keys) =
new_credential(bob_provider, b"Bob", ciphersuite.signature_algorithm());
let (_bob_group, _) = MlsGroup::external_commit_builder()
.build_group(bob_provider, verifiable_group_info, bob_credential)
.unwrap()
.load_psks(bob_provider.storage())
.unwrap()
.build(
bob_provider.rand(),
bob_provider.crypto(),
&bob_signature_keys,
|_| true,
)
.unwrap()
.finalize(bob_provider)
.unwrap();
}
{
let (bob_credential, _bob_signature_keys) =
new_credential(bob_provider, b"Bob", ciphersuite.signature_algorithm());
let got_error = MlsGroup::external_commit_builder()
.build_group(bob_provider, verifiable_group_info_broken, bob_credential)
.unwrap_err();
assert!(matches!(
got_error,
ExternalCommitBuilderError::PublicGroupError(
CreationFromExternalError::InvalidGroupInfoSignature
)
));
}
}
#[openmls_test]
fn test_group_info() {
let alice_provider = &Provider::default();
let bob_provider = &Provider::default();
let (mut alice_group, _, alice_signer) = create_alice_group(ciphersuite, alice_provider, true);
let group_info = alice_group
.self_update(alice_provider, &alice_signer, LeafNodeParameters::default())
.unwrap()
.into_group_info();
alice_group.merge_pending_commit(alice_provider).unwrap();
let (bob_credential, bob_signature_keys) =
new_credential(bob_provider, b"Bob", ciphersuite.signature_algorithm());
let verifiable_group_info = {
let serialized_group_info = group_info.unwrap().tls_serialize_detached().unwrap();
VerifiableGroupInfo::tls_deserialize(&mut serialized_group_info.as_slice()).unwrap()
};
let (mut bob_group, bundle) = MlsGroup::external_commit_builder()
.with_config(
MlsGroupJoinConfig::builder()
.use_ratchet_tree_extension(true)
.build(),
)
.build_group(bob_provider, verifiable_group_info, bob_credential)
.unwrap()
.load_psks(bob_provider.storage())
.unwrap()
.build(
bob_provider.rand(),
bob_provider.crypto(),
&bob_signature_keys,
|_| true,
)
.unwrap()
.finalize(bob_provider)
.unwrap();
let (msg, _, group_info) = bundle.into_contents();
let msg = MlsMessageIn::from(msg);
let msg = alice_group
.process_message(alice_provider, msg.try_into_protocol_message().unwrap())
.unwrap()
.into_content();
match msg {
ProcessedMessageContent::StagedCommitMessage(commit) => {
alice_group
.merge_staged_commit(alice_provider, *commit)
.unwrap();
}
_ => panic!("Unexpected message type"),
}
let message: MlsMessageIn = bob_group
.create_message(bob_provider, &bob_signature_keys, b"Hello Alice")
.unwrap()
.into();
let msg = alice_group
.process_message(alice_provider, message.try_into_protocol_message().unwrap())
.unwrap();
let decrypted = match msg.into_content() {
ProcessedMessageContent::ApplicationMessage(msg) => msg.into_bytes(),
_ => panic!("Not an ApplicationMessage"),
};
assert_eq!(decrypted, b"Hello Alice");
let (bob_credential, bob_signature_keys) =
new_credential(bob_provider, b"Bob 2", ciphersuite.signature_algorithm());
let verifiable_group_info = {
let serialized_group_info = group_info.unwrap().tls_serialize_detached().unwrap();
VerifiableGroupInfo::tls_deserialize(&mut serialized_group_info.as_slice()).unwrap()
};
let _ = MlsGroup::external_commit_builder()
.build_group(bob_provider, verifiable_group_info, bob_credential)
.unwrap()
.load_psks(bob_provider.storage())
.unwrap()
.build(
bob_provider.rand(),
bob_provider.crypto(),
&bob_signature_keys,
|_| true,
)
.unwrap()
.finalize(bob_provider)
.unwrap();
}
#[openmls_test]
fn test_not_present_group_info() {
let provider = &Provider::default();
let (mut alice_group, _, alice_signer) = create_alice_group(ciphersuite, provider, false);
let group_info = alice_group
.self_update(provider, &alice_signer, LeafNodeParameters::default())
.unwrap()
.into_group_info();
alice_group.merge_pending_commit(provider).unwrap();
assert!(group_info.is_none());
}