From b8b6bbf8009e104e0316689fdf64da2be3d109ea Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Mon, 8 Aug 2022 15:32:08 +0200 Subject: [PATCH] feat: Track the old Signed Prekey after rotation --- lib/src/double_ratchet/double_ratchet.dart | 1 - lib/src/double_ratchet/helpers.dart | 18 -------- lib/src/helpers.dart | 34 +++++++++++++++ lib/src/omemo/device.dart | 49 +++++++++++++++++++++- test/omemo_test.dart | 4 ++ test/serialisation_test.dart | 10 +++++ 6 files changed, 95 insertions(+), 21 deletions(-) delete mode 100644 lib/src/double_ratchet/helpers.dart diff --git a/lib/src/double_ratchet/double_ratchet.dart b/lib/src/double_ratchet/double_ratchet.dart index 0469446..c52122e 100644 --- a/lib/src/double_ratchet/double_ratchet.dart +++ b/lib/src/double_ratchet/double_ratchet.dart @@ -3,7 +3,6 @@ import 'package:cryptography/cryptography.dart'; import 'package:meta/meta.dart'; import 'package:omemo_dart/src/crypto.dart'; import 'package:omemo_dart/src/double_ratchet/crypto.dart'; -import 'package:omemo_dart/src/double_ratchet/helpers.dart'; import 'package:omemo_dart/src/double_ratchet/kdf.dart'; import 'package:omemo_dart/src/errors.dart'; import 'package:omemo_dart/src/helpers.dart'; diff --git a/lib/src/double_ratchet/helpers.dart b/lib/src/double_ratchet/helpers.dart deleted file mode 100644 index 7b7a2b2..0000000 --- a/lib/src/double_ratchet/helpers.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'dart:convert'; -import 'package:cryptography/cryptography.dart'; -import 'package:omemo_dart/src/keys.dart'; - -OmemoPublicKey? decodeKeyIfNotNull(Map map, String key, KeyPairType type) { - if (map[key] == null) return null; - - return OmemoPublicKey.fromBytes( - base64.decode(map[key]! as String), - type, - ); -} - -List? base64DecodeIfNotNull(Map map, String key) { - if (map[key] == null) return null; - - return base64.decode(map[key]! as String); -} diff --git a/lib/src/helpers.dart b/lib/src/helpers.dart index 018b0e2..bff59f5 100644 --- a/lib/src/helpers.dart +++ b/lib/src/helpers.dart @@ -1,4 +1,7 @@ +import 'dart:convert'; import 'dart:math'; +import 'package:cryptography/cryptography.dart'; +import 'package:omemo_dart/src/keys.dart'; /// Flattens [inputs] and concatenates the elements. List concat(List> inputs) { @@ -39,3 +42,34 @@ List generateRandomBytes(int length) { int generateRandom32BitNumber() { return Random.secure().nextInt(4294967295 /*pow(2, 32) - 1*/); } + +OmemoPublicKey? decodeKeyIfNotNull(Map map, String key, KeyPairType type) { + if (map[key] == null) return null; + + return OmemoPublicKey.fromBytes( + base64.decode(map[key]! as String), + type, + ); +} + +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, + ); +} diff --git a/lib/src/omemo/device.dart b/lib/src/omemo/device.dart index df3000d..c07b114 100644 --- a/lib/src/omemo/device.dart +++ b/lib/src/omemo/device.dart @@ -10,7 +10,18 @@ import 'package:omemo_dart/src/x3dh/x3dh.dart'; @immutable class Device { - const Device(this.jid, this.id, this.ik, this.spk, this.spkId, this.spkSignature, this.opks); + const Device( + this.jid, + this.id, + this.ik, + this.spk, + this.spkId, + this.spkSignature, + this.oldSpk, + this.oldSpkId, + this.oldSpkSignature, + this.opks, + ); /// Deserialize the Device factory Device.fromJson(Map data) { @@ -27,6 +38,10 @@ class Device { '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, + 'old_ spk_sig': 'base/64/encoded', 'opks': [ { 'id': 0, @@ -60,6 +75,13 @@ class Device { ), 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?, + base64DecodeIfNotNull(data, 'old_spk_sig'), opks, ); } @@ -77,7 +99,7 @@ class Device { opks[i] = await OmemoKeyPair.generateNewPair(KeyPairType.x25519); } - return Device(jid, id, ik, spk, spkId, signature, opks); + return Device(jid, id, ik, spk, spkId, signature, null, null, null, opks); } /// Our bare Jid @@ -96,6 +118,13 @@ class Device { /// ...and its signature final List spkSignature; + /// The old Signed Prekey... + final OmemoKeyPair? oldSpk; + /// its Id, ... + final int? oldSpkId; + /// ...and its signature + final List? oldSpkSignature; + /// Map of an id to the associated Onetime-Prekey final Map opks; @@ -112,6 +141,9 @@ class Device { spk, spkId, spkSignature, + oldSpk, + oldSpkId, + oldSpkSignature, opks, ); } @@ -131,6 +163,9 @@ class Device { newSpk, newSpkId, newSignature, + spk, + spkId, + spkSignature, opks, ); } @@ -175,6 +210,10 @@ class Device { '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, + 'old_spk_sig': base64EncodeIfNotNull(oldSpkSignature), 'opks': serialisedOpks, }; } @@ -198,12 +237,18 @@ class Device { final ikMatch = await ik.equals(other.ik); // ignore: invalid_use_of_visible_for_testing_member final spkMatch = await spk.equals(other.spk); + // ignore: invalid_use_of_visible_for_testing_member + final oldSpkMatch = oldSpk != null ? await oldSpk!.equals(other.oldSpk!) : other.oldSpk == null; + final oldSpkSigsMatch = oldSpkSignature != null ? listsEqual(oldSpkSignature!, other.oldSpkSignature!) : other.oldSpkSignature == null; return id == other.id && ikMatch && spkMatch && + oldSpkMatch && jid == other.jid && listsEqual(spkSignature, other.spkSignature) && spkId == other.spkId && + oldSpkId == other.oldSpkId && + oldSpkSigsMatch && opksMatch; } } diff --git a/test/omemo_test.dart b/test/omemo_test.dart index 45545c9..0689b4e 100644 --- a/test/omemo_test.dart +++ b/test/omemo_test.dart @@ -244,5 +244,9 @@ void main() { 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); + expect(listsEqual(newDevice!.oldSpkSignature!, oldDevice.spkSignature), true); }); } diff --git a/test/serialisation_test.dart b/test/serialisation_test.dart index dab1ef0..24ba53b 100644 --- a/test/serialisation_test.dart +++ b/test/serialisation_test.dart @@ -12,6 +12,16 @@ void main() { expect(await oldDevice.equals(newDevice), true); }); + test('Test serialising and deserialising the Device after rotating the SPK', () async { + // Generate a random session + final oldSession = await OmemoSessionManager.generateNewIdentity('user@test.server', opkAmount: 1); + final oldDevice = await (await oldSession.getDevice()).replaceSignedPrekey(); + final serialised = await oldDevice.toJson(); + + final newDevice = Device.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';