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/sessionmanager.dart'; | ||||
| export 'src/trust/base.dart'; | ||||
| //export 'src/trust/btbv.dart'; | ||||
| export 'src/trust/btbv.dart'; | ||||
| export 'src/x3dh/x3dh.dart'; | ||||
|  | ||||
| @ -134,6 +134,7 @@ class OmemoSessionManager { | ||||
|       kexResult.ad, | ||||
|     ); | ||||
| 
 | ||||
|     await _trustManager.onNewSession(jid, deviceId); | ||||
|     await _addSession(jid, deviceId, ratchet); | ||||
| 
 | ||||
|     return OmemoKeyExchange() | ||||
| @ -177,6 +178,7 @@ class OmemoSessionManager { | ||||
|       kexResult.ad, | ||||
|     ); | ||||
| 
 | ||||
|     await _trustManager.onNewSession(jid, deviceId); | ||||
|     await _addSession(jid, deviceId, ratchet); | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -8,4 +8,7 @@ import 'package:omemo_dart/src/trust/base.dart'; | ||||
| class AlwaysTrustingTrustManager extends TrustManager { | ||||
|   @override | ||||
|   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 | ||||
|   /// encrypted message should be sent to this device. If not, return false. | ||||
|   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