// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright by contributors to this project. // SPDX-License-Identifier: (Apache-2.0 OR MIT)
/// The example shows how how to create an MLS extension implementing an access control policy /// based on the concept of users, similar to /// https://bifurcation.github.io/ietf-mimi-protocol/draft-ralston-mimi-protocol.html. /// /// A user, e.g. "bob@b.example", owns zero or more MLS members, e.g. Bob's tablet and PC. /// Users do not have MLS cryptographic state, while MLS members do. At any point in time, /// the MLS group has a fixed set of users and for each user, zero or more MLS members they /// own. Each user also has a role, e.g. a regular user or moderator (which may possibly change /// over time). /// /// The goal is to implement the following rule: /// 1. Each MLS member belongs to a user in the group. /// /// To this end, we implement the following: /// * A GroupContext extension containing the current list of users. MLS guarantees agreement /// on the list. /// * An AddUser proposal that modifies the user list. /// * An MLS credential type for MLS members with the owning user's public key and signature. /// When MLS members join using MLS Add proposals, the signature is verified. /// * Proposal validation rules that enforce 1. above. /// use assert_matches::assert_matches; use mls_rs::{
client_builder::{MlsConfig, PaddingMode},
error::MlsError,
group::{
proposal::{MlsCustomProposal, Proposal},
Roster, Sender,
},
mls_rules::{
CommitDirection, CommitOptions, CommitSource, EncryptionOptions, ProposalBundle,
ProposalSource,
},
CipherSuite, CipherSuiteProvider, Client, CryptoProvider, ExtensionList, IdentityProvider,
MlsRules,
}; use mls_rs_codec::{MlsDecode, MlsEncode, MlsSize}; use mls_rs_core::{
crypto::{SignaturePublicKey, SignatureSecretKey},
error::IntoAnyError,
extension::{ExtensionError, ExtensionType, MlsCodecExtension},
group::ProposalType,
identity::{Credential, CredentialType, CustomCredential, MlsCredential, SigningIdentity},
time::MlsTime,
};
/// The roster will be stored in the custom RosterExtension, an extension in the MLS GroupContext #[derive(MlsSize, MlsDecode, MlsEncode)] struct RosterExtension {
roster: Vec<UserCredential>,
}
/// The custom AddUser proposal will be used to update the RosterExtension #[derive(MlsSize, MlsDecode, MlsEncode)] struct AddUserProposal {
new_user: UserCredential,
}
for add_user_info in add_user_proposals { let add_user = AddUserProposal::from_custom_proposal(&add_user_info.proposal)?;
// Eventually we should check for duplicates
roster.roster.push(add_user.new_user);
}
// Issue GroupContextExtensions proposal to modify our roster (eventually we don't have to do this if there were no AddUser proposals) letmut new_extensions = extension_list.clone();
new_extensions.set_from(roster)?; let gce_proposal = Proposal::GroupContextExtensions(new_extensions);
proposals.add(gce_proposal, Sender::Member(0), ProposalSource::Local);
// The IdentityProvider will tell MLS how to validate members' identities. We will use custom identity // type to store our User structs. impl MlsCredential for MemberCredential { type Error = CustomError;
let roster = extensions
.get_as::<RosterExtension>()
.ok()
.flatten()
.ok_or(CustomError)?;
// Retrieve the MemberCredential from the MLS credential let Credential::Custom(custom) = &signing_identity.credential else { return Err(CustomError);
};
if custom.credential_type != CREDENTIAL_V1 { return Err(CustomError);
}
let member = MemberCredential::mls_decode(&mut &*custom.data)?;
// Set up Client to use our custom providers fn make_client(member: Member) -> Result<Client<impl MlsConfig>, CustomError> { let mls_credential = member.credential.into_credential()?; let signing_identity = SigningIdentity::new(mls_credential, member.public_key);
fn main() -> Result<(), CustomError> { let alice = User::new("alice", UserRole::Moderator)?; let bob = User::new("bob", UserRole::Regular)?;
let alice_tablet = Member::new("alice tablet", &alice)?; let alice_pc = Member::new("alice pc", &alice)?; let bob_tablet = Member::new("bob tablet", &bob)?;
// Alice creates the group with our RosterExtension containing her user letmut context_extensions = ExtensionList::new(); let roster = vec![alice.credential];
context_extensions.set_from(RosterExtension { roster })?;
// Alice can add her other device let alice_pc_client = make_client(alice_pc)?; let key_package = alice_pc_client.generate_key_package_message()?;
let welcome = alice_tablet_group
.commit_builder()
.add_member(key_package)?
.build()?
.welcome_messages
.remove(0);
alice_tablet_group.apply_pending_commit()?; let (mut alice_pc_group, _) = alice_pc_client.join_group(None, &welcome)?;
// Alice cannot add bob's devices yet let bob_tablet_client = make_client(bob_tablet)?; let key_package = bob_tablet_client.generate_key_package_message()?;
let res = alice_tablet_group
.commit_builder()
.add_member(key_package.clone())?
.build();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.