@@ -42,14 +42,23 @@ pub enum CollectStrategy {
42
42
/// trusted via interactive verification.
43
43
/// - It is the current own device of the user.
44
44
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 ,
46
50
}
47
51
48
52
impl CollectStrategy {
49
53
/// Creates a new legacy strategy, based on per device trust.
50
54
pub const fn new_device_based ( only_allow_trusted_devices : bool ) -> Self {
51
55
CollectStrategy :: DeviceBasedStrategy { only_allow_trusted_devices }
52
56
}
57
+
58
+ /// Creates an identity based strategy
59
+ pub const fn new_identity_based ( ) -> Self {
60
+ CollectStrategy :: IdentityBasedStrategy
61
+ }
53
62
}
54
63
55
64
impl Default for CollectStrategy {
@@ -136,6 +145,13 @@ pub(crate) async fn collect_session_recipients(
136
145
only_allow_trusted_devices,
137
146
)
138
147
}
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
+ }
139
155
} ;
140
156
141
157
let recipients = recipient_devices. allowed_devices ;
@@ -225,6 +241,41 @@ fn split_recipients_withhelds_for_user(
225
241
RecipientDevices { allowed_devices : recipients, denied_devices_with_code : withheld_recipients }
226
242
}
227
243
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
+ let mut allowed_devices: Vec < ReadOnlyDevice > = Default :: default ( ) ;
249
+ let mut denied_devices_with_code: Vec < ( ReadOnlyDevice , WithheldCode ) > = Default :: default ( ) ;
250
+
251
+ match device_owner_identity {
252
+ None => {
253
+ // withheld all the users devices, we need to have an identity for this
254
+ // distribution mode
255
+ denied_devices_with_code
256
+ . extend ( user_devices. into_values ( ) . map ( |d| ( d, WithheldCode :: Unauthorised ) ) ) ;
257
+ }
258
+ Some ( device_owner_identity) => {
259
+ // Only accept devices signed by the current identity
260
+ let ( recipients, withheld_recipients) : (
261
+ Vec < ReadOnlyDevice > ,
262
+ Vec < ( ReadOnlyDevice , WithheldCode ) > ,
263
+ ) = user_devices. into_values ( ) . partition_map ( |d| {
264
+ if d. is_cross_signed_by_owner ( device_owner_identity) {
265
+ Either :: Left ( d)
266
+ } else {
267
+ Either :: Right ( ( d, WithheldCode :: Unauthorised ) )
268
+ }
269
+ } ) ;
270
+
271
+ allowed_devices. extend ( recipients) ;
272
+ denied_devices_with_code. extend ( withheld_recipients) ;
273
+ }
274
+ }
275
+
276
+ RecipientDevices { allowed_devices, denied_devices_with_code }
277
+ }
278
+
228
279
#[ cfg( test) ]
229
280
mod tests {
230
281
@@ -402,6 +453,81 @@ mod tests {
402
453
assert_eq ! ( code. as_str( ) , WithheldCode :: Unverified . as_str( ) ) ;
403
454
}
404
455
456
+ #[ async_test]
457
+ async fn test_share_with_identity_strategy ( ) {
458
+ let machine = set_up_test_machine ( ) . await ;
459
+
460
+ let fake_room_id = room_id ! ( "!roomid:localhost" ) ;
461
+
462
+ let strategy = CollectStrategy :: new_identity_based ( ) ;
463
+
464
+ let encryption_settings =
465
+ EncryptionSettings { sharing_strategy : strategy. clone ( ) , ..Default :: default ( ) } ;
466
+
467
+ let id_keys = machine. identity_keys ( ) ;
468
+ let group_session = OutboundGroupSession :: new (
469
+ machine. device_id ( ) . into ( ) ,
470
+ Arc :: new ( id_keys) ,
471
+ fake_room_id,
472
+ encryption_settings. clone ( ) ,
473
+ )
474
+ . unwrap ( ) ;
475
+
476
+ let share_result = collect_session_recipients (
477
+ machine. store ( ) ,
478
+ vec ! [
479
+ KeyDistributionTestData :: dan_id( ) ,
480
+ KeyDistributionTestData :: dave_id( ) ,
481
+ KeyDistributionTestData :: good_id( ) ,
482
+ ]
483
+ . into_iter ( ) ,
484
+ & encryption_settings,
485
+ & group_session,
486
+ )
487
+ . await
488
+ . unwrap ( ) ;
489
+
490
+ assert ! ( !share_result. should_rotate) ;
491
+
492
+ let dave_devices_shared = share_result. devices . get ( KeyDistributionTestData :: dave_id ( ) ) ;
493
+ let good_devices_shared = share_result. devices . get ( KeyDistributionTestData :: good_id ( ) ) ;
494
+ // dave has no published identity so will not receive the key
495
+ assert ! ( dave_devices_shared. unwrap( ) . is_empty( ) ) ;
496
+
497
+ // @good has properly signed his devices, he should get the keys
498
+ assert_eq ! ( good_devices_shared. unwrap( ) . len( ) , 2 ) ;
499
+
500
+ // dan has one of his devices self signed, so should get
501
+ // the key
502
+ let dan_devices_shared =
503
+ share_result. devices . get ( KeyDistributionTestData :: dan_id ( ) ) . unwrap ( ) ;
504
+
505
+ assert_eq ! ( dan_devices_shared. len( ) , 1 ) ;
506
+ let dan_device_that_will_get_the_key = & dan_devices_shared[ 0 ] ;
507
+ assert_eq ! (
508
+ dan_device_that_will_get_the_key. device_id( ) . as_str( ) ,
509
+ KeyDistributionTestData :: dan_signed_device_id( )
510
+ ) ;
511
+
512
+ // Check withhelds for others
513
+ let ( _, code) = share_result
514
+ . withheld_devices
515
+ . iter ( )
516
+ . find ( |( d, _) | d. device_id ( ) == KeyDistributionTestData :: dan_unsigned_device_id ( ) )
517
+ . expect ( "This dan's device should receive a withheld code" ) ;
518
+
519
+ assert_eq ! ( code. as_str( ) , WithheldCode :: Unauthorised . as_str( ) ) ;
520
+
521
+ // Check withhelds for others
522
+ let ( _, code) = share_result
523
+ . withheld_devices
524
+ . iter ( )
525
+ . find ( |( d, _) | d. device_id ( ) == KeyDistributionTestData :: dave_device_id ( ) )
526
+ . expect ( "This dave device should receive a withheld code" ) ;
527
+
528
+ assert_eq ! ( code. as_str( ) , WithheldCode :: Unauthorised . as_str( ) ) ;
529
+ }
530
+
405
531
#[ async_test]
406
532
async fn test_should_rotate_based_on_visibility ( ) {
407
533
let machine = set_up_test_machine ( ) . await ;
0 commit comments