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,76 +104,63 @@ 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; final trustCacheValue = trustCache[RatchetMapKey(jid, deviceId)];
await _lock.synchronized(() async { if (trustCacheValue == BTBVTrustState.notTrusted) {
final trustCacheValue = trustCache[RatchetMapKey(jid, deviceId)]; return false;
if (trustCacheValue == BTBVTrustState.notTrusted) { } else if (trustCacheValue == BTBVTrustState.verified) {
returnValue = false; // The key is verified, so it's safe.
return; return true;
} else if (trustCacheValue == BTBVTrustState.verified) { } else {
// The key is verified, so it's safe. if (_hasAtLeastOneVerifiedDevice(jid)) {
returnValue = true; // Do not trust if there is at least one device with full trust
return; return false;
} else { } else {
if (_hasAtLeastOneVerifiedDevice(jid)) { // We have not verified a key from [jid], so it is blind trust all the way.
// Do not trust if there is at least one device with full trust return true;
returnValue = false;
return;
} else {
// We have not verified a key from [jid], so it is blind trust all the way.
returnValue = 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; enablementCache[key] = false;
enablementCache[key] = false; } else {
} else { trustCache[key] = BTBVTrustState.blindTrust;
trustCache[key] = BTBVTrustState.blindTrust; enablementCache[key] = true;
enablementCache[key] = true; }
}
if (devices.containsKey(jid)) { if (devices.containsKey(jid)) {
devices[jid]!.add(deviceId); devices[jid]!.add(deviceId);
} else { } else {
devices[jid] = List<int>.from([deviceId]); devices[jid] = List<int>.from([deviceId]);
} }
// Commit the state // Commit the state
await commit( await commit(
BTBVTrustData( BTBVTrustData(
jid, jid,
deviceId, deviceId,
trustCache[key]!, trustCache[key]!,
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;
for (final deviceId in devices[jid]!) { for (final deviceId in devices[jid]!) {
map[deviceId] = trustCache[RatchetMapKey(jid, deviceId)]!; map[deviceId] = trustCache[RatchetMapKey(jid, deviceId)]!;
} }
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,76 +169,66 @@ 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;
// Commit the state // Commit the state
await commit( await commit(
BTBVTrustData( BTBVTrustData(
jid, jid,
deviceId, deviceId,
state, state,
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
await commit( await commit(
BTBVTrustData( BTBVTrustData(
jid, jid,
deviceId, deviceId,
trustCache[key]!, trustCache[key]!,
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); trustCache.remove(key);
trustCache.remove(key); enablementCache.remove(key);
enablementCache.remove(key); }
} devices.remove(jid);
devices.remove(jid);
// 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,9 +1529,14 @@ void main() {
expect(bobResult1.error, null); expect(bobResult1.error, null);
// Bob should have some trust state // Bob should have some trust state
expect( await bobManager.withTrustManager(
(bobManager.trustManager as TestingTrustManager).devices[aliceJid], bobJid,
await aliceManager.getDeviceId(), (tm) async {
expect(
(tm as TestingTrustManager).devices[aliceJid],
await aliceManager.getDeviceId(),
);
},
); );
}); });