Compare commits
12 Commits
v0.1.1
...
fda06cef55
| Author | SHA1 | Date | |
|---|---|---|---|
| fda06cef55 | |||
| 800b53b11f | |||
| 5a097e4d2a | |||
| 710b3c9497 | |||
| f540a80ec2 | |||
| 44ab31aebb | |||
| e4f1d7d4b0 | |||
| 7600804aa1 | |||
| a4589b6e09 | |||
| d0986a4608 | |||
| 683a76cc80 | |||
| dad707f71d |
2
.gitlint
2
.gitlint
@@ -5,6 +5,6 @@ line-length=72
|
|||||||
[title-trailing-punctuation]
|
[title-trailing-punctuation]
|
||||||
[title-hard-tab]
|
[title-hard-tab]
|
||||||
[title-match-regex]
|
[title-match-regex]
|
||||||
regex=^(feat|fix|test|release|chore|security|docs|refactor):.*$
|
regex=^(feat|fix|test|release|chore|security|docs|refactor|style):.*$
|
||||||
[body-trailing-whitespace]
|
[body-trailing-whitespace]
|
||||||
[body-first-line-empty]
|
[body-first-line-empty]
|
||||||
|
|||||||
@@ -4,3 +4,8 @@
|
|||||||
- Implement the Double Ratchet, X3DH and OMEMO specific bits
|
- Implement the Double Ratchet, X3DH and OMEMO specific bits
|
||||||
- Add a Blind-Trust-Before-Verification TrustManager
|
- Add a Blind-Trust-Before-Verification TrustManager
|
||||||
- Supported OMEMO version: 0.8.3
|
- Supported OMEMO version: 0.8.3
|
||||||
|
|
||||||
|
## 0.1.3
|
||||||
|
|
||||||
|
- Fix bug with the Double Ratchet causing only the initial message to be decryptable
|
||||||
|
- Expose `getDeviceMap` as a developer usable function
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ class OmemoDoubleRatchet {
|
|||||||
this.ik,
|
this.ik,
|
||||||
this.sessionAd,
|
this.sessionAd,
|
||||||
this.mkSkipped, // MKSKIPPED
|
this.mkSkipped, // MKSKIPPED
|
||||||
|
this.acknowledged,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory OmemoDoubleRatchet.fromJson(Map<String, dynamic> data) {
|
factory OmemoDoubleRatchet.fromJson(Map<String, dynamic> data) {
|
||||||
@@ -83,6 +84,7 @@ class OmemoDoubleRatchet {
|
|||||||
'pn': 0,
|
'pn': 0,
|
||||||
'ik_pub': 'base/64/encoded',
|
'ik_pub': 'base/64/encoded',
|
||||||
'session_ad': 'base/64/encoded',
|
'session_ad': 'base/64/encoded',
|
||||||
|
'acknowledged': true | false,
|
||||||
'mkskipped': [
|
'mkskipped': [
|
||||||
{
|
{
|
||||||
'key': 'base/64/encoded',
|
'key': 'base/64/encoded',
|
||||||
@@ -117,6 +119,7 @@ class OmemoDoubleRatchet {
|
|||||||
),
|
),
|
||||||
base64.decode(data['session_ad']! as String),
|
base64.decode(data['session_ad']! as String),
|
||||||
mkSkipped,
|
mkSkipped,
|
||||||
|
data['acknowledged']! as bool,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +151,10 @@ class OmemoDoubleRatchet {
|
|||||||
|
|
||||||
final Map<SkippedKey, List<int>> mkSkipped;
|
final Map<SkippedKey, List<int>> mkSkipped;
|
||||||
|
|
||||||
|
/// Indicates whether we received an empty OMEMO message after building a session with
|
||||||
|
/// the device.
|
||||||
|
bool acknowledged;
|
||||||
|
|
||||||
/// Create an OMEMO session using the Signed Pre Key [spk], the shared secret [sk] that
|
/// Create an OMEMO session using the Signed Pre Key [spk], the shared secret [sk] that
|
||||||
/// was obtained using a X3DH and the associated data [ad] that was also obtained through
|
/// was obtained using a X3DH and the associated data [ad] that was also obtained through
|
||||||
/// a X3DH. [ik] refers to Bob's (the receiver's) IK public key.
|
/// a X3DH. [ik] refers to Bob's (the receiver's) IK public key.
|
||||||
@@ -169,6 +176,7 @@ class OmemoDoubleRatchet {
|
|||||||
ik,
|
ik,
|
||||||
ad,
|
ad,
|
||||||
{},
|
{},
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,6 +197,7 @@ class OmemoDoubleRatchet {
|
|||||||
ik,
|
ik,
|
||||||
ad,
|
ad,
|
||||||
{},
|
{},
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +223,7 @@ class OmemoDoubleRatchet {
|
|||||||
'ik_pub': base64.encode(await ik.getBytes()),
|
'ik_pub': base64.encode(await ik.getBytes()),
|
||||||
'session_ad': base64.encode(sessionAd),
|
'session_ad': base64.encode(sessionAd),
|
||||||
'mkskipped': mkSkippedSerialised,
|
'mkskipped': mkSkippedSerialised,
|
||||||
|
'acknowledged': acknowledged,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +303,11 @@ class OmemoDoubleRatchet {
|
|||||||
return plaintext;
|
return plaintext;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.dhPub != await dhr?.getBytes()) {
|
final dhPubMatches = listsEqual(
|
||||||
|
header.dhPub ?? <int>[],
|
||||||
|
await dhr?.getBytes() ?? <int>[],
|
||||||
|
);
|
||||||
|
if (!dhPubMatches) {
|
||||||
await _skipMessageKeys(header.pn!);
|
await _skipMessageKeys(header.pn!);
|
||||||
await _dhRatchet(header);
|
await _dhRatchet(header);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ class RatchetModifiedEvent extends OmemoEvent {
|
|||||||
final OmemoDoubleRatchet ratchet;
|
final OmemoDoubleRatchet ratchet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Triggered when a ratchet has been removed and should be removed from storage.
|
||||||
|
class RatchetRemovedEvent extends OmemoEvent {
|
||||||
|
|
||||||
|
RatchetRemovedEvent(this.jid, this.deviceId);
|
||||||
|
final String jid;
|
||||||
|
final int deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
/// Triggered when the device map has been modified
|
/// Triggered when the device map has been modified
|
||||||
class DeviceMapModifiedEvent extends OmemoEvent {
|
class DeviceMapModifiedEvent extends OmemoEvent {
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,17 @@ class OmemoSessionManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deserialise the OmemoSessionManager from JSON data [data] that does not contain
|
||||||
|
/// the ratchet sessions.
|
||||||
|
factory OmemoSessionManager.fromJsonWithoutSessions(Map<String, dynamic> data, Map<RatchetMapKey, OmemoDoubleRatchet> ratchetMap, TrustManager trustManager) {
|
||||||
|
return OmemoSessionManager(
|
||||||
|
Device.fromJson(data['device']! as Map<String, dynamic>),
|
||||||
|
data['devices']! as Map<String, List<int>>,
|
||||||
|
ratchetMap,
|
||||||
|
trustManager,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate a new cryptographic identity.
|
/// Generate a new cryptographic identity.
|
||||||
static Future<OmemoSessionManager> generateNewIdentity(String jid, TrustManager trustManager, { int opkAmount = 100 }) async {
|
static Future<OmemoSessionManager> generateNewIdentity(String jid, TrustManager trustManager, { int opkAmount = 100 }) async {
|
||||||
assert(opkAmount > 0, 'opkAmount must be bigger than 0.');
|
assert(opkAmount > 0, 'opkAmount must be bigger than 0.');
|
||||||
@@ -83,6 +94,7 @@ class OmemoSessionManager {
|
|||||||
/// A stream that receives events regarding the session
|
/// A stream that receives events regarding the session
|
||||||
Stream<OmemoEvent> get eventStream => _eventStreamController.stream;
|
Stream<OmemoEvent> get eventStream => _eventStreamController.stream;
|
||||||
|
|
||||||
|
/// Returns our own device.
|
||||||
Future<Device> getDevice() async {
|
Future<Device> getDevice() async {
|
||||||
Device? dev;
|
Device? dev;
|
||||||
await _deviceLock.synchronized(() async {
|
await _deviceLock.synchronized(() async {
|
||||||
@@ -387,11 +399,67 @@ class OmemoSessionManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
/// Returns the device map, i.e. the mapping of bare Jid to its device identifiers
|
||||||
OmemoDoubleRatchet getRatchet(String jid, int deviceId) => _ratchetMap[RatchetMapKey(jid, deviceId)]!;
|
/// we have built sessions with.
|
||||||
|
Future<Map<String, List<int>>> getDeviceMap() async {
|
||||||
|
Map<String, List<int>>? map;
|
||||||
|
|
||||||
|
await _lock.synchronized(() async {
|
||||||
|
map = _deviceMap;
|
||||||
|
});
|
||||||
|
|
||||||
|
return map!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the ratchet identified by [jid] and [deviceId] from the session manager.
|
||||||
|
/// Also triggers events for commiting the new device map to storage and removing
|
||||||
|
/// the old ratchet.
|
||||||
|
Future<void> removeRatchet(String jid, int deviceId) async {
|
||||||
|
await _lock.synchronized(() async {
|
||||||
|
// Remove the ratchet
|
||||||
|
_ratchetMap.remove(RatchetMapKey(jid, deviceId));
|
||||||
|
// Commit it
|
||||||
|
_eventStreamController.add(RatchetRemovedEvent(jid, deviceId));
|
||||||
|
|
||||||
|
// Remove the device from jid
|
||||||
|
_deviceMap[jid]!.remove(deviceId);
|
||||||
|
if (_deviceMap[jid]!.isEmpty) {
|
||||||
|
_deviceMap.remove(jid);
|
||||||
|
}
|
||||||
|
// Commit it
|
||||||
|
_eventStreamController.add(DeviceMapModifiedEvent(_deviceMap));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the list of device identifiers belonging to [jid] that are yet unacked, i.e.
|
||||||
|
/// we have not yet received an empty OMEMO message from.
|
||||||
|
Future<List<int>> getUnacknowledgedRatchets(String jid) async {
|
||||||
|
final ret = List<int>.empty(growable: true);
|
||||||
|
|
||||||
|
await _lock.synchronized(() async {
|
||||||
|
final devices = _deviceMap[jid]!;
|
||||||
|
for (final device in devices) {
|
||||||
|
final ratchet = _ratchetMap[RatchetMapKey(jid, device)]!;
|
||||||
|
if (!ratchet.acknowledged) ret.add(device);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the ratchet for device [deviceId] from [jid] as acked.
|
||||||
|
Future<void> ratchetAcknowledged(String jid, int deviceId) async {
|
||||||
|
await _lock.synchronized(() async {
|
||||||
|
final ratchet = _ratchetMap[RatchetMapKey(jid, deviceId)]!
|
||||||
|
..acknowledged = true;
|
||||||
|
|
||||||
|
// Commit it
|
||||||
|
_eventStreamController.add(RatchetModifiedEvent(jid, deviceId, ratchet));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
Map<String, List<int>> getDeviceMap() => _deviceMap;
|
OmemoDoubleRatchet getRatchet(String jid, int deviceId) => _ratchetMap[RatchetMapKey(jid, deviceId)]!;
|
||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
Map<RatchetMapKey, OmemoDoubleRatchet> getRatchetMap() => _ratchetMap;
|
Map<RatchetMapKey, OmemoDoubleRatchet> getRatchetMap() => _ratchetMap;
|
||||||
@@ -414,7 +482,6 @@ class OmemoSessionManager {
|
|||||||
},
|
},
|
||||||
...
|
...
|
||||||
],
|
],
|
||||||
'trust': { ... }
|
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -430,8 +497,25 @@ class OmemoSessionManager {
|
|||||||
'devices': _deviceMap,
|
'devices': _deviceMap,
|
||||||
'device': await (await getDevice()).toJson(),
|
'device': await (await getDevice()).toJson(),
|
||||||
'sessions': sessions,
|
'sessions': sessions,
|
||||||
// TODO(PapaTutuWawa): Implement
|
};
|
||||||
'trust': <String, dynamic>{},
|
}
|
||||||
|
|
||||||
|
/// Serialise the entire session manager into a JSON object.
|
||||||
|
Future<Map<String, dynamic>> toJsonWithoutSessions() async {
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
'devices': {
|
||||||
|
'alice@...': [1, 2, ...],
|
||||||
|
'bob@...': [1],
|
||||||
|
...
|
||||||
|
},
|
||||||
|
'device': { ... },
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return {
|
||||||
|
'devices': _deviceMap,
|
||||||
|
'device': await (await getDevice()).toJson(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: omemo_dart
|
name: omemo_dart
|
||||||
description: An XMPP library independent OMEMO library
|
description: An XMPP library independent OMEMO library
|
||||||
version: 0.1.0
|
version: 0.1.3
|
||||||
homepage: https://github.com/PapaTutuWawa/omemo_dart
|
homepage: https://github.com/PapaTutuWawa/omemo_dart
|
||||||
publish_to: https://git.polynom.me/api/packages/PapaTutuWawa/pub
|
publish_to: https://git.polynom.me/api/packages/PapaTutuWawa/pub
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ void main() {
|
|||||||
test('Test using OMEMO sessions with only one device per user', () async {
|
test('Test using OMEMO sessions with only one device per user', () async {
|
||||||
const aliceJid = 'alice@server.example';
|
const aliceJid = 'alice@server.example';
|
||||||
const bobJid = 'bob@other.server.example';
|
const bobJid = 'bob@other.server.example';
|
||||||
|
|
||||||
// Alice and Bob generate their sessions
|
// Alice and Bob generate their sessions
|
||||||
var deviceModified = false;
|
var deviceModified = false;
|
||||||
var ratchetModified = 0;
|
var ratchetModified = 0;
|
||||||
@@ -367,4 +366,181 @@ void main() {
|
|||||||
// untrusted device.
|
// untrusted device.
|
||||||
expect(aliceMessage.encryptedKeys.length, 1);
|
expect(aliceMessage.encryptedKeys.length, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Test by sending multiple messages back and forth', () async {
|
||||||
|
const aliceJid = 'alice@server.example';
|
||||||
|
const bobJid = 'bob@other.server.example';
|
||||||
|
// Alice and Bob generate their sessions
|
||||||
|
final aliceSession = await OmemoSessionManager.generateNewIdentity(
|
||||||
|
aliceJid,
|
||||||
|
AlwaysTrustingTrustManager(),
|
||||||
|
opkAmount: 1,
|
||||||
|
);
|
||||||
|
final bobSession = await OmemoSessionManager.generateNewIdentity(
|
||||||
|
bobJid,
|
||||||
|
AlwaysTrustingTrustManager(),
|
||||||
|
opkAmount: 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Alice encrypts a message for Bob
|
||||||
|
final aliceMessage = await aliceSession.encryptToJid(
|
||||||
|
bobJid,
|
||||||
|
'Hello Bob!',
|
||||||
|
newSessions: [
|
||||||
|
await (await bobSession.getDevice()).toBundle(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Alice sends the message to Bob
|
||||||
|
// ...
|
||||||
|
|
||||||
|
await bobSession.decryptMessage(
|
||||||
|
aliceMessage.ciphertext,
|
||||||
|
aliceJid,
|
||||||
|
(await aliceSession.getDevice()).id,
|
||||||
|
aliceMessage.encryptedKeys,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (var i = 0; i < 100; i++) {
|
||||||
|
final messageText = 'Test Message #$i';
|
||||||
|
// Bob responds to Alice
|
||||||
|
final bobResponseMessage = await bobSession.encryptToJid(
|
||||||
|
aliceJid,
|
||||||
|
messageText,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Bob sends the message to Alice
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Alice decrypts it
|
||||||
|
final aliceReceivedMessage = await aliceSession.decryptMessage(
|
||||||
|
bobResponseMessage.ciphertext,
|
||||||
|
bobJid,
|
||||||
|
(await bobSession.getDevice()).id,
|
||||||
|
bobResponseMessage.encryptedKeys,
|
||||||
|
);
|
||||||
|
expect(messageText, aliceReceivedMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Test removing a ratchet', () {
|
||||||
|
test('Test removing a ratchet when the user has multiple', () async {
|
||||||
|
const aliceJid = 'alice@server.local';
|
||||||
|
const bobJid = 'bob@some.server.local';
|
||||||
|
final aliceSession = await OmemoSessionManager.generateNewIdentity(
|
||||||
|
aliceJid,
|
||||||
|
AlwaysTrustingTrustManager(),
|
||||||
|
opkAmount: 1,
|
||||||
|
);
|
||||||
|
final bobSession1 = await OmemoSessionManager.generateNewIdentity(
|
||||||
|
bobJid,
|
||||||
|
AlwaysTrustingTrustManager(),
|
||||||
|
opkAmount: 1,
|
||||||
|
);
|
||||||
|
final bobSession2 = await OmemoSessionManager.generateNewIdentity(
|
||||||
|
bobJid,
|
||||||
|
AlwaysTrustingTrustManager(),
|
||||||
|
opkAmount: 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Alice sends a message to those two Bobs
|
||||||
|
await aliceSession.encryptToJid(
|
||||||
|
bobJid,
|
||||||
|
'Hallo Welt',
|
||||||
|
newSessions: [
|
||||||
|
await (await bobSession1.getDevice()).toBundle(),
|
||||||
|
await (await bobSession2.getDevice()).toBundle(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// One of those two sessions is broken, so Alice removes the session2 ratchet
|
||||||
|
final id1 = (await bobSession1.getDevice()).id;
|
||||||
|
final id2 = (await bobSession2.getDevice()).id;
|
||||||
|
await aliceSession.removeRatchet(bobJid, id1);
|
||||||
|
|
||||||
|
final map = aliceSession.getRatchetMap();
|
||||||
|
expect(map.containsKey(RatchetMapKey(bobJid, id1)), false);
|
||||||
|
expect(map.containsKey(RatchetMapKey(bobJid, id2)), true);
|
||||||
|
final deviceMap = await aliceSession.getDeviceMap();
|
||||||
|
expect(deviceMap.containsKey(bobJid), true);
|
||||||
|
expect(deviceMap[bobJid], [id2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Test removing a ratchet when the user has only one', () async {
|
||||||
|
const aliceJid = 'alice@server.local';
|
||||||
|
const bobJid = 'bob@some.server.local';
|
||||||
|
final aliceSession = await OmemoSessionManager.generateNewIdentity(
|
||||||
|
aliceJid,
|
||||||
|
AlwaysTrustingTrustManager(),
|
||||||
|
opkAmount: 1,
|
||||||
|
);
|
||||||
|
final bobSession = await OmemoSessionManager.generateNewIdentity(
|
||||||
|
bobJid,
|
||||||
|
AlwaysTrustingTrustManager(),
|
||||||
|
opkAmount: 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Alice sends a message to those two Bobs
|
||||||
|
await aliceSession.encryptToJid(
|
||||||
|
bobJid,
|
||||||
|
'Hallo Welt',
|
||||||
|
newSessions: [
|
||||||
|
await (await bobSession.getDevice()).toBundle(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// One of those two sessions is broken, so Alice removes the session2 ratchet
|
||||||
|
final id = (await bobSession.getDevice()).id;
|
||||||
|
await aliceSession.removeRatchet(bobJid, id);
|
||||||
|
|
||||||
|
final map = aliceSession.getRatchetMap();
|
||||||
|
expect(map.containsKey(RatchetMapKey(bobJid, id)), false);
|
||||||
|
final deviceMap = await aliceSession.getDeviceMap();
|
||||||
|
expect(deviceMap.containsKey(bobJid), false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Test acknowledging a ratchet', () async {
|
||||||
|
const aliceJid = 'alice@server.example';
|
||||||
|
const bobJid = 'bob@other.server.example';
|
||||||
|
// Alice and Bob generate their sessions
|
||||||
|
final aliceSession = await OmemoSessionManager.generateNewIdentity(
|
||||||
|
aliceJid,
|
||||||
|
AlwaysTrustingTrustManager(),
|
||||||
|
opkAmount: 1,
|
||||||
|
);
|
||||||
|
final bobSession = await OmemoSessionManager.generateNewIdentity(
|
||||||
|
bobJid,
|
||||||
|
AlwaysTrustingTrustManager(),
|
||||||
|
opkAmount: 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Alice sends Bob a message
|
||||||
|
await aliceSession.encryptToJid(
|
||||||
|
bobJid,
|
||||||
|
'Hallo Welt',
|
||||||
|
newSessions: [
|
||||||
|
await (await bobSession.getDevice()).toBundle(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
await aliceSession.getUnacknowledgedRatchets(bobJid),
|
||||||
|
[
|
||||||
|
(await bobSession.getDevice()).id,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Bob sends alice an empty message
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Alice decrypts it
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Alice marks the ratchet as acknowledged
|
||||||
|
await aliceSession.ratchetAcknowledged(bobJid, (await bobSession.getDevice()).id);
|
||||||
|
expect(
|
||||||
|
(await aliceSession.getUnacknowledgedRatchets(bobJid)).isEmpty,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ void main() {
|
|||||||
final oldDevice = await oldSession.getDevice();
|
final oldDevice = await oldSession.getDevice();
|
||||||
final newDevice = await newSession.getDevice();
|
final newDevice = await newSession.getDevice();
|
||||||
expect(await oldDevice.equals(newDevice), true);
|
expect(await oldDevice.equals(newDevice), true);
|
||||||
expect(oldSession.getDeviceMap(), newSession.getDeviceMap());
|
expect(await oldSession.getDeviceMap(), await newSession.getDeviceMap());
|
||||||
|
|
||||||
expect(oldSession.getRatchetMap().length, newSession.getRatchetMap().length);
|
expect(oldSession.getRatchetMap().length, newSession.getRatchetMap().length);
|
||||||
for (final session in oldSession.getRatchetMap().entries) {
|
for (final session in oldSession.getRatchetMap().entries) {
|
||||||
|
|||||||
Reference in New Issue
Block a user