feat: Periodically fetch ssss secrets from other devices

This commit is contained in:
Sorunome 2020-09-10 13:19:05 +02:00
parent 2c7ae759f8
commit cb1ec86b32
No known key found for this signature in database
GPG key ID: B19471D07FC9BE9C
5 changed files with 79 additions and 10 deletions

View file

@ -96,6 +96,10 @@ class Encryption {
// some ssss thing. We can do this in the background
unawaited(Zone.root.run(() => ssss.handleToDeviceEvent(event)));
}
if (event.sender == client.userID) {
// maybe we need to re-try SSSS secrets
unawaited(Zone.root.run(() => ssss.periodicallyRequestMissingCache()));
}
}
Future<void> handleEventUpdate(EventUpdate update) async {
@ -111,6 +115,11 @@ class Encryption {
unawaited(Zone.root
.run(() => keyVerificationManager.handleEventUpdate(update)));
}
if (update.content['sender'] == client.userID &&
!update.content['unsigned'].containsKey('transaction_id')) {
// maybe we need to re-try SSSS secrets
unawaited(Zone.root.run(() => ssss.periodicallyRequestMissingCache()));
}
}
Future<ToDeviceEvent> decryptToDeviceEvent(ToDeviceEvent event) async {

View file

@ -451,10 +451,15 @@ class KeyManager {
try {
await loadSingleKey(room.id, sessionId);
} catch (err, stacktrace) {
Logs.error(
'[KeyManager] Failed to access online key backup: ' +
err.toString(),
stacktrace);
if (err is MatrixException && err.errcode == 'M_NOT_FOUND') {
Logs.info(
'[KeyManager] Key not in online key backup, requesting it from other devices...');
} else {
Logs.error(
'[KeyManager] Failed to access online key backup: ' +
err.toString(),
stacktrace);
}
}
if (!hadPreviously &&
getInboundGroupSession(room.id, sessionId, senderKey) != null) {

View file

@ -258,18 +258,27 @@ class SSSS {
}
}
Future<void> maybeRequestAll(List<DeviceKeys> devices) async {
Future<void> maybeRequestAll([List<DeviceKeys> devices]) async {
for (final type in CACHE_TYPES) {
final secret = await getCached(type);
if (secret == null) {
await request(type, devices);
if (keyIdsFromType(type) != null) {
final secret = await getCached(type);
if (secret == null) {
await request(type, devices);
}
}
}
}
Future<void> request(String type, List<DeviceKeys> devices) async {
Future<void> request(String type, [List<DeviceKeys> devices]) async {
// only send to own, verified devices
Logs.info('[SSSS] Requesting type ${type}...');
if (devices == null || devices.isEmpty) {
if (!client.userDeviceKeys.containsKey(client.userID)) {
Logs.warning('[SSSS] User does not have any devices');
return;
}
devices = client.userDeviceKeys[client.userID].deviceKeys.values.toList();
}
devices.removeWhere((DeviceKeys d) =>
d.userId != client.userID ||
!d.verified ||
@ -294,6 +303,27 @@ class SSSS {
});
}
DateTime _lastCacheRequest;
bool _isPeriodicallyRequestingMissingCache = false;
Future<void> periodicallyRequestMissingCache() async {
if (_isPeriodicallyRequestingMissingCache ||
(_lastCacheRequest != null &&
DateTime.now()
.subtract(Duration(minutes: 15))
.isBefore(_lastCacheRequest)) ||
client.isUnknownSession) {
// we are already requesting right now or we attempted to within the last 15 min
return;
}
_lastCacheRequest = DateTime.now();
_isPeriodicallyRequestingMissingCache = true;
try {
await maybeRequestAll();
} finally {
_isPeriodicallyRequestingMissingCache = false;
}
}
Future<void> handleToDeviceEvent(ToDeviceEvent event) async {
if (event.type == 'm.secret.request') {
// got a request to share a secret

View file

@ -32,7 +32,7 @@ class MockSSSS extends SSSS {
bool requestedSecrets = false;
@override
Future<void> maybeRequestAll(List<DeviceKeys> devices) async {
Future<void> maybeRequestAll([List<DeviceKeys> devices]) async {
requestedSecrets = true;
final handle = open();
handle.unlock(recoveryKey: SSSS_KEY);

View file

@ -30,6 +30,19 @@ import 'package:olm/olm.dart' as olm;
import '../fake_client.dart';
import '../fake_matrix_api.dart';
class MockSSSS extends SSSS {
MockSSSS(Encryption encryption) : super(encryption);
bool requestedSecrets = false;
@override
Future<void> maybeRequestAll([List<DeviceKeys> devices]) async {
requestedSecrets = true;
final handle = open();
handle.unlock(recoveryKey: SSSS_KEY);
await handle.maybeCacheAll();
}
}
void main() {
group('SSSS', () {
var olmEnabled = true;
@ -392,6 +405,18 @@ void main() {
expect(client.encryption.ssss.pendingShareRequests.length, 3);
});
test('periodicallyRequestMissingCache', () async {
client.userDeviceKeys[client.userID].masterKey.setDirectVerified(true);
client.encryption.ssss = MockSSSS(client.encryption);
(client.encryption.ssss as MockSSSS).requestedSecrets = false;
await client.encryption.ssss.periodicallyRequestMissingCache();
expect((client.encryption.ssss as MockSSSS).requestedSecrets, true);
// it should only retry once every 15 min
(client.encryption.ssss as MockSSSS).requestedSecrets = false;
await client.encryption.ssss.periodicallyRequestMissingCache();
expect((client.encryption.ssss as MockSSSS).requestedSecrets, false);
});
test('dispose client', () async {
await client.dispose(closeDatabase: true);
});