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