diff --git a/lib/src/helpers.dart b/lib/src/helpers.dart index 4f23632..8c4ac98 100644 --- a/lib/src/helpers.dart +++ b/lib/src/helpers.dart @@ -56,28 +56,6 @@ OmemoPublicKey? decodeKeyIfNotNull( ); } -List? base64DecodeIfNotNull(Map map, String key) { - if (map[key] == null) return null; - - return base64.decode(map[key]! as String); -} - -String? base64EncodeIfNotNull(List? bytes) { - if (bytes == null) return null; - - return base64.encode(bytes); -} - -OmemoKeyPair? decodeKeyPairIfNotNull(String? pk, String? sk, KeyPairType type) { - if (pk == null || sk == null) return null; - - return OmemoKeyPair.fromBytes( - base64.decode(pk), - base64.decode(sk), - type, - ); -} - int getTimestamp() { return DateTime.now().millisecondsSinceEpoch; } diff --git a/lib/src/omemo/device.dart b/lib/src/omemo/device.dart index 354c795..14e7a7d 100644 --- a/lib/src/omemo/device.dart +++ b/lib/src/omemo/device.dart @@ -22,76 +22,6 @@ class OmemoDevice { this.opks, ); - /// Deserialize the Device - factory OmemoDevice.fromJson(Map 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. - /* - { - 'jid': 'alice@...', - 'id': 123, - 'ik': 'base/64/encoded', - 'ik_pub': 'base/64/encoded', - 'spk': 'base/64/encoded', - 'spk_pub': 'base/64/encoded', - 'spk_id': 123, - 'spk_sig': 'base/64/encoded', - 'old_spk': 'base/64/encoded', - 'old_spk_pub': 'base/64/encoded', - 'old_spk_id': 122, - 'opks': [ - { - 'id': 0, - 'public': 'base/64/encoded', - 'private': 'base/64/encoded' - }, ... - ] - } - */ - // NOTE: Dart has some issues with just casting a List to List>, as - // such we need to convert the items by hand. - final opks = Map.fromEntries( - (data['opks']! as List).map>( - (opk) { - final map = opk as Map; - return MapEntry( - map['id']! as int, - OmemoKeyPair.fromBytes( - base64.decode(map['public']! as String), - base64.decode(map['private']! as String), - KeyPairType.x25519, - ), - ); - }, - ), - ); - - return OmemoDevice( - data['jid']! as String, - data['id']! as int, - OmemoKeyPair.fromBytes( - base64.decode(data['ik_pub']! as String), - base64.decode(data['ik']! as String), - KeyPairType.ed25519, - ), - OmemoKeyPair.fromBytes( - base64.decode(data['spk_pub']! as String), - base64.decode(data['spk']! as String), - KeyPairType.x25519, - ), - data['spk_id']! as int, - base64.decode(data['spk_sig']! as String), - decodeKeyPairIfNotNull( - data['old_spk_pub'] as String?, - data['old_spk'] as String?, - KeyPairType.x25519, - ), - data['old_spk_id'] as int?, - opks, - ); - } - /// Generate a completely new device, i.e. cryptographic identity. static Future generateNewDevice( String jid, { @@ -221,34 +151,6 @@ class OmemoDevice { return HEX.encode(await curveKey.getBytes()); } - /// Serialise the device information. - Future> toJson() async { - /// Serialise the OPKs - final serialisedOpks = List>.empty(growable: true); - for (final entry in opks.entries) { - serialisedOpks.add({ - 'id': entry.key, - 'public': base64.encode(await entry.value.pk.getBytes()), - 'private': base64.encode(await entry.value.sk.getBytes()), - }); - } - - return { - 'jid': jid, - 'id': id, - 'ik': base64.encode(await ik.sk.getBytes()), - 'ik_pub': base64.encode(await ik.pk.getBytes()), - 'spk': base64.encode(await spk.sk.getBytes()), - 'spk_pub': base64.encode(await spk.pk.getBytes()), - 'spk_id': spkId, - 'spk_sig': base64.encode(spkSignature), - 'old_spk': base64EncodeIfNotNull(await oldSpk?.sk.getBytes()), - 'old_spk_pub': base64EncodeIfNotNull(await oldSpk?.pk.getBytes()), - 'old_spk_id': oldSpkId, - 'opks': serialisedOpks, - }; - } - @visibleForTesting Future equals(OmemoDevice other) async { var opksMatch = true; diff --git a/lib/src/omemo/omemo.dart b/lib/src/omemo/omemo.dart index 2c6a54e..70fa49c 100644 --- a/lib/src/omemo/omemo.dart +++ b/lib/src/omemo/omemo.dart @@ -414,7 +414,12 @@ class OmemoManager { } else { // Check if we even have a ratchet if (!_ratchetMap.containsKey(ratchetKey)) { - // TODO: Build a session with the device + // TODO: Check if we recently failed to build a session with the device + // This causes omemo_dart to build a session with the device. + if (!_deviceList[stanza.bareSenderJid]!.contains(stanza.senderDeviceId)) { + _deviceList[stanza.bareSenderJid]!.add(stanza.senderDeviceId); + } + await sendOmemoHeartbeat(stanza.bareSenderJid); return DecryptionResult( null, diff --git a/test/serialisation_test.dart b/test/serialisation_test.dart deleted file mode 100644 index 379bf60..0000000 --- a/test/serialisation_test.dart +++ /dev/null @@ -1,155 +0,0 @@ -import 'dart:convert'; -import 'package:omemo_dart/omemo_dart.dart'; -import 'package:omemo_dart/src/protobuf/schema.pb.dart'; -import 'package:omemo_dart/src/trust/always.dart'; -import 'package:test/test.dart'; - -Map jsonify(Map map) { - return jsonDecode(jsonEncode(map)) as Map; -} - -void main() { - test('Test serialising and deserialising the Device', () async { - // Generate a random session - final oldDevice = - await OmemoDevice.generateNewDevice('user@test.server', opkAmount: 5); - final serialised = jsonify(await oldDevice.toJson()); - - final newDevice = OmemoDevice.fromJson(serialised); - expect(await oldDevice.equals(newDevice), true); - }); - - test('Test serialising and deserialising the Device after rotating the SPK', - () async { - // Generate a random session - 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 { - // Generate a random ratchet - const aliceJid = 'alice@server.example'; - const bobJid = 'bob@other.server.example'; - final aliceManager = OmemoSessionManager.generateNewIdentity( - aliceJid, - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - final bobSession = await OmemoSessionManager.generateNewIdentity( - bobJid, - AlwaysTrustingTrustManager(), - opkAmount: 1, - ); - final aliceMessage = await aliceSession.encryptToJid( - bobJid, - 'Hello Bob!', - newSessions: [ - await bobSession.getDeviceBundle(), - ], - ); - await bobSession.decryptMessage( - aliceMessage.ciphertext, - aliceJid, - await aliceSession.getDeviceId(), - aliceMessage.encryptedKeys, - getTimestamp(), - ); - final aliceOld = - aliceSession.getRatchet(bobJid, await bobSession.getDeviceId()); - final aliceSerialised = jsonify(await aliceOld.toJson()); - final aliceNew = OmemoDoubleRatchet.fromJson(aliceSerialised); - - expect(await aliceOld.equals(aliceNew), true); - }); - - test('Test serialising and deserialising the OmemoSessionManager', () async { - // Generate a random session - final oldSession = await OmemoSessionManager.generateNewIdentity( - 'a@server', - AlwaysTrustingTrustManager(), - opkAmount: 4, - ); - final bobSession = await OmemoSessionManager.generateNewIdentity( - 'b@other.server', - AlwaysTrustingTrustManager(), - opkAmount: 4, - ); - await oldSession.addSessionFromBundle( - 'bob@localhost', - await bobSession.getDeviceId(), - await bobSession.getDeviceBundle(), - ); - - // Serialise and deserialise - final serialised = jsonify(await oldSession.toJsonWithoutSessions()); - final newSession = OmemoSessionManager.fromJsonWithoutSessions( - serialised, - // NOTE: At this point, we don't care about this attribute - {}, - AlwaysTrustingTrustManager(), - ); - - final oldDevice = await oldSession.getDevice(); - final newDevice = await newSession.getDevice(); - expect(await oldDevice.equals(newDevice), true); - expect(await oldSession.getDeviceMap(), await newSession.getDeviceMap()); - }); - - test('Test serializing and deserializing RatchetMapKey', () { - const test1 = RatchetMapKey('user@example.org', 1234); - final result1 = RatchetMapKey.fromJsonKey(test1.toJsonKey()); - expect(result1.jid, test1.jid); - expect(result1.deviceId, test1.deviceId); - - const test2 = RatchetMapKey('user@example.org/hallo:welt', 3333); - final result2 = RatchetMapKey.fromJsonKey(test2.toJsonKey()); - expect(result2.jid, test2.jid); - expect(result2.deviceId, test2.deviceId); - }); - - test('Test serializing and deserializing the components of the BTBV manager', - () async { - // Caroline's BTBV manager - final btbv = MemoryBTBVTrustManager(); - // Example data - const aliceJid = 'alice@some.server'; - const bobJid = 'bob@other.server'; - - await btbv.onNewSession(aliceJid, 1); - await btbv.setDeviceTrust(aliceJid, 1, BTBVTrustState.verified); - await btbv.onNewSession(aliceJid, 2); - await btbv.onNewSession(bobJid, 3); - await btbv.onNewSession(bobJid, 4); - - final serialized = jsonify(await btbv.toJson()); - final deviceList = - BlindTrustBeforeVerificationTrustManager.deviceListFromJson( - serialized, - ); - expect(btbv.devices, deviceList); - - final trustCache = - BlindTrustBeforeVerificationTrustManager.trustCacheFromJson( - serialized, - ); - expect(btbv.trustCache, trustCache); - - final enableCache = - BlindTrustBeforeVerificationTrustManager.enableCacheFromJson( - serialized, - ); - expect(btbv.enablementCache, enableCache); - });*/ - - test('Test reading and writing protobuf', () { - final kex = OMEMOKeyExchange.fromBuffer(base64Decode('CFIQqvSv8AEaIHuVKKqf00vYpBIB7PKWheboVKcoKCWkRIUFeokzMYqxIiDOYPRA2vAlHwDLwPn/XIMSmenj3fgUIZHMgUXxVJtedyp8ChA+GfjScvWAllcTavRoyvfIEmgIDhAAGiANff1ES1HdSgtjy9JsoXcAywXJfBmZsFYTKUHRQsCMNiJAAOhS/CMrIdDm+ZZ/fmaOfwD0O7MNUaUMkVahvk4XDAy6mYk65r2TE4REW7h7akcKyoL94YSnTWp8p6fO91VSLA==')); - final newKex = OMEMOKeyExchange.fromBuffer(kex.writeToBuffer()); - - expect(kex.writeToBuffer(), newKex.writeToBuffer()); - }); -}