feat: Implement deferred loading of ratchet data
This commit is contained in:
		
							parent
							
								
									4fb25a3ab7
								
							
						
					
					
						commit
						dad85b8467
					
				| @ -27,6 +27,16 @@ import 'package:omemo_dart/src/trust/base.dart'; | |||||||
| import 'package:omemo_dart/src/x3dh/x3dh.dart'; | import 'package:omemo_dart/src/x3dh/x3dh.dart'; | ||||||
| import 'package:synchronized/synchronized.dart'; | import 'package:synchronized/synchronized.dart'; | ||||||
| 
 | 
 | ||||||
|  | class OmemoDataPackage { | ||||||
|  |   const OmemoDataPackage(this.devices, this.ratchets); | ||||||
|  | 
 | ||||||
|  |   /// The device list for the given JID. | ||||||
|  |   final List<int> devices; | ||||||
|  | 
 | ||||||
|  |   /// The ratchets for the JID. | ||||||
|  |   final Map<RatchetMapKey, OmemoDoubleRatchet> ratchets; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Callback type definitions | /// Callback type definitions | ||||||
| 
 | 
 | ||||||
| /// Directly "package" [result] into an OMEMO message and send it to [recipientJid]. | /// Directly "package" [result] into an OMEMO message and send it to [recipientJid]. | ||||||
| @ -84,6 +94,12 @@ typedef RemoveRatchetsFunction = Future<void> Function( | |||||||
| /// A stub implementation of [RemoveRatchetsFunction]. | /// A stub implementation of [RemoveRatchetsFunction]. | ||||||
| Future<void> removeRatchetsStub(List<RatchetMapKey> ratchets) async {} | Future<void> removeRatchetsStub(List<RatchetMapKey> ratchets) async {} | ||||||
| 
 | 
 | ||||||
|  | /// Loads all the required data for the ratchets of [jid]. | ||||||
|  | typedef LoadRatchetsCallback = Future<OmemoDataPackage?> Function(String jid); | ||||||
|  | 
 | ||||||
|  | /// A stub implementation of [LoadRatchetsCallback]. | ||||||
|  | Future<OmemoDataPackage?> loadRatchetsStub(String _) async => null; | ||||||
|  | 
 | ||||||
| class OmemoManager { | class OmemoManager { | ||||||
|   OmemoManager( |   OmemoManager( | ||||||
|     this._device, |     this._device, | ||||||
| @ -96,6 +112,7 @@ class OmemoManager { | |||||||
|     this.commitDeviceList = commitDeviceListStub, |     this.commitDeviceList = commitDeviceListStub, | ||||||
|     this.commitDevice = commitDeviceStub, |     this.commitDevice = commitDeviceStub, | ||||||
|     this.removeRatchets = removeRatchetsStub, |     this.removeRatchets = removeRatchetsStub, | ||||||
|  |     this.loadRatchets = loadRatchetsStub, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   final Logger _log = Logger('OmemoManager'); |   final Logger _log = Logger('OmemoManager'); | ||||||
| @ -128,6 +145,9 @@ class OmemoManager { | |||||||
|   /// Callback to remove ratchets from persistent storage. |   /// Callback to remove ratchets from persistent storage. | ||||||
|   final RemoveRatchetsFunction removeRatchets; |   final RemoveRatchetsFunction removeRatchets; | ||||||
| 
 | 
 | ||||||
|  |   /// Callback to load ratchets from persistent storage. | ||||||
|  |   final LoadRatchetsCallback loadRatchets; | ||||||
|  | 
 | ||||||
|   /// Map bare JID to its known devices |   /// Map bare JID to its known devices | ||||||
|   final Map<String, List<int>> _deviceList = {}; |   final Map<String, List<int>> _deviceList = {}; | ||||||
| 
 | 
 | ||||||
| @ -141,6 +161,9 @@ class OmemoManager { | |||||||
|   /// Map bare JID to whether we already tried to subscribe to the device list node. |   /// Map bare JID to whether we already tried to subscribe to the device list node. | ||||||
|   final Map<String, bool> _subscriptionMap = {}; |   final Map<String, bool> _subscriptionMap = {}; | ||||||
| 
 | 
 | ||||||
|  |   /// List of JIDs for which we cached trust data, the device list, and the ratchets. | ||||||
|  |   final List<String> _cachedJids = []; | ||||||
|  | 
 | ||||||
|   /// For preventing a race condition in encryption/decryption |   /// For preventing a race condition in encryption/decryption | ||||||
|   final RatchetAccessQueue _ratchetQueue = RatchetAccessQueue(); |   final RatchetAccessQueue _ratchetQueue = RatchetAccessQueue(); | ||||||
| 
 | 
 | ||||||
| @ -153,6 +176,33 @@ class OmemoManager { | |||||||
|   // ignore: prefer_final_fields |   // ignore: prefer_final_fields | ||||||
|   OmemoDevice _device; |   OmemoDevice _device; | ||||||
| 
 | 
 | ||||||
|  |   Future<void> _cacheJidsIfNeccessary(List<String> jids) async { | ||||||
|  |     for (final jid in jids) { | ||||||
|  |       await _cacheJidIfNeccessary(jid); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> _cacheJidIfNeccessary(String jid) async { | ||||||
|  |     // JID is already cached. We don't have to do anything. | ||||||
|  |     if (_cachedJids.contains(jid)) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     _cachedJids.add(jid); | ||||||
|  |     final result = await loadRatchets(jid); | ||||||
|  |     if (result == null) { | ||||||
|  |       _log.fine('Did not load ratchet data for $jid. Assuming there is none.'); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Cache the data | ||||||
|  |     _deviceList[jid] = result.devices; | ||||||
|  |     _ratchetMap.addAll(result.ratchets); | ||||||
|  | 
 | ||||||
|  |     // Load trust data | ||||||
|  |     await trustManager.loadTrustData(jid); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   Future<Result<OmemoError, String?>> _decryptAndVerifyHmac( |   Future<Result<OmemoError, String?>> _decryptAndVerifyHmac( | ||||||
|     List<int>? ciphertext, |     List<int>? ciphertext, | ||||||
|     List<int> keyAndHmac, |     List<int> keyAndHmac, | ||||||
| @ -294,6 +344,9 @@ class OmemoManager { | |||||||
|   Future<DecryptionResult> _onIncomingStanzaImpl( |   Future<DecryptionResult> _onIncomingStanzaImpl( | ||||||
|     OmemoIncomingStanza stanza, |     OmemoIncomingStanza stanza, | ||||||
|   ) async { |   ) async { | ||||||
|  |     // Populate the cache | ||||||
|  |     await _cacheJidIfNeccessary(stanza.bareSenderJid); | ||||||
|  | 
 | ||||||
|     // Find the correct key for our device |     // Find the correct key for our device | ||||||
|     final deviceId = await getDeviceId(); |     final deviceId = await getDeviceId(); | ||||||
|     final key = stanza.keys.firstWhereOrNull((key) => key.rid == deviceId); |     final key = stanza.keys.firstWhereOrNull((key) => key.rid == deviceId); | ||||||
| @ -530,6 +583,9 @@ class OmemoManager { | |||||||
|   Future<EncryptionResult> _onOutgoingStanzaImpl( |   Future<EncryptionResult> _onOutgoingStanzaImpl( | ||||||
|     OmemoOutgoingStanza stanza, |     OmemoOutgoingStanza stanza, | ||||||
|   ) async { |   ) async { | ||||||
|  |     // Populate the cache | ||||||
|  |     await _cacheJidsIfNeccessary(stanza.recipientJids); | ||||||
|  | 
 | ||||||
|     // Encrypt the payload, if we have any |     // Encrypt the payload, if we have any | ||||||
|     final List<int> payloadKey; |     final List<int> payloadKey; | ||||||
|     final List<int> ciphertext; |     final List<int> ciphertext; | ||||||
| @ -761,6 +817,8 @@ class OmemoManager { | |||||||
|         } |         } | ||||||
|         await removeRatchets(keys.toList()); |         await removeRatchets(keys.toList()); | ||||||
| 
 | 
 | ||||||
|  |         // TODO: Do we have to tell the trust manager? | ||||||
|  | 
 | ||||||
|         // Clear the device list |         // Clear the device list | ||||||
|         await commitDeviceList( |         await commitDeviceList( | ||||||
|           jid, |           jid, | ||||||
|  | |||||||
| @ -20,4 +20,7 @@ class AlwaysTrustingTrustManager extends TrustManager { | |||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Future<void> removeTrustDecisionsForJid(String jid) async {} |   Future<void> removeTrustDecisionsForJid(String jid) async {} | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<void> loadTrustData(String jid) async {} | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | import 'package:meta/meta.dart'; | ||||||
|  | 
 | ||||||
| /// The base class for managing trust in OMEMO sessions. | /// The base class for managing trust in OMEMO sessions. | ||||||
| // ignore: one_member_abstracts | // ignore: one_member_abstracts | ||||||
| abstract class TrustManager { | abstract class TrustManager { | ||||||
| @ -19,4 +21,11 @@ abstract class TrustManager { | |||||||
| 
 | 
 | ||||||
|   /// Removes all trust decisions for [jid]. |   /// Removes all trust decisions for [jid]. | ||||||
|   Future<void> removeTrustDecisionsForJid(String jid); |   Future<void> removeTrustDecisionsForJid(String jid); | ||||||
|  | 
 | ||||||
|  |   // ignore: comment_references | ||||||
|  |   /// Called from within the [OmemoManager]. | ||||||
|  |   /// Loads the trust data for the JID [jid] from persistent storage | ||||||
|  |   /// into the internal cache, if applicable. | ||||||
|  |   @internal | ||||||
|  |   Future<void> loadTrustData(String jid); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import 'package:meta/meta.dart'; | import 'package:meta/meta.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'; | import 'package:synchronized/synchronized.dart'; | ||||||
| @ -37,6 +38,12 @@ typedef BTBVRemoveTrustForJidCallback = Future<void> Function(String jid); | |||||||
| /// A stub-implementation of [BTBVRemoveTrustForJidCallback]. | /// A stub-implementation of [BTBVRemoveTrustForJidCallback]. | ||||||
| Future<void> btbvRemoveTrustStub(String _) async {} | Future<void> btbvRemoveTrustStub(String _) async {} | ||||||
| 
 | 
 | ||||||
|  | /// A callback for when trust data should be loaded. | ||||||
|  | typedef BTBVLoadDataCallback = Future<List<BTBVTrustData>> Function(String jid); | ||||||
|  | 
 | ||||||
|  | /// A stub-implementation for [BTBVLoadDataCallback]. | ||||||
|  | Future<List<BTBVTrustData>> btbvLoadDataStub(String _) async => []; | ||||||
|  | 
 | ||||||
| /// Every device is in either of those two trust states: | /// Every device is in either of those two trust states: | ||||||
| /// - notTrusted: The device is absolutely not trusted | /// - notTrusted: The device is absolutely not trusted | ||||||
| /// - blindTrust: The fingerprint is not verified using OOB means | /// - blindTrust: The fingerprint is not verified using OOB means | ||||||
| @ -56,33 +63,31 @@ enum BTBVTrustState { | |||||||
| /// See https://gultsch.de/trust.html for more details. | /// See https://gultsch.de/trust.html for more details. | ||||||
| class BlindTrustBeforeVerificationTrustManager extends TrustManager { | class BlindTrustBeforeVerificationTrustManager extends TrustManager { | ||||||
|   BlindTrustBeforeVerificationTrustManager({ |   BlindTrustBeforeVerificationTrustManager({ | ||||||
|     Map<RatchetMapKey, BTBVTrustState>? trustCache, |     this.loadData = btbvLoadDataStub, | ||||||
|     Map<RatchetMapKey, bool>? enablementCache, |  | ||||||
|     Map<String, List<int>>? devices, |  | ||||||
|     this.commit = btbvCommitStub, |     this.commit = btbvCommitStub, | ||||||
|     this.removeTrust = btbvRemoveTrustStub, |     this.removeTrust = btbvRemoveTrustStub, | ||||||
|   })  : trustCache = trustCache ?? {}, |   }); | ||||||
|         enablementCache = enablementCache ?? {}, |  | ||||||
|         devices = devices ?? {}, |  | ||||||
|         _lock = Lock(); |  | ||||||
| 
 | 
 | ||||||
|   /// The cache for mapping a RatchetMapKey to its trust state |   /// The cache for mapping a RatchetMapKey to its trust state | ||||||
|   @visibleForTesting |   @visibleForTesting | ||||||
|   @protected |   @protected | ||||||
|   final Map<RatchetMapKey, BTBVTrustState> trustCache; |   final Map<RatchetMapKey, BTBVTrustState> trustCache = {}; | ||||||
| 
 | 
 | ||||||
|   /// The cache for mapping a RatchetMapKey to whether it is enabled or not |   /// The cache for mapping a RatchetMapKey to whether it is enabled or not | ||||||
|   @visibleForTesting |   @visibleForTesting | ||||||
|   @protected |   @protected | ||||||
|   final Map<RatchetMapKey, bool> enablementCache; |   final Map<RatchetMapKey, bool> enablementCache = {}; | ||||||
| 
 | 
 | ||||||
|   /// Mapping of Jids to their device identifiers |   /// Mapping of Jids to their device identifiers | ||||||
|   @visibleForTesting |   @visibleForTesting | ||||||
|   @protected |   @protected | ||||||
|   final Map<String, List<int>> devices; |   final Map<String, List<int>> devices = {}; | ||||||
| 
 | 
 | ||||||
|   /// The lock for devices and trustCache |   /// The lock for devices and trustCache | ||||||
|   final Lock _lock; |   final Lock _lock = Lock(); | ||||||
|  | 
 | ||||||
|  |   /// Callback for loading trust data. | ||||||
|  |   final BTBVLoadDataCallback loadData; | ||||||
| 
 | 
 | ||||||
|   /// Callback for commiting trust data to persistent storage. |   /// Callback for commiting trust data to persistent storage. | ||||||
|   final BTBVTrustCommitCallback commit; |   final BTBVTrustCommitCallback commit; | ||||||
| @ -241,6 +246,18 @@ class BlindTrustBeforeVerificationTrustManager extends TrustManager { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   @override | ||||||
|  |   Future<void> loadTrustData(String jid) async { | ||||||
|  |     await _lock.synchronized(() async { | ||||||
|  |       for (final result in await loadData(jid)) { | ||||||
|  |         final key = RatchetMapKey(jid, result.device); | ||||||
|  |         trustCache[key] = result.state; | ||||||
|  |         enablementCache[key] = result.enabled; | ||||||
|  |         devices.appendOrCreate(jid, result.device); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @visibleForTesting |   @visibleForTesting | ||||||
|   BTBVTrustState getDeviceTrust(String jid, int deviceId) => |   BTBVTrustState getDeviceTrust(String jid, int deviceId) => | ||||||
|       trustCache[RatchetMapKey(jid, deviceId)]!; |       trustCache[RatchetMapKey(jid, deviceId)]!; | ||||||
|  | |||||||
| @ -20,4 +20,7 @@ class NeverTrustingTrustManager extends TrustManager { | |||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Future<void> removeTrustDecisionsForJid(String jid) async {} |   Future<void> removeTrustDecisionsForJid(String jid) async {} | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<void> loadTrustData(String jid) async {} | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user