feat: Add a base trust manager class

This commit is contained in:
PapaTutuWawa 2022-08-08 18:03:05 +02:00
parent e9f190036c
commit dafd0af1e5
6 changed files with 133 additions and 25 deletions

View File

@ -11,4 +11,6 @@ export 'src/omemo/encrypted_key.dart';
export 'src/omemo/encryption_result.dart'; export 'src/omemo/encryption_result.dart';
export 'src/omemo/fingerprint.dart'; export 'src/omemo/fingerprint.dart';
export 'src/omemo/sessionmanager.dart'; export 'src/omemo/sessionmanager.dart';
export 'src/trust/base.dart';
//export 'src/trust/btbv.dart';
export 'src/x3dh/x3dh.dart'; export 'src/x3dh/x3dh.dart';

View File

@ -19,6 +19,7 @@ import 'package:omemo_dart/src/omemo/ratchet_map_key.dart';
import 'package:omemo_dart/src/protobuf/omemo_authenticated_message.dart'; import 'package:omemo_dart/src/protobuf/omemo_authenticated_message.dart';
import 'package:omemo_dart/src/protobuf/omemo_key_exchange.dart'; import 'package:omemo_dart/src/protobuf/omemo_key_exchange.dart';
import 'package:omemo_dart/src/protobuf/omemo_message.dart'; import 'package:omemo_dart/src/protobuf/omemo_message.dart';
import 'package:omemo_dart/src/trust/base.dart';
import 'package:omemo_dart/src/x3dh/x3dh.dart'; import 'package:omemo_dart/src/x3dh/x3dh.dart';
import 'package:synchronized/synchronized.dart'; import 'package:synchronized/synchronized.dart';
@ -27,13 +28,13 @@ const omemoPayloadInfoString = 'OMEMO Payload';
class OmemoSessionManager { class OmemoSessionManager {
OmemoSessionManager(this._device, this._deviceMap, this._ratchetMap) OmemoSessionManager(this._device, this._deviceMap, this._ratchetMap, this._trustManager)
: _lock = Lock(), : _lock = Lock(),
_deviceLock = Lock(), _deviceLock = Lock(),
_eventStreamController = StreamController<OmemoEvent>.broadcast(); _eventStreamController = StreamController<OmemoEvent>.broadcast();
/// Deserialise the OmemoSessionManager from JSON data [data]. /// Deserialise the OmemoSessionManager from JSON data [data].
factory OmemoSessionManager.fromJson(Map<String, dynamic> data) { factory OmemoSessionManager.fromJson(Map<String, dynamic> data, TrustManager trustManager) {
final ratchetMap = <RatchetMapKey, OmemoDoubleRatchet>{}; final ratchetMap = <RatchetMapKey, OmemoDoubleRatchet>{};
for (final rawRatchet in data['sessions']! as List<Map<String, dynamic>>) { for (final rawRatchet in data['sessions']! as List<Map<String, dynamic>>) {
final key = RatchetMapKey(rawRatchet['jid']! as String, rawRatchet['deviceId']! as int); final key = RatchetMapKey(rawRatchet['jid']! as String, rawRatchet['deviceId']! as int);
@ -41,20 +42,20 @@ class OmemoSessionManager {
ratchetMap[key] = ratchet; ratchetMap[key] = ratchet;
} }
// TODO(PapaTutuWawa): Handle Trust behaviour
return OmemoSessionManager( return OmemoSessionManager(
Device.fromJson(data['device']! as Map<String, dynamic>), Device.fromJson(data['device']! as Map<String, dynamic>),
data['devices']! as Map<String, List<int>>, data['devices']! as Map<String, List<int>>,
ratchetMap, ratchetMap,
trustManager,
); );
} }
/// Generate a new cryptographic identity. /// Generate a new cryptographic identity.
static Future<OmemoSessionManager> generateNewIdentity(String jid, { int opkAmount = 100 }) async { static Future<OmemoSessionManager> generateNewIdentity(String jid, TrustManager trustManager, { int opkAmount = 100 }) async {
assert(opkAmount > 0, 'opkAmount must be bigger than 0.'); assert(opkAmount > 0, 'opkAmount must be bigger than 0.');
final device = await Device.generateNewDevice(jid, opkAmount: opkAmount); final device = await Device.generateNewDevice(jid, opkAmount: opkAmount);
return OmemoSessionManager(device, {}, {}); return OmemoSessionManager(device, {}, {}, trustManager);
} }
/// Lock for _ratchetMap and _bundleMap /// Lock for _ratchetMap and _bundleMap
@ -75,6 +76,9 @@ class OmemoSessionManager {
/// and its lock /// and its lock
final Lock _deviceLock; final Lock _deviceLock;
/// The trust manager
final TrustManager _trustManager;
/// A stream that receives events regarding the session /// A stream that receives events regarding the session
Stream<OmemoEvent> get eventStream => _eventStreamController.stream; Stream<OmemoEvent> get eventStream => _eventStreamController.stream;
@ -218,6 +222,9 @@ class OmemoSessionManager {
// We assume that the user already checked if the session exists // We assume that the user already checked if the session exists
for (final jid in jids) { for (final jid in jids) {
for (final deviceId in _deviceMap[jid]!) { for (final deviceId in _deviceMap[jid]!) {
// Only encrypt to devices that are trusted
if (!(await _trustManager.isTrusted(jid, deviceId))) continue;
final ratchetKey = RatchetMapKey(jid, deviceId); final ratchetKey = RatchetMapKey(jid, deviceId);
final ratchet = _ratchetMap[ratchetKey]!; final ratchet = _ratchetMap[ratchetKey]!;
final ciphertext = (await ratchet.ratchetEncrypt(keyPayload)).ciphertext; final ciphertext = (await ratchet.ratchetEncrypt(keyPayload)).ciphertext;

11
lib/src/trust/always.dart Normal file
View File

@ -0,0 +1,11 @@
import 'package:meta/meta.dart';
import 'package:omemo_dart/src/trust/base.dart';
/// Only use for testing!
/// An implementation of TrustManager that always trusts every device and thus
/// has no internal state.
@visibleForTesting
class AlwaysTrustingTrustManager extends TrustManager {
@override
Future<bool> isTrusted(String jid, int deviceId) async => true;
}

7
lib/src/trust/base.dart Normal file
View File

@ -0,0 +1,7 @@
/// The base class for managing trust in OMEMO sessions.
// ignore: one_member_abstracts
abstract class TrustManager {
/// Return true when the device with id [deviceId] of Jid [jid] is trusted, i.e. if an
/// encrypted message should be sent to this device. If not, return false.
Future<bool> isTrusted(String jid, int deviceId);
}

View File

@ -1,4 +1,5 @@
import 'package:omemo_dart/omemo_dart.dart'; import 'package:omemo_dart/omemo_dart.dart';
import 'package:omemo_dart/src/trust/always.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
@ -10,8 +11,16 @@ void main() {
var deviceModified = false; var deviceModified = false;
var ratchetModified = 0; var ratchetModified = 0;
var deviceMapModified = 0; var deviceMapModified = 0;
final aliceSession = await OmemoSessionManager.generateNewIdentity(aliceJid, opkAmount: 1); final aliceSession = await OmemoSessionManager.generateNewIdentity(
final bobSession = await OmemoSessionManager.generateNewIdentity(bobJid, opkAmount: 1); aliceJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final bobSession = await OmemoSessionManager.generateNewIdentity(
bobJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final bobOpks = (await bobSession.getDevice()).opks.values.toList(); final bobOpks = (await bobSession.getDevice()).opks.values.toList();
bobSession.eventStream.listen((event) { bobSession.eventStream.listen((event) {
if (event is DeviceModifiedEvent) { if (event is DeviceModifiedEvent) {
@ -83,10 +92,22 @@ void main() {
const bobJid = 'bob@other.server.example'; const bobJid = 'bob@other.server.example';
// Alice and Bob generate their sessions // Alice and Bob generate their sessions
final aliceSession = await OmemoSessionManager.generateNewIdentity(aliceJid, opkAmount: 1); final aliceSession = await OmemoSessionManager.generateNewIdentity(
final bobSession = await OmemoSessionManager.generateNewIdentity(bobJid, opkAmount: 1); aliceJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final bobSession = await OmemoSessionManager.generateNewIdentity(
bobJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
// Bob's other device // Bob's other device
final bobSession2 = await OmemoSessionManager.generateNewIdentity(bobJid, opkAmount: 1); final bobSession2 = await OmemoSessionManager.generateNewIdentity(
bobJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
// Alice encrypts a message for Bob // Alice encrypts a message for Bob
const messagePlaintext = 'Hello Bob!'; const messagePlaintext = 'Hello Bob!';
@ -149,9 +170,21 @@ void main() {
const bobJid = 'bob@other.server.example'; const bobJid = 'bob@other.server.example';
// Alice and Bob generate their sessions // Alice and Bob generate their sessions
final aliceSession1 = await OmemoSessionManager.generateNewIdentity(aliceJid, opkAmount: 1); final aliceSession1 = await OmemoSessionManager.generateNewIdentity(
final aliceSession2 = await OmemoSessionManager.generateNewIdentity(aliceJid, opkAmount: 1); aliceJid,
final bobSession = await OmemoSessionManager.generateNewIdentity(bobJid, opkAmount: 1); AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final aliceSession2 = await OmemoSessionManager.generateNewIdentity(
aliceJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final bobSession = await OmemoSessionManager.generateNewIdentity(
bobJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
// Alice encrypts a message for Bob // Alice encrypts a message for Bob
const messagePlaintext = 'Hello Bob!'; const messagePlaintext = 'Hello Bob!';
@ -192,8 +225,16 @@ void main() {
const bobJid = 'bob@other.server.example'; const bobJid = 'bob@other.server.example';
// Alice and Bob generate their sessions // Alice and Bob generate their sessions
final aliceSession = await OmemoSessionManager.generateNewIdentity(aliceJid, opkAmount: 1); final aliceSession = await OmemoSessionManager.generateNewIdentity(
final bobSession = await OmemoSessionManager.generateNewIdentity(bobJid, opkAmount: 1); aliceJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final bobSession = await OmemoSessionManager.generateNewIdentity(
bobJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
// Alice encrypts a message for Bob // Alice encrypts a message for Bob
final aliceMessage = await aliceSession.encryptToJid( final aliceMessage = await aliceSession.encryptToJid(
@ -225,7 +266,11 @@ void main() {
test('Test rotating the Signed Prekey', () async { test('Test rotating the Signed Prekey', () async {
// Generate the session // Generate the session
const aliceJid = 'alice@some.server'; const aliceJid = 'alice@some.server';
final aliceSession = await OmemoSessionManager.generateNewIdentity(aliceJid, opkAmount: 1); final aliceSession = await OmemoSessionManager.generateNewIdentity(
aliceJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
// Setup an event listener // Setup an event listener
final oldDevice = await aliceSession.getDevice(); final oldDevice = await aliceSession.getDevice();
@ -254,8 +299,16 @@ void main() {
const bobJid = 'bob@other.server.example'; const bobJid = 'bob@other.server.example';
// Alice and Bob generate their sessions // Alice and Bob generate their sessions
final aliceSession = await OmemoSessionManager.generateNewIdentity(aliceJid, opkAmount: 1); final aliceSession = await OmemoSessionManager.generateNewIdentity(
final bobSession = await OmemoSessionManager.generateNewIdentity(bobJid, opkAmount: 1); aliceJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final bobSession = await OmemoSessionManager.generateNewIdentity(
bobJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
// Alice encrypts a message for Bob // Alice encrypts a message for Bob
const messagePlaintext = 'Hello Bob!'; const messagePlaintext = 'Hello Bob!';

View File

@ -1,10 +1,15 @@
import 'package:omemo_dart/omemo_dart.dart'; import 'package:omemo_dart/omemo_dart.dart';
import 'package:omemo_dart/src/trust/always.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
test('Test serialising and deserialising the Device', () async { test('Test serialising and deserialising the Device', () async {
// Generate a random session // Generate a random session
final oldSession = await OmemoSessionManager.generateNewIdentity('user@test.server', opkAmount: 1); final oldSession = await OmemoSessionManager.generateNewIdentity(
'user@test.server',
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final oldDevice = await oldSession.getDevice(); final oldDevice = await oldSession.getDevice();
final serialised = await oldDevice.toJson(); final serialised = await oldDevice.toJson();
@ -14,7 +19,11 @@ void main() {
test('Test serialising and deserialising the Device after rotating the SPK', () async { test('Test serialising and deserialising the Device after rotating the SPK', () async {
// Generate a random session // Generate a random session
final oldSession = await OmemoSessionManager.generateNewIdentity('user@test.server', opkAmount: 1); final oldSession = await OmemoSessionManager.generateNewIdentity(
'user@test.server',
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final oldDevice = await (await oldSession.getDevice()).replaceSignedPrekey(); final oldDevice = await (await oldSession.getDevice()).replaceSignedPrekey();
final serialised = await oldDevice.toJson(); final serialised = await oldDevice.toJson();
@ -26,8 +35,16 @@ void main() {
// Generate a random ratchet // Generate a random ratchet
const aliceJid = 'alice@server.example'; const aliceJid = 'alice@server.example';
const bobJid = 'bob@other.server.example'; const bobJid = 'bob@other.server.example';
final aliceSession = await OmemoSessionManager.generateNewIdentity(aliceJid, opkAmount: 1); final aliceSession = await OmemoSessionManager.generateNewIdentity(
final bobSession = await OmemoSessionManager.generateNewIdentity(bobJid, opkAmount: 1); aliceJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final bobSession = await OmemoSessionManager.generateNewIdentity(
bobJid,
AlwaysTrustingTrustManager(),
opkAmount: 1,
);
final aliceMessage = await aliceSession.encryptToJid( final aliceMessage = await aliceSession.encryptToJid(
bobJid, bobJid,
'Hello Bob!', 'Hello Bob!',
@ -50,8 +67,16 @@ void main() {
test('Test serialising and deserialising the OmemoSessionManager', () async { test('Test serialising and deserialising the OmemoSessionManager', () async {
// Generate a random session // Generate a random session
final oldSession = await OmemoSessionManager.generateNewIdentity('a@server', opkAmount: 4); final oldSession = await OmemoSessionManager.generateNewIdentity(
final bobSession = await OmemoSessionManager.generateNewIdentity('b@other.server', opkAmount: 4); 'a@server',
AlwaysTrustingTrustManager(),
opkAmount: 4,
);
final bobSession = await OmemoSessionManager.generateNewIdentity(
'b@other.server',
AlwaysTrustingTrustManager(),
opkAmount: 4,
);
await oldSession.addSessionFromBundle( await oldSession.addSessionFromBundle(
'bob@localhost', 'bob@localhost',
(await bobSession.getDevice()).id, (await bobSession.getDevice()).id,
@ -60,7 +85,10 @@ void main() {
// Serialise and deserialise // Serialise and deserialise
final serialised = await oldSession.toJson(); final serialised = await oldSession.toJson();
final newSession = OmemoSessionManager.fromJson(serialised); final newSession = OmemoSessionManager.fromJson(
serialised,
AlwaysTrustingTrustManager(),
);
final oldDevice = await oldSession.getDevice(); final oldDevice = await oldSession.getDevice();
final newDevice = await newSession.getDevice(); final newDevice = await newSession.getDevice();