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: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';
|
||||
|
@ -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 'package:cryptography/cryptography.dart';
|
||||
import 'package:omemo_dart/src/keys.dart';
|
||||
|
||||
/// Flattens [inputs] and concatenates the elements.
|
||||
List<int> concat(List<List<int>> inputs) {
|
||||
@ -39,3 +42,34 @@ List<int> generateRandomBytes(int length) {
|
||||
int generateRandom32BitNumber() {
|
||||
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
|
||||
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<String, dynamic> 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<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
|
||||
final Map<int, OmemoKeyPair> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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';
|
||||
|
Loading…
Reference in New Issue
Block a user