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
|
// some ssss thing. We can do this in the background
|
||||||
unawaited(Zone.root.run(() => ssss.handleToDeviceEvent(event)));
|
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 {
|
Future<void> handleEventUpdate(EventUpdate update) async {
|
||||||
|
@ -111,6 +115,11 @@ class Encryption {
|
||||||
unawaited(Zone.root
|
unawaited(Zone.root
|
||||||
.run(() => keyVerificationManager.handleEventUpdate(update)));
|
.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 {
|
Future<ToDeviceEvent> decryptToDeviceEvent(ToDeviceEvent event) async {
|
||||||
|
|
|
@ -451,10 +451,15 @@ class KeyManager {
|
||||||
try {
|
try {
|
||||||
await loadSingleKey(room.id, sessionId);
|
await loadSingleKey(room.id, sessionId);
|
||||||
} catch (err, stacktrace) {
|
} catch (err, stacktrace) {
|
||||||
Logs.error(
|
if (err is MatrixException && err.errcode == 'M_NOT_FOUND') {
|
||||||
'[KeyManager] Failed to access online key backup: ' +
|
Logs.info(
|
||||||
err.toString(),
|
'[KeyManager] Key not in online key backup, requesting it from other devices...');
|
||||||
stacktrace);
|
} else {
|
||||||
|
Logs.error(
|
||||||
|
'[KeyManager] Failed to access online key backup: ' +
|
||||||
|
err.toString(),
|
||||||
|
stacktrace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!hadPreviously &&
|
if (!hadPreviously &&
|
||||||
getInboundGroupSession(room.id, sessionId, senderKey) != null) {
|
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) {
|
for (final type in CACHE_TYPES) {
|
||||||
final secret = await getCached(type);
|
if (keyIdsFromType(type) != null) {
|
||||||
if (secret == null) {
|
final secret = await getCached(type);
|
||||||
await request(type, devices);
|
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
|
// only send to own, verified devices
|
||||||
Logs.info('[SSSS] Requesting type ${type}...');
|
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) =>
|
devices.removeWhere((DeviceKeys d) =>
|
||||||
d.userId != client.userID ||
|
d.userId != client.userID ||
|
||||||
!d.verified ||
|
!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 {
|
Future<void> handleToDeviceEvent(ToDeviceEvent event) async {
|
||||||
if (event.type == 'm.secret.request') {
|
if (event.type == 'm.secret.request') {
|
||||||
// got a request to share a secret
|
// got a request to share a secret
|
||||||
|
|
|
@ -32,7 +32,7 @@ class MockSSSS extends SSSS {
|
||||||
|
|
||||||
bool requestedSecrets = false;
|
bool requestedSecrets = false;
|
||||||
@override
|
@override
|
||||||
Future<void> maybeRequestAll(List<DeviceKeys> devices) async {
|
Future<void> maybeRequestAll([List<DeviceKeys> devices]) async {
|
||||||
requestedSecrets = true;
|
requestedSecrets = true;
|
||||||
final handle = open();
|
final handle = open();
|
||||||
handle.unlock(recoveryKey: SSSS_KEY);
|
handle.unlock(recoveryKey: SSSS_KEY);
|
||||||
|
|
|
@ -30,6 +30,19 @@ import 'package:olm/olm.dart' as olm;
|
||||||
import '../fake_client.dart';
|
import '../fake_client.dart';
|
||||||
import '../fake_matrix_api.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() {
|
void main() {
|
||||||
group('SSSS', () {
|
group('SSSS', () {
|
||||||
var olmEnabled = true;
|
var olmEnabled = true;
|
||||||
|
@ -392,6 +405,18 @@ void main() {
|
||||||
expect(client.encryption.ssss.pendingShareRequests.length, 3);
|
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 {
|
test('dispose client', () async {
|
||||||
await client.dispose(closeDatabase: true);
|
await client.dispose(closeDatabase: true);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue