feat: Add last features for use in moxxmpp

This commit is contained in:
PapaTutuWawa 2023-01-01 16:49:19 +01:00
parent 4085631804
commit 092ce36410
14 changed files with 216 additions and 88 deletions

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:cryptography/cryptography.dart';
import 'package:hex/hex.dart';
import 'package:meta/meta.dart';
import 'package:omemo_dart/src/crypto.dart';
import 'package:omemo_dart/src/double_ratchet/crypto.dart';
@ -251,6 +252,12 @@ class OmemoDoubleRatchet {
'kex': kex,
};
}
/// Returns the OMEMO compatible fingerprint of the ratchet session.
Future<String> getOmemoFingerprint() async {
final curveKey = await ik.toCurve25519();
return HEX.encode(await curveKey.getBytes());
}
Future<List<int>?> _trySkippedMessageKeys(OmemoMessage header, List<int> ciphertext) async {
final key = SkippedKey(

View File

@ -9,7 +9,6 @@ const privateKeyLength = 32;
const publicKeyLength = 32;
class OmemoPublicKey {
const OmemoPublicKey(this._pubkey);
factory OmemoPublicKey.fromBytes(List<int> bytes, KeyPairType type) {
@ -55,7 +54,6 @@ class OmemoPublicKey {
}
class OmemoPrivateKey {
const OmemoPrivateKey(this._privkey, this.type);
final List<int> _privkey;
final KeyPairType type;
@ -85,7 +83,6 @@ class OmemoPrivateKey {
/// A generic wrapper class for both Ed25519 and X25519 keypairs
class OmemoKeyPair {
const OmemoKeyPair(this.pk, this.sk, this.type);
/// Create an OmemoKeyPair just from a [type] and the bytes of the private and public

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:cryptography/cryptography.dart';
import 'package:hex/hex.dart';
import 'package:meta/meta.dart';
import 'package:omemo_dart/src/helpers.dart';
import 'package:omemo_dart/src/keys.dart';
@ -8,8 +9,8 @@ import 'package:omemo_dart/src/x3dh/x3dh.dart';
/// This class represents an OmemoBundle but with all keypairs belonging to the keys
@immutable
class Device {
const Device(
class OmemoDevice {
const OmemoDevice(
this.jid,
this.id,
this.ik,
@ -22,7 +23,7 @@ class Device {
);
/// Deserialize the Device
factory Device.fromJson(Map<String, dynamic> data) {
factory OmemoDevice.fromJson(Map<String, dynamic> data) {
// NOTE: We use the way OpenSSH names their keys, meaning that ik is the Identity
// Keypair's private key, while ik_pub refers to the Identity Keypair's public
// key.
@ -66,7 +67,7 @@ class Device {
),
);
return Device(
return OmemoDevice(
data['jid']! as String,
data['id']! as int,
OmemoKeyPair.fromBytes(
@ -92,7 +93,7 @@ class Device {
}
/// Generate a completely new device, i.e. cryptographic identity.
static Future<Device> generateNewDevice(String jid, { int opkAmount = 100 }) async {
static Future<OmemoDevice> generateNewDevice(String jid, { int opkAmount = 100 }) async {
final id = generateRandom32BitNumber();
final ik = await OmemoKeyPair.generateNewPair(KeyPairType.ed25519);
final spk = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
@ -104,7 +105,7 @@ class Device {
opks[i] = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
}
return Device(jid, id, ik, spk, spkId, signature, null, null, opks);
return OmemoDevice(jid, id, ik, spk, spkId, signature, null, null, opks);
}
/// Our bare Jid
@ -134,10 +135,10 @@ class Device {
/// This replaces the Onetime-Prekey with id [id] with a completely new one. Returns
/// a new Device object that copies over everything but replaces said key.
@internal
Future<Device> replaceOnetimePrekey(int id) async {
Future<OmemoDevice> replaceOnetimePrekey(int id) async {
opks[id] = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
return Device(
return OmemoDevice(
jid,
this.id,
ik,
@ -153,12 +154,12 @@ class Device {
/// This replaces the Signed-Prekey with a completely new one. Returns a new Device object
/// that copies over everything but replaces the Signed-Prekey and its signature.
@internal
Future<Device> replaceSignedPrekey() async {
Future<OmemoDevice> replaceSignedPrekey() async {
final newSpk = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
final newSpkId = generateRandom32BitNumber();
final newSignature = await sig(ik, await newSpk.pk.getBytes());
return Device(
return OmemoDevice(
jid,
id,
ik,
@ -174,8 +175,8 @@ class Device {
/// Returns a new device that is equal to this one with the exception that the new
/// device's id is a new number between 0 and 2**32 - 1.
@internal
Device withNewId() {
return Device(
OmemoDevice withNewId() {
return OmemoDevice(
jid,
generateRandom32BitNumber(),
ik,
@ -207,6 +208,13 @@ class Device {
);
}
/// Returns the fingerprint of the current device
Future<String> getFingerprint() async {
// Since the local key is Ed25519, we must convert it to Curve25519 first
final curveKey = await ik.pk.toCurve25519();
return HEX.encode(await curveKey.getBytes());
}
/// Serialise the device information.
Future<Map<String, dynamic>> toJson() async {
/// Serialise the OPKs
@ -236,7 +244,7 @@ class Device {
}
@visibleForTesting
Future<bool> equals(Device other) async {
Future<bool> equals(OmemoDevice other) async {
var opksMatch = true;
if (opks.length != other.opks.length) {
opksMatch = false;

View File

@ -31,5 +31,5 @@ class DeviceListModifiedEvent extends OmemoEvent {
/// and thus should be republished.
class DeviceModifiedEvent extends OmemoEvent {
DeviceModifiedEvent(this.device);
final Device device;
final OmemoDevice device;
}

View File

@ -38,9 +38,10 @@ class OmemoManager {
OmemoManager(
this._device,
this._trustManager,
this.sendEmptyOmemoMessage,
this.fetchDeviceList,
this.fetchDeviceBundle,
this.sendEmptyOmemoMessageImpl,
this.fetchDeviceListImpl,
this.fetchDeviceBundleImpl,
this.subscribeToDeviceListNodeImpl,
);
final Logger _log = Logger('OmemoManager');
@ -49,14 +50,17 @@ class OmemoManager {
/// Send an empty OMEMO:2 message using the encrypted payload @result to
/// @recipientJid.
final Future<void> Function(EncryptionResult result, String recipientJid) sendEmptyOmemoMessage;
final Future<void> Function(EncryptionResult result, String recipientJid) sendEmptyOmemoMessageImpl;
/// Fetch the list of device ids associated with @jid. If the device list cannot be
/// fetched, return null.
final Future<List<int>?> Function(String jid) fetchDeviceList;
final Future<List<int>?> Function(String jid) fetchDeviceListImpl;
/// Fetch the device bundle for the device with id @id of jid. If it cannot be fetched, return null.
final Future<OmemoBundle?> Function(String jid, int id) fetchDeviceBundle;
final Future<OmemoBundle?> Function(String jid, int id) fetchDeviceBundleImpl;
/// Subscribe to the device list PEP node of @jid.
final Future<void> Function(String jid) subscribeToDeviceListNodeImpl;
/// Map bare JID to its known devices
Map<String, List<int>> _deviceList = {};
@ -65,6 +69,8 @@ class OmemoManager {
/// Map bare a ratchet key to its ratchet. Note that this is also locked by
/// _ratchetCriticalSectionLock.
Map<RatchetMapKey, OmemoDoubleRatchet> _ratchetMap = {};
/// Map bare JID to whether we already tried to subscribe to the device list node.
final Map<String, bool> _subscriptionMap = {};
/// For preventing a race condition in encryption/decryption
final Map<String, Queue<Completer<void>>> _ratchetCriticalSectionQueue = {};
final Lock _ratchetCriticalSectionLock = Lock();
@ -76,10 +82,11 @@ class OmemoManager {
/// Our own keys...
final Lock _deviceLock = Lock();
// ignore: prefer_final_fields
Device _device;
OmemoDevice _device;
/// The event bus of the session manager
final StreamController<OmemoEvent> _eventStreamController = StreamController<OmemoEvent>.broadcast();
Stream<OmemoEvent> get eventStream => _eventStreamController.stream;
/// Enter the critical section for performing cryptographic operations on the ratchets
Future<void> _enterRatchetCriticalSection(String jid) async {
@ -379,7 +386,7 @@ class OmemoManager {
List<int> bundlesToFetch;
if (!_deviceListRequested.containsKey(jid) || !_deviceList.containsKey(jid)) {
// We don't have an up-to-date version of the device list
final newDeviceList = await fetchDeviceList(jid);
final newDeviceList = await fetchDeviceListImpl(jid);
if (newDeviceList == null) return [];
_deviceList[jid] = newDeviceList;
@ -404,7 +411,7 @@ class OmemoManager {
final newBundles = List<OmemoBundle>.empty(growable: true);
for (final id in bundlesToFetch) {
final bundle = await fetchDeviceBundle(jid, id);
final bundle = await fetchDeviceBundleImpl(jid, id);
if (bundle != null) newBundles.add(bundle);
}
@ -460,6 +467,11 @@ class OmemoManager {
continue;
}
if (!_subscriptionMap.containsKey(jid)) {
unawaited(subscribeToDeviceListNodeImpl(jid));
_subscriptionMap[jid] = true;
}
for (final deviceId in devices) {
// Empty OMEMO messages are allowed to bypass trust
if (plaintext != null) {
@ -553,11 +565,18 @@ class OmemoManager {
Future<DecryptionResult> onIncomingStanza(OmemoIncomingStanza stanza) async {
await _enterRatchetCriticalSection(stanza.bareSenderJid);
if (!_subscriptionMap.containsKey(stanza.bareSenderJid)) {
unawaited(subscribeToDeviceListNodeImpl(stanza.bareSenderJid));
_subscriptionMap[stanza.bareSenderJid] = true;
}
final ratchetKey = RatchetMapKey(stanza.bareSenderJid, stanza.senderDeviceId);
final _InternalDecryptionResult result;
try {
result = await _decryptMessage(
base64.decode(stanza.payload),
stanza.payload != null ?
base64.decode(stanza.payload!) :
null,
stanza.bareSenderJid,
stanza.senderDeviceId,
stanza.keys,
@ -578,7 +597,7 @@ class OmemoManager {
if (ratchet!.acknowledged) {
// Ratchet is acknowledged
if (ratchet.nr > 53 || result.ratchetCreated) {
await sendEmptyOmemoMessage(
await sendEmptyOmemoMessageImpl(
await _encryptToJids(
[stanza.bareSenderJid],
null,
@ -601,7 +620,7 @@ class OmemoManager {
stanza.senderDeviceId,
enterCriticalSection: false,
);
await sendEmptyOmemoMessage(
await sendEmptyOmemoMessageImpl(
await _encryptToJids(
[stanza.bareSenderJid],
null,
@ -619,11 +638,29 @@ class OmemoManager {
/// Call when sending out an encrypted stanza. Will handle everything and
/// encrypt it.
Future<EncryptionResult?> onOutgoingStanza(OmemoOutgoingStanza stanza) async {
return _encryptToJids(
Future<EncryptionResult> onOutgoingStanza(OmemoOutgoingStanza stanza) async {
_log.finest('Waiting to enter critical section');
await _enterRatchetCriticalSection(stanza.recipientJids.first);
_log.finest('Entered critical section');
final result = _encryptToJids(
stanza.recipientJids,
stanza.payload,
);
await _leaveRatchetCriticalSection(stanza.recipientJids.first);
return result;
}
// Sends a hearbeat message as specified by XEP-0384 to [jid].
Future<void> sendOmemoHeartbeat(String jid) async {
// TODO(Unknown): Include some error handling
final result = await _encryptToJids(
[jid],
null,
);
await sendEmptyOmemoMessageImpl(result, jid);
}
/// Mark the ratchet for device [deviceId] from [jid] as acked.
@ -646,9 +683,9 @@ class OmemoManager {
/// Generates an entirely new device. May be useful when the user wants to reset their cryptographic
/// identity. Triggers an event to commit it to storage.
Future<void> regenerateDevice({ int opkAmount = 100 }) async {
Future<void> regenerateDevice() async {
await _deviceLock.synchronized(() async {
_device = await Device.generateNewDevice(_device.jid, opkAmount: opkAmount);
_device = await OmemoDevice.generateNewDevice(_device.jid);
// Commit it
_eventStreamController.add(DeviceModifiedEvent(_device));
@ -656,11 +693,17 @@ class OmemoManager {
}
/// Returns the device used for encryption and decryption.
Future<Device> getDevice() => _deviceLock.synchronized(() => _device);
Future<OmemoDevice> getDevice() => _deviceLock.synchronized(() => _device);
/// Returns the id of the device used for encryption and decryption.
Future<int> getDeviceId() async => (await getDevice()).id;
/// Directly aquire the current device as a OMEMO device bundle.
Future<OmemoBundle> getDeviceBundle() async => (await getDevice()).toBundle();
/// Directly aquire the current device's fingerprint.
Future<String> getDeviceFingerprint() async => (await getDevice()).getFingerprint();
/// Returns the fingerprints for all devices of [jid] that we have a session with.
/// If there are not sessions with [jid], then returns null.
Future<List<DeviceFingerprint>?> getFingerprintsForJid(String jid) async {
@ -674,10 +717,11 @@ class OmemoManager {
final fingerprints = List<DeviceFingerprint>.empty(growable: true);
for (final key in fingerprintKeys) {
final curveKey = await _ratchetMap[key]!.ik.toCurve25519();
fingerprints.add(
DeviceFingerprint(
key.deviceId,
HEX.encode(await _ratchetMap[key]!.ik.getBytes()),
HEX.encode(await curveKey.getBytes()),
),
);
}
@ -689,6 +733,7 @@ class OmemoManager {
/// Ensures that the device list is fetched again on the next message sending.
void onNewConnection() {
_deviceListRequested.clear();
_subscriptionMap.clear();
}
/// Sets the device list for [jid] to [devices]. Triggers a DeviceListModifiedEvent.
@ -704,4 +749,35 @@ class OmemoManager {
_deviceList = deviceList;
_ratchetMap = ratchetMap;
}
/// Removes all ratchets for JID [jid]. This also removes all trust decisions for
/// [jid] from the trust manager. This function triggers a RatchetRemovedEvent for
/// every removed ratchet and a DeviceListModifiedEvent afterwards. Behaviour for
/// the trust manager is dependent on its implementation.
Future<void> removeAllRatchets(String jid) async {
await _enterRatchetCriticalSection(jid);
for (final deviceId in _deviceList[jid]!) {
// Remove the ratchet and commit it
_ratchetMap.remove(jid);
_eventStreamController.add(RatchetRemovedEvent(jid, deviceId));
}
// Remove the devices from the device list cache and commit it
_deviceList.remove(jid);
_deviceListRequested.remove(jid);
_eventStreamController.add(DeviceListModifiedEvent(_deviceList));
// Remove trust decisions
await _trustManager.removeTrustDecisionsForJid(jid);
await _leaveRatchetCriticalSection(jid);
}
/// Replaces the internal device with [newDevice]. Does not trigger an event.
Future<void> replaceDevice(OmemoDevice newDevice) async {
await _deviceLock.synchronized(() {
_device = newDevice;
});
}
}

View File

@ -45,7 +45,7 @@ class OmemoSessionManager {
// NOTE: Dart has some issues with just casting a List<dynamic> to List<Map<...>>, as
// such we need to convert the items by hand.
return OmemoSessionManager(
Device.fromJson(data['device']! as Map<String, dynamic>),
OmemoDevice.fromJson(data['device']! as Map<String, dynamic>),
(data['devices']! as Map<String, dynamic>).map<String, List<int>>(
(key, value) {
return MapEntry(
@ -62,7 +62,7 @@ class OmemoSessionManager {
/// Generate a new cryptographic identity.
static Future<OmemoSessionManager> generateNewIdentity(String jid, TrustManager trustManager, { int opkAmount = 100 }) async {
assert(opkAmount > 0, 'opkAmount must be bigger than 0.');
final device = await Device.generateNewDevice(jid, opkAmount: opkAmount);
final device = await OmemoDevice.generateNewDevice(jid, opkAmount: opkAmount);
return OmemoSessionManager(device, {}, {}, trustManager);
}
@ -84,7 +84,7 @@ class OmemoSessionManager {
/// Our own keys...
// ignore: prefer_final_fields
Device _device;
OmemoDevice _device;
/// and its lock
final Lock _deviceLock;
@ -96,7 +96,7 @@ class OmemoSessionManager {
Stream<OmemoEvent> get eventStream => _eventStreamController.stream;
/// Returns our own device.
Future<Device> getDevice() async {
Future<OmemoDevice> getDevice() async {
return _deviceLock.synchronized(() => _device);
}
@ -625,7 +625,7 @@ class OmemoSessionManager {
/// identity. Triggers an event to commit it to storage.
Future<void> regenerateDevice({ int opkAmount = 100 }) async {
await _deviceLock.synchronized(() async {
_device = await Device.generateNewDevice(_device.jid, opkAmount: opkAmount);
_device = await OmemoDevice.generateNewDevice(_device.jid, opkAmount: opkAmount);
// Commit it
_eventStreamController.add(DeviceModifiedEvent(_device));

View File

@ -23,7 +23,7 @@ class OmemoIncomingStanza {
final List<EncryptedKey> keys;
/// The string payload included in the <encrypted /> element.
final String payload;
final String? payload;
}
/// Describes a stanza that is to be sent out

View File

@ -18,6 +18,9 @@ class AlwaysTrustingTrustManager extends TrustManager {
@override
Future<void> setEnabled(String jid, int deviceId, bool enabled) async {}
@override
Future<void> removeTrustDecisionsForJid(String jid) async {}
@override
Future<Map<String, dynamic>> toJson() async => <String, dynamic>{};
}

View File

@ -19,4 +19,7 @@ abstract class TrustManager {
/// Serialize the trust manager to JSON.
Future<Map<String, dynamic>> toJson();
/// Removes all trust decisions for [jid].
Future<void> removeTrustDecisionsForJid(String jid);
}

View File

@ -211,6 +211,14 @@ abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager {
),
);
}
@override
Future<void> removeTrustDecisionsForJid(String jid) async {
await _lock.synchronized(() async {
devices.remove(jid);
await commitState();
});
}
/// Called when the state of the trust manager has been changed. Allows the user to
/// commit the trust state to persistent storage.

View File

@ -18,6 +18,9 @@ class NeverTrustingTrustManager extends TrustManager {
@override
Future<void> setEnabled(String jid, int deviceId, bool enabled) async {}
@override
Future<void> removeTrustDecisionsForJid(String jid) async {}
@override
Future<Map<String, dynamic>> toJson() async => <String, dynamic>{};
}

View File

@ -14,7 +14,7 @@ void main() {
test('Test replacing a onetime prekey', () async {
const aliceJid = 'alice@server.example';
final device = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final device = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final newDevice = await device.replaceOnetimePrekey(0);
@ -327,7 +327,7 @@ void main() {
// Setup an event listener
final oldDevice = await aliceSession.getDevice();
Device? newDevice;
OmemoDevice? newDevice;
aliceSession.eventStream.listen((event) {
if (event is DeviceModifiedEvent) {
newDevice = event.device;

View File

@ -18,8 +18,8 @@ void main() {
var aliceEmptyMessageSent = 0;
var bobEmptyMessageSent = 0;
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await Device.generateNewDevice(bobJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -35,6 +35,7 @@ void main() {
expect(jid, bobJid);
return bobDevice.toBundle();
},
(jid) async {},
);
final bobManager = OmemoManager(
bobDevice,
@ -50,6 +51,7 @@ void main() {
expect(jid, aliceJid);
return aliceDevice.toBundle();
},
(jid) async {},
);
// Alice sends a message
@ -66,7 +68,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResult!.encryptedKeys,
aliceResult.encryptedKeys,
base64.encode(aliceResult.ciphertext!),
),
);
@ -94,7 +96,7 @@ void main() {
bobJid,
bobDevice.id,
DateTime.now().millisecondsSinceEpoch,
bobResult2!.encryptedKeys,
bobResult2.encryptedKeys,
base64.encode(bobResult2.ciphertext!),
),
);
@ -111,8 +113,8 @@ void main() {
var aliceEmptyMessageSent = 0;
var bobEmptyMessageSent = 0;
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await Device.generateNewDevice(bobJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -128,6 +130,7 @@ void main() {
expect(jid, bobJid);
return bobDevice.toBundle();
},
(jid) async {},
);
final bobManager = OmemoManager(
bobDevice,
@ -143,6 +146,7 @@ void main() {
expect(jid, aliceJid);
return aliceDevice.toBundle();
},
(jid) async {},
);
// Alice sends a message
@ -159,7 +163,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResult!.encryptedKeys,
aliceResult.encryptedKeys,
base64.encode(aliceResult.ciphertext!),
),
);
@ -185,7 +189,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResultLoop!.encryptedKeys,
aliceResultLoop.encryptedKeys,
base64.encode(aliceResultLoop.ciphertext!),
),
);
@ -208,7 +212,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResultFinal!.encryptedKeys,
aliceResultFinal.encryptedKeys,
base64.encode(aliceResultFinal.ciphertext!),
),
);
@ -221,7 +225,7 @@ void main() {
test('Test accessing data without it existing', () async {
const aliceJid = 'alice@server1';
const bobJid = 'bob@server2';
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -229,6 +233,7 @@ void main() {
(result, recipientJid) async {},
(jid) async => [],
(jid, id) async => null,
(jid) async {},
);
// Get non-existant fingerprints
@ -249,9 +254,9 @@ void main() {
const bobJid = 'bob@server2';
var oldDevice = true;
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final bobOldDevice = await Device.generateNewDevice(bobJid, opkAmount: 1);
final bobCurrentDevice = await Device.generateNewDevice(bobJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final bobOldDevice = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final bobCurrentDevice = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -270,6 +275,7 @@ void main() {
bobOldDevice.toBundle() :
bobCurrentDevice.toBundle();
},
(jid) async {},
);
final bobManager = OmemoManager(
bobCurrentDevice,
@ -277,6 +283,7 @@ void main() {
(result, recipientJid) async {},
(jid) async => [],
(jid, id) async => null,
(jid) async {},
);
// Alice encrypts a message to Bob
@ -293,7 +300,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResult1!.encryptedKeys,
aliceResult1.encryptedKeys,
base64.encode(aliceResult1.ciphertext!),
),
);
@ -317,7 +324,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResult2!.encryptedKeys,
aliceResult2.encryptedKeys,
base64.encode(aliceResult2.ciphertext!),
),
);
@ -331,9 +338,9 @@ void main() {
const bobJid = 'bob@server2';
var bothDevices = false;
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice1 = await Device.generateNewDevice(bobJid, opkAmount: 1);
final bobDevice2 = await Device.generateNewDevice(bobJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice1 = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final bobDevice2 = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -364,6 +371,7 @@ void main() {
return null;
},
(jid) async {},
);
final bobManager1 = OmemoManager(
bobDevice1,
@ -371,6 +379,7 @@ void main() {
(result, recipientJid) async {},
(jid) async => [],
(jid, id) async => null,
(jid) async {},
);
final bobManager2 = OmemoManager(
bobDevice2,
@ -384,6 +393,7 @@ void main() {
expect(jid, aliceJid);
return aliceDevice.toBundle();
},
(jid) async {},
);
// Alice sends a message to Bob
@ -400,7 +410,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResult1!.encryptedKeys,
aliceResult1.encryptedKeys,
base64.encode(aliceResult1.ciphertext!),
),
);
@ -423,7 +433,7 @@ void main() {
bobJid,
bobDevice2.id,
DateTime.now().millisecondsSinceEpoch,
bobResult2!.encryptedKeys,
bobResult2.encryptedKeys,
base64.encode(bobResult2.ciphertext!),
),
);
@ -436,9 +446,9 @@ void main() {
const bobJid = 'bob@server2';
var bothDevices = false;
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice1 = await Device.generateNewDevice(bobJid, opkAmount: 1);
final bobDevice2 = await Device.generateNewDevice(bobJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice1 = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final bobDevice2 = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -469,6 +479,7 @@ void main() {
return null;
},
(jid) async {},
);
final bobManager1 = OmemoManager(
bobDevice1,
@ -476,6 +487,7 @@ void main() {
(result, recipientJid) async {},
(jid) async => null,
(jid, id) async => null,
(jid) async {},
);
final bobManager2 = OmemoManager(
bobDevice2,
@ -483,6 +495,7 @@ void main() {
(result, recipientJid) async {},
(jid) async => null,
(jid, id) async => null,
(jid) async {},
);
// Alice sends a message to Bob
@ -499,7 +512,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResult1!.encryptedKeys,
aliceResult1.encryptedKeys,
base64.encode(aliceResult1.ciphertext!),
),
);
@ -528,7 +541,7 @@ void main() {
),
);
expect(aliceResult2!.encryptedKeys.length, 2);
expect(aliceResult2.encryptedKeys.length, 2);
// And Bob decrypts it
final bobResult21 = await bobManager1.onIncomingStanza(
@ -567,7 +580,7 @@ void main() {
bobJid,
bobDevice2.id,
DateTime.now().millisecondsSinceEpoch,
bobResult32!.encryptedKeys,
bobResult32.encryptedKeys,
base64.encode(bobResult32.ciphertext!),
),
);
@ -580,9 +593,9 @@ void main() {
const bobJid = 'bob@server2';
const cocoJid = 'coco@server3';
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await Device.generateNewDevice(bobJid, opkAmount: 1);
final cocoDevice = await Device.generateNewDevice(cocoJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final cocoDevice = await OmemoDevice.generateNewDevice(cocoJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -606,6 +619,7 @@ void main() {
return null;
},
(jid) async {},
);
final bobManager = OmemoManager(
bobDevice,
@ -613,6 +627,7 @@ void main() {
(result, recipientJid) async {},
(jid) async => null,
(jid, id) async => null,
(jid) async {},
);
final cocoManager = OmemoManager(
cocoDevice,
@ -620,6 +635,7 @@ void main() {
(result, recipientJid) async {},
(jid) async => null,
(jid, id) async => null,
(jid) async {},
);
// Alice sends a message to Bob and Coco
@ -636,7 +652,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResult!.encryptedKeys,
aliceResult.encryptedKeys,
base64.encode(aliceResult.ciphertext!),
),
);
@ -661,8 +677,8 @@ void main() {
const bobJid = 'bob@server2';
var failure = false;
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await Device.generateNewDevice(bobJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -682,6 +698,7 @@ void main() {
null :
bobDevice.toBundle();
},
(jid) async {},
);
final bobManager = OmemoManager(
bobDevice,
@ -689,6 +706,7 @@ void main() {
(result, recipientJid) async {},
(jid) async => null,
(jid, id) async => null,
(jid) async {},
);
// Alice sends a message to Bob and Coco
@ -705,7 +723,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResult1!.encryptedKeys,
aliceResult1.encryptedKeys,
base64.encode(aliceResult1.ciphertext!),
),
);
@ -737,7 +755,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceResult2!.encryptedKeys,
aliceResult2.encryptedKeys,
base64.encode(aliceResult2.ciphertext!),
),
);
@ -750,7 +768,7 @@ void main() {
const aliceJid = 'alice@server1';
const bobJid = 'bob@server2';
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -766,6 +784,7 @@ void main() {
return null;
},
(jid) async {},
);
// Alice sends a message to Bob
@ -776,7 +795,7 @@ void main() {
),
);
expect(aliceResult!.isSuccess(1), false);
expect(aliceResult.isSuccess(1), false);
expect(aliceResult.jidEncryptionErrors[bobJid] is NoKeyMaterialAvailableException, true);
});
@ -785,8 +804,8 @@ void main() {
const bobJid = 'bob@server2';
const cocoJid = 'coco@server3';
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await Device.generateNewDevice(bobJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -806,6 +825,7 @@ void main() {
return null;
},
(jid) async {},
);
final bobManager = OmemoManager(
bobDevice,
@ -813,6 +833,7 @@ void main() {
(result, recipientJid) async {},
(jid) async => null,
(jid, id) async => null,
(jid) async {},
);
// Alice sends a message to Bob and Coco
@ -823,7 +844,7 @@ void main() {
),
);
expect(aliceResult!.isSuccess(2), true);
expect(aliceResult.isSuccess(2), true);
expect(aliceResult.jidEncryptionErrors[cocoJid] is NoKeyMaterialAvailableException, true);
// Bob decrypts it
@ -844,8 +865,8 @@ void main() {
const aliceJid = 'alice@server1';
const bobJid = 'bob@server2';
final aliceDevice = await Device.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await Device.generateNewDevice(bobJid, opkAmount: 1);
final aliceDevice = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1);
final bobDevice = await OmemoDevice.generateNewDevice(bobJid, opkAmount: 1);
final aliceManager = OmemoManager(
aliceDevice,
@ -861,6 +882,7 @@ void main() {
return bobDevice.toBundle();
},
(jid) async {},
);
final bobManager = OmemoManager(
bobDevice,
@ -868,6 +890,7 @@ void main() {
(result, recipientJid) async {},
(jid) async => null,
(jid, id) async => null,
(jid) async {},
);
// Alice encrypts a message for Bob
@ -884,7 +907,7 @@ void main() {
aliceJid,
aliceDevice.id,
DateTime.now().millisecondsSinceEpoch,
aliceMessage!.encryptedKeys,
aliceMessage.encryptedKeys,
base64.encode(aliceMessage.ciphertext!),
),
@ -905,7 +928,7 @@ void main() {
messageText,
),
);
expect(bobResponseMessage!.isSuccess(1), true);
expect(bobResponseMessage.isSuccess(1), true);
final aliceReceivedMessage = await aliceManager.onIncomingStanza(
OmemoIncomingStanza(

View File

@ -18,7 +18,7 @@ void main() {
final oldDevice = await oldSession.getDevice();
final serialised = jsonify(await oldDevice.toJson());
final newDevice = Device.fromJson(serialised);
final newDevice = OmemoDevice.fromJson(serialised);
expect(await oldDevice.equals(newDevice), true);
});
@ -32,7 +32,7 @@ void main() {
final oldDevice = await (await oldSession.getDevice()).replaceSignedPrekey();
final serialised = jsonify(await oldDevice.toJson());
final newDevice = Device.fromJson(serialised);
final newDevice = OmemoDevice.fromJson(serialised);
expect(await oldDevice.equals(newDevice), true);
});