twonly-app/lib/src/services/signal/identity.signal.dart
2026-04-21 17:29:01 +02:00

154 lines
4.6 KiB
Dart

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<IdentityKeyPair?> 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<void> 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<List<PreKeyRecord>> 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<SignalIdentity?> 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<String, dynamic>);
} catch (e) {
Log.error('could not load signal identity: $e');
return null;
}
}
Future<void> 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<SignedPreKeyRecord?> _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;
}