From 50f6513c6fd71ce0a3dcdab32591138243717cc8 Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Mon, 12 Jun 2023 23:39:08 +0200 Subject: [PATCH] feat: Remove custom protobuf parsing --- lib/src/double_ratchet/crypto.dart | 15 +- lib/src/double_ratchet/double_ratchet.dart | 24 +- lib/src/omemo/omemomanager.dart | 44 +- .../protobuf/omemo_authenticated_message.dart | 38 - lib/src/protobuf/omemo_key_exchange.dart | 71 -- lib/src/protobuf/omemo_message.dart | 75 -- lib/src/protobuf/protobuf.dart | 64 -- pubspec.yaml | 4 +- test/omemo_test.dart | 956 ------------------ test/omemomanager_test.dart | 6 +- test/protobuf_test.dart | 186 ---- test/serialisation_test.dart | 24 +- 12 files changed, 53 insertions(+), 1454 deletions(-) delete mode 100644 lib/src/protobuf/omemo_authenticated_message.dart delete mode 100644 lib/src/protobuf/omemo_key_exchange.dart delete mode 100644 lib/src/protobuf/omemo_message.dart delete mode 100644 lib/src/protobuf/protobuf.dart delete mode 100644 test/omemo_test.dart delete mode 100644 test/protobuf_test.dart diff --git a/lib/src/double_ratchet/crypto.dart b/lib/src/double_ratchet/crypto.dart index 42033ed..fad14d9 100644 --- a/lib/src/double_ratchet/crypto.dart +++ b/lib/src/double_ratchet/crypto.dart @@ -1,8 +1,7 @@ +import 'package:omemo_dart/protobuf/schema.pb.dart'; import 'package:omemo_dart/src/crypto.dart'; import 'package:omemo_dart/src/errors.dart'; import 'package:omemo_dart/src/helpers.dart'; -import 'package:omemo_dart/src/protobuf/omemo_authenticated_message.dart'; -import 'package:omemo_dart/src/protobuf/omemo_message.dart'; /// Info string for ENCRYPT const encryptHkdfInfoString = 'OMEMO Message Key Material'; @@ -22,12 +21,12 @@ Future> encrypt( await aes256CbcEncrypt(plaintext, keys.encryptionKey, keys.iv); final header = - OmemoMessage.fromBuffer(associatedData.sublist(sessionAd.length)) + OMEMOMessage.fromBuffer(associatedData.sublist(sessionAd.length)) ..ciphertext = ciphertext; final headerBytes = header.writeToBuffer(); final hmacInput = concat([sessionAd, headerBytes]); final hmacResult = await truncatedHmac(hmacInput, keys.authenticationKey); - final message = OmemoAuthenticatedMessage() + final message = OMEMOAuthenticatedMessage() ..mac = hmacResult ..message = headerBytes; return message.writeToBuffer(); @@ -46,15 +45,15 @@ Future> decrypt( final keys = await deriveEncryptionKeys(mk, encryptHkdfInfoString); // Assumption ciphertext is a OMEMOAuthenticatedMessage - final message = OmemoAuthenticatedMessage.fromBuffer(ciphertext); - final header = OmemoMessage.fromBuffer(message.message!); + final message = OMEMOAuthenticatedMessage.fromBuffer(ciphertext); + final header = OMEMOMessage.fromBuffer(message.message); final hmacInput = concat([sessionAd, header.writeToBuffer()]); final hmacResult = await truncatedHmac(hmacInput, keys.authenticationKey); - if (!listsEqual(hmacResult, message.mac!)) { + if (!listsEqual(hmacResult, message.mac)) { throw InvalidMessageHMACException(); } - return aes256CbcDecrypt(header.ciphertext!, keys.encryptionKey, keys.iv); + return aes256CbcDecrypt(header.ciphertext, keys.encryptionKey, keys.iv); } diff --git a/lib/src/double_ratchet/double_ratchet.dart b/lib/src/double_ratchet/double_ratchet.dart index b997d4a..aa328c8 100644 --- a/lib/src/double_ratchet/double_ratchet.dart +++ b/lib/src/double_ratchet/double_ratchet.dart @@ -2,20 +2,20 @@ import 'dart:convert'; import 'package:cryptography/cryptography.dart'; import 'package:hex/hex.dart'; import 'package:meta/meta.dart'; +import 'package:omemo_dart/protobuf/schema.pb.dart'; import 'package:omemo_dart/src/crypto.dart'; import 'package:omemo_dart/src/double_ratchet/crypto.dart'; import 'package:omemo_dart/src/double_ratchet/kdf.dart'; import 'package:omemo_dart/src/errors.dart'; import 'package:omemo_dart/src/helpers.dart'; import 'package:omemo_dart/src/keys.dart'; -import 'package:omemo_dart/src/protobuf/omemo_message.dart'; /// Amount of messages we may skip per session const maxSkip = 1000; class RatchetStep { const RatchetStep(this.header, this.ciphertext); - final OmemoMessage header; + final OMEMOMessage header; final List ciphertext; } @@ -274,12 +274,12 @@ class OmemoDoubleRatchet { } Future?> _trySkippedMessageKeys( - OmemoMessage header, + OMEMOMessage header, List ciphertext, ) async { final key = SkippedKey( - OmemoPublicKey.fromBytes(header.dhPub!, KeyPairType.x25519), - header.n!, + OmemoPublicKey.fromBytes(header.dhPub, KeyPairType.x25519), + header.n, ); if (mkSkipped.containsKey(key)) { final mk = mkSkipped[key]!; @@ -312,11 +312,11 @@ class OmemoDoubleRatchet { } } - Future _dhRatchet(OmemoMessage header) async { + Future _dhRatchet(OMEMOMessage header) async { pn = ns; ns = 0; nr = 0; - dhr = OmemoPublicKey.fromBytes(header.dhPub!, KeyPairType.x25519); + dhr = OmemoPublicKey.fromBytes(header.dhPub, KeyPairType.x25519); final newRk = await kdfRk(rk, await omemoDH(dhs, dhr!, 0)); rk = List.from(newRk); @@ -333,7 +333,7 @@ class OmemoDoubleRatchet { final mk = await kdfCk(cks!, kdfCkNextMessageKey); cks = newCks; - final header = OmemoMessage() + final header = OMEMOMessage() ..dhPub = await dhs.pk.getBytes() ..pn = pn ..n = ns; @@ -356,7 +356,7 @@ class OmemoDoubleRatchet { /// /// Throws an SkippingTooManyMessagesException if too many messages were to be skipped. Future> ratchetDecrypt( - OmemoMessage header, + OMEMOMessage header, List ciphertext, ) async { // Check if we skipped too many messages @@ -366,15 +366,15 @@ class OmemoDoubleRatchet { } final dhPubMatches = listsEqual( - header.dhPub!, + header.dhPub, (await dhr?.getBytes()) ?? [], ); if (!dhPubMatches) { - await _skipMessageKeys(header.pn!); + await _skipMessageKeys(header.pn); await _dhRatchet(header); } - await _skipMessageKeys(header.n!); + await _skipMessageKeys(header.n); final newCkr = await kdfCk(ckr!, kdfCkNextChainKey); final mk = await kdfCk(ckr!, kdfCkNextMessageKey); ckr = newCkr; diff --git a/lib/src/omemo/omemomanager.dart b/lib/src/omemo/omemomanager.dart index 20cc3e2..f58b694 100644 --- a/lib/src/omemo/omemomanager.dart +++ b/lib/src/omemo/omemomanager.dart @@ -6,6 +6,7 @@ import 'package:cryptography/cryptography.dart'; import 'package:hex/hex.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; +import 'package:omemo_dart/protobuf/schema.pb.dart'; import 'package:omemo_dart/src/crypto.dart'; import 'package:omemo_dart/src/double_ratchet/double_ratchet.dart'; import 'package:omemo_dart/src/errors.dart'; @@ -21,9 +22,6 @@ import 'package:omemo_dart/src/omemo/events.dart'; import 'package:omemo_dart/src/omemo/fingerprint.dart'; import 'package:omemo_dart/src/omemo/ratchet_map_key.dart'; import 'package:omemo_dart/src/omemo/stanza.dart'; -import 'package:omemo_dart/src/protobuf/omemo_authenticated_message.dart'; -import 'package:omemo_dart/src/protobuf/omemo_key_exchange.dart'; -import 'package:omemo_dart/src/protobuf/omemo_message.dart'; import 'package:omemo_dart/src/trust/base.dart'; import 'package:omemo_dart/src/x3dh/x3dh.dart'; import 'package:synchronized/synchronized.dart'; @@ -194,7 +192,7 @@ class OmemoManager { Future _addSessionFromKeyExchange( String jid, int deviceId, - OmemoKeyExchange kex, + OMEMOKeyExchange kex, ) async { // Pick the correct SPK final device = await getDevice(); @@ -209,17 +207,17 @@ class OmemoManager { final kexResult = await x3dhFromInitialMessage( X3DHMessage( - OmemoPublicKey.fromBytes(kex.ik!, KeyPairType.ed25519), - OmemoPublicKey.fromBytes(kex.ek!, KeyPairType.x25519), - kex.pkId!, + OmemoPublicKey.fromBytes(kex.ik, KeyPairType.ed25519), + OmemoPublicKey.fromBytes(kex.ek, KeyPairType.x25519), + kex.pkId, ), spk, - device.opks.values.elementAt(kex.pkId!), + device.opks.values.elementAt(kex.pkId), device.ik, ); final ratchet = await OmemoDoubleRatchet.acceptNewSession( spk, - OmemoPublicKey.fromBytes(kex.ik!, KeyPairType.ed25519), + OmemoPublicKey.fromBytes(kex.ik, KeyPairType.ed25519), kexResult.sk, kexResult.ad, getTimestamp(), @@ -234,7 +232,7 @@ class OmemoManager { /// Create a ratchet session initiated by Alice to the user with Jid [jid] and the device /// [deviceId] from the bundle [bundle]. @visibleForTesting - Future addSessionFromBundle( + Future addSessionFromBundle( String jid, int deviceId, OmemoBundle bundle, @@ -255,7 +253,7 @@ class OmemoManager { await _trustManager.onNewSession(jid, deviceId); _addSession(jid, deviceId, ratchet); - return OmemoKeyExchange() + return OMEMOKeyExchange() ..pkId = kexResult.opkId ..spkId = bundle.spkId ..ik = await device.ik.pk.getBytes() @@ -312,17 +310,17 @@ class OmemoManager { final decodedRawKey = base64.decode(rawKey.value); List? keyAndHmac; - OmemoAuthenticatedMessage authMessage; - OmemoMessage? message; + OMEMOAuthenticatedMessage authMessage; + OMEMOMessage? message; // If the ratchet already existed, we store it. If it didn't, oldRatchet will stay // null. final ratchetKey = RatchetMapKey(senderJid, senderDeviceId); final oldRatchet = getRatchet(ratchetKey)?.clone(); if (rawKey.kex) { - final kex = OmemoKeyExchange.fromBuffer(decodedRawKey); - authMessage = kex.message!; - message = OmemoMessage.fromBuffer(authMessage.message!); + final kex = OMEMOKeyExchange.fromBuffer(decodedRawKey); + authMessage = kex.message; + message = OMEMOMessage.fromBuffer(authMessage.message); // Guard against old key exchanges if (oldRatchet != null) { @@ -348,7 +346,7 @@ class OmemoManager { // Replace the OPK await _deviceLock.synchronized(() async { - device = await device.replaceOnetimePrekey(kex.pkId!); + device = await device.replaceOnetimePrekey(kex.pkId); // Commit the device _eventStreamController.add(DeviceModifiedEvent(device)); @@ -374,8 +372,8 @@ class OmemoManager { _log.finest('Kex failed due to $ex. Not proceeding with kex.'); } } else { - authMessage = OmemoAuthenticatedMessage.fromBuffer(decodedRawKey); - message = OmemoMessage.fromBuffer(authMessage.message!); + authMessage = OMEMOAuthenticatedMessage.fromBuffer(decodedRawKey); + message = OMEMOMessage.fromBuffer(authMessage.message); } final devices = _deviceList[senderJid]; @@ -499,7 +497,7 @@ class OmemoManager { keyPayload = List.filled(32, 0x0); } - final kex = {}; + final kex = {}; for (final jid in jids) { for (final newSession in await _fetchNewBundles(jid)) { kex[RatchetMapKey(jid, newSession.id)] = await addSessionFromBundle( @@ -551,7 +549,7 @@ class OmemoManager { if (kex.containsKey(ratchetKey)) { // The ratchet did not exist final k = kex[ratchetKey]! - ..message = OmemoAuthenticatedMessage.fromBuffer(ciphertext); + ..message = OMEMOAuthenticatedMessage.fromBuffer(ciphertext); final buffer = base64.encode(k.writeToBuffer()); encryptedKeys.add( EncryptedKey( @@ -568,8 +566,8 @@ class OmemoManager { // The ratchet exists but is not acked if (ratchet.kex != null) { final oldKex = - OmemoKeyExchange.fromBuffer(base64.decode(ratchet.kex!)) - ..message = OmemoAuthenticatedMessage.fromBuffer(ciphertext); + OMEMOKeyExchange.fromBuffer(base64.decode(ratchet.kex!)) + ..message = OMEMOAuthenticatedMessage.fromBuffer(ciphertext); encryptedKeys.add( EncryptedKey( diff --git a/lib/src/protobuf/omemo_authenticated_message.dart b/lib/src/protobuf/omemo_authenticated_message.dart deleted file mode 100644 index 3d34e5b..0000000 --- a/lib/src/protobuf/omemo_authenticated_message.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:omemo_dart/src/helpers.dart'; -import 'package:omemo_dart/src/protobuf/protobuf.dart'; - -class OmemoAuthenticatedMessage { - OmemoAuthenticatedMessage(); - - factory OmemoAuthenticatedMessage.fromBuffer(List data) { - var i = 0; - - // required bytes mac = 1; - if (data[0] != fieldId(1, fieldTypeByteArray)) { - throw Exception(); - } - final mac = data.sublist(2, i + 2 + data[1]); - i += data[1] + 2; - - if (data[i] != fieldId(2, fieldTypeByteArray)) { - throw Exception(); - } - final message = data.sublist(i + 2, i + 2 + data[i + 1]); - - return OmemoAuthenticatedMessage() - ..mac = mac - ..message = message; - } - - List? mac; - List? message; - - List writeToBuffer() { - return concat([ - [fieldId(1, fieldTypeByteArray), mac!.length], - mac!, - [fieldId(2, fieldTypeByteArray), message!.length], - message!, - ]); - } -} diff --git a/lib/src/protobuf/omemo_key_exchange.dart b/lib/src/protobuf/omemo_key_exchange.dart deleted file mode 100644 index 46ccce4..0000000 --- a/lib/src/protobuf/omemo_key_exchange.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:omemo_dart/src/helpers.dart'; -import 'package:omemo_dart/src/protobuf/omemo_authenticated_message.dart'; -import 'package:omemo_dart/src/protobuf/protobuf.dart'; - -class OmemoKeyExchange { - OmemoKeyExchange(); - - factory OmemoKeyExchange.fromBuffer(List data) { - var i = 0; - - if (data[i] != fieldId(1, fieldTypeUint32)) { - throw Exception(); - } - var decoded = decodeVarint(data, 1); - final pkId = decoded.n; - i += decoded.length + 1; - - if (data[i] != fieldId(2, fieldTypeUint32)) { - throw Exception(); - } - decoded = decodeVarint(data, i + 1); - final spkId = decoded.n; - i += decoded.length + 1; - - if (data[i] != fieldId(3, fieldTypeByteArray)) { - throw Exception(); - } - final ik = data.sublist(i + 2, i + 2 + data[i + 1]); - i += 2 + data[i + 1]; - - if (data[i] != fieldId(4, fieldTypeByteArray)) { - throw Exception(); - } - final ek = data.sublist(i + 2, i + 2 + data[i + 1]); - i += 2 + data[i + 1]; - - if (data[i] != fieldId(5, fieldTypeByteArray)) { - throw Exception(); - } - final message = OmemoAuthenticatedMessage.fromBuffer(data.sublist(i + 2)); - - return OmemoKeyExchange() - ..pkId = pkId - ..spkId = spkId - ..ik = ik - ..ek = ek - ..message = message; - } - - int? pkId; - int? spkId; - List? ik; - List? ek; - OmemoAuthenticatedMessage? message; - - List writeToBuffer() { - final msg = message!.writeToBuffer(); - return concat([ - [fieldId(1, fieldTypeUint32)], - encodeVarint(pkId!), - [fieldId(2, fieldTypeUint32)], - encodeVarint(spkId!), - [fieldId(3, fieldTypeByteArray), ik!.length], - ik!, - [fieldId(4, fieldTypeByteArray), ek!.length], - ek!, - [fieldId(5, fieldTypeByteArray), msg.length], - msg, - ]); - } -} diff --git a/lib/src/protobuf/omemo_message.dart b/lib/src/protobuf/omemo_message.dart deleted file mode 100644 index ccb4599..0000000 --- a/lib/src/protobuf/omemo_message.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:omemo_dart/src/helpers.dart'; -import 'package:omemo_dart/src/protobuf/protobuf.dart'; - -class OmemoMessage { - OmemoMessage(); - - factory OmemoMessage.fromBuffer(List data) { - var i = 0; - - // required uint32 n = 1; - if (data[0] != fieldId(1, fieldTypeUint32)) { - throw Exception(); - } - var decode = decodeVarint(data, 1); - final n = decode.n; - i += decode.length + 1; - - // required uint32 pn = 2; - if (data[i] != fieldId(2, fieldTypeUint32)) { - throw Exception(); - } - decode = decodeVarint(data, i + 1); - final pn = decode.n; - i += decode.length + 1; - - // required bytes dh_pub = 3; - if (data[i] != fieldId(3, fieldTypeByteArray)) { - throw Exception(); - } - final dhPub = data.sublist(i + 2, i + 2 + data[i + 1]); - i += 2 + data[i + 1]; - - // optional bytes ciphertext = 4; - List? ciphertext; - if (i < data.length) { - if (data[i] != fieldId(4, fieldTypeByteArray)) { - throw Exception(); - } - - ciphertext = data.sublist(i + 2, i + 2 + data[i + 1]); - } - - return OmemoMessage() - ..n = n - ..pn = pn - ..dhPub = dhPub - ..ciphertext = ciphertext; - } - - int? n; - int? pn; - List? dhPub; - List? ciphertext; - - List writeToBuffer() { - final data = concat([ - [fieldId(1, fieldTypeUint32)], - encodeVarint(n!), - [fieldId(2, fieldTypeUint32)], - encodeVarint(pn!), - [fieldId(3, fieldTypeByteArray), dhPub!.length], - dhPub!, - ]); - - if (ciphertext != null) { - return concat([ - data, - [fieldId(4, fieldTypeByteArray), ciphertext!.length], - ciphertext!, - ]); - } - - return data; - } -} diff --git a/lib/src/protobuf/protobuf.dart b/lib/src/protobuf/protobuf.dart deleted file mode 100644 index 2aed675..0000000 --- a/lib/src/protobuf/protobuf.dart +++ /dev/null @@ -1,64 +0,0 @@ -/// Masks the 7 LSB -const lsb7Mask = 0x7F; - -/// Constant for setting the MSB -const msb = 1 << 7; - -/// Field types -const fieldTypeUint32 = 0; -const fieldTypeByteArray = 2; - -int fieldId(int number, int type) { - return (number << 3) | type; -} - -class VarintDecode { - const VarintDecode(this.n, this.length); - final int n; - final int length; -} - -/// Decode a Varint that begins at [input]'s index [offset]. -VarintDecode decodeVarint(List input, int offset) { - // The return value - var n = 0; - // The byte offset counter - var i = 0; - - // Iterate until the MSB of the byte is 0 - while (true) { - // Mask only the 7 LSB and "move" them accordingly - n += (input[offset + i] & lsb7Mask) << (7 * i); - - // Break if we reached the end - if (input[offset + i] & 1 << 7 == 0) { - break; - } - i++; - } - - return VarintDecode(n, i + 1); -} - -// Encodes the integer [i] into a Varint. -List encodeVarint(int i) { - assert(i >= 0, "Two's complement is not implemented"); - final ret = List.empty(growable: true); - - // Thanks to https://github.com/hathibelagal-dev/LEB128 for the trick with toRadixString! - final numSevenBlocks = (i.toRadixString(2).length / 7).ceil(); - for (var j = 0; j < numSevenBlocks; j++) { - // The 7 LSB of the byte we're creating - final x = (i & (lsb7Mask << j * 7)) >> j * 7; - - if (j == numSevenBlocks - 1) { - // If we were to shift further, we only get zero, so we're at the end - ret.add(x); - } else { - // We still have at least one bit more to go, so set the MSB to 1 - ret.add(x + msb); - } - } - - return ret; -} diff --git a/pubspec.yaml b/pubspec.yaml index ee5d811..6f7ff94 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,11 +14,11 @@ dependencies: logging: ^1.0.2 meta: ^1.7.0 pinenacl: ^0.5.1 + protobuf: ^2.1.0 + protoc_plugin: ^20.0.1 synchronized: ^3.0.0+2 dev_dependencies: lints: ^2.0.0 - protobuf: ^2.1.0 - protoc_plugin: ^20.0.1 test: ^1.21.0 very_good_analysis: ^3.0.1 diff --git a/test/omemo_test.dart b/test/omemo_test.dart deleted file mode 100644 index da0b0c5..0000000 --- a/test/omemo_test.dart +++ /dev/null @@ -1,956 +0,0 @@ -import 'package:logging/logging.dart'; -import 'package:omemo_dart/omemo_dart.dart'; -import 'package:omemo_dart/src/trust/always.dart'; -import 'package:omemo_dart/src/trust/never.dart'; -import 'package:test/test.dart'; - -void main() { - Logger.root - ..level = Level.ALL - ..onRecord.listen((record) { - // ignore: avoid_print - print('${record.level.name}: ${record.message}'); - }); - - test('Test replacing a onetime prekey', () async { - const aliceJid = 'alice@server.example'; - final device = await OmemoDevice.generateNewDevice(aliceJid, opkAmount: 1); - - final newDevice = await device.replaceOnetimePrekey(0); - - expect(device.jid, newDevice.jid); - expect(device.id, newDevice.id); - - var opksMatch = true; - if (newDevice.opks.length != device.opks.length) { - opksMatch = false; - } else { - for (final entry in device.opks.entries) { - final m = await newDevice.opks[entry.key]?.equals(entry.value) ?? false; - if (!m) opksMatch = false; - } - } - - expect(opksMatch, true); - expect(await device.ik.equals(newDevice.ik), true); - expect(await device.spk.equals(newDevice.spk), true); - - final oldSpkMatch = device.oldSpk != null - ? await device.oldSpk!.equals(newDevice.oldSpk!) - : newDevice.oldSpk == null; - expect(oldSpkMatch, true); - expect(listsEqual(device.spkSignature, newDevice.spkSignature), true); - }); - - test('Test using OMEMO sessions with only one device per user', () async { - const aliceJid = 'alice@server.example'; - const bobJid = 'bob@other.server.example'; - // Alice and Bob generate their sessions - var deviceModified = false; - var ratchetModified = 0; - var deviceMapModified = 0; - final aliceSession = await OmemoSessionManager.generateNewIdentity( - aliceJid, - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - final bobSession = await OmemoSessionManager.generateNewIdentity( - bobJid, - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - final bobOpks = (await bobSession.getDevice()).opks.values.toList(); - bobSession.eventStream.listen((event) { - if (event is DeviceModifiedEvent) { - deviceModified = true; - } else if (event is RatchetModifiedEvent) { - ratchetModified++; - } else if (event is DeviceListModifiedEvent) { - deviceMapModified++; - } - }); - - // Alice encrypts a message for Bob - const messagePlaintext = 'Hello Bob!'; - final aliceMessage = await aliceSession.encryptToJid( - bobJid, - messagePlaintext, - newSessions: [ - await bobSession.getDeviceBundle(), - ], - ); - expect(aliceMessage.encryptedKeys.length, 1); - - // Alice sends the message to Bob - // ... - - // Bob decrypts it - final bobMessage = await bobSession.decryptMessage( - aliceMessage.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - aliceMessage.encryptedKeys, - 0, - ); - expect(messagePlaintext, bobMessage); - // The ratchet should be modified two times: Once for when the ratchet is created and - // other time for when the message is decrypted - expect(ratchetModified, 2); - // Bob's device map should be modified once - expect(deviceMapModified, 1); - // The event should be triggered - expect(deviceModified, true); - // Bob should have replaced his OPK - expect( - listsEqual(bobOpks, (await bobSession.getDevice()).opks.values.toList()), - false, - ); - - // Ratchets are acked - await aliceSession.ratchetAcknowledged( - bobJid, await bobSession.getDeviceId()); - await bobSession.ratchetAcknowledged( - aliceJid, await aliceSession.getDeviceId()); - - // Bob responds to Alice - const bobResponseText = 'Oh, hello Alice!'; - final bobResponseMessage = await bobSession.encryptToJid( - aliceJid, - bobResponseText, - ); - - // Bob sends the message to Alice - // ... - - // Alice decrypts it - final aliceReceivedMessage = await aliceSession.decryptMessage( - bobResponseMessage.ciphertext, - bobJid, - await bobSession.getDeviceId(), - bobResponseMessage.encryptedKeys, - 0, - ); - expect(bobResponseText, aliceReceivedMessage); - }); - - test('Test using OMEMO sessions with only two devices for the receiver', - () 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, - ); - // Bob's other device - final bobSession2 = await OmemoSessionManager.generateNewIdentity( - bobJid, - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - - // Alice encrypts a message for Bob - const messagePlaintext = 'Hello Bob!'; - final aliceMessage = await aliceSession.encryptToJid( - bobJid, - messagePlaintext, - newSessions: [ - await bobSession.getDeviceBundle(), - await bobSession2.getDeviceBundle(), - ], - ); - expect(aliceMessage.encryptedKeys.length, 2); - expect(aliceMessage.encryptedKeys[0].kex, true); - expect(aliceMessage.encryptedKeys[1].kex, true); - - // Alice sends the message to Bob - // ... - - // Bob decrypts it - final bobMessage = await bobSession.decryptMessage( - aliceMessage.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - aliceMessage.encryptedKeys, - 0, - ); - expect(messagePlaintext, bobMessage); - - // Ratchets are acked - await aliceSession.ratchetAcknowledged( - bobJid, await bobSession.getDeviceId()); - await bobSession.ratchetAcknowledged( - aliceJid, await aliceSession.getDeviceId()); - - // Bob responds to Alice - const bobResponseText = 'Oh, hello Alice!'; - final bobResponseMessage = await bobSession.encryptToJid( - aliceJid, - bobResponseText, - ); - - // Bob sends the message to Alice - // ... - - // Alice decrypts it - final aliceReceivedMessage = await aliceSession.decryptMessage( - bobResponseMessage.ciphertext, - bobJid, - await bobSession.getDeviceId(), - bobResponseMessage.encryptedKeys, - 0, - ); - expect(bobResponseText, aliceReceivedMessage); - - // Alice checks the fingerprints - final fingerprints = await aliceSession.getHexFingerprintsForJid(bobJid); - // Check that they the fingerprints are correct - expect(fingerprints.length, 2); - expect(fingerprints[0] != fingerprints[1], true); - // Check that those two calls do not throw an exception - aliceSession - ..getRatchet(bobJid, fingerprints[0].deviceId) - ..getRatchet(bobJid, fingerprints[1].deviceId); - }); - - test('Test using OMEMO sessions with encrypt to self', () async { - const aliceJid = 'alice@server.example'; - const bobJid = 'bob@other.server.example'; - - // Alice and Bob generate their sessions - final aliceSession1 = await OmemoSessionManager.generateNewIdentity( - aliceJid, - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - final aliceSession2 = await OmemoSessionManager.generateNewIdentity( - aliceJid, - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - final bobSession = await OmemoSessionManager.generateNewIdentity( - bobJid, - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - - // Alice encrypts a message for Bob - const messagePlaintext = 'Hello Bob!'; - final aliceMessage = await aliceSession1.encryptToJids( - [bobJid, aliceJid], - messagePlaintext, - newSessions: [ - await bobSession.getDeviceBundle(), - await aliceSession2.getDeviceBundle(), - ], - ); - expect(aliceMessage.encryptedKeys.length, 2); - - // Alice sends the message to Bob - // ... - - // Bob decrypts it - final bobMessage = await bobSession.decryptMessage( - aliceMessage.ciphertext, - aliceJid, - await aliceSession1.getDeviceId(), - aliceMessage.encryptedKeys, - 0, - ); - expect(messagePlaintext, bobMessage); - - // Alice's other device decrypts it - final aliceMessage2 = await aliceSession2.decryptMessage( - aliceMessage.ciphertext, - aliceJid, - await aliceSession1.getDeviceId(), - aliceMessage.encryptedKeys, - 0, - ); - expect(messagePlaintext, aliceMessage2); - }); - - test('Test sending empty OMEMO messages', () 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, - null, - newSessions: [ - await bobSession.getDeviceBundle(), - ], - ); - expect(aliceMessage.encryptedKeys.length, 1); - expect(aliceMessage.ciphertext, null); - - // Alice sends the message to Bob - // ... - - // Bob decrypts it - final bobMessage = await bobSession.decryptMessage( - aliceMessage.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - aliceMessage.encryptedKeys, - 0, - ); - expect(bobMessage, null); - - // This call must not cause an exception - bobSession.getRatchet(aliceJid, await aliceSession.getDeviceId()); - }); - - test('Test rotating the Signed Prekey', () async { - // Generate the session - const aliceJid = 'alice@some.server'; - final aliceSession = await OmemoSessionManager.generateNewIdentity( - aliceJid, - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - - // Setup an event listener - final oldDevice = await aliceSession.getDevice(); - OmemoDevice? newDevice; - aliceSession.eventStream.listen((event) { - if (event is DeviceModifiedEvent) { - newDevice = event.device; - } - }); - - // Rotate the Signed Prekey - await aliceSession.rotateSignedPrekey(); - - // Just for safety... - await Future.delayed(const Duration(seconds: 2)); - - expect(await oldDevice.equals(newDevice!), false); - expect(await newDevice!.equals(await aliceSession.getDevice()), true); - - expect(await newDevice!.oldSpk!.equals(oldDevice.spk), true); - expect(newDevice!.oldSpkId, oldDevice.spkId); - }); - - test('Test accepting a session with an old SPK', () 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 - const messagePlaintext = 'Hello Bob!'; - final aliceMessage = await aliceSession.encryptToJid( - bobJid, - messagePlaintext, - newSessions: [ - await bobSession.getDeviceBundle(), - ], - ); - expect(aliceMessage.encryptedKeys.length, 1); - - // Alice loses her Internet connection. Bob rotates his SPK. - await bobSession.rotateSignedPrekey(); - - // Alice regains her Internet connection and sends the message to Bob - // ... - - // Bob decrypts it - final bobMessage = await bobSession.decryptMessage( - aliceMessage.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - aliceMessage.encryptedKeys, - 0, - ); - expect(messagePlaintext, bobMessage); - }); - - test('Test trust bypassing with empty OMEMO messages', () async { - const aliceJid = 'alice@server.example'; - const bobJid = 'bob@other.server.example'; - - // Alice and Bob generate their sessions - final aliceSession = await OmemoSessionManager.generateNewIdentity( - aliceJid, - NeverTrustingTrustManager(), - opkAmount: 1, - ); - final bobSession = await OmemoSessionManager.generateNewIdentity( - bobJid, - NeverTrustingTrustManager(), - opkAmount: 1, - ); - - // Alice encrypts an empty message for Bob - final aliceMessage = await aliceSession.encryptToJid( - bobJid, - null, - newSessions: [ - await bobSession.getDeviceBundle(), - ], - ); - - // Despite Alice not trusting Bob's device, we should have encrypted it for his - // untrusted device. - 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 bobSession.getDeviceBundle(), - ], - ); - - // Alice sends the message to Bob - // ... - - await bobSession.decryptMessage( - aliceMessage.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - aliceMessage.encryptedKeys, - 0, - ); - - // Ratchets are acked - await aliceSession.ratchetAcknowledged( - bobJid, await bobSession.getDeviceId()); - await bobSession.ratchetAcknowledged( - aliceJid, await aliceSession.getDeviceId()); - - 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.getDeviceId(), - bobResponseMessage.encryptedKeys, - 0, - ); - 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 bobSession1.getDeviceBundle(), - await bobSession2.getDeviceBundle(), - ], - ); - - // One of those two sessions is broken, so Alice removes the session2 ratchet - final id1 = await bobSession1.getDeviceId(); - final id2 = await bobSession2.getDeviceId(); - 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 bobSession.getDeviceBundle(), - ], - ); - - // One of those two sessions is broken, so Alice removes the session2 ratchet - final id = await bobSession.getDeviceId(); - 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 bobSession.getDeviceBundle(), - ], - ); - expect( - await aliceSession.getUnacknowledgedRatchets(bobJid), - [ - await bobSession.getDeviceId(), - ], - ); - - // Bob sends alice an empty message - // ... - - // Alice decrypts it - // ... - - // Alice marks the ratchet as acknowledged - await aliceSession.ratchetAcknowledged( - bobJid, await bobSession.getDeviceId()); - expect( - (await aliceSession.getUnacknowledgedRatchets(bobJid))!.isEmpty, - true, - ); - }); - - test('Test overwriting sessions', () 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: 2, - ); - - // Alice sends Bob a message - final msg1 = await aliceSession.encryptToJid( - bobJid, - 'Hallo Welt', - newSessions: [ - await bobSession.getDeviceBundle(), - ], - ); - await bobSession.decryptMessage( - msg1.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg1.encryptedKeys, - 0, - ); - final aliceRatchet1 = aliceSession.getRatchet( - bobJid, - await bobSession.getDeviceId(), - ); - final bobRatchet1 = bobSession.getRatchet( - aliceJid, - await aliceSession.getDeviceId(), - ); - - // Alice is impatient and immediately sends another message before the original one - // can be acknowledged by Bob - final msg2 = await aliceSession.encryptToJid( - bobJid, - "Why don't you answer?", - newSessions: [ - await bobSession.getDeviceBundle(), - ], - ); - await bobSession.decryptMessage( - msg2.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg2.encryptedKeys, - getTimestamp(), - ); - final aliceRatchet2 = aliceSession.getRatchet( - bobJid, - await bobSession.getDeviceId(), - ); - final bobRatchet2 = bobSession.getRatchet( - aliceJid, - await aliceSession.getDeviceId(), - ); - - // Both should only have one ratchet - expect(aliceSession.getRatchetMap().length, 1); - expect(bobSession.getRatchetMap().length, 1); - - // The ratchets should both be different - expect(await aliceRatchet1.equals(aliceRatchet2), false); - expect(await bobRatchet1.equals(bobRatchet2), false); - }); - - test('Test resending key exchanges', () 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: 2, - ); - - // Alice sends Bob a message - final msg1 = await aliceSession.encryptToJid( - bobJid, - 'Hallo Welt', - newSessions: [ - await bobSession.getDeviceBundle(), - ], - ); - // The first message should be a kex message - expect(msg1.encryptedKeys.first.kex, true); - - await bobSession.decryptMessage( - msg1.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg1.encryptedKeys, - 0, - ); - - // Alice is impatient and immediately sends another message before the original one - // can be acknowledged by Bob - final msg2 = await aliceSession.encryptToJid( - bobJid, - "Why don't you answer?", - ); - expect(msg2.encryptedKeys.first.kex, true); - - await bobSession.decryptMessage( - msg2.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg2.encryptedKeys, - getTimestamp(), - ); - }); - - test('Test receiving old messages including a KEX', () 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: 2, - ); - - final bobsReceivedMessages = List.empty(growable: true); - final bobsReceivedMessagesTimestamps = List.empty(growable: true); - - // Alice sends Bob a message - final msg1 = await aliceSession.encryptToJid( - bobJid, - 'Hallo Welt', - newSessions: [ - await bobSession.getDeviceBundle(), - ], - ); - bobsReceivedMessages.add(msg1); - final t1 = getTimestamp(); - bobsReceivedMessagesTimestamps.add(t1); - - await bobSession.decryptMessage( - msg1.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg1.encryptedKeys, - t1, - ); - - // Ratchets are acked - await aliceSession.ratchetAcknowledged( - bobJid, await bobSession.getDeviceId()); - await bobSession.ratchetAcknowledged( - aliceJid, await aliceSession.getDeviceId()); - - // Bob responds - final msg2 = await bobSession.encryptToJid( - aliceJid, - 'Hello!', - ); - - await aliceSession.decryptMessage( - msg2.ciphertext, - bobJid, - await bobSession.getDeviceId(), - msg2.encryptedKeys, - getTimestamp(), - ); - - // Send some messages between the two - for (var i = 0; i < 100; i++) { - final msg = await aliceSession.encryptToJid( - bobJid, - 'Hello $i', - ); - bobsReceivedMessages.add(msg); - final t = getTimestamp(); - bobsReceivedMessagesTimestamps.add(t); - final result = await bobSession.decryptMessage( - msg.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg.encryptedKeys, - t, - ); - - expect(result, 'Hello $i'); - } - - // Due to some issue with the transport protocol, the messages to Bob are received - // again. - final ratchetPreError = bobSession - .getRatchet(aliceJid, await aliceSession.getDeviceId()) - .clone(); - var invalidKex = 0; - var errorCounter = 0; - for (var i = 0; i < bobsReceivedMessages.length; i++) { - final msg = bobsReceivedMessages[i]; - try { - await bobSession.decryptMessage( - msg.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg.encryptedKeys, - bobsReceivedMessagesTimestamps[i], - ); - expect(true, false); - } on InvalidMessageHMACException catch (_) { - errorCounter++; - } on InvalidKeyExchangeException catch (_) { - invalidKex++; - } - } - final ratchetPostError = bobSession - .getRatchet(aliceJid, await aliceSession.getDeviceId()) - .clone(); - - // The 100 messages including the initial KEX message - expect(invalidKex, 1); - expect(errorCounter, 100); - expect(await ratchetPreError.equals(ratchetPostError), true); - - final msg3 = await aliceSession.encryptToJid( - bobJid, - 'Are you okay?', - ); - final result = await bobSession.decryptMessage( - msg3.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg3.encryptedKeys, - 104, - ); - - expect(result, 'Are you okay?'); - }); - - test("Test ignoring a new KEX when we haven't acket it yet", () 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 - final msg1 = await aliceSession.encryptToJid( - bobJid, - 'Hallo Welt', - newSessions: [ - await bobSession.getDeviceBundle(), - ], - ); - expect(msg1.encryptedKeys.first.kex, true); - - await bobSession.decryptMessage( - msg1.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg1.encryptedKeys, - getTimestamp(), - ); - - // Alice sends another message before the ack can reach us - final msg2 = await aliceSession.encryptToJid( - bobJid, - 'ANSWER ME!', - ); - expect(msg2.encryptedKeys.first.kex, true); - - await bobSession.decryptMessage( - msg2.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg2.encryptedKeys, - getTimestamp(), - ); - - // Now the acks reach us - await aliceSession.ratchetAcknowledged( - bobJid, await bobSession.getDeviceId()); - await bobSession.ratchetAcknowledged( - aliceJid, await aliceSession.getDeviceId()); - - // Alice sends another message - final msg3 = await aliceSession.encryptToJid( - bobJid, - "You read the message, didn't you?", - ); - expect(msg3.encryptedKeys.first.kex, false); - - await bobSession.decryptMessage( - msg3.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - msg3.encryptedKeys, - getTimestamp(), - ); - - 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.getDeviceId(), - bobResponseMessage.encryptedKeys, - 0, - ); - expect(messageText, aliceReceivedMessage); - } - }); -} diff --git a/test/omemomanager_test.dart b/test/omemomanager_test.dart index 9ce7cc4..72be302 100644 --- a/test/omemomanager_test.dart +++ b/test/omemomanager_test.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:logging/logging.dart'; import 'package:omemo_dart/omemo_dart.dart'; -import 'package:omemo_dart/src/protobuf/omemo_key_exchange.dart'; +import 'package:omemo_dart/protobuf/schema.pb.dart'; import 'package:omemo_dart/src/trust/always.dart'; import 'package:test/test.dart'; @@ -1304,10 +1304,10 @@ void main() { expect(aliceResult2.encryptedKeys.first.kex, true); // The basic data should be the same - final parsedFirstKex = OmemoKeyExchange.fromBuffer( + final parsedFirstKex = OMEMOKeyExchange.fromBuffer( base64.decode(aliceResult1.encryptedKeys.first.value), ); - final parsedSecondKex = OmemoKeyExchange.fromBuffer( + final parsedSecondKex = OMEMOKeyExchange.fromBuffer( base64.decode(aliceResult2.encryptedKeys.first.value), ); expect(parsedSecondKex.pkId, parsedFirstKex.pkId); diff --git a/test/protobuf_test.dart b/test/protobuf_test.dart deleted file mode 100644 index 103be11..0000000 --- a/test/protobuf_test.dart +++ /dev/null @@ -1,186 +0,0 @@ -import 'package:omemo_dart/protobuf/schema.pb.dart'; -import 'package:omemo_dart/src/protobuf/omemo_authenticated_message.dart'; -import 'package:omemo_dart/src/protobuf/omemo_key_exchange.dart'; -import 'package:omemo_dart/src/protobuf/omemo_message.dart'; -import 'package:omemo_dart/src/protobuf/protobuf.dart'; -import 'package:test/test.dart'; - -void main() { - group('Base 128 Varints', () { - test('Test simple parsing of Varints', () { - expect( - decodeVarint([1], 0).n, - 1, - ); - expect( - decodeVarint([1], 0).length, - 1, - ); - expect( - decodeVarint([0x96, 0x01, 0x00], 0).n, - 150, - ); - expect( - decodeVarint([0x96, 0x01, 0x00], 0).length, - 2, - ); - expect( - decodeVarint([172, 2, 0x8], 0).n, - 300, - ); - expect( - decodeVarint([172, 2, 0x8], 0).length, - 2, - ); - }); - - test('Test encoding Varints', () { - expect( - encodeVarint(1), - [1], - ); - expect( - encodeVarint(150), - [0x96, 0x01], - ); - expect( - encodeVarint(300), - [172, 2], - ); - }); - - test('Test some special cases', () { - expect(decodeVarint(encodeVarint(1042464893), 0).n, 1042464893); - }); - }); - - group('OMEMOMessage', () { - test('Decode a OMEMOMessage', () { - final pbMessage = OMEMOMessage() - ..n = 1 - ..pn = 5 - ..dhPub = [1, 2, 3] - ..ciphertext = [4, 5, 6]; - final serial = pbMessage.writeToBuffer(); - final msg = OmemoMessage.fromBuffer(serial); - - expect(msg.n, 1); - expect(msg.pn, 5); - expect(msg.dhPub, [1, 2, 3]); - expect(msg.ciphertext, [4, 5, 6]); - }); - test('Decode a OMEMOMessage without ciphertext', () { - final pbMessage = OMEMOMessage() - ..n = 1 - ..pn = 5 - ..dhPub = [1, 2, 3]; - final serial = pbMessage.writeToBuffer(); - final msg = OmemoMessage.fromBuffer(serial); - - expect(msg.n, 1); - expect(msg.pn, 5); - expect(msg.dhPub, [1, 2, 3]); - expect(msg.ciphertext, null); - }); - test('Encode a OMEMOMessage', () { - final m = OmemoMessage() - ..n = 1 - ..pn = 5 - ..dhPub = [1, 2, 3] - ..ciphertext = [4, 5, 6]; - final serial = m.writeToBuffer(); - final msg = OMEMOMessage.fromBuffer(serial); - - expect(msg.n, 1); - expect(msg.pn, 5); - expect(msg.dhPub, [1, 2, 3]); - expect(msg.ciphertext, [4, 5, 6]); - }); - test('Encode a OMEMOMessage without ciphertext', () { - final m = OmemoMessage() - ..n = 1 - ..pn = 5 - ..dhPub = [1, 2, 3]; - final serial = m.writeToBuffer(); - final msg = OMEMOMessage.fromBuffer(serial); - - expect(msg.n, 1); - expect(msg.pn, 5); - expect(msg.dhPub, [1, 2, 3]); - expect(msg.ciphertext, []); - }); - }); - - group('OMEMOAuthenticatedMessage', () { - test('Test encoding a message', () { - final msg = OmemoAuthenticatedMessage() - ..mac = [1, 2, 3] - ..message = [4, 5, 6]; - final decoded = OMEMOAuthenticatedMessage.fromBuffer(msg.writeToBuffer()); - - expect(decoded.mac, [1, 2, 3]); - expect(decoded.message, [4, 5, 6]); - }); - test('Test decoding a message', () { - final msg = OMEMOAuthenticatedMessage() - ..mac = [1, 2, 3] - ..message = [4, 5, 6]; - final bytes = msg.writeToBuffer(); - final decoded = OmemoAuthenticatedMessage.fromBuffer(bytes); - - expect(decoded.mac, [1, 2, 3]); - expect(decoded.message, [4, 5, 6]); - }); - }); - - group('OMEMOKeyExchange', () { - test('Test encoding a message', () { - final authMessage = OmemoAuthenticatedMessage() - ..mac = [5, 6, 8, 0] - ..message = [4, 5, 7, 3, 2]; - final message = OmemoKeyExchange() - ..pkId = 698 - ..spkId = 245 - ..ik = [1, 4, 6] - ..ek = [4, 6, 7, 80] - ..message = authMessage; - final kex = OMEMOKeyExchange.fromBuffer(message.writeToBuffer()); - - expect(kex.pkId, 698); - expect(kex.spkId, 245); - expect(kex.ik, [1, 4, 6]); - expect(kex.ek, [4, 6, 7, 80]); - - expect(kex.message.mac, [5, 6, 8, 0]); - expect(kex.message.message, [4, 5, 7, 3, 2]); - }); - test('Test decoding a message', () { - final message = OMEMOAuthenticatedMessage() - ..mac = [5, 6, 8, 0] - ..message = [4, 5, 7, 3, 2]; - final kex = OMEMOKeyExchange() - ..pkId = 698 - ..spkId = 245 - ..ik = [1, 4, 6] - ..ek = [4, 6, 7, 80] - ..message = message; - final decoded = OmemoKeyExchange.fromBuffer(kex.writeToBuffer()); - - expect(decoded.pkId, 698); - expect(decoded.spkId, 245); - expect(decoded.ik, [1, 4, 6]); - expect(decoded.ek, [4, 6, 7, 80]); - - expect(decoded.message!.mac, [5, 6, 8, 0]); - expect(decoded.message!.message, [4, 5, 7, 3, 2]); - }); - test('Test decoding an issue', () { - /* - final data = 'CAAQfRogc2GwslU219dUkrMHNM4KdZRmuFnBTae+bQaJ+55IsAMiII7aZKj2sUpb6xR/3Ari7WZUmKFV0G6czUc4NMvjKDBaKnwKEM2ZpI8X3TgcxhxwENANnlsSaAgAEAAaICy8T9WPgLb7RdYd8/4JkrLF0RahEkC3ZaEfk5jw3dsLIkBMILzLyByweLgF4lCn0oNea+kbdrFr6rY7r/7WyI8hXEQz38QpnN+jyGGwC7Ga0dq70WuyqE7VpiFArQwqZh2G'; - final kex = OmemoKeyExchange.fromBuffer(base64Decode(data)); - - expect(kex.spkId!, 1042464893); - */ - }); - }); -} diff --git a/test/serialisation_test.dart b/test/serialisation_test.dart index 902d89d..8233768 100644 --- a/test/serialisation_test.dart +++ b/test/serialisation_test.dart @@ -10,12 +10,8 @@ Map jsonify(Map map) { void main() { test('Test serialising and deserialising the Device', () async { // Generate a random session - final oldSession = await OmemoSessionManager.generateNewIdentity( - 'user@test.server', - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - final oldDevice = await oldSession.getDevice(); + final oldDevice = + await OmemoDevice.generateNewDevice('user@test.server', opkAmount: 5); final serialised = jsonify(await oldDevice.toJson()); final newDevice = OmemoDevice.fromJson(serialised); @@ -25,24 +21,20 @@ void main() { test('Test serialising and deserialising the Device after rotating the SPK', () async { // Generate a random session - final oldSession = await OmemoSessionManager.generateNewIdentity( - 'user@test.server', - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - final oldDevice = - await (await oldSession.getDevice()).replaceSignedPrekey(); + final device = + await OmemoDevice.generateNewDevice('user@test.server', opkAmount: 1); + final oldDevice = await device.replaceSignedPrekey(); final serialised = jsonify(await oldDevice.toJson()); final newDevice = OmemoDevice.fromJson(serialised); expect(await oldDevice.equals(newDevice), true); }); - test('Test serialising and deserialising the OmemoDoubleRatchet', () async { + /*test('Test serialising and deserialising the OmemoDoubleRatchet', () async { // Generate a random ratchet const aliceJid = 'alice@server.example'; const bobJid = 'bob@other.server.example'; - final aliceSession = await OmemoSessionManager.generateNewIdentity( + final aliceManager = OmemoSessionManager.generateNewIdentity( aliceJid, AlwaysTrustingTrustManager(), opkAmount: 1, @@ -151,5 +143,5 @@ void main() { serialized, ); expect(btbv.enablementCache, enableCache); - }); + });*/ }