Allow requesting and updating of session keys with lower index and lower forwarded chain
This commit is contained in:
parent
9b00b9b158
commit
7ddb6be30e
|
@ -87,6 +87,7 @@ class Encryption {
|
||||||
if (event.type != EventTypes.Encrypted ||
|
if (event.type != EventTypes.Encrypted ||
|
||||||
event.content['ciphertext'] == null) return event;
|
event.content['ciphertext'] == null) return event;
|
||||||
Map<String, dynamic> decryptedPayload;
|
Map<String, dynamic> decryptedPayload;
|
||||||
|
var canRequestSession = false;
|
||||||
try {
|
try {
|
||||||
if (event.content['algorithm'] != 'm.megolm.v1.aes-sha2') {
|
if (event.content['algorithm'] != 'm.megolm.v1.aes-sha2') {
|
||||||
throw (DecryptError.UNKNOWN_ALGORITHM);
|
throw (DecryptError.UNKNOWN_ALGORITHM);
|
||||||
|
@ -96,6 +97,7 @@ class Encryption {
|
||||||
final inboundGroupSession =
|
final inboundGroupSession =
|
||||||
keyManager.getInboundGroupSession(roomId, sessionId, senderKey);
|
keyManager.getInboundGroupSession(roomId, sessionId, senderKey);
|
||||||
if (inboundGroupSession == null) {
|
if (inboundGroupSession == null) {
|
||||||
|
canRequestSession = true;
|
||||||
throw (DecryptError.UNKNOWN_SESSION);
|
throw (DecryptError.UNKNOWN_SESSION);
|
||||||
}
|
}
|
||||||
final decryptResult = inboundGroupSession.inboundGroupSession
|
final decryptResult = inboundGroupSession.inboundGroupSession
|
||||||
|
@ -122,6 +124,8 @@ class Encryption {
|
||||||
roomId,
|
roomId,
|
||||||
sessionId);
|
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);
|
decryptedPayload = json.decode(decryptResult.plaintext);
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
// alright, if this was actually by our own outbound group session, we might as well clear it
|
// 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']) {
|
event.content['session_id']) {
|
||||||
keyManager.clearOutboundGroupSession(roomId, wipe: true);
|
keyManager.clearOutboundGroupSession(roomId, wipe: true);
|
||||||
}
|
}
|
||||||
if (exception.toString() == DecryptError.UNKNOWN_SESSION) {
|
if (canRequestSession) {
|
||||||
decryptedPayload = {
|
decryptedPayload = {
|
||||||
'content': event.content,
|
'content': event.content,
|
||||||
'type': EventTypes.Encrypted,
|
'type': EventTypes.Encrypted,
|
||||||
};
|
};
|
||||||
decryptedPayload['content']['body'] = exception.toString();
|
decryptedPayload['content']['body'] = exception.toString();
|
||||||
decryptedPayload['content']['msgtype'] = 'm.bad.encrypted';
|
decryptedPayload['content']['msgtype'] = 'm.bad.encrypted';
|
||||||
|
decryptedPayload['content']['can_request_session'] = true;
|
||||||
} else {
|
} else {
|
||||||
decryptedPayload = {
|
decryptedPayload = {
|
||||||
'content': <String, dynamic>{
|
'content': <String, dynamic>{
|
||||||
|
|
|
@ -49,9 +49,6 @@ class KeyManager {
|
||||||
{bool forwarded = false}) {
|
{bool forwarded = false}) {
|
||||||
final oldSession =
|
final oldSession =
|
||||||
getInboundGroupSession(roomId, sessionId, senderKey, otherRooms: false);
|
getInboundGroupSession(roomId, sessionId, senderKey, otherRooms: false);
|
||||||
if (oldSession != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (content['algorithm'] != 'm.megolm.v1.aes-sha2') {
|
if (content['algorithm'] != 'm.megolm.v1.aes-sha2') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -69,15 +66,31 @@ class KeyManager {
|
||||||
'[LibOlm] Could not create new InboundGroupSession: ' + e.toString());
|
'[LibOlm] Could not create new InboundGroupSession: ' + e.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!_inboundGroupSessions.containsKey(roomId)) {
|
final newSession = SessionKey(
|
||||||
_inboundGroupSessions[roomId] = <String, SessionKey>{};
|
|
||||||
}
|
|
||||||
_inboundGroupSessions[roomId][sessionId] = SessionKey(
|
|
||||||
content: content,
|
content: content,
|
||||||
inboundGroupSession: inboundGroupSession,
|
inboundGroupSession: inboundGroupSession,
|
||||||
indexes: {},
|
indexes: {},
|
||||||
key: client.userID,
|
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.database?.storeInboundGroupSession(
|
||||||
client.id,
|
client.id,
|
||||||
roomId,
|
roomId,
|
||||||
|
|
|
@ -341,8 +341,8 @@ class Event extends MatrixEvent {
|
||||||
Future<void> requestKey() async {
|
Future<void> requestKey() async {
|
||||||
if (type != EventTypes.Encrypted ||
|
if (type != EventTypes.Encrypted ||
|
||||||
messageType != MessageTypes.BadEncrypted ||
|
messageType != MessageTypes.BadEncrypted ||
|
||||||
content['body'] != DecryptError.UNKNOWN_SESSION) {
|
content['can_request_session'] != true) {
|
||||||
throw ('Session key not unknown');
|
throw ('Session key not requestable');
|
||||||
}
|
}
|
||||||
await room.requestSessionKey(content['session_id'], content['sender_key']);
|
await room.requestSessionKey(content['session_id'], content['sender_key']);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:famedlysdk/matrix_api.dart';
|
import 'package:famedlysdk/matrix_api.dart';
|
||||||
import 'package:famedlysdk/encryption.dart';
|
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:famedlysdk/src/client.dart';
|
import 'package:famedlysdk/src/client.dart';
|
||||||
import 'package:famedlysdk/src/event.dart';
|
import 'package:famedlysdk/src/event.dart';
|
||||||
|
@ -996,7 +995,7 @@ class Room {
|
||||||
await client.database.transaction(() async {
|
await client.database.transaction(() async {
|
||||||
for (var i = 0; i < events.length; i++) {
|
for (var i = 0; i < events.length; i++) {
|
||||||
if (events[i].type == EventTypes.Encrypted &&
|
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
|
events[i] = await client.encryption
|
||||||
.decryptRoomEvent(id, events[i], store: true);
|
.decryptRoomEvent(id, events[i], store: true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:famedlysdk/matrix_api.dart';
|
import 'package:famedlysdk/matrix_api.dart';
|
||||||
import 'package:famedlysdk/encryption.dart';
|
|
||||||
|
|
||||||
import 'event.dart';
|
import 'event.dart';
|
||||||
import 'room.dart';
|
import 'room.dart';
|
||||||
|
@ -104,7 +103,7 @@ class Timeline {
|
||||||
for (var i = 0; i < events.length; i++) {
|
for (var i = 0; i < events.length; i++) {
|
||||||
if (events[i].type == EventTypes.Encrypted &&
|
if (events[i].type == EventTypes.Encrypted &&
|
||||||
events[i].messageType == MessageTypes.BadEncrypted &&
|
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].content['session_id'] == sessionId) {
|
||||||
events[i] = await room.client.encryption
|
events[i] = await room.client.encryption
|
||||||
.decryptRoomEvent(room.id, events[i], store: true);
|
.decryptRoomEvent(room.id, events[i], store: true);
|
||||||
|
|
|
@ -216,6 +216,148 @@ void main() {
|
||||||
true);
|
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 {
|
test('dispose client', () async {
|
||||||
await client.dispose(closeDatabase: true);
|
await client.dispose(closeDatabase: true);
|
||||||
});
|
});
|
||||||
|
|
|
@ -237,9 +237,9 @@ void main() {
|
||||||
try {
|
try {
|
||||||
await event.requestKey();
|
await event.requestKey();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
exception = e;
|
exception = e.toString();
|
||||||
}
|
}
|
||||||
expect(exception, 'Session key not unknown');
|
expect(exception, 'Session key not requestable');
|
||||||
|
|
||||||
var event2 = Event.fromJson({
|
var event2 = Event.fromJson({
|
||||||
'event_id': id,
|
'event_id': id,
|
||||||
|
@ -251,6 +251,7 @@ void main() {
|
||||||
'content': json.encode({
|
'content': json.encode({
|
||||||
'msgtype': 'm.bad.encrypted',
|
'msgtype': 'm.bad.encrypted',
|
||||||
'body': DecryptError.UNKNOWN_SESSION,
|
'body': DecryptError.UNKNOWN_SESSION,
|
||||||
|
'can_request_session': true,
|
||||||
'algorithm': 'm.megolm.v1.aes-sha2',
|
'algorithm': 'm.megolm.v1.aes-sha2',
|
||||||
'ciphertext': 'AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...',
|
'ciphertext': 'AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...',
|
||||||
'device_id': 'RJYKSTBOIE',
|
'device_id': 'RJYKSTBOIE',
|
||||||
|
|
Loading…
Reference in a new issue