Merge branch 'soru/olm-session-recovery' into 'master'
Adds olm session recovery See merge request famedly/famedlysdk!355
This commit is contained in:
commit
62f63ebf1f
|
@ -18,11 +18,13 @@
|
|||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:canonical_json/canonical_json.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:famedlysdk/matrix_api.dart';
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import './encryption.dart';
|
||||
import './utils/olm_session.dart';
|
||||
|
||||
class OlmManager {
|
||||
final Encryption encryption;
|
||||
|
@ -43,8 +45,8 @@ class OlmManager {
|
|||
OlmManager(this.encryption);
|
||||
|
||||
/// A map from Curve25519 identity keys to existing olm sessions.
|
||||
Map<String, List<olm.Session>> get olmSessions => _olmSessions;
|
||||
final Map<String, List<olm.Session>> _olmSessions = {};
|
||||
Map<String, List<OlmSession>> get olmSessions => _olmSessions;
|
||||
final Map<String, List<OlmSession>> _olmSessions = {};
|
||||
|
||||
Future<void> init(String olmAccount) async {
|
||||
if (olmAccount == null) {
|
||||
|
@ -204,25 +206,24 @@ class OlmManager {
|
|||
}
|
||||
}
|
||||
|
||||
void storeOlmSession(String curve25519IdentityKey, olm.Session session) {
|
||||
void storeOlmSession(OlmSession session) {
|
||||
if (client.database == null) {
|
||||
return;
|
||||
}
|
||||
if (!_olmSessions.containsKey(curve25519IdentityKey)) {
|
||||
_olmSessions[curve25519IdentityKey] = [];
|
||||
if (!_olmSessions.containsKey(session.identityKey)) {
|
||||
_olmSessions[session.identityKey] = [];
|
||||
}
|
||||
final ix = _olmSessions[curve25519IdentityKey]
|
||||
.indexWhere((s) => s.session_id() == session.session_id());
|
||||
final ix = _olmSessions[session.identityKey]
|
||||
.indexWhere((s) => s.sessionId == session.sessionId);
|
||||
if (ix == -1) {
|
||||
// add a new session
|
||||
_olmSessions[curve25519IdentityKey].add(session);
|
||||
_olmSessions[session.identityKey].add(session);
|
||||
} else {
|
||||
// update an existing session
|
||||
_olmSessions[curve25519IdentityKey][ix] = session;
|
||||
_olmSessions[session.identityKey][ix] = session;
|
||||
}
|
||||
final pickle = session.pickle(client.userID);
|
||||
client.database.storeOlmSession(
|
||||
client.id, curve25519IdentityKey, session.session_id(), pickle);
|
||||
client.database.storeOlmSession(client.id, session.identityKey,
|
||||
session.sessionId, session.pickledSession, session.lastReceived);
|
||||
}
|
||||
|
||||
ToDeviceEvent _decryptToDeviceEvent(ToDeviceEvent event) {
|
||||
|
@ -245,14 +246,16 @@ class OlmManager {
|
|||
var existingSessions = olmSessions[senderKey];
|
||||
if (existingSessions != null) {
|
||||
for (var session in existingSessions) {
|
||||
if (type == 0 && session.matches_inbound(body) == true) {
|
||||
plaintext = session.decrypt(type, body);
|
||||
storeOlmSession(senderKey, session);
|
||||
if (type == 0 && session.session.matches_inbound(body) == true) {
|
||||
plaintext = session.session.decrypt(type, body);
|
||||
session.lastReceived = DateTime.now();
|
||||
storeOlmSession(session);
|
||||
break;
|
||||
} else if (type == 1) {
|
||||
try {
|
||||
plaintext = session.decrypt(type, body);
|
||||
storeOlmSession(senderKey, session);
|
||||
plaintext = session.session.decrypt(type, body);
|
||||
session.lastReceived = DateTime.now();
|
||||
storeOlmSession(session);
|
||||
break;
|
||||
} catch (_) {
|
||||
plaintext = null;
|
||||
|
@ -271,7 +274,13 @@ class OlmManager {
|
|||
_olmAccount.remove_one_time_keys(newSession);
|
||||
client.database?.updateClientKeys(pickledOlmAccount, client.id);
|
||||
plaintext = newSession.decrypt(type, body);
|
||||
storeOlmSession(senderKey, newSession);
|
||||
storeOlmSession(OlmSession(
|
||||
key: client.userID,
|
||||
identityKey: senderKey,
|
||||
sessionId: newSession.session_id(),
|
||||
session: newSession,
|
||||
lastReceived: DateTime.now(),
|
||||
));
|
||||
} catch (_) {
|
||||
newSession?.free();
|
||||
rethrow;
|
||||
|
@ -299,6 +308,35 @@ class OlmManager {
|
|||
);
|
||||
}
|
||||
|
||||
Future<List<OlmSession>> getOlmSessionsFromDatabase(String senderKey) async {
|
||||
if (client.database == null) {
|
||||
return [];
|
||||
}
|
||||
final rows =
|
||||
await client.database.dbGetOlmSessions(client.id, senderKey).get();
|
||||
final res = <OlmSession>[];
|
||||
for (final row in rows) {
|
||||
final sess = OlmSession.fromDb(row, client.userID);
|
||||
if (sess.isValid) {
|
||||
res.add(sess);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<void> restoreOlmSession(String userId, String senderKey) async {
|
||||
if (!client.userDeviceKeys.containsKey(userId)) {
|
||||
return;
|
||||
}
|
||||
final device = client.userDeviceKeys[userId].deviceKeys.values
|
||||
.firstWhere((d) => d.curve25519Key == senderKey, orElse: () => null);
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
await startOutgoingOlmSessions([device]);
|
||||
await client.sendToDevice([device], 'm.dummy', {});
|
||||
}
|
||||
|
||||
Future<ToDeviceEvent> decryptToDeviceEvent(ToDeviceEvent event) async {
|
||||
if (event.type != EventTypes.Encrypted) {
|
||||
return event;
|
||||
|
@ -308,8 +346,7 @@ class OlmManager {
|
|||
if (client.database == null) {
|
||||
return false;
|
||||
}
|
||||
final sessions = await client.database
|
||||
.getSingleOlmSessions(client.id, senderKey, client.userID);
|
||||
final sessions = await getOlmSessionsFromDatabase(senderKey);
|
||||
if (sessions.isEmpty) {
|
||||
return false; // okay, can't do anything
|
||||
}
|
||||
|
@ -319,12 +356,20 @@ class OlmManager {
|
|||
if (!_olmSessions.containsKey(senderKey)) {
|
||||
await loadFromDb();
|
||||
}
|
||||
try {
|
||||
event = _decryptToDeviceEvent(event);
|
||||
if (event.type != EventTypes.Encrypted || !(await loadFromDb())) {
|
||||
return event;
|
||||
}
|
||||
// retry to decrypt!
|
||||
return _decryptToDeviceEvent(event);
|
||||
} catch (_) {
|
||||
// okay, the thing errored while decrypting. It is safe to assume that the olm session is corrupt and we should generate a new one
|
||||
if (client.enableE2eeRecovery) {
|
||||
unawaited(restoreOlmSession(event.senderId, senderKey));
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> startOutgoingOlmSessions(List<DeviceKeys> deviceKeys) async {
|
||||
|
@ -352,11 +397,19 @@ class OlmManager {
|
|||
fingerprintKey, deviceKey, userId, deviceId)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
var session = olm.Session();
|
||||
try {
|
||||
session.create_outbound(_olmAccount, identityKey, deviceKey['key']);
|
||||
await storeOlmSession(identityKey, session);
|
||||
await storeOlmSession(OlmSession(
|
||||
key: client.userID,
|
||||
identityKey: identityKey,
|
||||
sessionId: session.session_id(),
|
||||
session: session,
|
||||
lastReceived:
|
||||
DateTime.now(), // we want to use a newly created session
|
||||
));
|
||||
} catch (e) {
|
||||
session.free();
|
||||
print('[LibOlm] Could not create new outbound olm session: ' +
|
||||
e.toString());
|
||||
}
|
||||
|
@ -369,14 +422,15 @@ class OlmManager {
|
|||
DeviceKeys device, String type, Map<String, dynamic> payload) async {
|
||||
var sess = olmSessions[device.curve25519Key];
|
||||
if (sess == null || sess.isEmpty) {
|
||||
final sessions = await client.database
|
||||
.getSingleOlmSessions(client.id, device.curve25519Key, client.userID);
|
||||
final sessions = await getOlmSessionsFromDatabase(device.curve25519Key);
|
||||
if (sessions.isEmpty) {
|
||||
throw ('No olm session found');
|
||||
}
|
||||
sess = _olmSessions[device.curve25519Key] = sessions;
|
||||
}
|
||||
sess.sort((a, b) => a.session_id().compareTo(b.session_id()));
|
||||
sess.sort((a, b) => a.lastReceived == b.lastReceived
|
||||
? a.sessionId.compareTo(b.sessionId)
|
||||
: b.lastReceived.compareTo(a.lastReceived));
|
||||
final fullPayload = {
|
||||
'type': type,
|
||||
'content': payload,
|
||||
|
@ -385,8 +439,8 @@ class OlmManager {
|
|||
'recipient': device.userId,
|
||||
'recipient_keys': {'ed25519': device.ed25519Key},
|
||||
};
|
||||
final encryptResult = sess.first.encrypt(json.encode(fullPayload));
|
||||
storeOlmSession(device.curve25519Key, sess.first);
|
||||
final encryptResult = sess.first.session.encrypt(json.encode(fullPayload));
|
||||
storeOlmSession(sess.first);
|
||||
final encryptedBody = <String, dynamic>{
|
||||
'algorithm': 'm.olm.v1.curve25519-aes-sha2',
|
||||
'sender_key': identityKey,
|
||||
|
@ -408,8 +462,8 @@ class OlmManager {
|
|||
if (client.database != null) {
|
||||
for (final device in deviceKeys) {
|
||||
if (!olmSessions.containsKey(device.curve25519Key)) {
|
||||
final sessions = await client.database.getSingleOlmSessions(
|
||||
client.id, device.curve25519Key, client.userID);
|
||||
final sessions =
|
||||
await getOlmSessionsFromDatabase(device.curve25519Key);
|
||||
if (sessions.isNotEmpty) {
|
||||
_olmSessions[device.curve25519Key] = sessions;
|
||||
}
|
||||
|
@ -440,7 +494,7 @@ class OlmManager {
|
|||
void dispose() {
|
||||
for (final sessions in olmSessions.values) {
|
||||
for (final sess in sessions) {
|
||||
sess.free();
|
||||
sess.dispose();
|
||||
}
|
||||
}
|
||||
_olmAccount?.free();
|
||||
|
|
59
lib/encryption/utils/olm_session.dart
Normal file
59
lib/encryption/utils/olm_session.dart
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Famedly Matrix SDK
|
||||
* Copyright (C) 2020 Famedly GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import '../../src/database/database.dart' show DbOlmSessions;
|
||||
|
||||
class OlmSession {
|
||||
String identityKey;
|
||||
String sessionId;
|
||||
olm.Session session;
|
||||
DateTime lastReceived;
|
||||
final String key;
|
||||
String get pickledSession => session.pickle(key);
|
||||
|
||||
bool get isValid => session != null;
|
||||
|
||||
OlmSession({
|
||||
this.key,
|
||||
this.identityKey,
|
||||
this.sessionId,
|
||||
this.session,
|
||||
this.lastReceived,
|
||||
});
|
||||
|
||||
OlmSession.fromDb(DbOlmSessions dbEntry, String key) : key = key {
|
||||
session = olm.Session();
|
||||
try {
|
||||
session.unpickle(key, dbEntry.pickle);
|
||||
identityKey = dbEntry.identityKey;
|
||||
sessionId = dbEntry.sessionId;
|
||||
lastReceived =
|
||||
dbEntry.lastReceived ?? DateTime.fromMillisecondsSinceEpoch(0);
|
||||
assert(sessionId == session.session_id());
|
||||
} catch (e) {
|
||||
print('[LibOlm] Could not unpickle olm session: ' + e.toString());
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
session?.free();
|
||||
session = null;
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ class Database extends _$Database {
|
|||
Database(QueryExecutor e) : super(e);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 4;
|
||||
int get schemaVersion => 5;
|
||||
|
||||
int get maxFileSize => 1 * 1024 * 1024;
|
||||
|
||||
|
@ -55,6 +55,10 @@ class Database extends _$Database {
|
|||
'UPDATE user_device_keys SET outdated = true');
|
||||
from++;
|
||||
}
|
||||
if (from == 4) {
|
||||
await m.addColumn(olmSessions, olmSessions.lastReceived);
|
||||
from++;
|
||||
}
|
||||
},
|
||||
beforeOpen: (_) async {
|
||||
if (executor.dialect == SqlDialect.sqlite) {
|
||||
|
@ -114,22 +118,6 @@ class Database extends _$Database {
|
|||
return res;
|
||||
}
|
||||
|
||||
Future<List<olm.Session>> getSingleOlmSessions(
|
||||
int clientId, String identityKey, String userId) async {
|
||||
final rows = await dbGetOlmSessions(clientId, identityKey).get();
|
||||
final res = <olm.Session>[];
|
||||
for (final row in rows) {
|
||||
try {
|
||||
var session = olm.Session();
|
||||
session.unpickle(userId, row.pickle);
|
||||
res.add(session);
|
||||
} catch (e) {
|
||||
print('[LibOlm] Could not unpickle olm session: ' + e.toString());
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<DbOutboundGroupSession> getDbOutboundGroupSession(
|
||||
int clientId, String roomId) async {
|
||||
final res = await dbGetOutboundGroupSession(clientId, roomId).get();
|
||||
|
|
|
@ -1391,17 +1391,20 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> {
|
|||
final String identityKey;
|
||||
final String sessionId;
|
||||
final String pickle;
|
||||
final DateTime lastReceived;
|
||||
DbOlmSessions(
|
||||
{@required this.clientId,
|
||||
@required this.identityKey,
|
||||
@required this.sessionId,
|
||||
@required this.pickle});
|
||||
@required this.pickle,
|
||||
this.lastReceived});
|
||||
factory DbOlmSessions.fromData(
|
||||
Map<String, dynamic> data, GeneratedDatabase db,
|
||||
{String prefix}) {
|
||||
final effectivePrefix = prefix ?? '';
|
||||
final intType = db.typeSystem.forDartType<int>();
|
||||
final stringType = db.typeSystem.forDartType<String>();
|
||||
final dateTimeType = db.typeSystem.forDartType<DateTime>();
|
||||
return DbOlmSessions(
|
||||
clientId:
|
||||
intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']),
|
||||
|
@ -1411,6 +1414,8 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> {
|
|||
.mapFromDatabaseResponse(data['${effectivePrefix}session_id']),
|
||||
pickle:
|
||||
stringType.mapFromDatabaseResponse(data['${effectivePrefix}pickle']),
|
||||
lastReceived: dateTimeType
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}last_received']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
|
@ -1428,6 +1433,9 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> {
|
|||
if (!nullToAbsent || pickle != null) {
|
||||
map['pickle'] = Variable<String>(pickle);
|
||||
}
|
||||
if (!nullToAbsent || lastReceived != null) {
|
||||
map['last_received'] = Variable<DateTime>(lastReceived);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
@ -1439,6 +1447,7 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> {
|
|||
identityKey: serializer.fromJson<String>(json['identity_key']),
|
||||
sessionId: serializer.fromJson<String>(json['session_id']),
|
||||
pickle: serializer.fromJson<String>(json['pickle']),
|
||||
lastReceived: serializer.fromJson<DateTime>(json['last_received']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
|
@ -1449,6 +1458,7 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> {
|
|||
'identity_key': serializer.toJson<String>(identityKey),
|
||||
'session_id': serializer.toJson<String>(sessionId),
|
||||
'pickle': serializer.toJson<String>(pickle),
|
||||
'last_received': serializer.toJson<DateTime>(lastReceived),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1456,12 +1466,14 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> {
|
|||
{int clientId,
|
||||
String identityKey,
|
||||
String sessionId,
|
||||
String pickle}) =>
|
||||
String pickle,
|
||||
DateTime lastReceived}) =>
|
||||
DbOlmSessions(
|
||||
clientId: clientId ?? this.clientId,
|
||||
identityKey: identityKey ?? this.identityKey,
|
||||
sessionId: sessionId ?? this.sessionId,
|
||||
pickle: pickle ?? this.pickle,
|
||||
lastReceived: lastReceived ?? this.lastReceived,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
|
@ -1469,14 +1481,19 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> {
|
|||
..write('clientId: $clientId, ')
|
||||
..write('identityKey: $identityKey, ')
|
||||
..write('sessionId: $sessionId, ')
|
||||
..write('pickle: $pickle')
|
||||
..write('pickle: $pickle, ')
|
||||
..write('lastReceived: $lastReceived')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(clientId.hashCode,
|
||||
$mrjc(identityKey.hashCode, $mrjc(sessionId.hashCode, pickle.hashCode))));
|
||||
int get hashCode => $mrjf($mrjc(
|
||||
clientId.hashCode,
|
||||
$mrjc(
|
||||
identityKey.hashCode,
|
||||
$mrjc(sessionId.hashCode,
|
||||
$mrjc(pickle.hashCode, lastReceived.hashCode)))));
|
||||
@override
|
||||
bool operator ==(dynamic other) =>
|
||||
identical(this, other) ||
|
||||
|
@ -1484,7 +1501,8 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> {
|
|||
other.clientId == this.clientId &&
|
||||
other.identityKey == this.identityKey &&
|
||||
other.sessionId == this.sessionId &&
|
||||
other.pickle == this.pickle);
|
||||
other.pickle == this.pickle &&
|
||||
other.lastReceived == this.lastReceived);
|
||||
}
|
||||
|
||||
class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> {
|
||||
|
@ -1492,17 +1510,20 @@ class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> {
|
|||
final Value<String> identityKey;
|
||||
final Value<String> sessionId;
|
||||
final Value<String> pickle;
|
||||
final Value<DateTime> lastReceived;
|
||||
const OlmSessionsCompanion({
|
||||
this.clientId = const Value.absent(),
|
||||
this.identityKey = const Value.absent(),
|
||||
this.sessionId = const Value.absent(),
|
||||
this.pickle = const Value.absent(),
|
||||
this.lastReceived = const Value.absent(),
|
||||
});
|
||||
OlmSessionsCompanion.insert({
|
||||
@required int clientId,
|
||||
@required String identityKey,
|
||||
@required String sessionId,
|
||||
@required String pickle,
|
||||
this.lastReceived = const Value.absent(),
|
||||
}) : clientId = Value(clientId),
|
||||
identityKey = Value(identityKey),
|
||||
sessionId = Value(sessionId),
|
||||
|
@ -1512,12 +1533,14 @@ class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> {
|
|||
Expression<String> identityKey,
|
||||
Expression<String> sessionId,
|
||||
Expression<String> pickle,
|
||||
Expression<DateTime> lastReceived,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (clientId != null) 'client_id': clientId,
|
||||
if (identityKey != null) 'identity_key': identityKey,
|
||||
if (sessionId != null) 'session_id': sessionId,
|
||||
if (pickle != null) 'pickle': pickle,
|
||||
if (lastReceived != null) 'last_received': lastReceived,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1525,12 +1548,14 @@ class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> {
|
|||
{Value<int> clientId,
|
||||
Value<String> identityKey,
|
||||
Value<String> sessionId,
|
||||
Value<String> pickle}) {
|
||||
Value<String> pickle,
|
||||
Value<DateTime> lastReceived}) {
|
||||
return OlmSessionsCompanion(
|
||||
clientId: clientId ?? this.clientId,
|
||||
identityKey: identityKey ?? this.identityKey,
|
||||
sessionId: sessionId ?? this.sessionId,
|
||||
pickle: pickle ?? this.pickle,
|
||||
lastReceived: lastReceived ?? this.lastReceived,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1549,6 +1574,9 @@ class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> {
|
|||
if (pickle.present) {
|
||||
map['pickle'] = Variable<String>(pickle.value);
|
||||
}
|
||||
if (lastReceived.present) {
|
||||
map['last_received'] = Variable<DateTime>(lastReceived.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
@ -1591,9 +1619,19 @@ class OlmSessions extends Table with TableInfo<OlmSessions, DbOlmSessions> {
|
|||
$customConstraints: 'NOT NULL');
|
||||
}
|
||||
|
||||
final VerificationMeta _lastReceivedMeta =
|
||||
const VerificationMeta('lastReceived');
|
||||
GeneratedDateTimeColumn _lastReceived;
|
||||
GeneratedDateTimeColumn get lastReceived =>
|
||||
_lastReceived ??= _constructLastReceived();
|
||||
GeneratedDateTimeColumn _constructLastReceived() {
|
||||
return GeneratedDateTimeColumn('last_received', $tableName, true,
|
||||
$customConstraints: '');
|
||||
}
|
||||
|
||||
@override
|
||||
List<GeneratedColumn> get $columns =>
|
||||
[clientId, identityKey, sessionId, pickle];
|
||||
[clientId, identityKey, sessionId, pickle, lastReceived];
|
||||
@override
|
||||
OlmSessions get asDslTable => this;
|
||||
@override
|
||||
|
@ -1631,6 +1669,12 @@ class OlmSessions extends Table with TableInfo<OlmSessions, DbOlmSessions> {
|
|||
} else if (isInserting) {
|
||||
context.missing(_pickleMeta);
|
||||
}
|
||||
if (data.containsKey('last_received')) {
|
||||
context.handle(
|
||||
_lastReceivedMeta,
|
||||
lastReceived.isAcceptableOrUnknown(
|
||||
data['last_received'], _lastReceivedMeta));
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
|
@ -5520,6 +5564,7 @@ abstract class _$Database extends GeneratedDatabase {
|
|||
identityKey: row.readString('identity_key'),
|
||||
sessionId: row.readString('session_id'),
|
||||
pickle: row.readString('pickle'),
|
||||
lastReceived: row.readDateTime('last_received'),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -5543,15 +5588,16 @@ abstract class _$Database extends GeneratedDatabase {
|
|||
}).map(_rowToDbOlmSessions);
|
||||
}
|
||||
|
||||
Future<int> storeOlmSession(
|
||||
int client_id, String identitiy_key, String session_id, String pickle) {
|
||||
Future<int> storeOlmSession(int client_id, String identitiy_key,
|
||||
String session_id, String pickle, DateTime last_received) {
|
||||
return customInsert(
|
||||
'INSERT OR REPLACE INTO olm_sessions (client_id, identity_key, session_id, pickle) VALUES (:client_id, :identitiy_key, :session_id, :pickle)',
|
||||
'INSERT OR REPLACE INTO olm_sessions (client_id, identity_key, session_id, pickle, last_received) VALUES (:client_id, :identitiy_key, :session_id, :pickle, :last_received)',
|
||||
variables: [
|
||||
Variable.withInt(client_id),
|
||||
Variable.withString(identitiy_key),
|
||||
Variable.withString(session_id),
|
||||
Variable.withString(pickle)
|
||||
Variable.withString(pickle),
|
||||
Variable.withDateTime(last_received)
|
||||
],
|
||||
updates: {olmSessions},
|
||||
);
|
||||
|
|
|
@ -48,6 +48,7 @@ CREATE TABLE olm_sessions (
|
|||
identity_key TEXT NOT NULL,
|
||||
session_id TEXT NOT NULL,
|
||||
pickle TEXT NOT NULL,
|
||||
last_received DATETIME,
|
||||
UNIQUE(client_id, identity_key, session_id)
|
||||
) AS DbOlmSessions;
|
||||
CREATE INDEX olm_sessions_index ON olm_sessions(client_id);
|
||||
|
@ -177,7 +178,7 @@ getAllUserDeviceKeysKeys: SELECT * FROM user_device_keys_key WHERE client_id = :
|
|||
getAllUserCrossSigningKeys: SELECT * FROM user_cross_signing_keys WHERE client_id = :client_id;
|
||||
getAllOlmSessions: SELECT * FROM olm_sessions WHERE client_id = :client_id;
|
||||
dbGetOlmSessions: SELECT * FROM olm_sessions WHERE client_id = :client_id AND identity_key = :identity_key;
|
||||
storeOlmSession: INSERT OR REPLACE INTO olm_sessions (client_id, identity_key, session_id, pickle) VALUES (:client_id, :identitiy_key, :session_id, :pickle);
|
||||
storeOlmSession: INSERT OR REPLACE INTO olm_sessions (client_id, identity_key, session_id, pickle, last_received) VALUES (:client_id, :identitiy_key, :session_id, :pickle, :last_received);
|
||||
getAllOutboundGroupSessions: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id;
|
||||
dbGetOutboundGroupSession: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id;
|
||||
storeOutboundGroupSession: INSERT OR REPLACE INTO outbound_group_sessions (client_id, room_id, pickle, device_ids, creation_time, sent_messages) VALUES (:client_id, :room_id, :pickle, :device_ids, :creation_time, :sent_messages);
|
||||
|
|
|
@ -99,8 +99,26 @@ void main() {
|
|||
false);
|
||||
});
|
||||
|
||||
test('restoreOlmSession', () async {
|
||||
client.encryption.olmManager.olmSessions.clear();
|
||||
await client.encryption.olmManager
|
||||
.restoreOlmSession(client.userID, client.identityKey);
|
||||
expect(client.encryption.olmManager.olmSessions.length, 1);
|
||||
|
||||
client.encryption.olmManager.olmSessions.clear();
|
||||
await client.encryption.olmManager
|
||||
.restoreOlmSession(client.userID, 'invalid');
|
||||
expect(client.encryption.olmManager.olmSessions.length, 0);
|
||||
|
||||
client.encryption.olmManager.olmSessions.clear();
|
||||
await client.encryption.olmManager
|
||||
.restoreOlmSession('invalid', client.identityKey);
|
||||
expect(client.encryption.olmManager.olmSessions.length, 0);
|
||||
});
|
||||
|
||||
test('startOutgoingOlmSessions', () async {
|
||||
// start an olm session.....with ourself!
|
||||
client.encryption.olmManager.olmSessions.clear();
|
||||
await client.encryption.olmManager.startOutgoingOlmSessions(
|
||||
[client.userDeviceKeys[client.userID].deviceKeys[client.deviceID]]);
|
||||
expect(
|
||||
|
|
|
@ -139,12 +139,10 @@ void test() async {
|
|||
assert(testClientB
|
||||
.encryption.olmManager.olmSessions[testClientA.identityKey].length ==
|
||||
1);
|
||||
assert(testClientA
|
||||
.encryption.olmManager.olmSessions[testClientB.identityKey].first
|
||||
.session_id() ==
|
||||
testClientB
|
||||
.encryption.olmManager.olmSessions[testClientA.identityKey].first
|
||||
.session_id());
|
||||
assert(testClientA.encryption.olmManager.olmSessions[testClientB.identityKey]
|
||||
.first.sessionId ==
|
||||
testClientB.encryption.olmManager.olmSessions[testClientA.identityKey]
|
||||
.first.sessionId);
|
||||
assert(inviteRoom.client.encryption.keyManager
|
||||
.getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') !=
|
||||
null);
|
||||
|
@ -162,12 +160,10 @@ void test() async {
|
|||
assert(testClientB
|
||||
.encryption.olmManager.olmSessions[testClientA.identityKey].length ==
|
||||
1);
|
||||
assert(testClientA
|
||||
.encryption.olmManager.olmSessions[testClientB.identityKey].first
|
||||
.session_id() ==
|
||||
testClientB
|
||||
.encryption.olmManager.olmSessions[testClientA.identityKey].first
|
||||
.session_id());
|
||||
assert(testClientA.encryption.olmManager.olmSessions[testClientB.identityKey]
|
||||
.first.sessionId ==
|
||||
testClientB.encryption.olmManager.olmSessions[testClientA.identityKey]
|
||||
.first.sessionId);
|
||||
|
||||
assert(room.client.encryption.keyManager
|
||||
.getOutboundGroupSession(room.id)
|
||||
|
@ -231,24 +227,20 @@ void test() async {
|
|||
assert(testClientB
|
||||
.encryption.olmManager.olmSessions[testClientA.identityKey].length ==
|
||||
1);
|
||||
assert(testClientA
|
||||
.encryption.olmManager.olmSessions[testClientB.identityKey].first
|
||||
.session_id() ==
|
||||
testClientB
|
||||
.encryption.olmManager.olmSessions[testClientA.identityKey].first
|
||||
.session_id());
|
||||
assert(testClientA.encryption.olmManager.olmSessions[testClientB.identityKey]
|
||||
.first.sessionId ==
|
||||
testClientB.encryption.olmManager.olmSessions[testClientA.identityKey]
|
||||
.first.sessionId);
|
||||
assert(testClientA
|
||||
.encryption.olmManager.olmSessions[testClientC.identityKey].length ==
|
||||
1);
|
||||
assert(testClientC
|
||||
.encryption.olmManager.olmSessions[testClientA.identityKey].length ==
|
||||
1);
|
||||
assert(testClientA
|
||||
.encryption.olmManager.olmSessions[testClientC.identityKey].first
|
||||
.session_id() ==
|
||||
testClientC
|
||||
.encryption.olmManager.olmSessions[testClientA.identityKey].first
|
||||
.session_id());
|
||||
assert(testClientA.encryption.olmManager.olmSessions[testClientC.identityKey]
|
||||
.first.sessionId ==
|
||||
testClientC.encryption.olmManager.olmSessions[testClientA.identityKey]
|
||||
.first.sessionId);
|
||||
assert(room.client.encryption.keyManager
|
||||
.getOutboundGroupSession(room.id)
|
||||
.outboundGroupSession
|
||||
|
@ -281,12 +273,10 @@ void test() async {
|
|||
assert(testClientB
|
||||
.encryption.olmManager.olmSessions[testClientA.identityKey].length ==
|
||||
1);
|
||||
assert(testClientA
|
||||
.encryption.olmManager.olmSessions[testClientB.identityKey].first
|
||||
.session_id() ==
|
||||
testClientB
|
||||
.encryption.olmManager.olmSessions[testClientA.identityKey].first
|
||||
.session_id());
|
||||
assert(testClientA.encryption.olmManager.olmSessions[testClientB.identityKey]
|
||||
.first.sessionId ==
|
||||
testClientB.encryption.olmManager.olmSessions[testClientA.identityKey]
|
||||
.first.sessionId);
|
||||
assert(room.client.encryption.keyManager
|
||||
.getOutboundGroupSession(room.id)
|
||||
.outboundGroupSession
|
||||
|
|
Loading…
Reference in a new issue