11//! The methods in this module all produce or handle commits.
22
3- use std:: borrow:: Borrow ;
3+ use std:: { borrow:: Borrow , collections :: HashMap } ;
44
55use openmls:: prelude:: KeyPackageIn ;
66use wire_e2e_identity:: NewCrlDistributionPoints ;
77
88use super :: history_sharing:: HistoryClientUpdateOutcome ;
99use crate :: {
10- ClientIdRef , CredentialRef , LeafError , MlsError , MlsGroupInfoBundle , MlsTransportResponse , RecursiveError ,
10+ ClientId , ClientIdRef , CredentialRef , LeafError , MlsError , MlsGroupInfoBundle , MlsTransportResponse ,
11+ RecursiveError ,
1112 mls:: {
1213 conversation:: {
1314 Conversation as _, ConversationGuard , ConversationWithMls as _, Error , Result , commit:: MlsCommitBundle ,
@@ -130,9 +131,16 @@ impl ConversationGuard {
130131
131132 let ( commit, welcome, group_info) = conversation
132133 . group
133- . add_members ( & backend, signer, key_packages)
134+ . add_members ( & backend, signer, key_packages. clone ( ) )
134135 . await
135- . map_err ( MlsError :: wrap ( "group add members" ) ) ?;
136+ . map_err ( |err| {
137+ if Self :: has_duplicate_signature_key ( & err) {
138+ let affected_clients = Self :: clients_with_duplicate_signature_keys ( key_packages) ;
139+ Error :: DuplicateSignature { affected_clients }
140+ } else {
141+ MlsError :: wrap ( "group add members" ) ( err) . into ( )
142+ }
143+ } ) ?;
136144
137145 // commit requires an optional welcome
138146 let welcome = Some ( welcome) ;
@@ -150,6 +158,36 @@ impl ConversationGuard {
150158 Ok ( ( crl_new_distribution_points, commit) )
151159 }
152160
161+ fn has_duplicate_signature_key (
162+ err : & openmls:: prelude:: AddMembersError < core_crypto_keystore:: CryptoKeystoreError > ,
163+ ) -> bool {
164+ matches ! (
165+ err,
166+ openmls:: prelude:: AddMembersError :: CreateCommitError (
167+ openmls:: prelude:: CreateCommitError :: ProposalValidationError (
168+ openmls:: prelude:: ProposalValidationError :: DuplicateSignatureKey
169+ )
170+ )
171+ )
172+ }
173+
174+ fn clients_with_duplicate_signature_keys ( key_packages : Vec < KeyPackageIn > ) -> Vec < ( ClientId , ClientId ) > {
175+ let mut seen_signature_keys = HashMap :: new ( ) ;
176+ let mut duplicate_pairs = Vec :: new ( ) ;
177+
178+ for key_package in key_packages {
179+ let signature_key = key_package. unverified_credential ( ) . signature_key . as_slice ( ) . to_vec ( ) ;
180+
181+ let client_id: ClientId = key_package. credential ( ) . identity ( ) . to_vec ( ) . into ( ) ;
182+
183+ if let Some ( previous_client_id) = seen_signature_keys. insert ( signature_key, client_id. clone ( ) ) {
184+ duplicate_pairs. push ( ( previous_client_id, client_id) ) ;
185+ }
186+ }
187+
188+ duplicate_pairs
189+ }
190+
153191 /// Removes clients from the group/conversation.
154192 ///
155193 /// # Arguments
0 commit comments