feat: Track the old Signed Prekey after rotation
This commit is contained in:
parent
9c23721904
commit
b8b6bbf800
@ -3,7 +3,6 @@ import 'package:cryptography/cryptography.dart';
|
|||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:omemo_dart/src/crypto.dart';
|
import 'package:omemo_dart/src/crypto.dart';
|
||||||
import 'package:omemo_dart/src/double_ratchet/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/double_ratchet/kdf.dart';
|
||||||
import 'package:omemo_dart/src/errors.dart';
|
import 'package:omemo_dart/src/errors.dart';
|
||||||
import 'package:omemo_dart/src/helpers.dart';
|
import 'package:omemo_dart/src/helpers.dart';
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'package:cryptography/cryptography.dart';
|
|
||||||
import 'package:omemo_dart/src/keys.dart';
|
|
||||||
|
|
||||||
OmemoPublicKey? decodeKeyIfNotNull(Map<String, dynamic> map, String key, KeyPairType type) {
|
|
||||||
if (map[key] == null) return null;
|
|
||||||
|
|
||||||
return OmemoPublicKey.fromBytes(
|
|
||||||
base64.decode(map[key]! as String),
|
|
||||||
type,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<int>? base64DecodeIfNotNull(Map<String, dynamic> map, String key) {
|
|
||||||
if (map[key] == null) return null;
|
|
||||||
|
|
||||||
return base64.decode(map[key]! as String);
|
|
||||||
}
|
|
@ -1,4 +1,7 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'package:cryptography/cryptography.dart';
|
||||||
|
import 'package:omemo_dart/src/keys.dart';
|
||||||
|
|
||||||
/// Flattens [inputs] and concatenates the elements.
|
/// Flattens [inputs] and concatenates the elements.
|
||||||
List<int> concat(List<List<int>> inputs) {
|
List<int> concat(List<List<int>> inputs) {
|
||||||
@ -39,3 +42,34 @@ List<int> generateRandomBytes(int length) {
|
|||||||
int generateRandom32BitNumber() {
|
int generateRandom32BitNumber() {
|
||||||
return Random.secure().nextInt(4294967295 /*pow(2, 32) - 1*/);
|
return Random.secure().nextInt(4294967295 /*pow(2, 32) - 1*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OmemoPublicKey? decodeKeyIfNotNull(Map<String, dynamic> map, String key, KeyPairType type) {
|
||||||
|
if (map[key] == null) return null;
|
||||||
|
|
||||||
|
return OmemoPublicKey.fromBytes(
|
||||||
|
base64.decode(map[key]! as String),
|
||||||
|
type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<int>? base64DecodeIfNotNull(Map<String, dynamic> map, String key) {
|
||||||
|
if (map[key] == null) return null;
|
||||||
|
|
||||||
|
return base64.decode(map[key]! as String);
|
||||||
|
}
|
||||||
|
|
||||||
|
String? base64EncodeIfNotNull(List<int>? 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -10,7 +10,18 @@ import 'package:omemo_dart/src/x3dh/x3dh.dart';
|
|||||||
@immutable
|
@immutable
|
||||||
class Device {
|
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
|
/// Deserialize the Device
|
||||||
factory Device.fromJson(Map<String, dynamic> data) {
|
factory Device.fromJson(Map<String, dynamic> data) {
|
||||||
@ -27,6 +38,10 @@ class Device {
|
|||||||
'spk_pub': 'base/64/encoded',
|
'spk_pub': 'base/64/encoded',
|
||||||
'spk_id': 123,
|
'spk_id': 123,
|
||||||
'spk_sig': 'base/64/encoded',
|
'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': [
|
'opks': [
|
||||||
{
|
{
|
||||||
'id': 0,
|
'id': 0,
|
||||||
@ -60,6 +75,13 @@ class Device {
|
|||||||
),
|
),
|
||||||
data['spk_id']! as int,
|
data['spk_id']! as int,
|
||||||
base64.decode(data['spk_sig']! as String),
|
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,
|
opks,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -77,7 +99,7 @@ class Device {
|
|||||||
opks[i] = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
|
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
|
/// Our bare Jid
|
||||||
@ -96,6 +118,13 @@ class Device {
|
|||||||
/// ...and its signature
|
/// ...and its signature
|
||||||
final List<int> spkSignature;
|
final List<int> spkSignature;
|
||||||
|
|
||||||
|
/// The old Signed Prekey...
|
||||||
|
final OmemoKeyPair? oldSpk;
|
||||||
|
/// its Id, ...
|
||||||
|
final int? oldSpkId;
|
||||||
|
/// ...and its signature
|
||||||
|
final List<int>? oldSpkSignature;
|
||||||
|
|
||||||
/// Map of an id to the associated Onetime-Prekey
|
/// Map of an id to the associated Onetime-Prekey
|
||||||
final Map<int, OmemoKeyPair> opks;
|
final Map<int, OmemoKeyPair> opks;
|
||||||
|
|
||||||
@ -112,6 +141,9 @@ class Device {
|
|||||||
spk,
|
spk,
|
||||||
spkId,
|
spkId,
|
||||||
spkSignature,
|
spkSignature,
|
||||||
|
oldSpk,
|
||||||
|
oldSpkId,
|
||||||
|
oldSpkSignature,
|
||||||
opks,
|
opks,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -131,6 +163,9 @@ class Device {
|
|||||||
newSpk,
|
newSpk,
|
||||||
newSpkId,
|
newSpkId,
|
||||||
newSignature,
|
newSignature,
|
||||||
|
spk,
|
||||||
|
spkId,
|
||||||
|
spkSignature,
|
||||||
opks,
|
opks,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -175,6 +210,10 @@ class Device {
|
|||||||
'spk_pub': base64.encode(await spk.pk.getBytes()),
|
'spk_pub': base64.encode(await spk.pk.getBytes()),
|
||||||
'spk_id': spkId,
|
'spk_id': spkId,
|
||||||
'spk_sig': base64.encode(spkSignature),
|
'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,
|
'opks': serialisedOpks,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -198,12 +237,18 @@ class Device {
|
|||||||
final ikMatch = await ik.equals(other.ik);
|
final ikMatch = await ik.equals(other.ik);
|
||||||
// ignore: invalid_use_of_visible_for_testing_member
|
// ignore: invalid_use_of_visible_for_testing_member
|
||||||
final spkMatch = await spk.equals(other.spk);
|
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 &&
|
return id == other.id &&
|
||||||
ikMatch &&
|
ikMatch &&
|
||||||
spkMatch &&
|
spkMatch &&
|
||||||
|
oldSpkMatch &&
|
||||||
jid == other.jid &&
|
jid == other.jid &&
|
||||||
listsEqual(spkSignature, other.spkSignature) &&
|
listsEqual(spkSignature, other.spkSignature) &&
|
||||||
spkId == other.spkId &&
|
spkId == other.spkId &&
|
||||||
|
oldSpkId == other.oldSpkId &&
|
||||||
|
oldSpkSigsMatch &&
|
||||||
opksMatch;
|
opksMatch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,5 +244,9 @@ void main() {
|
|||||||
|
|
||||||
expect(await oldDevice.equals(newDevice!), false);
|
expect(await oldDevice.equals(newDevice!), false);
|
||||||
expect(await newDevice!.equals(await aliceSession.getDevice()), true);
|
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,16 @@ void main() {
|
|||||||
expect(await oldDevice.equals(newDevice), true);
|
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 {
|
test('Test serialising and deserialising the OmemoDoubleRatchet', () async {
|
||||||
// Generate a random ratchet
|
// Generate a random ratchet
|
||||||
const aliceJid = 'alice@server.example';
|
const aliceJid = 'alice@server.example';
|
||||||
|
Loading…
Reference in New Issue
Block a user