Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: (BREAKING) Make share keys with logic configurable #2018

Merged
merged 1 commit into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class Client extends MatrixApi {

krille-chan marked this conversation as resolved.
Show resolved Hide resolved
final bool mxidLocalPartFallback;

bool shareKeysWithUnverifiedDevices;
ShareKeysWith shareKeysWith;

Future<void> Function(Client client)? onSoftLogout;

Expand Down Expand Up @@ -219,7 +219,7 @@ class Client extends MatrixApi {
Duration defaultNetworkRequestTimeout = const Duration(seconds: 35),
this.sendTimelineEventTimeout = const Duration(minutes: 1),
this.customImageResizer,
this.shareKeysWithUnverifiedDevices = true,
this.shareKeysWith = ShareKeysWith.crossVerifiedIfEnabled,
this.enableDehydratedDevices = false,
this.receiptsPublicByDefault = true,

Expand Down Expand Up @@ -4082,3 +4082,25 @@ enum InitState {
/// Initialization has been completed with an error.
error,
}

/// Sets the security level with which devices keys should be shared with
enum ShareKeysWith {
/// Keys are shared with all devices if they are not explicitely blocked
all,

/// Once a user has enabled cross signing, keys are no longer shared with
/// devices which are not cross verified by the cross signing keys of this
/// user. This does not require that the user needs to be verified.
crossVerifiedIfEnabled,

/// Keys are only shared with cross verified devices. If a user has not
/// enabled cross signing, then all devices must be verified manually first.
/// This does not require that the user needs to be verified.
crossVerified,

/// Keys are only shared with direct verified devices. So either the device
/// or the user must be manually verified first, before keys are shared. By
/// using cross signing, it is enough to verify the user and then the user
/// can verify their devices.
directlyVerifiedOnly,
}
12 changes: 11 additions & 1 deletion lib/src/utils/device_keys_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,17 @@ abstract class SignableKey extends MatrixSignableKey {

if (identifier == null || ed25519Key == null) return false;

return client.shareKeysWithUnverifiedDevices || verified;
switch (client.shareKeysWith) {
case ShareKeysWith.all:
return true;
case ShareKeysWith.crossVerifiedIfEnabled:
if (client.userDeviceKeys[userId]?.masterKey == null) return true;
return hasValidSignatureChain(verifiedByTheirMasterKey: true);
case ShareKeysWith.crossVerified:
return hasValidSignatureChain(verifiedByTheirMasterKey: true);
case ShareKeysWith.directlyVerifiedOnly:
return directVerified;
}
}

void setDirectVerified(bool isVerified) {
Expand Down
24 changes: 21 additions & 3 deletions test/device_keys_list_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,29 @@ void main() {
},
client,
);
expect(client.shareKeysWithUnverifiedDevices, true);

client.shareKeysWith = ShareKeysWith.all;
krille-chan marked this conversation as resolved.
Show resolved Hide resolved
expect(key.encryptToDevice, true);
client.shareKeysWithUnverifiedDevices = false;

client.shareKeysWith = ShareKeysWith.directlyVerifiedOnly;
expect(key.encryptToDevice, false);
client.shareKeysWithUnverifiedDevices = true;
await key.setVerified(true);
expect(key.encryptToDevice, true);
await key.setVerified(false);

client.shareKeysWith = ShareKeysWith.crossVerified;
expect(key.encryptToDevice, true);

client.shareKeysWith = ShareKeysWith.crossVerified;
// Disable cross signing for this user manually so encryptToDevice should return `false`
final dropUserDeviceKeys = client.userDeviceKeys.remove(key.userId);
expect(key.encryptToDevice, false);
// But crossVerifiedIfEnabled should return `true` now:
client.shareKeysWith = ShareKeysWith.crossVerifiedIfEnabled;
expect(key.encryptToDevice, true);

client.userDeviceKeys[key.userId] = dropUserDeviceKeys!;
client.shareKeysWith = ShareKeysWith.all;
final masterKey = client.userDeviceKeys[client.userID]!.masterKey!;
masterKey.setDirectVerified(true);
// we need to populate the ssss cache to be able to test signing easily
Expand Down