feat: Periodically fetch ssss secrets from other devices
This commit is contained in:
parent
2c7ae759f8
commit
cb1ec86b32
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue