import 'dart:convert'; import 'package:clock/clock.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:twonly/locator.dart'; import 'package:twonly/src/constants/secure_storage.keys.dart'; import 'package:twonly/src/database/signal/signal_protocol_store.dart'; import 'package:twonly/src/model/json/signal_identity.model.dart'; import 'package:twonly/src/services/signal/consts.signal.dart'; import 'package:twonly/src/services/signal/utils.signal.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/utils/log.dart'; Future getSignalIdentityKeyPair() async { final signalIdentity = await getSignalIdentity(); if (signalIdentity == null) return null; return IdentityKeyPair.fromSerialized(signalIdentity.identityKeyPairU8List); } // This function runs after the clients authenticated with the server. // It then checks if it should update a new session key Future signalHandleNewServerConnection() async { if (appSession.currentUser.signalLastSignedPreKeyUpdated != null) { final fortyEightHoursAgo = clock.now().subtract(const Duration(hours: 48)); final isYoungerThan48Hours = (appSession.currentUser.signalLastSignedPreKeyUpdated!).isAfter( fortyEightHoursAgo, ); if (isYoungerThan48Hours) { // The key does live for 48 hours then it expires and a new key is generated. return; } } final signedPreKey = await _getNewSignalSignedPreKey(); if (signedPreKey == null) { Log.error('could not generate a new signed pre key!'); return; } await updateUser((user) { user.signalLastSignedPreKeyUpdated = clock.now(); }); final res = await apiService.updateSignedPreKey( signedPreKey.id, signedPreKey.getKeyPair().publicKey.serialize(), signedPreKey.signature, ); if (res.isError) { Log.error('could not update the signed pre key: ${res.error}'); await updateUser((user) { user.signalLastSignedPreKeyUpdated = null; }); } else { Log.info('updated signed pre key'); } } Future> signalGetPreKeys() async { final user = await getUser(); if (user == null) return []; final start = user.currentPreKeyIndexStart; await updateUser((user) { user.currentPreKeyIndexStart = (user.currentPreKeyIndexStart + 200) % maxValue; }); final preKeys = generatePreKeys(start, 200); final signalStore = await getSignalStore(); if (signalStore == null) return []; for (final p in preKeys) { await signalStore.preKeyStore.storePreKey(p.id, p); } return preKeys; } Future getSignalIdentity() async { try { const storage = FlutterSecureStorage(); var signalIdentityJson = await storage.read( key: SecureStorageKeys.signalIdentity, ); if (signalIdentityJson == null) { return null; } final decoded = jsonDecode(signalIdentityJson); signalIdentityJson = null; return SignalIdentity.fromJson(decoded as Map); } catch (e) { Log.error('could not load signal identity: $e'); return null; } } Future createIfNotExistsSignalIdentity() async { const storage = FlutterSecureStorage(); final signalIdentity = await storage.read( key: SecureStorageKeys.signalIdentity, ); if (signalIdentity != null) { return; } final identityKeyPair = generateIdentityKeyPair(); final registrationId = generateRegistrationId(true); final signalStore = SignalSignalProtocolStore( identityKeyPair, registrationId, ); final signedPreKey = generateSignedPreKey(identityKeyPair, defaultDeviceId); await signalStore.signedPreKeyStore.storeSignedPreKey( signedPreKey.id, signedPreKey, ); final storedSignalIdentity = SignalIdentity( identityKeyPairU8List: identityKeyPair.serialize(), registrationId: registrationId, ); await storage.write( key: SecureStorageKeys.signalIdentity, value: jsonEncode(storedSignalIdentity), ); } Future _getNewSignalSignedPreKey() async { var identityKeyPair = await getSignalIdentityKeyPair(); final user = await getUser(); final signalStore = await getSignalStore(); if (identityKeyPair == null || signalStore == null || user == null) { return null; } final signedPreKeyId = user.currentSignedPreKeyIndexStart; await updateUser((user) { user.currentSignedPreKeyIndexStart += 1; }); final signedPreKey = generateSignedPreKey( identityKeyPair, signedPreKeyId, ); identityKeyPair = null; await signalStore.storeSignedPreKey(signedPreKeyId, signedPreKey); return signedPreKey; }