Allow requesting and updating of session keys with lower index and lower forwarded chain

This commit is contained in:
Sorunome 2020-06-10 10:44:22 +02:00
parent 9b00b9b158
commit 7ddb6be30e
No known key found for this signature in database
GPG key ID: B19471D07FC9BE9C
7 changed files with 175 additions and 16 deletions

View file

@ -87,6 +87,7 @@ class Encryption {
if (event.type != EventTypes.Encrypted ||
event.content['ciphertext'] == null) return event;
Map<String, dynamic> decryptedPayload;
var canRequestSession = false;
try {
if (event.content['algorithm'] != 'm.megolm.v1.aes-sha2') {
throw (DecryptError.UNKNOWN_ALGORITHM);
@ -96,6 +97,7 @@ class Encryption {
final inboundGroupSession =
keyManager.getInboundGroupSession(roomId, sessionId, senderKey);
if (inboundGroupSession == null) {
canRequestSession = true;
throw (DecryptError.UNKNOWN_SESSION);
}
final decryptResult = inboundGroupSession.inboundGroupSession
@ -122,6 +124,8 @@ class Encryption {
roomId,
sessionId);
}
// decrypt errors here may mean we have a bad session key - others might have a better one
canRequestSession = true;
decryptedPayload = json.decode(decryptResult.plaintext);
} catch (exception) {
// alright, if this was actually by our own outbound group session, we might as well clear it
@ -134,13 +138,14 @@ class Encryption {
event.content['session_id']) {
keyManager.clearOutboundGroupSession(roomId, wipe: true);
}
if (exception.toString() == DecryptError.UNKNOWN_SESSION) {
if (canRequestSession) {
decryptedPayload = {
'content': event.content,
'type': EventTypes.Encrypted,
};
decryptedPayload['content']['body'] = exception.toString();
decryptedPayload['content']['msgtype'] = 'm.bad.encrypted';
decryptedPayload['content']['can_request_session'] = true;
} else {
decryptedPayload = {
'content': <String, dynamic>{

View file

@ -49,9 +49,6 @@ class KeyManager {
{bool forwarded = false}) {
final oldSession =
getInboundGroupSession(roomId, sessionId, senderKey, otherRooms: false);
if (oldSession != null) {
return;
}
if (content['algorithm'] != 'm.megolm.v1.aes-sha2') {
return;
}
@ -69,15 +66,31 @@ class KeyManager {
'[LibOlm] Could not create new InboundGroupSession: ' + e.toString());
return;
}
if (!_inboundGroupSessions.containsKey(roomId)) {
_inboundGroupSessions[roomId] = <String, SessionKey>{};
}
_inboundGroupSessions[roomId][sessionId] = SessionKey(
final newSession = SessionKey(
content: content,
inboundGroupSession: inboundGroupSession,
indexes: {},
key: client.userID,
);
final oldFirstIndex =
oldSession?.inboundGroupSession?.first_known_index() ?? 0;
final newFirstIndex = newSession.inboundGroupSession.first_known_index();
if (oldSession == null ||
newFirstIndex < oldFirstIndex ||
(oldFirstIndex == newFirstIndex &&
newSession.forwardingCurve25519KeyChain.length <
oldSession.forwardingCurve25519KeyChain.length)) {
// use new session
oldSession?.dispose();
} else {
// we are gonna keep our old session
newSession.dispose();
return;
}
if (!_inboundGroupSessions.containsKey(roomId)) {
_inboundGroupSessions[roomId] = <String, SessionKey>{};
}
_inboundGroupSessions[roomId][sessionId] = newSession;
client.database?.storeInboundGroupSession(
client.id,
roomId,

View file

@ -341,8 +341,8 @@ class Event extends MatrixEvent {
Future<void> requestKey() async {
if (type != EventTypes.Encrypted ||
messageType != MessageTypes.BadEncrypted ||
content['body'] != DecryptError.UNKNOWN_SESSION) {
throw ('Session key not unknown');
content['can_request_session'] != true) {
throw ('Session key not requestable');
}
await room.requestSessionKey(content['session_id'], content['sender_key']);
return;

View file

@ -19,7 +19,6 @@
import 'dart:async';
import 'package:famedlysdk/matrix_api.dart';
import 'package:famedlysdk/encryption.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:famedlysdk/src/client.dart';
import 'package:famedlysdk/src/event.dart';
@ -996,7 +995,7 @@ class Room {
await client.database.transaction(() async {
for (var i = 0; i < events.length; i++) {
if (events[i].type == EventTypes.Encrypted &&
events[i].content['body'] == DecryptError.UNKNOWN_SESSION) {
events[i].content['can_request_session'] == true) {
events[i] = await client.encryption
.decryptRoomEvent(id, events[i], store: true);
}

View file

@ -19,7 +19,6 @@
import 'dart:async';
import 'package:famedlysdk/matrix_api.dart';
import 'package:famedlysdk/encryption.dart';
import 'event.dart';
import 'room.dart';
@ -104,7 +103,7 @@ class Timeline {
for (var i = 0; i < events.length; i++) {
if (events[i].type == EventTypes.Encrypted &&
events[i].messageType == MessageTypes.BadEncrypted &&
events[i].content['body'] == DecryptError.UNKNOWN_SESSION &&
events[i].content['can_request_session'] == true &&
events[i].content['session_id'] == sessionId) {
events[i] = await room.client.encryption
.decryptRoomEvent(room.id, events[i], store: true);

View file

@ -216,6 +216,148 @@ void main() {
true);
});
test('setInboundGroupSession', () async {
final session = olm.OutboundGroupSession();
session.create();
final inbound = olm.InboundGroupSession();
inbound.create(session.session_key());
final senderKey = client.identityKey;
final roomId = '!someroom:example.org';
final sessionId = inbound.session_id();
// set a payload...
var sessionPayload = <String, dynamic>{
'algorithm': 'm.megolm.v1.aes-sha2',
'room_id': roomId,
'forwarding_curve25519_key_chain': [client.identityKey],
'session_id': sessionId,
'session_key': inbound.export_session(1),
'sender_key': senderKey,
'sender_claimed_ed25519_key': client.fingerprintKey,
};
client.encryption.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload,
forwarded: true);
expect(
client.encryption.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey)
.inboundGroupSession
.first_known_index(),
1);
expect(
client.encryption.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey)
.forwardingCurve25519KeyChain
.length,
1);
// not set one with a higher first known index
sessionPayload = <String, dynamic>{
'algorithm': 'm.megolm.v1.aes-sha2',
'room_id': roomId,
'forwarding_curve25519_key_chain': [client.identityKey],
'session_id': sessionId,
'session_key': inbound.export_session(2),
'sender_key': senderKey,
'sender_claimed_ed25519_key': client.fingerprintKey,
};
client.encryption.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload,
forwarded: true);
expect(
client.encryption.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey)
.inboundGroupSession
.first_known_index(),
1);
expect(
client.encryption.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey)
.forwardingCurve25519KeyChain
.length,
1);
// set one with a lower first known index
sessionPayload = <String, dynamic>{
'algorithm': 'm.megolm.v1.aes-sha2',
'room_id': roomId,
'forwarding_curve25519_key_chain': [client.identityKey],
'session_id': sessionId,
'session_key': inbound.export_session(0),
'sender_key': senderKey,
'sender_claimed_ed25519_key': client.fingerprintKey,
};
client.encryption.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload,
forwarded: true);
expect(
client.encryption.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey)
.inboundGroupSession
.first_known_index(),
0);
expect(
client.encryption.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey)
.forwardingCurve25519KeyChain
.length,
1);
// not set one with a longer forwarding chain
sessionPayload = <String, dynamic>{
'algorithm': 'm.megolm.v1.aes-sha2',
'room_id': roomId,
'forwarding_curve25519_key_chain': [client.identityKey, 'beep'],
'session_id': sessionId,
'session_key': inbound.export_session(0),
'sender_key': senderKey,
'sender_claimed_ed25519_key': client.fingerprintKey,
};
client.encryption.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload,
forwarded: true);
expect(
client.encryption.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey)
.inboundGroupSession
.first_known_index(),
0);
expect(
client.encryption.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey)
.forwardingCurve25519KeyChain
.length,
1);
// set one with a shorter forwarding chain
sessionPayload = <String, dynamic>{
'algorithm': 'm.megolm.v1.aes-sha2',
'room_id': roomId,
'forwarding_curve25519_key_chain': [],
'session_id': sessionId,
'session_key': inbound.export_session(0),
'sender_key': senderKey,
'sender_claimed_ed25519_key': client.fingerprintKey,
};
client.encryption.keyManager.setInboundGroupSession(
roomId, sessionId, senderKey, sessionPayload,
forwarded: true);
expect(
client.encryption.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey)
.inboundGroupSession
.first_known_index(),
0);
expect(
client.encryption.keyManager
.getInboundGroupSession(roomId, sessionId, senderKey)
.forwardingCurve25519KeyChain
.length,
0);
inbound.free();
session.free();
});
test('dispose client', () async {
await client.dispose(closeDatabase: true);
});

View file

@ -237,9 +237,9 @@ void main() {
try {
await event.requestKey();
} catch (e) {
exception = e;
exception = e.toString();
}
expect(exception, 'Session key not unknown');
expect(exception, 'Session key not requestable');
var event2 = Event.fromJson({
'event_id': id,
@ -251,6 +251,7 @@ void main() {
'content': json.encode({
'msgtype': 'm.bad.encrypted',
'body': DecryptError.UNKNOWN_SESSION,
'can_request_session': true,
'algorithm': 'm.megolm.v1.aes-sha2',
'ciphertext': 'AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...',
'device_id': 'RJYKSTBOIE',