feat: Implement a BTBV TrustManager
This commit is contained in:
parent
dafd0af1e5
commit
c1fb79a20f
@ -12,5 +12,5 @@ export 'src/omemo/encryption_result.dart';
|
|||||||
export 'src/omemo/fingerprint.dart';
|
export 'src/omemo/fingerprint.dart';
|
||||||
export 'src/omemo/sessionmanager.dart';
|
export 'src/omemo/sessionmanager.dart';
|
||||||
export 'src/trust/base.dart';
|
export 'src/trust/base.dart';
|
||||||
//export 'src/trust/btbv.dart';
|
export 'src/trust/btbv.dart';
|
||||||
export 'src/x3dh/x3dh.dart';
|
export 'src/x3dh/x3dh.dart';
|
||||||
|
@ -134,6 +134,7 @@ class OmemoSessionManager {
|
|||||||
kexResult.ad,
|
kexResult.ad,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await _trustManager.onNewSession(jid, deviceId);
|
||||||
await _addSession(jid, deviceId, ratchet);
|
await _addSession(jid, deviceId, ratchet);
|
||||||
|
|
||||||
return OmemoKeyExchange()
|
return OmemoKeyExchange()
|
||||||
@ -177,6 +178,7 @@ class OmemoSessionManager {
|
|||||||
kexResult.ad,
|
kexResult.ad,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await _trustManager.onNewSession(jid, deviceId);
|
||||||
await _addSession(jid, deviceId, ratchet);
|
await _addSession(jid, deviceId, ratchet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,4 +8,7 @@ import 'package:omemo_dart/src/trust/base.dart';
|
|||||||
class AlwaysTrustingTrustManager extends TrustManager {
|
class AlwaysTrustingTrustManager extends TrustManager {
|
||||||
@override
|
@override
|
||||||
Future<bool> isTrusted(String jid, int deviceId) async => true;
|
Future<bool> isTrusted(String jid, int deviceId) async => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onNewSession(String jid, int deviceId) async {}
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,8 @@ abstract class TrustManager {
|
|||||||
/// Return true when the device with id [deviceId] of Jid [jid] is trusted, i.e. if an
|
/// Return true when the device with id [deviceId] of Jid [jid] is trusted, i.e. if an
|
||||||
/// encrypted message should be sent to this device. If not, return false.
|
/// encrypted message should be sent to this device. If not, return false.
|
||||||
Future<bool> isTrusted(String jid, int deviceId);
|
Future<bool> isTrusted(String jid, int deviceId);
|
||||||
|
|
||||||
|
/// Called by the OmemoSessionManager when a new session has been built. Should set
|
||||||
|
/// a default trust state to [jid]'s device with identifier [deviceId].
|
||||||
|
Future<void> onNewSession(String jid, int deviceId);
|
||||||
}
|
}
|
||||||
|
109
lib/src/trust/btbv.dart
Normal file
109
lib/src/trust/btbv.dart
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:omemo_dart/src/omemo/ratchet_map_key.dart';
|
||||||
|
import 'package:omemo_dart/src/trust/base.dart';
|
||||||
|
import 'package:synchronized/synchronized.dart';
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
blindTrust,
|
||||||
|
verified,
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlindTrustBeforeVerificationTrustManager extends TrustManager {
|
||||||
|
BlindTrustBeforeVerificationTrustManager()
|
||||||
|
: _trustCache = {},
|
||||||
|
_devices = {},
|
||||||
|
_lock = Lock();
|
||||||
|
|
||||||
|
/// The cache for Mapping a RatchetMapKey to its trust state
|
||||||
|
final Map<RatchetMapKey, BTBVTrustState> _trustCache;
|
||||||
|
|
||||||
|
/// Mapping of Jids to their device identifiers
|
||||||
|
final Map<String, List<int>> _devices;
|
||||||
|
|
||||||
|
/// The lock for _devices and _trustCache
|
||||||
|
final Lock _lock;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
bool _hasAtLeastOneVerifiedDevice(String jid) {
|
||||||
|
if (!_devices.containsKey(jid)) return false;
|
||||||
|
|
||||||
|
return _devices[jid]!.any((id) {
|
||||||
|
return _trustCache[RatchetMapKey(jid, id)]! == BTBVTrustState.verified;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> isTrusted(String jid, int deviceId) async {
|
||||||
|
var returnValue = false;
|
||||||
|
await _lock.synchronized(() async {
|
||||||
|
final trustCacheValue = _trustCache[RatchetMapKey(jid, deviceId)];
|
||||||
|
if (trustCacheValue == BTBVTrustState.notTrusted) {
|
||||||
|
returnValue = false;
|
||||||
|
return;
|
||||||
|
} else if (trustCacheValue == BTBVTrustState.verified) {
|
||||||
|
// The key is verified, so it's safe.
|
||||||
|
returnValue = true;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (_hasAtLeastOneVerifiedDevice(jid)) {
|
||||||
|
// Do not trust if there is at least one device with full trust
|
||||||
|
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
|
||||||
|
Future<void> onNewSession(String jid, int deviceId) async {
|
||||||
|
await _lock.synchronized(() async {
|
||||||
|
if (_hasAtLeastOneVerifiedDevice(jid)) {
|
||||||
|
_trustCache[RatchetMapKey(jid, deviceId)] = BTBVTrustState.notTrusted;
|
||||||
|
} else {
|
||||||
|
_trustCache[RatchetMapKey(jid, deviceId)] = BTBVTrustState.blindTrust;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_devices.containsKey(jid)) {
|
||||||
|
_devices[jid]!.add(deviceId);
|
||||||
|
} else {
|
||||||
|
_devices[jid] = List<int>.from([deviceId]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mapping from the device identifiers of [jid] to their trust state.
|
||||||
|
Future<Map<int, BTBVTrustState>> getDevicesTrust(String jid) async {
|
||||||
|
final map = <int, BTBVTrustState>{};
|
||||||
|
|
||||||
|
await _lock.synchronized(() async {
|
||||||
|
for (final deviceId in _devices[jid]!) {
|
||||||
|
map[deviceId] = _trustCache[RatchetMapKey(jid, deviceId)]!;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the trust of [jid]'s device with identifier [deviceId] to [state].
|
||||||
|
Future<void> setDeviceTrust(String jid, int deviceId, BTBVTrustState state) async {
|
||||||
|
await _lock.synchronized(() async {
|
||||||
|
_trustCache[RatchetMapKey(jid, deviceId)] = state;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
BTBVTrustState getDeviceTrust(String jid, int deviceId) => _trustCache[RatchetMapKey(jid, deviceId)]!;
|
||||||
|
}
|
36
test/trust_test.dart
Normal file
36
test/trust_test.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:omemo_dart/omemo_dart.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('Test the Blind Trust Before Verification TrustManager', () async {
|
||||||
|
// Caroline's BTBV manager
|
||||||
|
final btbv = BlindTrustBeforeVerificationTrustManager();
|
||||||
|
// Example data
|
||||||
|
const aliceJid = 'alice@some.server';
|
||||||
|
const bobJid = 'bob@other.server';
|
||||||
|
|
||||||
|
// Caroline starts a chat a device from Alice
|
||||||
|
await btbv.onNewSession(aliceJid, 1);
|
||||||
|
expect(await btbv.isTrusted(aliceJid, 1), true);
|
||||||
|
|
||||||
|
// Caroline meets with Alice and verifies her fingerprint
|
||||||
|
await btbv.setDeviceTrust(aliceJid, 1, BTBVTrustState.verified);
|
||||||
|
expect(await btbv.isTrusted(aliceJid, 1), true);
|
||||||
|
|
||||||
|
// Alice adds a new device
|
||||||
|
await btbv.onNewSession(aliceJid, 2);
|
||||||
|
expect(await btbv.isTrusted(aliceJid, 2), false);
|
||||||
|
expect(btbv.getDeviceTrust(aliceJid, 2), BTBVTrustState.notTrusted);
|
||||||
|
|
||||||
|
// Caronline starts a chat with Bob but since they live far apart, Caroline cannot
|
||||||
|
// verify his fingerprint.
|
||||||
|
await btbv.onNewSession(bobJid, 3);
|
||||||
|
|
||||||
|
// Bob adds a new device
|
||||||
|
await btbv.onNewSession(bobJid, 4);
|
||||||
|
expect(await btbv.isTrusted(bobJid, 3), true);
|
||||||
|
expect(await btbv.isTrusted(bobJid, 4), true);
|
||||||
|
expect(btbv.getDeviceTrust(bobJid, 3), BTBVTrustState.blindTrust);
|
||||||
|
expect(btbv.getDeviceTrust(bobJid, 4), BTBVTrustState.blindTrust);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user