diff --git a/example/omemo_dart_example.dart b/example/omemo_dart_example.dart index 23f7e05..d8237fa 100644 --- a/example/omemo_dart_example.dart +++ b/example/omemo_dart_example.dart @@ -42,7 +42,7 @@ void main() async { // request it using PEP and then convert the device bundle into a OmemoBundle object. final bobManager = OmemoManager( await OmemoDevice.generateNewDevice(bobJid), - MemoryBTBVTrustManager(), + BlindTrustBeforeVerificationTrustManager(), (result, recipient) async => {}, (jid) async => [], (jid, id) async => null, diff --git a/lib/src/trust/always.dart b/lib/src/trust/always.dart index dd0fcec..eb6a47b 100644 --- a/lib/src/trust/always.dart +++ b/lib/src/trust/always.dart @@ -20,7 +20,4 @@ class AlwaysTrustingTrustManager extends TrustManager { @override Future removeTrustDecisionsForJid(String jid) async {} - - @override - Future> toJson() async => {}; } diff --git a/lib/src/trust/base.dart b/lib/src/trust/base.dart index 86f46bb..0d2547f 100644 --- a/lib/src/trust/base.dart +++ b/lib/src/trust/base.dart @@ -17,9 +17,6 @@ abstract class TrustManager { /// if [enabled] is false. Future setEnabled(String jid, int deviceId, bool enabled); - /// Serialize the trust manager to JSON. - Future> toJson(); - /// Removes all trust decisions for [jid]. Future removeTrustDecisionsForJid(String jid); } diff --git a/lib/src/trust/btbv.dart b/lib/src/trust/btbv.dart index 7ea4304..7640882 100644 --- a/lib/src/trust/btbv.dart +++ b/lib/src/trust/btbv.dart @@ -3,47 +3,64 @@ import 'package:omemo_dart/src/omemo/ratchet_map_key.dart'; import 'package:omemo_dart/src/trust/base.dart'; import 'package:synchronized/synchronized.dart'; +@immutable +class BTBVTrustData { + const BTBVTrustData( + this.jid, + this.device, + this.state, + this.enabled, + ); + + /// The JID in question. + final String jid; + + /// The device (ratchet) in question. + final int device; + + /// The trust state of the ratchet. + final BTBVTrustState state; + + /// Flag indicating whether the ratchet is enabled (true) or not (false). + final bool enabled; +} + +/// A callback for when a trust decision is to be commited to persistent storage. +typedef BTBVTrustCommitCallback = Future Function(BTBVTrustData data); + +/// A stub-implementation of [BTBVTrustCommitCallback]. +Future btbvCommitStub(BTBVTrustData _) async {} + +/// A callback for when all trust decisions for a JID should be removed from persistent storage. +typedef BTBVRemoveTrustForJidCallback = Future Function(String jid); + +/// A stub-implementation of [BTBVRemoveTrustForJidCallback]. +Future btbvRemoveTrustStub(String _) async {} + /// Every device is in either of those two trust states: /// - notTrusted: The device is absolutely not trusted /// - blindTrust: The fingerprint is not verified using OOB means /// - verified: The fingerprint has been verified using OOB means enum BTBVTrustState { - notTrusted, // = 1 - blindTrust, // = 2 - verified, // = 3 -} + notTrusted(1), + blindTrust(2), + verified(3); -int _trustToInt(BTBVTrustState state) { - switch (state) { - case BTBVTrustState.notTrusted: - return 1; - case BTBVTrustState.blindTrust: - return 2; - case BTBVTrustState.verified: - return 3; - } -} + const BTBVTrustState(this.value); -BTBVTrustState _trustFromInt(int i) { - switch (i) { - case 1: - return BTBVTrustState.notTrusted; - case 2: - return BTBVTrustState.blindTrust; - case 3: - return BTBVTrustState.verified; - default: - return BTBVTrustState.notTrusted; - } + /// The value backing the trust state. + final int value; } /// A TrustManager that implements the idea of Blind Trust Before Verification. /// See https://gultsch.de/trust.html for more details. -abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager { +class BlindTrustBeforeVerificationTrustManager extends TrustManager { BlindTrustBeforeVerificationTrustManager({ Map? trustCache, Map? enablementCache, Map>? devices, + this.commit = btbvCommitStub, + this.removeTrust = btbvRemoveTrustStub, }) : trustCache = trustCache ?? {}, enablementCache = enablementCache ?? {}, devices = devices ?? {}, @@ -67,6 +84,12 @@ abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager { /// The lock for devices and trustCache final Lock _lock; + /// Callback for commiting trust data to persistent storage. + final BTBVTrustCommitCallback commit; + + /// Callback for removing trust data for a JID. + final BTBVRemoveTrustForJidCallback removeTrust; + /// Returns true if [jid] has at least one device that is verified. If not, returns false. /// Note that this function accesses devices and trustCache, which requires that the /// lock for those two maps (_lock) has been aquired before calling. @@ -125,7 +148,14 @@ abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager { } // Commit the state - await commitState(); + await commit( + BTBVTrustData( + jid, + deviceId, + trustCache[key]!, + enablementCache[key]!, + ), + ); }); } @@ -152,10 +182,18 @@ abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager { BTBVTrustState state, ) async { await _lock.synchronized(() async { - trustCache[RatchetMapKey(jid, deviceId)] = state; + final key = RatchetMapKey(jid, deviceId); + trustCache[key] = state; // Commit the state - await commitState(); + await commit( + BTBVTrustData( + jid, + deviceId, + state, + enablementCache[key]!, + ), + ); }); } @@ -171,88 +209,39 @@ abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager { @override Future setEnabled(String jid, int deviceId, bool enabled) async { + final key = RatchetMapKey(jid, deviceId); await _lock.synchronized(() async { - enablementCache[RatchetMapKey(jid, deviceId)] = enabled; - }); + enablementCache[key] = enabled; - // Commit the state - await commitState(); - } - - @override - Future> toJson() async { - return { - 'devices': devices, - 'trust': trustCache.map( - (key, value) => MapEntry( - key.toJsonKey(), - _trustToInt(value), + // Commit the state + await commit( + BTBVTrustData( + jid, + deviceId, + trustCache[key]!, + enabled, ), - ), - 'enable': - enablementCache.map((key, value) => MapEntry(key.toJsonKey(), value)), - }; - } - - /// From a serialized version of a BTBV trust manager, extract the device list. - /// NOTE: This is needed as Dart cannot just cast a List to List and so on. - static Map> deviceListFromJson(Map json) { - return (json['devices']! as Map).map>( - (key, value) => MapEntry( - key, - (value as List).map((i) => i as int).toList(), - ), - ); - } - - /// From a serialized version of a BTBV trust manager, extract the trust cache. - /// NOTE: This is needed as Dart cannot just cast a List to List and so on. - static Map trustCacheFromJson( - Map json, - ) { - return (json['trust']! as Map) - .map( - (key, value) => MapEntry( - RatchetMapKey.fromJsonKey(key), - _trustFromInt(value as int), - ), - ); - } - - /// From a serialized version of a BTBV trust manager, extract the enable cache. - /// NOTE: This is needed as Dart cannot just cast a List to List and so on. - static Map enableCacheFromJson( - Map json, - ) { - return (json['enable']! as Map).map( - (key, value) => MapEntry( - RatchetMapKey.fromJsonKey(key), - value as bool, - ), - ); + ); + }); } @override Future removeTrustDecisionsForJid(String jid) async { await _lock.synchronized(() async { + // Clear the caches + for (final device in devices[jid]!) { + final key = RatchetMapKey(jid, device); + trustCache.remove(key); + enablementCache.remove(key); + } devices.remove(jid); - await commitState(); + + // Commit the state + await removeTrust(jid); }); } - /// Called when the state of the trust manager has been changed. Allows the user to - /// commit the trust state to persistent storage. - @visibleForOverriding - Future commitState(); - @visibleForTesting BTBVTrustState getDeviceTrust(String jid, int deviceId) => trustCache[RatchetMapKey(jid, deviceId)]!; } - -/// A BTBV TrustManager that does not commit its state to persistent storage. Well suited -/// for testing. -class MemoryBTBVTrustManager extends BlindTrustBeforeVerificationTrustManager { - @override - Future commitState() async {} -} diff --git a/lib/src/trust/never.dart b/lib/src/trust/never.dart index 63444c9..204ae74 100644 --- a/lib/src/trust/never.dart +++ b/lib/src/trust/never.dart @@ -20,7 +20,4 @@ class NeverTrustingTrustManager extends TrustManager { @override Future removeTrustDecisionsForJid(String jid) async {} - - @override - Future> toJson() async => {}; } diff --git a/test/trust_test.dart b/test/trust_test.dart index 363ac63..01f4170 100644 --- a/test/trust_test.dart +++ b/test/trust_test.dart @@ -4,7 +4,7 @@ import 'package:test/test.dart'; void main() { test('Test the Blind Trust Before Verification TrustManager', () async { // Caroline's BTBV manager - final btbv = MemoryBTBVTrustManager(); + final btbv = BlindTrustBeforeVerificationTrustManager(); // Example data const aliceJid = 'alice@some.server'; const bobJid = 'bob@other.server';