feat: Remove locking from the BTBV trust manager

This commit is contained in:
PapaTutuWawa 2023-06-17 17:47:07 +02:00
parent dad85b8467
commit ed0701bdcd
4 changed files with 102 additions and 114 deletions

View File

@ -169,7 +169,6 @@ class OmemoManager {
/// The OmemoManager's trust management /// The OmemoManager's trust management
final TrustManager _trustManager; final TrustManager _trustManager;
TrustManager get trustManager => _trustManager;
/// Our own keys... /// Our own keys...
final Lock _deviceLock = Lock(); final Lock _deviceLock = Lock();
@ -200,7 +199,7 @@ class OmemoManager {
_ratchetMap.addAll(result.ratchets); _ratchetMap.addAll(result.ratchets);
// Load trust data // Load trust data
await trustManager.loadTrustData(jid); await _trustManager.loadTrustData(jid);
} }
Future<Result<OmemoError, String?>> _decryptAndVerifyHmac( Future<Result<OmemoError, String?>> _decryptAndVerifyHmac(
@ -449,7 +448,7 @@ class OmemoManager {
} }
// Notify the trust manager // Notify the trust manager
await trustManager.onNewSession( await _trustManager.onNewSession(
stanza.bareSenderJid, stanza.bareSenderJid,
stanza.senderDeviceId, stanza.senderDeviceId,
); );
@ -653,7 +652,7 @@ class OmemoManager {
addedRatchetKeys.add(ratchetKey); addedRatchetKeys.add(ratchetKey);
// Initiate trust // Initiate trust
await trustManager.onNewSession(jid, bundle.id); await _trustManager.onNewSession(jid, bundle.id);
// Track the KEX for later // Track the KEX for later
final ik = await ownDevice.ik.pk.getBytes(); final ik = await ownDevice.ik.pk.getBytes();
@ -940,4 +939,13 @@ class OmemoManager {
@visibleForTesting @visibleForTesting
OmemoDoubleRatchet? getRatchet(RatchetMapKey key) => _ratchetMap[key]; OmemoDoubleRatchet? getRatchet(RatchetMapKey key) => _ratchetMap[key];
/// Trust management functions
Future<void> withTrustManager(
String jid, Future<void> Function(TrustManager) callback) async {
await _ratchetQueue.synchronized(
[jid],
() => callback(_trustManager),
);
}
} }

View File

@ -9,6 +9,7 @@ abstract class TrustManager {
/// Called by the OmemoSessionManager when a new session has been built. Should set /// Called by the OmemoSessionManager when a new session has been built. Should set
/// a default trust state to [jid]'s device with identifier [deviceId]. /// a default trust state to [jid]'s device with identifier [deviceId].
@internal
Future<void> onNewSession(String jid, int deviceId); Future<void> onNewSession(String jid, int deviceId);
/// Return true if the device with id [deviceId] of Jid [jid] should be used for encryption. /// Return true if the device with id [deviceId] of Jid [jid] should be used for encryption.
@ -20,6 +21,7 @@ abstract class TrustManager {
Future<void> setEnabled(String jid, int deviceId, bool enabled); Future<void> setEnabled(String jid, int deviceId, bool enabled);
/// Removes all trust decisions for [jid]. /// Removes all trust decisions for [jid].
@internal
Future<void> removeTrustDecisionsForJid(String jid); Future<void> removeTrustDecisionsForJid(String jid);
// ignore: comment_references // ignore: comment_references

View File

@ -2,7 +2,6 @@ import 'package:meta/meta.dart';
import 'package:omemo_dart/src/helpers.dart'; import 'package:omemo_dart/src/helpers.dart';
import 'package:omemo_dart/src/omemo/ratchet_map_key.dart'; import 'package:omemo_dart/src/omemo/ratchet_map_key.dart';
import 'package:omemo_dart/src/trust/base.dart'; import 'package:omemo_dart/src/trust/base.dart';
import 'package:synchronized/synchronized.dart';
@immutable @immutable
class BTBVTrustData { class BTBVTrustData {
@ -83,9 +82,6 @@ class BlindTrustBeforeVerificationTrustManager extends TrustManager {
@protected @protected
final Map<String, List<int>> devices = {}; final Map<String, List<int>> devices = {};
/// The lock for devices and trustCache
final Lock _lock = Lock();
/// Callback for loading trust data. /// Callback for loading trust data.
final BTBVLoadDataCallback loadData; final BTBVLoadDataCallback loadData;
@ -108,35 +104,25 @@ class BlindTrustBeforeVerificationTrustManager extends TrustManager {
@override @override
Future<bool> isTrusted(String jid, int deviceId) async { Future<bool> isTrusted(String jid, int deviceId) async {
var returnValue = false;
await _lock.synchronized(() async {
final trustCacheValue = trustCache[RatchetMapKey(jid, deviceId)]; final trustCacheValue = trustCache[RatchetMapKey(jid, deviceId)];
if (trustCacheValue == BTBVTrustState.notTrusted) { if (trustCacheValue == BTBVTrustState.notTrusted) {
returnValue = false; return false;
return;
} else if (trustCacheValue == BTBVTrustState.verified) { } else if (trustCacheValue == BTBVTrustState.verified) {
// The key is verified, so it's safe. // The key is verified, so it's safe.
returnValue = true; return true;
return;
} else { } else {
if (_hasAtLeastOneVerifiedDevice(jid)) { if (_hasAtLeastOneVerifiedDevice(jid)) {
// Do not trust if there is at least one device with full trust // Do not trust if there is at least one device with full trust
returnValue = false; return false;
return;
} else { } else {
// We have not verified a key from [jid], so it is blind trust all the way. // We have not verified a key from [jid], so it is blind trust all the way.
returnValue = true; return true;
return;
} }
} }
});
return returnValue;
} }
@override @override
Future<void> onNewSession(String jid, int deviceId) async { Future<void> onNewSession(String jid, int deviceId) async {
await _lock.synchronized(() async {
final key = RatchetMapKey(jid, deviceId); final key = RatchetMapKey(jid, deviceId);
if (_hasAtLeastOneVerifiedDevice(jid)) { if (_hasAtLeastOneVerifiedDevice(jid)) {
trustCache[key] = BTBVTrustState.notTrusted; trustCache[key] = BTBVTrustState.notTrusted;
@ -161,13 +147,11 @@ class BlindTrustBeforeVerificationTrustManager extends TrustManager {
enablementCache[key]!, enablementCache[key]!,
), ),
); );
});
} }
/// Returns a mapping from the device identifiers of [jid] to their trust state. If /// Returns a mapping from the device identifiers of [jid] to their trust state. If
/// there are no devices known for [jid], then an empty map is returned. /// there are no devices known for [jid], then an empty map is returned.
Future<Map<int, BTBVTrustState>> getDevicesTrust(String jid) async { Future<Map<int, BTBVTrustState>> getDevicesTrust(String jid) async {
return _lock.synchronized(() async {
final map = <int, BTBVTrustState>{}; final map = <int, BTBVTrustState>{};
if (!devices.containsKey(jid)) return map; if (!devices.containsKey(jid)) return map;
@ -177,7 +161,6 @@ class BlindTrustBeforeVerificationTrustManager extends TrustManager {
} }
return map; return map;
});
} }
/// Sets the trust of [jid]'s device with identifier [deviceId] to [state]. /// Sets the trust of [jid]'s device with identifier [deviceId] to [state].
@ -186,7 +169,6 @@ class BlindTrustBeforeVerificationTrustManager extends TrustManager {
int deviceId, int deviceId,
BTBVTrustState state, BTBVTrustState state,
) async { ) async {
await _lock.synchronized(() async {
final key = RatchetMapKey(jid, deviceId); final key = RatchetMapKey(jid, deviceId);
trustCache[key] = state; trustCache[key] = state;
@ -199,23 +181,19 @@ class BlindTrustBeforeVerificationTrustManager extends TrustManager {
enablementCache[key]!, enablementCache[key]!,
), ),
); );
});
} }
@override @override
Future<bool> isEnabled(String jid, int deviceId) async { Future<bool> isEnabled(String jid, int deviceId) async {
return _lock.synchronized(() async {
final value = enablementCache[RatchetMapKey(jid, deviceId)]; final value = enablementCache[RatchetMapKey(jid, deviceId)];
if (value == null) return false; if (value == null) return false;
return value; return value;
});
} }
@override @override
Future<void> setEnabled(String jid, int deviceId, bool enabled) async { Future<void> setEnabled(String jid, int deviceId, bool enabled) async {
final key = RatchetMapKey(jid, deviceId); final key = RatchetMapKey(jid, deviceId);
await _lock.synchronized(() async {
enablementCache[key] = enabled; enablementCache[key] = enabled;
// Commit the state // Commit the state
@ -227,12 +205,10 @@ class BlindTrustBeforeVerificationTrustManager extends TrustManager {
enabled, enabled,
), ),
); );
});
} }
@override @override
Future<void> removeTrustDecisionsForJid(String jid) async { Future<void> removeTrustDecisionsForJid(String jid) async {
await _lock.synchronized(() async {
// Clear the caches // Clear the caches
for (final device in devices[jid]!) { for (final device in devices[jid]!) {
final key = RatchetMapKey(jid, device); final key = RatchetMapKey(jid, device);
@ -243,19 +219,16 @@ class BlindTrustBeforeVerificationTrustManager extends TrustManager {
// Commit the state // Commit the state
await removeTrust(jid); await removeTrust(jid);
});
} }
@override @override
Future<void> loadTrustData(String jid) async { Future<void> loadTrustData(String jid) async {
await _lock.synchronized(() async {
for (final result in await loadData(jid)) { for (final result in await loadData(jid)) {
final key = RatchetMapKey(jid, result.device); final key = RatchetMapKey(jid, result.device);
trustCache[key] = result.state; trustCache[key] = result.state;
enablementCache[key] = result.enabled; enablementCache[key] = result.enabled;
devices.appendOrCreate(jid, result.device); devices.appendOrCreate(jid, result.device);
} }
});
} }
@visibleForTesting @visibleForTesting

View File

@ -1529,10 +1529,15 @@ void main() {
expect(bobResult1.error, null); expect(bobResult1.error, null);
// Bob should have some trust state // Bob should have some trust state
await bobManager.withTrustManager(
bobJid,
(tm) async {
expect( expect(
(bobManager.trustManager as TestingTrustManager).devices[aliceJid], (tm as TestingTrustManager).devices[aliceJid],
await aliceManager.getDeviceId(), await aliceManager.getDeviceId(),
); );
},
);
}); });
test('Test receiving a non-KEX from a new device', () async { test('Test receiving a non-KEX from a new device', () async {