Compare commits

..

2 Commits

10 changed files with 167 additions and 38 deletions

View File

@ -9,6 +9,7 @@ const omemoRatchetsTable = 'OmemoSessions';
const omemoTrustCacheTable = 'OmemoTrustCacheList';
const omemoTrustDeviceListTable = 'OmemoTrustDeviceList';
const omemoTrustEnableListTable = 'OmemoTrustEnableList';
const omemoFingerprintCache = 'OmemoFingerprintCache';
const xmppStateTable = 'XmppState';
const typeString = 0;

View File

@ -160,7 +160,7 @@ Future<void> createDatabase(Database db, int version) async {
PRIMARY KEY (jid, id)
)''',
);
await db.execute(
await db.execute(
'''
CREATE TABLE $omemoDeviceListTable (
jid TEXT NOT NULL,
@ -168,6 +168,15 @@ Future<void> createDatabase(Database db, int version) async {
PRIMARY KEY (jid, id)
)''',
);
await db.execute(
'''
CREATE TABLE $omemoFingerprintCache (
jid TEXT NOT NULL,
id INTEGER NOT NULL,
fingerprint TEXT NOT NULL,
PRIMARY KEY (jid, id)
)''',
);
// Settings
await db.execute(

View File

@ -13,6 +13,7 @@ import 'package:moxxyv2/service/database/migrations/0000_conversations2.dart';
import 'package:moxxyv2/service/database/migrations/0000_conversations3.dart';
import 'package:moxxyv2/service/database/migrations/0000_language.dart';
import 'package:moxxyv2/service/database/migrations/0000_lmc.dart';
import 'package:moxxyv2/service/database/migrations/0000_omemo_fingerprint_cache.dart';
import 'package:moxxyv2/service/database/migrations/0000_reactions.dart';
import 'package:moxxyv2/service/database/migrations/0000_reactions_store_hint.dart';
import 'package:moxxyv2/service/database/migrations/0000_retraction.dart';
@ -21,6 +22,7 @@ import 'package:moxxyv2/service/database/migrations/0000_shared_media.dart';
import 'package:moxxyv2/service/database/migrations/0000_xmpp_state.dart';
import 'package:moxxyv2/service/not_specified.dart';
import 'package:moxxyv2/service/omemo/omemo.dart';
import 'package:moxxyv2/service/omemo/types.dart';
import 'package:moxxyv2/service/roster.dart';
import 'package:moxxyv2/service/state.dart';
import 'package:moxxyv2/shared/models/conversation.dart';
@ -65,7 +67,7 @@ class DatabaseService {
_db = await openDatabase(
dbPath,
password: key,
version: 12,
version: 13,
onCreate: createDatabase,
onConfigure: (db) async {
// In order to do schema changes during database upgrades, we disable foreign
@ -122,6 +124,10 @@ class DatabaseService {
_log.finest('Running migration for database version 12');
await upgradeFromV11ToV12(db);
}
if (oldVersion < 13) {
_log.finest('Running migration for database version 13');
await upgradeFromV12ToV13(db);
}
},
);
@ -1002,4 +1008,38 @@ class DatabaseService {
await batch.commit();
}
Future<void> addFingerprintsToCache(List<OmemoCacheTriple> items) async {
final batch = _db.batch();
for (final item in items) {
batch.insert(
omemoFingerprintCache,
<String, dynamic>{
'jid': item.jid,
'id': item.deviceId,
'fingerprint': item.fingerprint,
},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
await batch.commit();
}
Future<List<OmemoCacheTriple>> getFingerprintsFromCache(String jid) async {
final rawItems = await _db.query(
omemoFingerprintCache,
where: 'jid = ?',
whereArgs: [jid],
);
return rawItems
.map((item) {
return OmemoCacheTriple(
jid,
item['id']! as int,
item['fingerprint']! as String,
);
})
.toList();
}
}

View File

@ -9,5 +9,4 @@ Future<void> upgradeFromV6ToV7(Database db) async {
await db.execute(
"ALTER TABLE $conversationsTable ADD COLUMN lastMessageSender TEXT NOT NULL DEFAULT '';"
);
}

View File

@ -0,0 +1,14 @@
import 'package:moxxyv2/service/database/constants.dart';
import 'package:sqflite_sqlcipher/sqflite.dart';
Future<void> upgradeFromV12ToV13(Database db) async {
await db.execute(
'''
CREATE TABLE $omemoFingerprintCache (
jid TEXT NOT NULL,
id INTEGER NOT NULL,
fingerprint TEXT NOT NULL,
PRIMARY KEY (jid, id)
)''',
);
}

View File

@ -8,6 +8,7 @@ import 'package:moxxmpp/moxxmpp.dart';
import 'package:moxxyv2/service/database/database.dart';
import 'package:moxxyv2/service/moxxmpp/omemo.dart';
import 'package:moxxyv2/service/omemo/implementations.dart';
import 'package:moxxyv2/service/omemo/types.dart';
import 'package:moxxyv2/shared/models/omemo_device.dart';
import 'package:omemo_dart/omemo_dart.dart';
import 'package:synchronized/synchronized.dart';
@ -25,6 +26,7 @@ class OmemoService {
bool _initialized = false;
final Lock _lock = Lock();
final Queue<Completer<void>> _waitingForInitialization = Queue<Completer<void>>();
final Map<String, Map<int, String>> _fingerprintCache = {};
late OmemoSessionManager omemoState;
@ -64,6 +66,22 @@ class OmemoService {
await GetIt.I.get<DatabaseService>().saveRatchet(
OmemoDoubleRatchetWrapper(event.ratchet, event.deviceId, event.jid),
);
if (event.added) {
// Cache the fingerprint
final fingerprint = HEX.encode(await event.ratchet.ik.getBytes());
await GetIt.I.get<DatabaseService>().addFingerprintsToCache([
OmemoCacheTriple(
event.jid,
event.deviceId,
fingerprint,
),
]);
if (_fingerprintCache.containsKey(event.jid)) {
_fingerprintCache[event.jid]![event.deviceId] = fingerprint;
}
}
} else if (event is DeviceMapModifiedEvent) {
await commitDeviceMap(event.map);
} else if (event is DeviceModifiedEvent) {
@ -202,20 +220,70 @@ class OmemoService {
return null;
}
Future<void> _fetchFingerprintsAndCache(JID jid) async {
final bareJid = jid.toBare().toString();
final allDevicesRaw = await GetIt.I.get<XmppConnection>()
.getManagerById<OmemoManager>(omemoManager)!
.retrieveDeviceBundles(jid);
if (allDevicesRaw.isType<List<OmemoBundle>>()) {
final allDevices = allDevicesRaw.get<List<OmemoBundle>>();
final map = <int, String>{};
final items = List<OmemoCacheTriple>.empty(growable: true);
for (final device in allDevices) {
final curveIk = await device.ik.toCurve25519();
final fingerprint = HEX.encode(await curveIk.getBytes());
map[device.id] = fingerprint;
items.add(OmemoCacheTriple(bareJid, device.id, fingerprint));
}
// Cache them in memory
_fingerprintCache[bareJid] = map;
// Cache them in the database
await GetIt.I.get<DatabaseService>().addFingerprintsToCache(items);
}
}
Future<void> _loadOrFetchFingerprints(JID jid) async {
final bareJid = jid.toBare().toString();
if (!_fingerprintCache.containsKey(bareJid)) {
// First try to load it from the database
final triples = await GetIt.I.get<DatabaseService>()
.getFingerprintsFromCache(bareJid);
if (triples.isEmpty) {
// We found no fingerprints in the database, so try to fetch them
await _fetchFingerprintsAndCache(jid);
} else {
// We have fetched fingerprints from the database
_fingerprintCache[bareJid] = Map<int, String>.fromEntries(
triples.map((triple) {
return MapEntry<int, String>(
triple.deviceId,
triple.fingerprint,
);
}),
);
}
}
}
Future<List<OmemoDevice>> getOmemoKeysForJid(String jid) async {
await ensureInitialized();
final fingerprints = await omemoState.getHexFingerprintsForJid(jid);
// Get finger prints if we have to
await _loadOrFetchFingerprints(JID.fromString(jid));
final keys = List<OmemoDevice>.empty(growable: true);
final tm = omemoState.trustManager as BlindTrustBeforeVerificationTrustManager;
final trustMap = await tm.getDevicesTrust(jid);
for (final fp in fingerprints) {
for (final deviceId in _fingerprintCache[jid]!.keys) {
keys.add(
OmemoDevice(
fp.fingerprint,
await tm.isTrusted(jid, fp.deviceId),
trustMap[fp.deviceId] == BTBVTrustState.verified,
await tm.isEnabled(jid, fp.deviceId),
fp.deviceId,
_fingerprintCache[jid]![deviceId]!,
await tm.isTrusted(jid, deviceId),
trustMap[deviceId] == BTBVTrustState.verified,
await tm.isEnabled(jid, deviceId),
deviceId,
),
);
}
@ -267,36 +335,28 @@ class OmemoService {
/// published on [ownJid]'s devices PubSub node.
/// Note that the list is made so that the current device is excluded.
Future<List<OmemoDevice>> getOwnFingerprints(JID ownJid) async {
final conn = GetIt.I.get<XmppConnection>();
final ownId = await getDeviceId();
final keys = List<OmemoDevice>.from(
await getOmemoKeysForJid(ownJid.toString()),
);
final bareJid = ownJid.toBare().toString();
// TODO(PapaTutuWawa): This should be cached in the database and only requested if
// it's not cached.
final allDevicesRaw = await conn.getManagerById<OmemoManager>(omemoManager)!
.retrieveDeviceBundles(ownJid);
if (allDevicesRaw.isType<List<OmemoBundle>>()) {
final allDevices = allDevicesRaw.get<List<OmemoBundle>>();
for (final device in allDevices) {
// All devices that are publishes that is not the current device
if (device.id == ownId) continue;
final curveIk = await device.ik.toCurve25519();
keys.add(
OmemoDevice(
HEX.encode(await curveIk.getBytes()),
false,
false,
false,
device.id,
hasSessionWith: false,
),
);
}
}
// Get finger prints if we have to
await _loadOrFetchFingerprints(ownJid);
_fingerprintCache[bareJid]!.forEach((deviceId, fingerprint) {
if (deviceId == ownId) return;
keys.add(
OmemoDevice(
fingerprint,
false,
false,
false,
deviceId,
hasSessionWith: false,
),
);
});
return keys;
}

View File

@ -0,0 +1,6 @@
class OmemoCacheTriple {
const OmemoCacheTriple(this.jid, this.deviceId, this.fingerprint);
final String jid;
final int deviceId;
final String fingerprint;
}

View File

@ -16,7 +16,7 @@ class OmemoDevice with _$OmemoDevice {
@Default(true) bool hasSessionWith,
}
) = _OmemoDevice;
/// JSON
factory OmemoDevice.fromJson(Map<String, dynamic> json) => _$OmemoDeviceFromJson(json);
}

View File

@ -831,7 +831,7 @@ packages:
description:
path: "."
ref: HEAD
resolved-ref: "797bf6985638f0fe5a6e12a7a8339cf7d9334f88"
resolved-ref: fe1ba99b14b516ecf6d147c57bd986d8afe7f3fc
url: "https://codeberg.org/PapaTutuWawa/omemo_dart.git"
source: git
version: "0.3.2"

View File

@ -144,7 +144,7 @@ dependency_overrides:
omemo_dart:
git:
url: https://codeberg.org/PapaTutuWawa/omemo_dart.git
rev: 797bf6985638f0fe5a6e12a7a8339cf7d9334f88
rev: fe1ba99b14b516ecf6d147c57bd986d8afe7f3fc
extra_licenses:
- name: undraw.co