feat: Start megolm sessions while typing

This commit is contained in:
Sorunome 2020-12-28 16:03:56 +01:00
parent bae9299fa1
commit 68afe362ce
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
3 changed files with 65 additions and 4 deletions

View File

@ -271,7 +271,7 @@ class KeyManager {
/// devices have been changed. Returns false if the session has not been cleared because /// devices have been changed. Returns false if the session has not been cleared because
/// it wasn't necessary. Otherwise returns true. /// it wasn't necessary. Otherwise returns true.
Future<bool> clearOrUseOutboundGroupSession(String roomId, Future<bool> clearOrUseOutboundGroupSession(String roomId,
{bool wipe = false}) async { {bool wipe = false, bool use = true}) async {
final room = client.getRoomById(roomId); final room = client.getRoomById(roomId);
final sess = getOutboundGroupSession(roomId); final sess = getOutboundGroupSession(roomId);
if (room == null || sess == null) { if (room == null || sess == null) {
@ -334,8 +334,13 @@ class KeyManager {
break; break;
} }
// and now add all the new devices! // and now add all the new devices!
final oldDeviceIds = Set.from(sess.devices[userId].keys); final oldDeviceIds = Set.from(sess.devices[userId].entries
final newDeviceIds = Set.from(newDeviceKeyIds[userId].keys); .where((e) => !e.value)
.map((e) => e.key));
final newDeviceIds = Set.from(newDeviceKeyIds[userId]
.entries
.where((e) => !e.value)
.map((e) => e.key));
final newDevices = newDeviceIds.difference(oldDeviceIds); final newDevices = newDeviceIds.difference(oldDeviceIds);
if (newDeviceIds.isNotEmpty) { if (newDeviceIds.isNotEmpty) {
devicesToReceive.addAll(newDeviceKeys.where( devicesToReceive.addAll(newDeviceKeys.where(
@ -345,6 +350,9 @@ class KeyManager {
} }
if (!wipe) { if (!wipe) {
if (!use) {
return false;
}
// okay, we use the outbound group session! // okay, we use the outbound group session!
sess.sentMessages++; sess.sentMessages++;
sess.devices = newDeviceKeyIds; sess.devices = newDeviceKeyIds;
@ -355,7 +363,7 @@ class KeyManager {
'session_key': sess.outboundGroupSession.session_key(), 'session_key': sess.outboundGroupSession.session_key(),
}; };
try { try {
devicesToReceive.removeWhere((k) => k.blocked); devicesToReceive.removeWhere((k) => !k.encryptToDevice);
if (devicesToReceive.isNotEmpty) { if (devicesToReceive.isNotEmpty) {
// update allowedAtIndex // update allowedAtIndex
for (final device in devicesToReceive) { for (final device in devicesToReceive) {
@ -394,6 +402,7 @@ class KeyManager {
return true; return true;
} }
/// Store an outbound group session in the database
Future<void> storeOutboundGroupSession( Future<void> storeOutboundGroupSession(
String roomId, OutboundGroupSession sess) async { String roomId, OutboundGroupSession sess) async {
if (sess == null) { if (sess == null) {
@ -411,6 +420,7 @@ class KeyManager {
final Map<String, Future<OutboundGroupSession>> final Map<String, Future<OutboundGroupSession>>
_pendingNewOutboundGroupSessions = {}; _pendingNewOutboundGroupSessions = {};
/// Creates an outbound group session for a given room id
Future<OutboundGroupSession> createOutboundGroupSession(String roomId) async { Future<OutboundGroupSession> createOutboundGroupSession(String roomId) async {
if (_pendingNewOutboundGroupSessions.containsKey(roomId)) { if (_pendingNewOutboundGroupSessions.containsKey(roomId)) {
return _pendingNewOutboundGroupSessions[roomId]; return _pendingNewOutboundGroupSessions[roomId];
@ -421,6 +431,18 @@ class KeyManager {
return _pendingNewOutboundGroupSessions.remove(roomId); return _pendingNewOutboundGroupSessions.remove(roomId);
} }
/// Prepares an outbound group session for a given room ID. That is, load it from
/// the database, cycle it if needed and create it if absent.
Future<void> prepareOutboundGroupSession(String roomId) async {
if (getOutboundGroupSession(roomId) == null) {
await loadOutboundGroupSession(roomId);
}
await clearOrUseOutboundGroupSession(roomId, use: false);
if (getOutboundGroupSession(roomId) == null) {
await createOutboundGroupSession(roomId);
}
}
Future<OutboundGroupSession> _createOutboundGroupSession( Future<OutboundGroupSession> _createOutboundGroupSession(
String roomId) async { String roomId) async {
await clearOrUseOutboundGroupSession(roomId, wipe: true); await clearOrUseOutboundGroupSession(roomId, wipe: true);
@ -477,10 +499,12 @@ class KeyManager {
return sess; return sess;
} }
/// Get an outbound group session for a room id
OutboundGroupSession getOutboundGroupSession(String roomId) { OutboundGroupSession getOutboundGroupSession(String roomId) {
return _outboundGroupSessions[roomId]; return _outboundGroupSessions[roomId];
} }
/// Load an outbound group session from database
Future<void> loadOutboundGroupSession(String roomId) async { Future<void> loadOutboundGroupSession(String roomId) async {
if (_loadedOutboundGroupSessions.contains(roomId) || if (_loadedOutboundGroupSessions.contains(roomId) ||
_outboundGroupSessions.containsKey(roomId) || _outboundGroupSessions.containsKey(roomId) ||

View File

@ -22,6 +22,7 @@ import 'dart:core';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:pedantic/pedantic.dart';
import '../encryption.dart'; import '../encryption.dart';
import '../famedlysdk.dart'; import '../famedlysdk.dart';
@ -542,6 +543,22 @@ class Client extends MatrixApi {
return mxc; return mxc;
} }
/// Sends a typing notification and initiates a megolm session, if needed
@override
Future<void> sendTypingNotification(
String userId,
String roomId,
bool typing, {
int timeout,
}) async {
await super
.sendTypingNotification(userId, roomId, typing, timeout: timeout);
final room = getRoomById(roomId);
if (typing && room != null && encryptionEnabled && room.encrypted) {
unawaited(encryption.keyManager.prepareOutboundGroupSession(roomId));
}
}
/// Uploads a new user avatar for this user. /// Uploads a new user avatar for this user.
Future<void> setAvatar(MatrixFile file) async { Future<void> setAvatar(MatrixFile file) async {
final uploadResp = await upload(file.bytes, file.name); final uploadResp = await upload(file.bytes, file.name);

View File

@ -133,6 +133,26 @@ void main() {
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS'] client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS']
.blocked = false; .blocked = false;
// lazy-create if it would rotate
sess =
await client.encryption.keyManager.createOutboundGroupSession(roomId);
final oldSessKey = sess.outboundGroupSession.session_key();
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS']
.blocked = true;
await client.encryption.keyManager.prepareOutboundGroupSession(roomId);
expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
true);
expect(
client.encryption.keyManager
.getOutboundGroupSession(roomId)
.outboundGroupSession
.session_key() !=
oldSessKey,
true);
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS']
.blocked = false;
// rotate if too far in the past // rotate if too far in the past
sess = sess =
await client.encryption.keyManager.createOutboundGroupSession(roomId); await client.encryption.keyManager.createOutboundGroupSession(roomId);