lib/core/services/signal/direct_message_cipher.dart
class DirectMessageDecryption {
const DirectMessageDecryption.text(
String this.text, {
this.downgraded = false,
}) : failure = null;
const DirectMessageDecryption.failed(
DirectMessageDecryptionFailure this.failure,
) : text = null,
downgraded = false;
final String? text;
final DirectMessageDecryptionFailure? failure;
final bool downgraded;
bool get isText => text != null;
}
/// Routes a direct-message payload to its declared encryption protocol.
///
/// The local plaintext store is only a reconciliation aid for this devi
/// optimistic outbound messages. It is bound to the conversation or rec
/// ciphertext and must not be used as a general decryption fallback.
class DirectMessageCipher {
DirectMessageCipher({
required E2eeDmCryptoService legacy,
SignalDmCryptoService? signal,
SignalSelfMessageStore? selfMessages,
int? ownDeviceNumber,
}) : _legacy = legacy,
_signal = signal,
_selfMessages = selfMessages,
_ownDeviceNumber = ownDeviceNumber;
final E2eeDmCryptoService _legacy;
final SignalDmCryptoService? _signal;
final SignalSelfMessageStore? _selfMessages;
final int? _ownDeviceNumber;
bool get isSignalReady => _signal != null && _ownDeviceNumber != null;
Future<Map<String, dynamic>> encryptText({
required String ownerUserId,
required String conversationId,
}
class SignalLayerStatus {
const SignalLayerStatus({
required this.state,
this.deviceId,
this.deviceNumber,
this.failure,
this.storeVerdict,
});
final SignalLayerState state;
final String? deviceId;
final int? deviceNumber;
final String? failure;
final SignalStoreVerdict? storeVerdict;
bool get isRunning => state == SignalLayerState.running;
bool get wasRestored => storeVerdict == SignalStoreVerdict.restored;
}
class SignalSession {
const SignalSession({
required this.userId,
required this.deviceId,
required this.deviceNumber,
required this.stores,
required this.lifecycle,
required this.dm,
required this.selfMessages,
required this.safetyNumbers,
});
final String userId;
final String deviceId;
final int deviceNumber;
final SignalProtocolStores stores;
final SignalKeyLifecycleService lifecycle;
final SignalDmCryptoService dm;
final SignalSelfMessageStore selfMessages;
final SignalSafetyNumberService safetyNumbers;
Future<void> close() => stores.close();
final int peerDeviceNumber;
final String digits;
final Uint8List scannable;
List<String> get groups {
final groups = <String>[];
for (var i = 0; i < digits.length; i += 5) {
groups.add(
digits.substring(i, i + 5 > digits.length ? digits.length : i
}
return groups;
}
}
class SignalSafetyNumberService {
const SignalSafetyNumberService(this._identities, this._sessions);
final DurableIdentityKeyStore _identities;
final DurableSessionStore _sessions;
static const _iterations = 5200;
static const _version = 2;
Future<SignalSafetyNumber?> forPeer({
required String ownerUserId,
required String peerUserId,
required int peerDeviceNumber,
}) async {
final peerAddress = ProtocolAddress(
name: peerUserId,
deviceId: peerDeviceNumber,
);
final peerIdentity = await _identities.getIdentity(peerAddress);
if (peerIdentity == null) return null;
final localIdentity = await _identities.getIdentityKeyPair();
final fingerprint = Fingerprint(
iterations: _iterations,
version: _version,
localIdentifier: utf8.encode(ownerUserId),
localPublicKey: localIdentity.publicKey.toList(),
remoteIdentifier: utf8.encode(peerUserId),
remotePublicKey: peerIdentity.serialize().toList(),
);
final int deviceNumber;
final String ciphertext;