@@ -42,14 +42,23 @@ pub enum CollectStrategy {
4242 /// trusted via interactive verification.
4343 /// - It is the current own device of the user.
4444 only_allow_trusted_devices : bool ,
45- } , // XXX some new strategy to be defined later
45+ } ,
46+ /// Share based on identity. Only distribute to devices signed by their
47+ /// owner. If a user has no published identity he will not receive
48+ /// any room keys.
49+ IdentityBasedStrategy ,
4650}
4751
4852impl CollectStrategy {
4953 /// Creates a new legacy strategy, based on per device trust.
5054 pub const fn new_device_based ( only_allow_trusted_devices : bool ) -> Self {
5155 CollectStrategy :: DeviceBasedStrategy { only_allow_trusted_devices }
5256 }
57+
58+ /// Creates an identity based strategy
59+ pub const fn new_identity_based ( ) -> Self {
60+ CollectStrategy :: IdentityBasedStrategy
61+ }
5362}
5463
5564impl Default for CollectStrategy {
@@ -136,6 +145,13 @@ pub(crate) async fn collect_session_recipients(
136145 only_allow_trusted_devices,
137146 )
138147 }
148+ CollectStrategy :: IdentityBasedStrategy => {
149+ let device_owner_identity = store. get_user_identity ( user_id) . await ?;
150+ split_recipients_withhelds_for_user_based_on_identity (
151+ user_devices,
152+ & device_owner_identity,
153+ )
154+ }
139155 } ;
140156
141157 let recipients = recipient_devices. allowed_devices ;
@@ -225,6 +241,42 @@ fn split_recipients_withhelds_for_user(
225241 RecipientDevices { allowed_devices : recipients, denied_devices_with_code : withheld_recipients }
226242}
227243
244+ fn split_recipients_withhelds_for_user_based_on_identity (
245+ user_devices : HashMap < OwnedDeviceId , ReadOnlyDevice > ,
246+ device_owner_identity : & Option < ReadOnlyUserIdentities > ,
247+ ) -> RecipientDevices {
248+ match device_owner_identity {
249+ None => {
250+ // withheld all the users devices, we need to have an identity for this
251+ // distribution mode
252+ RecipientDevices {
253+ allowed_devices : Vec :: default ( ) ,
254+ denied_devices_with_code : user_devices
255+ . into_values ( )
256+ . map ( |d| ( d, WithheldCode :: Unauthorised ) )
257+ . collect ( ) ,
258+ }
259+ }
260+ Some ( device_owner_identity) => {
261+ // Only accept devices signed by the current identity
262+ let ( recipients, withheld_recipients) : (
263+ Vec < ReadOnlyDevice > ,
264+ Vec < ( ReadOnlyDevice , WithheldCode ) > ,
265+ ) = user_devices. into_values ( ) . partition_map ( |d| {
266+ if d. is_cross_signed_by_owner ( device_owner_identity) {
267+ Either :: Left ( d)
268+ } else {
269+ Either :: Right ( ( d, WithheldCode :: Unauthorised ) )
270+ }
271+ } ) ;
272+ RecipientDevices {
273+ allowed_devices : recipients,
274+ denied_devices_with_code : withheld_recipients,
275+ }
276+ }
277+ }
278+ }
279+
228280#[ cfg( test) ]
229281mod tests {
230282
@@ -391,15 +443,90 @@ mod tests {
391443 . find ( |( d, _) | d. device_id ( ) == KeyDistributionTestData :: dan_unsigned_device_id ( ) )
392444 . expect ( "This dan's device should receive a withheld code" ) ;
393445
394- assert_eq ! ( code. as_str ( ) , WithheldCode :: Unverified . as_str ( ) ) ;
446+ assert_eq ! ( code, & WithheldCode :: Unverified ) ;
395447
396448 let ( _, code) = share_result
397449 . withheld_devices
398450 . iter ( )
399451 . find ( |( d, _) | d. device_id ( ) == KeyDistributionTestData :: dave_device_id ( ) )
400452 . expect ( "This daves's device should receive a withheld code" ) ;
401453
402- assert_eq ! ( code. as_str( ) , WithheldCode :: Unverified . as_str( ) ) ;
454+ assert_eq ! ( code, & WithheldCode :: Unverified ) ;
455+ }
456+
457+ #[ async_test]
458+ async fn test_share_with_identity_strategy ( ) {
459+ let machine = set_up_test_machine ( ) . await ;
460+
461+ let fake_room_id = room_id ! ( "!roomid:localhost" ) ;
462+
463+ let strategy = CollectStrategy :: new_identity_based ( ) ;
464+
465+ let encryption_settings =
466+ EncryptionSettings { sharing_strategy : strategy. clone ( ) , ..Default :: default ( ) } ;
467+
468+ let id_keys = machine. identity_keys ( ) ;
469+ let group_session = OutboundGroupSession :: new (
470+ machine. device_id ( ) . into ( ) ,
471+ Arc :: new ( id_keys) ,
472+ fake_room_id,
473+ encryption_settings. clone ( ) ,
474+ )
475+ . unwrap ( ) ;
476+
477+ let share_result = collect_session_recipients (
478+ machine. store ( ) ,
479+ vec ! [
480+ KeyDistributionTestData :: dan_id( ) ,
481+ KeyDistributionTestData :: dave_id( ) ,
482+ KeyDistributionTestData :: good_id( ) ,
483+ ]
484+ . into_iter ( ) ,
485+ & encryption_settings,
486+ & group_session,
487+ )
488+ . await
489+ . unwrap ( ) ;
490+
491+ assert ! ( !share_result. should_rotate) ;
492+
493+ let dave_devices_shared = share_result. devices . get ( KeyDistributionTestData :: dave_id ( ) ) ;
494+ let good_devices_shared = share_result. devices . get ( KeyDistributionTestData :: good_id ( ) ) ;
495+ // dave has no published identity so will not receive the key
496+ assert ! ( dave_devices_shared. unwrap( ) . is_empty( ) ) ;
497+
498+ // @good has properly signed his devices, he should get the keys
499+ assert_eq ! ( good_devices_shared. unwrap( ) . len( ) , 2 ) ;
500+
501+ // dan has one of his devices self signed, so should get
502+ // the key
503+ let dan_devices_shared =
504+ share_result. devices . get ( KeyDistributionTestData :: dan_id ( ) ) . unwrap ( ) ;
505+
506+ assert_eq ! ( dan_devices_shared. len( ) , 1 ) ;
507+ let dan_device_that_will_get_the_key = & dan_devices_shared[ 0 ] ;
508+ assert_eq ! (
509+ dan_device_that_will_get_the_key. device_id( ) . as_str( ) ,
510+ KeyDistributionTestData :: dan_signed_device_id( )
511+ ) ;
512+
513+ // Check withhelds for others
514+ let ( _, code) = share_result
515+ . withheld_devices
516+ . iter ( )
517+ . find ( |( d, _) | d. device_id ( ) == KeyDistributionTestData :: dan_unsigned_device_id ( ) )
518+ . expect ( "This dan's device should receive a withheld code" ) ;
519+
520+ assert_eq ! ( code, & WithheldCode :: Unauthorised ) ;
521+
522+ // Check withhelds for others
523+ let ( _, code) = share_result
524+ . withheld_devices
525+ . iter ( )
526+ . find ( |( d, _) | d. device_id ( ) == KeyDistributionTestData :: dave_device_id ( ) )
527+ . expect ( "This dave device should receive a withheld code" ) ;
528+
529+ assert_eq ! ( code, & WithheldCode :: Unauthorised ) ;
403530 }
404531
405532 #[ async_test]
0 commit comments