feat: Help with serializing and deserializing the BTVT manager
This commit is contained in:
parent
12e09947f6
commit
ff52c82039
@ -4,9 +4,24 @@ import 'package:meta/meta.dart';
|
|||||||
class RatchetMapKey {
|
class RatchetMapKey {
|
||||||
|
|
||||||
const RatchetMapKey(this.jid, this.deviceId);
|
const RatchetMapKey(this.jid, this.deviceId);
|
||||||
|
|
||||||
|
factory RatchetMapKey.fromJsonKey(String key) {
|
||||||
|
final parts = key.split(':');
|
||||||
|
final deviceId = int.parse(parts.first);
|
||||||
|
|
||||||
|
return RatchetMapKey(
|
||||||
|
parts.sublist(1).join(':'),
|
||||||
|
deviceId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final String jid;
|
final String jid;
|
||||||
final int deviceId;
|
final int deviceId;
|
||||||
|
|
||||||
|
String toJsonKey() {
|
||||||
|
return '$deviceId:$jid';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return other is RatchetMapKey && jid == other.jid && deviceId == other.deviceId;
|
return other is RatchetMapKey && jid == other.jid && deviceId == other.deviceId;
|
||||||
|
@ -17,4 +17,7 @@ class AlwaysTrustingTrustManager extends TrustManager {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> setEnabled(String jid, int deviceId, bool enabled) async {}
|
Future<void> setEnabled(String jid, int deviceId, bool enabled) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Map<String, dynamic>> toJson() async => <String, dynamic>{};
|
||||||
}
|
}
|
||||||
|
@ -16,4 +16,7 @@ abstract class TrustManager {
|
|||||||
/// Mark the device with id [deviceId] of Jid [jid] as enabled if [enabled] is true or as disabled
|
/// Mark the device with id [deviceId] of Jid [jid] as enabled if [enabled] is true or as disabled
|
||||||
/// if [enabled] is false.
|
/// if [enabled] is false.
|
||||||
Future<void> setEnabled(String jid, int deviceId, bool enabled);
|
Future<void> setEnabled(String jid, int deviceId, bool enabled);
|
||||||
|
|
||||||
|
/// Serialize the trust manager to JSON.
|
||||||
|
Future<Map<String, dynamic>> toJson();
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,26 @@ import 'package:synchronized/synchronized.dart';
|
|||||||
/// - blindTrust: The fingerprint is not verified using OOB means
|
/// - blindTrust: The fingerprint is not verified using OOB means
|
||||||
/// - verified: The fingerprint has been verified using OOB means
|
/// - verified: The fingerprint has been verified using OOB means
|
||||||
enum BTBVTrustState {
|
enum BTBVTrustState {
|
||||||
notTrusted,
|
notTrusted, // = 1
|
||||||
blindTrust,
|
blindTrust, // = 2
|
||||||
verified,
|
verified, // = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
int _trustToInt(BTBVTrustState state) {
|
||||||
|
switch (state) {
|
||||||
|
case BTBVTrustState.notTrusted: return 1;
|
||||||
|
case BTBVTrustState.blindTrust: return 2;
|
||||||
|
case BTBVTrustState.verified: return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BTBVTrustState _trustFromInt(int i) {
|
||||||
|
switch (i) {
|
||||||
|
case 1: return BTBVTrustState.notTrusted;
|
||||||
|
case 2: return BTBVTrustState.blindTrust;
|
||||||
|
case 3: return BTBVTrustState.verified;
|
||||||
|
default: return BTBVTrustState.notTrusted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A TrustManager that implements the idea of Blind Trust Before Verification.
|
/// A TrustManager that implements the idea of Blind Trust Before Verification.
|
||||||
@ -23,14 +40,17 @@ abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager {
|
|||||||
_lock = Lock();
|
_lock = Lock();
|
||||||
|
|
||||||
/// The cache for mapping a RatchetMapKey to its trust state
|
/// The cache for mapping a RatchetMapKey to its trust state
|
||||||
|
@visibleForTesting
|
||||||
@protected
|
@protected
|
||||||
final Map<RatchetMapKey, BTBVTrustState> trustCache;
|
final Map<RatchetMapKey, BTBVTrustState> trustCache;
|
||||||
|
|
||||||
/// The cache for mapping a RatchetMapKey to whether it is enabled or not
|
/// The cache for mapping a RatchetMapKey to whether it is enabled or not
|
||||||
|
@visibleForTesting
|
||||||
@protected
|
@protected
|
||||||
final Map<RatchetMapKey, bool> enablementCache;
|
final Map<RatchetMapKey, bool> enablementCache;
|
||||||
|
|
||||||
/// Mapping of Jids to their device identifiers
|
/// Mapping of Jids to their device identifiers
|
||||||
|
@visibleForTesting
|
||||||
@protected
|
@protected
|
||||||
final Map<String, List<int>> devices;
|
final Map<String, List<int>> devices;
|
||||||
|
|
||||||
@ -141,6 +161,50 @@ abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager {
|
|||||||
// Commit the state
|
// Commit the state
|
||||||
await commitState();
|
await commitState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Map<String, dynamic>> toJson() async {
|
||||||
|
return {
|
||||||
|
'devices': devices,
|
||||||
|
'trust': trustCache.map((key, value) => MapEntry(
|
||||||
|
key.toJsonKey(), _trustToInt(value),
|
||||||
|
),),
|
||||||
|
'enable': enablementCache.map((key, value) => MapEntry(key.toJsonKey(), value)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// From a serialized version of a BTBV trust manager, extract the device list.
|
||||||
|
/// NOTE: This is needed as Dart cannot just cast a List<dynamic> to List<int> and so on.
|
||||||
|
static Map<String, List<int>> deviceListFromJson(Map<String, dynamic> json) {
|
||||||
|
return (json['devices']! as Map<String, dynamic>).map<String, List<int>>(
|
||||||
|
(key, value) => MapEntry(
|
||||||
|
key,
|
||||||
|
(value as List<dynamic>).map<int>((i) => i as int).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// From a serialized version of a BTBV trust manager, extract the trust cache.
|
||||||
|
/// NOTE: This is needed as Dart cannot just cast a List<dynamic> to List<int> and so on.
|
||||||
|
static Map<RatchetMapKey, BTBVTrustState> trustCacheFromJson(Map<String, dynamic> json) {
|
||||||
|
return (json['trust']! as Map<String, dynamic>).map<RatchetMapKey, BTBVTrustState>(
|
||||||
|
(key, value) => MapEntry(
|
||||||
|
RatchetMapKey.fromJsonKey(key),
|
||||||
|
_trustFromInt(value as int),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// From a serialized version of a BTBV trust manager, extract the enable cache.
|
||||||
|
/// NOTE: This is needed as Dart cannot just cast a List<dynamic> to List<int> and so on.
|
||||||
|
static Map<RatchetMapKey, bool> enableCacheFromJson(Map<String, dynamic> json) {
|
||||||
|
return (json['enable']! as Map<String, dynamic>).map<RatchetMapKey, bool>(
|
||||||
|
(key, value) => MapEntry(
|
||||||
|
RatchetMapKey.fromJsonKey(key),
|
||||||
|
value as bool,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when the state of the trust manager has been changed. Allows the user to
|
/// Called when the state of the trust manager has been changed. Allows the user to
|
||||||
/// commit the trust state to persistent storage.
|
/// commit the trust state to persistent storage.
|
||||||
|
@ -17,4 +17,7 @@ class NeverTrustingTrustManager extends TrustManager {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> setEnabled(String jid, int deviceId, bool enabled) async {}
|
Future<void> setEnabled(String jid, int deviceId, bool enabled) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Map<String, dynamic>> toJson() async => <String, dynamic>{};
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'package:omemo_dart/omemo_dart.dart';
|
import 'package:omemo_dart/omemo_dart.dart';
|
||||||
import 'package:omemo_dart/src/trust/always.dart';
|
import 'package:omemo_dart/src/trust/always.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
@ -104,4 +105,84 @@ void main() {
|
|||||||
expect(await oldRatchet.equals(newRatchet), true);
|
expect(await oldRatchet.equals(newRatchet), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Test serialising and deserialising the BlindTrustBeforeVerificationTrustManager', () async {
|
||||||
|
// Caroline's BTBV manager
|
||||||
|
final btbv = MemoryBTBVTrustManager();
|
||||||
|
// Example data
|
||||||
|
const aliceJid = 'alice@some.server';
|
||||||
|
const bobJid = 'bob@other.server';
|
||||||
|
|
||||||
|
// Caroline starts a chat a device from Alice
|
||||||
|
await btbv.onNewSession(aliceJid, 1);
|
||||||
|
expect(await btbv.isTrusted(aliceJid, 1), true);
|
||||||
|
expect(await btbv.isEnabled(aliceJid, 1), true);
|
||||||
|
|
||||||
|
// Caroline meets with Alice and verifies her fingerprint
|
||||||
|
await btbv.setDeviceTrust(aliceJid, 1, BTBVTrustState.verified);
|
||||||
|
expect(await btbv.isTrusted(aliceJid, 1), true);
|
||||||
|
|
||||||
|
// Alice adds a new device
|
||||||
|
await btbv.onNewSession(aliceJid, 2);
|
||||||
|
expect(await btbv.isTrusted(aliceJid, 2), false);
|
||||||
|
expect(btbv.getDeviceTrust(aliceJid, 2), BTBVTrustState.notTrusted);
|
||||||
|
expect(await btbv.isEnabled(aliceJid, 2), false);
|
||||||
|
|
||||||
|
// Caronline starts a chat with Bob but since they live far apart, Caroline cannot
|
||||||
|
// verify his fingerprint.
|
||||||
|
await btbv.onNewSession(bobJid, 3);
|
||||||
|
|
||||||
|
// Bob adds a new device
|
||||||
|
await btbv.onNewSession(bobJid, 4);
|
||||||
|
expect(await btbv.isTrusted(bobJid, 3), true);
|
||||||
|
expect(await btbv.isTrusted(bobJid, 4), true);
|
||||||
|
expect(btbv.getDeviceTrust(bobJid, 3), BTBVTrustState.blindTrust);
|
||||||
|
expect(btbv.getDeviceTrust(bobJid, 4), BTBVTrustState.blindTrust);
|
||||||
|
expect(await btbv.isEnabled(bobJid, 3), true);
|
||||||
|
expect(await btbv.isEnabled(bobJid, 4), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
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 managerJson = await btbv.toJson();
|
||||||
|
final managerString = jsonEncode(managerJson);
|
||||||
|
final managerPostJson = jsonDecode(managerString) as Map<String, dynamic>;
|
||||||
|
final deviceList = BlindTrustBeforeVerificationTrustManager.deviceListFromJson(
|
||||||
|
managerPostJson,
|
||||||
|
);
|
||||||
|
expect(btbv.devices, deviceList);
|
||||||
|
|
||||||
|
final trustCache = BlindTrustBeforeVerificationTrustManager.trustCacheFromJson(
|
||||||
|
managerPostJson,
|
||||||
|
);
|
||||||
|
expect(btbv.trustCache, trustCache);
|
||||||
|
|
||||||
|
final enableCache = BlindTrustBeforeVerificationTrustManager.enableCacheFromJson(
|
||||||
|
managerPostJson,
|
||||||
|
);
|
||||||
|
expect(btbv.enablementCache, enableCache);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user