get device verification status based on cross signing
This commit is contained in:
parent
998ee66650
commit
af961b99dc
|
@ -796,7 +796,7 @@ class Client {
|
||||||
pickledOlmAccount,
|
pickledOlmAccount,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_userDeviceKeys = await database.getUserDeviceKeys(id);
|
_userDeviceKeys = await database.getUserDeviceKeys(this);
|
||||||
_olmSessions = await database.getOlmSessions(id, _userID);
|
_olmSessions = await database.getOlmSessions(id, _userID);
|
||||||
_rooms = await database.getRoomList(this, onlyLeft: false);
|
_rooms = await database.getRoomList(this, onlyLeft: false);
|
||||||
_sortRooms();
|
_sortRooms();
|
||||||
|
@ -1644,6 +1644,7 @@ class Client {
|
||||||
action: '/client/r0/keys/query',
|
action: '/client/r0/keys/query',
|
||||||
data: {'timeout': 10000, 'device_keys': outdatedLists});
|
data: {'timeout': 10000, 'device_keys': outdatedLists});
|
||||||
|
|
||||||
|
// first we parse and persist the device keys
|
||||||
for (final rawDeviceKeyListEntry in response['device_keys'].entries) {
|
for (final rawDeviceKeyListEntry in response['device_keys'].entries) {
|
||||||
final String userId = rawDeviceKeyListEntry.key;
|
final String userId = rawDeviceKeyListEntry.key;
|
||||||
final oldKeys =
|
final oldKeys =
|
||||||
|
@ -1653,10 +1654,17 @@ class Client {
|
||||||
final String deviceId = rawDeviceKeyEntry.key;
|
final String deviceId = rawDeviceKeyEntry.key;
|
||||||
|
|
||||||
// Set the new device key for this device
|
// Set the new device key for this device
|
||||||
|
final entry = DeviceKeys.fromJson(rawDeviceKeyEntry.value, this);
|
||||||
if (!oldKeys.containsKey(deviceId)) {
|
|
||||||
final entry = DeviceKeys.fromJson(rawDeviceKeyEntry.value);
|
|
||||||
if (entry.isValid) {
|
if (entry.isValid) {
|
||||||
|
// is this a new key or the same one as an old one?
|
||||||
|
// better store an update - the signatures might have changed!
|
||||||
|
if (!oldKeys.containsKey(deviceId) || oldKeys[deviceId].ed25519Key == entry.ed25519Key) {
|
||||||
|
if (oldKeys.containsKey(deviceId)) {
|
||||||
|
// be sure to save the verified status
|
||||||
|
entry.verified = oldKeys[deviceId].verified;
|
||||||
|
entry.blocked = oldKeys[deviceId].blocked;
|
||||||
|
entry.validSignatures = oldKeys[deviceId].validSignatures;
|
||||||
|
}
|
||||||
_userDeviceKeys[userId].deviceKeys[deviceId] = entry;
|
_userDeviceKeys[userId].deviceKeys[deviceId] = entry;
|
||||||
if (deviceId == deviceID &&
|
if (deviceId == deviceID &&
|
||||||
entry.ed25519Key ==
|
entry.ed25519Key ==
|
||||||
|
@ -1664,22 +1672,26 @@ class Client {
|
||||||
// Always trust the own device
|
// Always trust the own device
|
||||||
entry.verified = true;
|
entry.verified = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// This shouldn't ever happen. The same device ID has gotten
|
||||||
|
// a new public key. So we ignore the update. TODO: ask krille
|
||||||
|
// if we should instead use the new key with unknown verified / blocked status
|
||||||
|
_userDeviceKeys[userId].deviceKeys[deviceId] = oldKeys[deviceId];
|
||||||
}
|
}
|
||||||
if (database != null) {
|
if (database != null) {
|
||||||
dbActions.add(() => database.storeUserDeviceKey(
|
dbActions.add(() => database.storeUserDeviceKey(
|
||||||
id,
|
id,
|
||||||
userId,
|
userId,
|
||||||
deviceId,
|
deviceId,
|
||||||
json.encode(
|
json.encode(entry.toJson()),
|
||||||
_userDeviceKeys[userId].deviceKeys[deviceId].toJson()),
|
json.encode(entry.validSignatures),
|
||||||
_userDeviceKeys[userId].deviceKeys[deviceId].verified,
|
entry.verified,
|
||||||
_userDeviceKeys[userId].deviceKeys[deviceId].blocked,
|
entry.blocked,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
_userDeviceKeys[userId].deviceKeys[deviceId] = oldKeys[deviceId];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// delete old/unused entries
|
||||||
if (database != null) {
|
if (database != null) {
|
||||||
for (final oldDeviceKeyEntry in oldKeys.entries) {
|
for (final oldDeviceKeyEntry in oldKeys.entries) {
|
||||||
final deviceId = oldDeviceKeyEntry.key;
|
final deviceId = oldDeviceKeyEntry.key;
|
||||||
|
@ -1696,6 +1708,68 @@ class Client {
|
||||||
.add(() => database.storeUserDeviceKeysInfo(id, userId, false));
|
.add(() => database.storeUserDeviceKeysInfo(id, userId, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// next we parse and persist the cross signing keys
|
||||||
|
for (final keyType in ['master_keys', 'self_signing_keys', 'user_signing_keys']) {
|
||||||
|
if (!(response[keyType] is Map)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (final rawDeviceKeyListEntry in response[keyType].entries) {
|
||||||
|
final String userId = rawDeviceKeyListEntry.key;
|
||||||
|
final oldKeys = Map<String, CrossSigningKey>.from(_userDeviceKeys[userId].crossSigningKeys);
|
||||||
|
_userDeviceKeys[userId].crossSigningKeys = {};
|
||||||
|
// add the types we arne't handling atm back
|
||||||
|
for (final oldEntry in oldKeys.entries) {
|
||||||
|
if (!oldEntry.value.usage.contains(keyType.substring(0, keyType.length - '_keys'.length))) {
|
||||||
|
_userDeviceKeys[userId].crossSigningKeys[oldEntry.key] = oldEntry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final entry = CrossSigningKey.fromJson(rawDeviceKeyListEntry.value, this);
|
||||||
|
if (entry.isValid) {
|
||||||
|
final publicKey = entry.publicKey;
|
||||||
|
if (!oldKeys.containsKey(publicKey) || oldKeys[publicKey].ed25519Key == entry.ed25519Key) {
|
||||||
|
if (oldKeys.containsKey(publicKey)) {
|
||||||
|
// be sure to save the verification status
|
||||||
|
entry.verified = oldKeys[publicKey].verified;
|
||||||
|
entry.blocked = oldKeys[publicKey].blocked;
|
||||||
|
entry.validSignatures = oldKeys[publicKey].validSignatures;
|
||||||
|
}
|
||||||
|
_userDeviceKeys[userId].crossSigningKeys[publicKey] = entry;
|
||||||
|
} else {
|
||||||
|
// This shouldn't ever happen. The same device ID has gotten
|
||||||
|
// a new public key. So we ignore the update. TODO: ask krille
|
||||||
|
// if we should instead use the new key with unknown verified / blocked status
|
||||||
|
_userDeviceKeys[userId].crossSigningKeys[publicKey] = oldKeys[publicKey];
|
||||||
|
}
|
||||||
|
if (database != null) {
|
||||||
|
dbActions.add(() => database.storeUserCrossSigningKey(
|
||||||
|
id,
|
||||||
|
userId,
|
||||||
|
publicKey,
|
||||||
|
json.encode(entry.toJson()),
|
||||||
|
json.encode(entry.validSignatures),
|
||||||
|
entry.verified,
|
||||||
|
entry.blocked,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// delete old/unused entries
|
||||||
|
if (database != null) {
|
||||||
|
for (final oldCrossSigningKeyEntry in oldKeys.entries) {
|
||||||
|
final publicKey = oldCrossSigningKeyEntry.key;
|
||||||
|
if (!_userDeviceKeys[userId].crossSigningKeys.containsKey(publicKey)) {
|
||||||
|
// we need to remove an old key
|
||||||
|
dbActions.add(
|
||||||
|
() => database.removeUserCrossSigningKey(id, userId, publicKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_userDeviceKeys[userId].outdated = false;
|
||||||
|
if (database != null) {
|
||||||
|
dbActions
|
||||||
|
.add(() => database.storeUserDeviceKeysInfo(id, userId, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await database?.transaction(() async {
|
await database?.transaction(() async {
|
||||||
for (final f in dbActions) {
|
for (final f in dbActions) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ class Database extends _$Database {
|
||||||
Database(QueryExecutor e) : super(e);
|
Database(QueryExecutor e) : super(e);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 3;
|
int get schemaVersion => 4;
|
||||||
|
|
||||||
int get maxFileSize => 1 * 1024 * 1024;
|
int get maxFileSize => 1 * 1024 * 1024;
|
||||||
|
|
||||||
|
@ -41,6 +41,15 @@ class Database extends _$Database {
|
||||||
if (from == 2) {
|
if (from == 2) {
|
||||||
await m.deleteTable('outbound_group_sessions');
|
await m.deleteTable('outbound_group_sessions');
|
||||||
await m.createTable(outboundGroupSessions);
|
await m.createTable(outboundGroupSessions);
|
||||||
|
from++;
|
||||||
|
}
|
||||||
|
if (from == 3) {
|
||||||
|
await m.createTable(userCrossSigningKeys);
|
||||||
|
await m.createIndex(userCrossSigningKeysIndex);
|
||||||
|
await m.addColumn(userDeviceKeysKey, userDeviceKeysKey.validSignatures);
|
||||||
|
// mark all keys as outdated so that the cross signing keys will be fetched
|
||||||
|
await m.issueCustomQuery('UPDATE user_device_keys SET outdated = true');
|
||||||
|
from++;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -51,15 +60,19 @@ class Database extends _$Database {
|
||||||
return res.first;
|
return res.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, sdk.DeviceKeysList>> getUserDeviceKeys(int clientId) async {
|
Future<Map<String, sdk.DeviceKeysList>> getUserDeviceKeys(sdk.Client client) async {
|
||||||
final deviceKeys = await getAllUserDeviceKeys(clientId).get();
|
final deviceKeys = await getAllUserDeviceKeys(client.id).get();
|
||||||
if (deviceKeys.isEmpty) {
|
if (deviceKeys.isEmpty) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
final deviceKeysKeys = await getAllUserDeviceKeysKeys(clientId).get();
|
final deviceKeysKeys = await getAllUserDeviceKeysKeys(client.id).get();
|
||||||
|
final crossSigningKeys = await getAllUserCrossSigningKeys(client.id).get();
|
||||||
final res = <String, sdk.DeviceKeysList>{};
|
final res = <String, sdk.DeviceKeysList>{};
|
||||||
for (final entry in deviceKeys) {
|
for (final entry in deviceKeys) {
|
||||||
res[entry.userId] = sdk.DeviceKeysList.fromDb(entry, deviceKeysKeys.where((k) => k.userId == entry.userId).toList());
|
res[entry.userId] = sdk.DeviceKeysList.fromDb(entry,
|
||||||
|
deviceKeysKeys.where((k) => k.userId == entry.userId).toList(),
|
||||||
|
crossSigningKeys.where((k) => k.userId == entry.userId).toList(),
|
||||||
|
client);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -699,6 +699,7 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
final String userId;
|
final String userId;
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
final String content;
|
final String content;
|
||||||
|
final String validSignatures;
|
||||||
final bool verified;
|
final bool verified;
|
||||||
final bool blocked;
|
final bool blocked;
|
||||||
DbUserDeviceKeysKey(
|
DbUserDeviceKeysKey(
|
||||||
|
@ -706,6 +707,7 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
@required this.userId,
|
@required this.userId,
|
||||||
@required this.deviceId,
|
@required this.deviceId,
|
||||||
@required this.content,
|
@required this.content,
|
||||||
|
this.validSignatures,
|
||||||
this.verified,
|
this.verified,
|
||||||
this.blocked});
|
this.blocked});
|
||||||
factory DbUserDeviceKeysKey.fromData(
|
factory DbUserDeviceKeysKey.fromData(
|
||||||
|
@ -724,6 +726,8 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
.mapFromDatabaseResponse(data['${effectivePrefix}device_id']),
|
.mapFromDatabaseResponse(data['${effectivePrefix}device_id']),
|
||||||
content:
|
content:
|
||||||
stringType.mapFromDatabaseResponse(data['${effectivePrefix}content']),
|
stringType.mapFromDatabaseResponse(data['${effectivePrefix}content']),
|
||||||
|
validSignatures: stringType
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}valid_signatures']),
|
||||||
verified:
|
verified:
|
||||||
boolType.mapFromDatabaseResponse(data['${effectivePrefix}verified']),
|
boolType.mapFromDatabaseResponse(data['${effectivePrefix}verified']),
|
||||||
blocked:
|
blocked:
|
||||||
|
@ -745,6 +749,9 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
if (!nullToAbsent || content != null) {
|
if (!nullToAbsent || content != null) {
|
||||||
map['content'] = Variable<String>(content);
|
map['content'] = Variable<String>(content);
|
||||||
}
|
}
|
||||||
|
if (!nullToAbsent || validSignatures != null) {
|
||||||
|
map['valid_signatures'] = Variable<String>(validSignatures);
|
||||||
|
}
|
||||||
if (!nullToAbsent || verified != null) {
|
if (!nullToAbsent || verified != null) {
|
||||||
map['verified'] = Variable<bool>(verified);
|
map['verified'] = Variable<bool>(verified);
|
||||||
}
|
}
|
||||||
|
@ -762,6 +769,7 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
userId: serializer.fromJson<String>(json['user_id']),
|
userId: serializer.fromJson<String>(json['user_id']),
|
||||||
deviceId: serializer.fromJson<String>(json['device_id']),
|
deviceId: serializer.fromJson<String>(json['device_id']),
|
||||||
content: serializer.fromJson<String>(json['content']),
|
content: serializer.fromJson<String>(json['content']),
|
||||||
|
validSignatures: serializer.fromJson<String>(json['valid_signatures']),
|
||||||
verified: serializer.fromJson<bool>(json['verified']),
|
verified: serializer.fromJson<bool>(json['verified']),
|
||||||
blocked: serializer.fromJson<bool>(json['blocked']),
|
blocked: serializer.fromJson<bool>(json['blocked']),
|
||||||
);
|
);
|
||||||
|
@ -774,6 +782,7 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
'user_id': serializer.toJson<String>(userId),
|
'user_id': serializer.toJson<String>(userId),
|
||||||
'device_id': serializer.toJson<String>(deviceId),
|
'device_id': serializer.toJson<String>(deviceId),
|
||||||
'content': serializer.toJson<String>(content),
|
'content': serializer.toJson<String>(content),
|
||||||
|
'valid_signatures': serializer.toJson<String>(validSignatures),
|
||||||
'verified': serializer.toJson<bool>(verified),
|
'verified': serializer.toJson<bool>(verified),
|
||||||
'blocked': serializer.toJson<bool>(blocked),
|
'blocked': serializer.toJson<bool>(blocked),
|
||||||
};
|
};
|
||||||
|
@ -784,6 +793,7 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
String userId,
|
String userId,
|
||||||
String deviceId,
|
String deviceId,
|
||||||
String content,
|
String content,
|
||||||
|
String validSignatures,
|
||||||
bool verified,
|
bool verified,
|
||||||
bool blocked}) =>
|
bool blocked}) =>
|
||||||
DbUserDeviceKeysKey(
|
DbUserDeviceKeysKey(
|
||||||
|
@ -791,6 +801,7 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
userId: userId ?? this.userId,
|
userId: userId ?? this.userId,
|
||||||
deviceId: deviceId ?? this.deviceId,
|
deviceId: deviceId ?? this.deviceId,
|
||||||
content: content ?? this.content,
|
content: content ?? this.content,
|
||||||
|
validSignatures: validSignatures ?? this.validSignatures,
|
||||||
verified: verified ?? this.verified,
|
verified: verified ?? this.verified,
|
||||||
blocked: blocked ?? this.blocked,
|
blocked: blocked ?? this.blocked,
|
||||||
);
|
);
|
||||||
|
@ -801,6 +812,7 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
..write('userId: $userId, ')
|
..write('userId: $userId, ')
|
||||||
..write('deviceId: $deviceId, ')
|
..write('deviceId: $deviceId, ')
|
||||||
..write('content: $content, ')
|
..write('content: $content, ')
|
||||||
|
..write('validSignatures: $validSignatures, ')
|
||||||
..write('verified: $verified, ')
|
..write('verified: $verified, ')
|
||||||
..write('blocked: $blocked')
|
..write('blocked: $blocked')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
|
@ -814,8 +826,10 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
userId.hashCode,
|
userId.hashCode,
|
||||||
$mrjc(
|
$mrjc(
|
||||||
deviceId.hashCode,
|
deviceId.hashCode,
|
||||||
$mrjc(content.hashCode,
|
$mrjc(
|
||||||
$mrjc(verified.hashCode, blocked.hashCode))))));
|
content.hashCode,
|
||||||
|
$mrjc(validSignatures.hashCode,
|
||||||
|
$mrjc(verified.hashCode, blocked.hashCode)))))));
|
||||||
@override
|
@override
|
||||||
bool operator ==(dynamic other) =>
|
bool operator ==(dynamic other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
|
@ -824,6 +838,7 @@ class DbUserDeviceKeysKey extends DataClass
|
||||||
other.userId == this.userId &&
|
other.userId == this.userId &&
|
||||||
other.deviceId == this.deviceId &&
|
other.deviceId == this.deviceId &&
|
||||||
other.content == this.content &&
|
other.content == this.content &&
|
||||||
|
other.validSignatures == this.validSignatures &&
|
||||||
other.verified == this.verified &&
|
other.verified == this.verified &&
|
||||||
other.blocked == this.blocked);
|
other.blocked == this.blocked);
|
||||||
}
|
}
|
||||||
|
@ -833,6 +848,7 @@ class UserDeviceKeysKeyCompanion extends UpdateCompanion<DbUserDeviceKeysKey> {
|
||||||
final Value<String> userId;
|
final Value<String> userId;
|
||||||
final Value<String> deviceId;
|
final Value<String> deviceId;
|
||||||
final Value<String> content;
|
final Value<String> content;
|
||||||
|
final Value<String> validSignatures;
|
||||||
final Value<bool> verified;
|
final Value<bool> verified;
|
||||||
final Value<bool> blocked;
|
final Value<bool> blocked;
|
||||||
const UserDeviceKeysKeyCompanion({
|
const UserDeviceKeysKeyCompanion({
|
||||||
|
@ -840,6 +856,7 @@ class UserDeviceKeysKeyCompanion extends UpdateCompanion<DbUserDeviceKeysKey> {
|
||||||
this.userId = const Value.absent(),
|
this.userId = const Value.absent(),
|
||||||
this.deviceId = const Value.absent(),
|
this.deviceId = const Value.absent(),
|
||||||
this.content = const Value.absent(),
|
this.content = const Value.absent(),
|
||||||
|
this.validSignatures = const Value.absent(),
|
||||||
this.verified = const Value.absent(),
|
this.verified = const Value.absent(),
|
||||||
this.blocked = const Value.absent(),
|
this.blocked = const Value.absent(),
|
||||||
});
|
});
|
||||||
|
@ -848,6 +865,7 @@ class UserDeviceKeysKeyCompanion extends UpdateCompanion<DbUserDeviceKeysKey> {
|
||||||
@required String userId,
|
@required String userId,
|
||||||
@required String deviceId,
|
@required String deviceId,
|
||||||
@required String content,
|
@required String content,
|
||||||
|
this.validSignatures = const Value.absent(),
|
||||||
this.verified = const Value.absent(),
|
this.verified = const Value.absent(),
|
||||||
this.blocked = const Value.absent(),
|
this.blocked = const Value.absent(),
|
||||||
}) : clientId = Value(clientId),
|
}) : clientId = Value(clientId),
|
||||||
|
@ -859,6 +877,7 @@ class UserDeviceKeysKeyCompanion extends UpdateCompanion<DbUserDeviceKeysKey> {
|
||||||
Expression<String> userId,
|
Expression<String> userId,
|
||||||
Expression<String> deviceId,
|
Expression<String> deviceId,
|
||||||
Expression<String> content,
|
Expression<String> content,
|
||||||
|
Expression<String> validSignatures,
|
||||||
Expression<bool> verified,
|
Expression<bool> verified,
|
||||||
Expression<bool> blocked,
|
Expression<bool> blocked,
|
||||||
}) {
|
}) {
|
||||||
|
@ -867,6 +886,7 @@ class UserDeviceKeysKeyCompanion extends UpdateCompanion<DbUserDeviceKeysKey> {
|
||||||
if (userId != null) 'user_id': userId,
|
if (userId != null) 'user_id': userId,
|
||||||
if (deviceId != null) 'device_id': deviceId,
|
if (deviceId != null) 'device_id': deviceId,
|
||||||
if (content != null) 'content': content,
|
if (content != null) 'content': content,
|
||||||
|
if (validSignatures != null) 'valid_signatures': validSignatures,
|
||||||
if (verified != null) 'verified': verified,
|
if (verified != null) 'verified': verified,
|
||||||
if (blocked != null) 'blocked': blocked,
|
if (blocked != null) 'blocked': blocked,
|
||||||
});
|
});
|
||||||
|
@ -877,6 +897,7 @@ class UserDeviceKeysKeyCompanion extends UpdateCompanion<DbUserDeviceKeysKey> {
|
||||||
Value<String> userId,
|
Value<String> userId,
|
||||||
Value<String> deviceId,
|
Value<String> deviceId,
|
||||||
Value<String> content,
|
Value<String> content,
|
||||||
|
Value<String> validSignatures,
|
||||||
Value<bool> verified,
|
Value<bool> verified,
|
||||||
Value<bool> blocked}) {
|
Value<bool> blocked}) {
|
||||||
return UserDeviceKeysKeyCompanion(
|
return UserDeviceKeysKeyCompanion(
|
||||||
|
@ -884,6 +905,7 @@ class UserDeviceKeysKeyCompanion extends UpdateCompanion<DbUserDeviceKeysKey> {
|
||||||
userId: userId ?? this.userId,
|
userId: userId ?? this.userId,
|
||||||
deviceId: deviceId ?? this.deviceId,
|
deviceId: deviceId ?? this.deviceId,
|
||||||
content: content ?? this.content,
|
content: content ?? this.content,
|
||||||
|
validSignatures: validSignatures ?? this.validSignatures,
|
||||||
verified: verified ?? this.verified,
|
verified: verified ?? this.verified,
|
||||||
blocked: blocked ?? this.blocked,
|
blocked: blocked ?? this.blocked,
|
||||||
);
|
);
|
||||||
|
@ -904,6 +926,9 @@ class UserDeviceKeysKeyCompanion extends UpdateCompanion<DbUserDeviceKeysKey> {
|
||||||
if (content.present) {
|
if (content.present) {
|
||||||
map['content'] = Variable<String>(content.value);
|
map['content'] = Variable<String>(content.value);
|
||||||
}
|
}
|
||||||
|
if (validSignatures.present) {
|
||||||
|
map['valid_signatures'] = Variable<String>(validSignatures.value);
|
||||||
|
}
|
||||||
if (verified.present) {
|
if (verified.present) {
|
||||||
map['verified'] = Variable<bool>(verified.value);
|
map['verified'] = Variable<bool>(verified.value);
|
||||||
}
|
}
|
||||||
|
@ -951,6 +976,16 @@ class UserDeviceKeysKey extends Table
|
||||||
$customConstraints: 'NOT NULL');
|
$customConstraints: 'NOT NULL');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final VerificationMeta _validSignaturesMeta =
|
||||||
|
const VerificationMeta('validSignatures');
|
||||||
|
GeneratedTextColumn _validSignatures;
|
||||||
|
GeneratedTextColumn get validSignatures =>
|
||||||
|
_validSignatures ??= _constructValidSignatures();
|
||||||
|
GeneratedTextColumn _constructValidSignatures() {
|
||||||
|
return GeneratedTextColumn('valid_signatures', $tableName, true,
|
||||||
|
$customConstraints: '');
|
||||||
|
}
|
||||||
|
|
||||||
final VerificationMeta _verifiedMeta = const VerificationMeta('verified');
|
final VerificationMeta _verifiedMeta = const VerificationMeta('verified');
|
||||||
GeneratedBoolColumn _verified;
|
GeneratedBoolColumn _verified;
|
||||||
GeneratedBoolColumn get verified => _verified ??= _constructVerified();
|
GeneratedBoolColumn get verified => _verified ??= _constructVerified();
|
||||||
|
@ -971,7 +1006,7 @@ class UserDeviceKeysKey extends Table
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns =>
|
List<GeneratedColumn> get $columns =>
|
||||||
[clientId, userId, deviceId, content, verified, blocked];
|
[clientId, userId, deviceId, content, validSignatures, verified, blocked];
|
||||||
@override
|
@override
|
||||||
UserDeviceKeysKey get asDslTable => this;
|
UserDeviceKeysKey get asDslTable => this;
|
||||||
@override
|
@override
|
||||||
|
@ -1008,6 +1043,12 @@ class UserDeviceKeysKey extends Table
|
||||||
} else if (isInserting) {
|
} else if (isInserting) {
|
||||||
context.missing(_contentMeta);
|
context.missing(_contentMeta);
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('valid_signatures')) {
|
||||||
|
context.handle(
|
||||||
|
_validSignaturesMeta,
|
||||||
|
validSignatures.isAcceptableOrUnknown(
|
||||||
|
data['valid_signatures'], _validSignaturesMeta));
|
||||||
|
}
|
||||||
if (data.containsKey('verified')) {
|
if (data.containsKey('verified')) {
|
||||||
context.handle(_verifiedMeta,
|
context.handle(_verifiedMeta,
|
||||||
verified.isAcceptableOrUnknown(data['verified'], _verifiedMeta));
|
verified.isAcceptableOrUnknown(data['verified'], _verifiedMeta));
|
||||||
|
@ -1039,6 +1080,401 @@ class UserDeviceKeysKey extends Table
|
||||||
bool get dontWriteConstraints => true;
|
bool get dontWriteConstraints => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DbUserCrossSigningKey extends DataClass
|
||||||
|
implements Insertable<DbUserCrossSigningKey> {
|
||||||
|
final int clientId;
|
||||||
|
final String userId;
|
||||||
|
final String publicKey;
|
||||||
|
final String content;
|
||||||
|
final String validSignatures;
|
||||||
|
final bool verified;
|
||||||
|
final bool blocked;
|
||||||
|
DbUserCrossSigningKey(
|
||||||
|
{@required this.clientId,
|
||||||
|
@required this.userId,
|
||||||
|
@required this.publicKey,
|
||||||
|
@required this.content,
|
||||||
|
this.validSignatures,
|
||||||
|
this.verified,
|
||||||
|
this.blocked});
|
||||||
|
factory DbUserCrossSigningKey.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 boolType = db.typeSystem.forDartType<bool>();
|
||||||
|
return DbUserCrossSigningKey(
|
||||||
|
clientId:
|
||||||
|
intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']),
|
||||||
|
userId:
|
||||||
|
stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']),
|
||||||
|
publicKey: stringType
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}public_key']),
|
||||||
|
content:
|
||||||
|
stringType.mapFromDatabaseResponse(data['${effectivePrefix}content']),
|
||||||
|
validSignatures: stringType
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}valid_signatures']),
|
||||||
|
verified:
|
||||||
|
boolType.mapFromDatabaseResponse(data['${effectivePrefix}verified']),
|
||||||
|
blocked:
|
||||||
|
boolType.mapFromDatabaseResponse(data['${effectivePrefix}blocked']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, Expression>{};
|
||||||
|
if (!nullToAbsent || clientId != null) {
|
||||||
|
map['client_id'] = Variable<int>(clientId);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || userId != null) {
|
||||||
|
map['user_id'] = Variable<String>(userId);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || publicKey != null) {
|
||||||
|
map['public_key'] = Variable<String>(publicKey);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || content != null) {
|
||||||
|
map['content'] = Variable<String>(content);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || validSignatures != null) {
|
||||||
|
map['valid_signatures'] = Variable<String>(validSignatures);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || verified != null) {
|
||||||
|
map['verified'] = Variable<bool>(verified);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || blocked != null) {
|
||||||
|
map['blocked'] = Variable<bool>(blocked);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory DbUserCrossSigningKey.fromJson(Map<String, dynamic> json,
|
||||||
|
{ValueSerializer serializer}) {
|
||||||
|
serializer ??= moorRuntimeOptions.defaultSerializer;
|
||||||
|
return DbUserCrossSigningKey(
|
||||||
|
clientId: serializer.fromJson<int>(json['client_id']),
|
||||||
|
userId: serializer.fromJson<String>(json['user_id']),
|
||||||
|
publicKey: serializer.fromJson<String>(json['public_key']),
|
||||||
|
content: serializer.fromJson<String>(json['content']),
|
||||||
|
validSignatures: serializer.fromJson<String>(json['valid_signatures']),
|
||||||
|
verified: serializer.fromJson<bool>(json['verified']),
|
||||||
|
blocked: serializer.fromJson<bool>(json['blocked']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({ValueSerializer serializer}) {
|
||||||
|
serializer ??= moorRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'client_id': serializer.toJson<int>(clientId),
|
||||||
|
'user_id': serializer.toJson<String>(userId),
|
||||||
|
'public_key': serializer.toJson<String>(publicKey),
|
||||||
|
'content': serializer.toJson<String>(content),
|
||||||
|
'valid_signatures': serializer.toJson<String>(validSignatures),
|
||||||
|
'verified': serializer.toJson<bool>(verified),
|
||||||
|
'blocked': serializer.toJson<bool>(blocked),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
DbUserCrossSigningKey copyWith(
|
||||||
|
{int clientId,
|
||||||
|
String userId,
|
||||||
|
String publicKey,
|
||||||
|
String content,
|
||||||
|
String validSignatures,
|
||||||
|
bool verified,
|
||||||
|
bool blocked}) =>
|
||||||
|
DbUserCrossSigningKey(
|
||||||
|
clientId: clientId ?? this.clientId,
|
||||||
|
userId: userId ?? this.userId,
|
||||||
|
publicKey: publicKey ?? this.publicKey,
|
||||||
|
content: content ?? this.content,
|
||||||
|
validSignatures: validSignatures ?? this.validSignatures,
|
||||||
|
verified: verified ?? this.verified,
|
||||||
|
blocked: blocked ?? this.blocked,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('DbUserCrossSigningKey(')
|
||||||
|
..write('clientId: $clientId, ')
|
||||||
|
..write('userId: $userId, ')
|
||||||
|
..write('publicKey: $publicKey, ')
|
||||||
|
..write('content: $content, ')
|
||||||
|
..write('validSignatures: $validSignatures, ')
|
||||||
|
..write('verified: $verified, ')
|
||||||
|
..write('blocked: $blocked')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => $mrjf($mrjc(
|
||||||
|
clientId.hashCode,
|
||||||
|
$mrjc(
|
||||||
|
userId.hashCode,
|
||||||
|
$mrjc(
|
||||||
|
publicKey.hashCode,
|
||||||
|
$mrjc(
|
||||||
|
content.hashCode,
|
||||||
|
$mrjc(validSignatures.hashCode,
|
||||||
|
$mrjc(verified.hashCode, blocked.hashCode)))))));
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is DbUserCrossSigningKey &&
|
||||||
|
other.clientId == this.clientId &&
|
||||||
|
other.userId == this.userId &&
|
||||||
|
other.publicKey == this.publicKey &&
|
||||||
|
other.content == this.content &&
|
||||||
|
other.validSignatures == this.validSignatures &&
|
||||||
|
other.verified == this.verified &&
|
||||||
|
other.blocked == this.blocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserCrossSigningKeysCompanion
|
||||||
|
extends UpdateCompanion<DbUserCrossSigningKey> {
|
||||||
|
final Value<int> clientId;
|
||||||
|
final Value<String> userId;
|
||||||
|
final Value<String> publicKey;
|
||||||
|
final Value<String> content;
|
||||||
|
final Value<String> validSignatures;
|
||||||
|
final Value<bool> verified;
|
||||||
|
final Value<bool> blocked;
|
||||||
|
const UserCrossSigningKeysCompanion({
|
||||||
|
this.clientId = const Value.absent(),
|
||||||
|
this.userId = const Value.absent(),
|
||||||
|
this.publicKey = const Value.absent(),
|
||||||
|
this.content = const Value.absent(),
|
||||||
|
this.validSignatures = const Value.absent(),
|
||||||
|
this.verified = const Value.absent(),
|
||||||
|
this.blocked = const Value.absent(),
|
||||||
|
});
|
||||||
|
UserCrossSigningKeysCompanion.insert({
|
||||||
|
@required int clientId,
|
||||||
|
@required String userId,
|
||||||
|
@required String publicKey,
|
||||||
|
@required String content,
|
||||||
|
this.validSignatures = const Value.absent(),
|
||||||
|
this.verified = const Value.absent(),
|
||||||
|
this.blocked = const Value.absent(),
|
||||||
|
}) : clientId = Value(clientId),
|
||||||
|
userId = Value(userId),
|
||||||
|
publicKey = Value(publicKey),
|
||||||
|
content = Value(content);
|
||||||
|
static Insertable<DbUserCrossSigningKey> custom({
|
||||||
|
Expression<int> clientId,
|
||||||
|
Expression<String> userId,
|
||||||
|
Expression<String> publicKey,
|
||||||
|
Expression<String> content,
|
||||||
|
Expression<String> validSignatures,
|
||||||
|
Expression<bool> verified,
|
||||||
|
Expression<bool> blocked,
|
||||||
|
}) {
|
||||||
|
return RawValuesInsertable({
|
||||||
|
if (clientId != null) 'client_id': clientId,
|
||||||
|
if (userId != null) 'user_id': userId,
|
||||||
|
if (publicKey != null) 'public_key': publicKey,
|
||||||
|
if (content != null) 'content': content,
|
||||||
|
if (validSignatures != null) 'valid_signatures': validSignatures,
|
||||||
|
if (verified != null) 'verified': verified,
|
||||||
|
if (blocked != null) 'blocked': blocked,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
UserCrossSigningKeysCompanion copyWith(
|
||||||
|
{Value<int> clientId,
|
||||||
|
Value<String> userId,
|
||||||
|
Value<String> publicKey,
|
||||||
|
Value<String> content,
|
||||||
|
Value<String> validSignatures,
|
||||||
|
Value<bool> verified,
|
||||||
|
Value<bool> blocked}) {
|
||||||
|
return UserCrossSigningKeysCompanion(
|
||||||
|
clientId: clientId ?? this.clientId,
|
||||||
|
userId: userId ?? this.userId,
|
||||||
|
publicKey: publicKey ?? this.publicKey,
|
||||||
|
content: content ?? this.content,
|
||||||
|
validSignatures: validSignatures ?? this.validSignatures,
|
||||||
|
verified: verified ?? this.verified,
|
||||||
|
blocked: blocked ?? this.blocked,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, Expression>{};
|
||||||
|
if (clientId.present) {
|
||||||
|
map['client_id'] = Variable<int>(clientId.value);
|
||||||
|
}
|
||||||
|
if (userId.present) {
|
||||||
|
map['user_id'] = Variable<String>(userId.value);
|
||||||
|
}
|
||||||
|
if (publicKey.present) {
|
||||||
|
map['public_key'] = Variable<String>(publicKey.value);
|
||||||
|
}
|
||||||
|
if (content.present) {
|
||||||
|
map['content'] = Variable<String>(content.value);
|
||||||
|
}
|
||||||
|
if (validSignatures.present) {
|
||||||
|
map['valid_signatures'] = Variable<String>(validSignatures.value);
|
||||||
|
}
|
||||||
|
if (verified.present) {
|
||||||
|
map['verified'] = Variable<bool>(verified.value);
|
||||||
|
}
|
||||||
|
if (blocked.present) {
|
||||||
|
map['blocked'] = Variable<bool>(blocked.value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserCrossSigningKeys extends Table
|
||||||
|
with TableInfo<UserCrossSigningKeys, DbUserCrossSigningKey> {
|
||||||
|
final GeneratedDatabase _db;
|
||||||
|
final String _alias;
|
||||||
|
UserCrossSigningKeys(this._db, [this._alias]);
|
||||||
|
final VerificationMeta _clientIdMeta = const VerificationMeta('clientId');
|
||||||
|
GeneratedIntColumn _clientId;
|
||||||
|
GeneratedIntColumn get clientId => _clientId ??= _constructClientId();
|
||||||
|
GeneratedIntColumn _constructClientId() {
|
||||||
|
return GeneratedIntColumn('client_id', $tableName, false,
|
||||||
|
$customConstraints: 'NOT NULL REFERENCES clients(client_id)');
|
||||||
|
}
|
||||||
|
|
||||||
|
final VerificationMeta _userIdMeta = const VerificationMeta('userId');
|
||||||
|
GeneratedTextColumn _userId;
|
||||||
|
GeneratedTextColumn get userId => _userId ??= _constructUserId();
|
||||||
|
GeneratedTextColumn _constructUserId() {
|
||||||
|
return GeneratedTextColumn('user_id', $tableName, false,
|
||||||
|
$customConstraints: 'NOT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
final VerificationMeta _publicKeyMeta = const VerificationMeta('publicKey');
|
||||||
|
GeneratedTextColumn _publicKey;
|
||||||
|
GeneratedTextColumn get publicKey => _publicKey ??= _constructPublicKey();
|
||||||
|
GeneratedTextColumn _constructPublicKey() {
|
||||||
|
return GeneratedTextColumn('public_key', $tableName, false,
|
||||||
|
$customConstraints: 'NOT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
final VerificationMeta _contentMeta = const VerificationMeta('content');
|
||||||
|
GeneratedTextColumn _content;
|
||||||
|
GeneratedTextColumn get content => _content ??= _constructContent();
|
||||||
|
GeneratedTextColumn _constructContent() {
|
||||||
|
return GeneratedTextColumn('content', $tableName, false,
|
||||||
|
$customConstraints: 'NOT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
final VerificationMeta _validSignaturesMeta =
|
||||||
|
const VerificationMeta('validSignatures');
|
||||||
|
GeneratedTextColumn _validSignatures;
|
||||||
|
GeneratedTextColumn get validSignatures =>
|
||||||
|
_validSignatures ??= _constructValidSignatures();
|
||||||
|
GeneratedTextColumn _constructValidSignatures() {
|
||||||
|
return GeneratedTextColumn('valid_signatures', $tableName, true,
|
||||||
|
$customConstraints: '');
|
||||||
|
}
|
||||||
|
|
||||||
|
final VerificationMeta _verifiedMeta = const VerificationMeta('verified');
|
||||||
|
GeneratedBoolColumn _verified;
|
||||||
|
GeneratedBoolColumn get verified => _verified ??= _constructVerified();
|
||||||
|
GeneratedBoolColumn _constructVerified() {
|
||||||
|
return GeneratedBoolColumn('verified', $tableName, true,
|
||||||
|
$customConstraints: 'DEFAULT false',
|
||||||
|
defaultValue: const CustomExpression<bool>('false'));
|
||||||
|
}
|
||||||
|
|
||||||
|
final VerificationMeta _blockedMeta = const VerificationMeta('blocked');
|
||||||
|
GeneratedBoolColumn _blocked;
|
||||||
|
GeneratedBoolColumn get blocked => _blocked ??= _constructBlocked();
|
||||||
|
GeneratedBoolColumn _constructBlocked() {
|
||||||
|
return GeneratedBoolColumn('blocked', $tableName, true,
|
||||||
|
$customConstraints: 'DEFAULT false',
|
||||||
|
defaultValue: const CustomExpression<bool>('false'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [
|
||||||
|
clientId,
|
||||||
|
userId,
|
||||||
|
publicKey,
|
||||||
|
content,
|
||||||
|
validSignatures,
|
||||||
|
verified,
|
||||||
|
blocked
|
||||||
|
];
|
||||||
|
@override
|
||||||
|
UserCrossSigningKeys get asDslTable => this;
|
||||||
|
@override
|
||||||
|
String get $tableName => _alias ?? 'user_cross_signing_keys';
|
||||||
|
@override
|
||||||
|
final String actualTableName = 'user_cross_signing_keys';
|
||||||
|
@override
|
||||||
|
VerificationContext validateIntegrity(
|
||||||
|
Insertable<DbUserCrossSigningKey> instance,
|
||||||
|
{bool isInserting = false}) {
|
||||||
|
final context = VerificationContext();
|
||||||
|
final data = instance.toColumns(true);
|
||||||
|
if (data.containsKey('client_id')) {
|
||||||
|
context.handle(_clientIdMeta,
|
||||||
|
clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_clientIdMeta);
|
||||||
|
}
|
||||||
|
if (data.containsKey('user_id')) {
|
||||||
|
context.handle(_userIdMeta,
|
||||||
|
userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_userIdMeta);
|
||||||
|
}
|
||||||
|
if (data.containsKey('public_key')) {
|
||||||
|
context.handle(_publicKeyMeta,
|
||||||
|
publicKey.isAcceptableOrUnknown(data['public_key'], _publicKeyMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_publicKeyMeta);
|
||||||
|
}
|
||||||
|
if (data.containsKey('content')) {
|
||||||
|
context.handle(_contentMeta,
|
||||||
|
content.isAcceptableOrUnknown(data['content'], _contentMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_contentMeta);
|
||||||
|
}
|
||||||
|
if (data.containsKey('valid_signatures')) {
|
||||||
|
context.handle(
|
||||||
|
_validSignaturesMeta,
|
||||||
|
validSignatures.isAcceptableOrUnknown(
|
||||||
|
data['valid_signatures'], _validSignaturesMeta));
|
||||||
|
}
|
||||||
|
if (data.containsKey('verified')) {
|
||||||
|
context.handle(_verifiedMeta,
|
||||||
|
verified.isAcceptableOrUnknown(data['verified'], _verifiedMeta));
|
||||||
|
}
|
||||||
|
if (data.containsKey('blocked')) {
|
||||||
|
context.handle(_blockedMeta,
|
||||||
|
blocked.isAcceptableOrUnknown(data['blocked'], _blockedMeta));
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
||||||
|
@override
|
||||||
|
DbUserCrossSigningKey map(Map<String, dynamic> data, {String tablePrefix}) {
|
||||||
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
|
||||||
|
return DbUserCrossSigningKey.fromData(data, _db, prefix: effectivePrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
UserCrossSigningKeys createAlias(String alias) {
|
||||||
|
return UserCrossSigningKeys(_db, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get customConstraints =>
|
||||||
|
const ['UNIQUE(client_id, user_id, public_key)'];
|
||||||
|
@override
|
||||||
|
bool get dontWriteConstraints => true;
|
||||||
|
}
|
||||||
|
|
||||||
class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> {
|
class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> {
|
||||||
final int clientId;
|
final int clientId;
|
||||||
final String identityKey;
|
final String identityKey;
|
||||||
|
@ -4680,6 +5116,13 @@ abstract class _$Database extends GeneratedDatabase {
|
||||||
Index get userDeviceKeysKeyIndex => _userDeviceKeysKeyIndex ??= Index(
|
Index get userDeviceKeysKeyIndex => _userDeviceKeysKeyIndex ??= Index(
|
||||||
'user_device_keys_key_index',
|
'user_device_keys_key_index',
|
||||||
'CREATE INDEX user_device_keys_key_index ON user_device_keys_key(client_id);');
|
'CREATE INDEX user_device_keys_key_index ON user_device_keys_key(client_id);');
|
||||||
|
UserCrossSigningKeys _userCrossSigningKeys;
|
||||||
|
UserCrossSigningKeys get userCrossSigningKeys =>
|
||||||
|
_userCrossSigningKeys ??= UserCrossSigningKeys(this);
|
||||||
|
Index _userCrossSigningKeysIndex;
|
||||||
|
Index get userCrossSigningKeysIndex => _userCrossSigningKeysIndex ??= Index(
|
||||||
|
'user_cross_signing_keys_index',
|
||||||
|
'CREATE INDEX user_cross_signing_keys_index ON user_cross_signing_keys(client_id);');
|
||||||
OlmSessions _olmSessions;
|
OlmSessions _olmSessions;
|
||||||
OlmSessions get olmSessions => _olmSessions ??= OlmSessions(this);
|
OlmSessions get olmSessions => _olmSessions ??= OlmSessions(this);
|
||||||
Index _olmSessionsIndex;
|
Index _olmSessionsIndex;
|
||||||
|
@ -4823,6 +5266,7 @@ abstract class _$Database extends GeneratedDatabase {
|
||||||
userId: row.readString('user_id'),
|
userId: row.readString('user_id'),
|
||||||
deviceId: row.readString('device_id'),
|
deviceId: row.readString('device_id'),
|
||||||
content: row.readString('content'),
|
content: row.readString('content'),
|
||||||
|
validSignatures: row.readString('valid_signatures'),
|
||||||
verified: row.readBool('verified'),
|
verified: row.readBool('verified'),
|
||||||
blocked: row.readBool('blocked'),
|
blocked: row.readBool('blocked'),
|
||||||
);
|
);
|
||||||
|
@ -4835,6 +5279,25 @@ abstract class _$Database extends GeneratedDatabase {
|
||||||
readsFrom: {userDeviceKeysKey}).map(_rowToDbUserDeviceKeysKey);
|
readsFrom: {userDeviceKeysKey}).map(_rowToDbUserDeviceKeysKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DbUserCrossSigningKey _rowToDbUserCrossSigningKey(QueryRow row) {
|
||||||
|
return DbUserCrossSigningKey(
|
||||||
|
clientId: row.readInt('client_id'),
|
||||||
|
userId: row.readString('user_id'),
|
||||||
|
publicKey: row.readString('public_key'),
|
||||||
|
content: row.readString('content'),
|
||||||
|
validSignatures: row.readString('valid_signatures'),
|
||||||
|
verified: row.readBool('verified'),
|
||||||
|
blocked: row.readBool('blocked'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Selectable<DbUserCrossSigningKey> getAllUserCrossSigningKeys(int client_id) {
|
||||||
|
return customSelect(
|
||||||
|
'SELECT * FROM user_cross_signing_keys WHERE client_id = :client_id',
|
||||||
|
variables: [Variable.withInt(client_id)],
|
||||||
|
readsFrom: {userCrossSigningKeys}).map(_rowToDbUserCrossSigningKey);
|
||||||
|
}
|
||||||
|
|
||||||
DbOlmSessions _rowToDbOlmSessions(QueryRow row) {
|
DbOlmSessions _rowToDbOlmSessions(QueryRow row) {
|
||||||
return DbOlmSessions(
|
return DbOlmSessions(
|
||||||
clientId: row.readInt('client_id'),
|
clientId: row.readInt('client_id'),
|
||||||
|
@ -5036,15 +5499,22 @@ abstract class _$Database extends GeneratedDatabase {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> storeUserDeviceKey(int client_id, String user_id,
|
Future<int> storeUserDeviceKey(
|
||||||
String device_id, String content, bool verified, bool blocked) {
|
int client_id,
|
||||||
|
String user_id,
|
||||||
|
String device_id,
|
||||||
|
String content,
|
||||||
|
String valid_signatures,
|
||||||
|
bool verified,
|
||||||
|
bool blocked) {
|
||||||
return customInsert(
|
return customInsert(
|
||||||
'INSERT OR REPLACE INTO user_device_keys_key (client_id, user_id, device_id, content, verified, blocked) VALUES (:client_id, :user_id, :device_id, :content, :verified, :blocked)',
|
'INSERT OR REPLACE INTO user_device_keys_key (client_id, user_id, device_id, content, valid_signatures, verified, blocked) VALUES (:client_id, :user_id, :device_id, :content, :valid_signatures, :verified, :blocked)',
|
||||||
variables: [
|
variables: [
|
||||||
Variable.withInt(client_id),
|
Variable.withInt(client_id),
|
||||||
Variable.withString(user_id),
|
Variable.withString(user_id),
|
||||||
Variable.withString(device_id),
|
Variable.withString(device_id),
|
||||||
Variable.withString(content),
|
Variable.withString(content),
|
||||||
|
Variable.withString(valid_signatures),
|
||||||
Variable.withBool(verified),
|
Variable.withBool(verified),
|
||||||
Variable.withBool(blocked)
|
Variable.withBool(blocked)
|
||||||
],
|
],
|
||||||
|
@ -5066,6 +5536,73 @@ abstract class _$Database extends GeneratedDatabase {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<int> setVerifiedUserCrossSigningKey(
|
||||||
|
bool verified, int client_id, String user_id, String public_key) {
|
||||||
|
return customUpdate(
|
||||||
|
'UPDATE user_cross_signing_keys SET verified = :verified WHERE client_id = :client_id AND user_id = :user_id AND public_key = :public_key',
|
||||||
|
variables: [
|
||||||
|
Variable.withBool(verified),
|
||||||
|
Variable.withInt(client_id),
|
||||||
|
Variable.withString(user_id),
|
||||||
|
Variable.withString(public_key)
|
||||||
|
],
|
||||||
|
updates: {userCrossSigningKeys},
|
||||||
|
updateKind: UpdateKind.update,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> setBlockedUserCrossSigningKey(
|
||||||
|
bool blocked, int client_id, String user_id, String public_key) {
|
||||||
|
return customUpdate(
|
||||||
|
'UPDATE user_cross_signing_keys SET blocked = :blocked WHERE client_id = :client_id AND user_id = :user_id AND public_key = :public_key',
|
||||||
|
variables: [
|
||||||
|
Variable.withBool(blocked),
|
||||||
|
Variable.withInt(client_id),
|
||||||
|
Variable.withString(user_id),
|
||||||
|
Variable.withString(public_key)
|
||||||
|
],
|
||||||
|
updates: {userCrossSigningKeys},
|
||||||
|
updateKind: UpdateKind.update,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> storeUserCrossSigningKey(
|
||||||
|
int client_id,
|
||||||
|
String user_id,
|
||||||
|
String public_key,
|
||||||
|
String content,
|
||||||
|
String valid_signatures,
|
||||||
|
bool verified,
|
||||||
|
bool blocked) {
|
||||||
|
return customInsert(
|
||||||
|
'INSERT OR REPLACE INTO user_cross_signing_keys (client_id, user_id, public_key, content, valid_signatures, verified, blocked) VALUES (:client_id, :user_id, :public_key, :content, :valid_signatures, :verified, :blocked)',
|
||||||
|
variables: [
|
||||||
|
Variable.withInt(client_id),
|
||||||
|
Variable.withString(user_id),
|
||||||
|
Variable.withString(public_key),
|
||||||
|
Variable.withString(content),
|
||||||
|
Variable.withString(valid_signatures),
|
||||||
|
Variable.withBool(verified),
|
||||||
|
Variable.withBool(blocked)
|
||||||
|
],
|
||||||
|
updates: {userCrossSigningKeys},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> removeUserCrossSigningKey(
|
||||||
|
int client_id, String user_id, String public_key) {
|
||||||
|
return customUpdate(
|
||||||
|
'DELETE FROM user_cross_signing_keys WHERE client_id = :client_id AND user_id = :user_id AND public_key = :public_key',
|
||||||
|
variables: [
|
||||||
|
Variable.withInt(client_id),
|
||||||
|
Variable.withString(user_id),
|
||||||
|
Variable.withString(public_key)
|
||||||
|
],
|
||||||
|
updates: {userCrossSigningKeys},
|
||||||
|
updateKind: UpdateKind.delete,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<int> insertClient(
|
Future<int> insertClient(
|
||||||
String name,
|
String name,
|
||||||
String homeserver_url,
|
String homeserver_url,
|
||||||
|
@ -5472,6 +6009,8 @@ abstract class _$Database extends GeneratedDatabase {
|
||||||
userDeviceKeysIndex,
|
userDeviceKeysIndex,
|
||||||
userDeviceKeysKey,
|
userDeviceKeysKey,
|
||||||
userDeviceKeysKeyIndex,
|
userDeviceKeysKeyIndex,
|
||||||
|
userCrossSigningKeys,
|
||||||
|
userCrossSigningKeysIndex,
|
||||||
olmSessions,
|
olmSessions,
|
||||||
olmSessionsIndex,
|
olmSessionsIndex,
|
||||||
outboundGroupSessions,
|
outboundGroupSessions,
|
||||||
|
|
|
@ -26,12 +26,25 @@ CREATE TABLE user_device_keys_key (
|
||||||
user_id TEXT NOT NULL,
|
user_id TEXT NOT NULL,
|
||||||
device_id TEXT NOT NULL,
|
device_id TEXT NOT NULL,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
|
valid_signatures TEXT,
|
||||||
verified BOOLEAN DEFAULT false,
|
verified BOOLEAN DEFAULT false,
|
||||||
blocked BOOLEAN DEFAULT false,
|
blocked BOOLEAN DEFAULT false,
|
||||||
UNIQUE(client_id, user_id, device_id)
|
UNIQUE(client_id, user_id, device_id)
|
||||||
) as DbUserDeviceKeysKey;
|
) as DbUserDeviceKeysKey;
|
||||||
CREATE INDEX user_device_keys_key_index ON user_device_keys_key(client_id);
|
CREATE INDEX user_device_keys_key_index ON user_device_keys_key(client_id);
|
||||||
|
|
||||||
|
CREATE TABLE user_cross_signing_keys (
|
||||||
|
client_id INTEGER NOT NULL REFERENCES clients(client_id),
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
public_key TEXT NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
valid_signatures TEXT,
|
||||||
|
verified BOOLEAN DEFAULT false,
|
||||||
|
blocked BOOLEAN DEFAULT false,
|
||||||
|
UNIQUE(client_id, user_id, public_key)
|
||||||
|
) as DbUserCrossSigningKey;
|
||||||
|
CREATE INDEX user_cross_signing_keys_index ON user_cross_signing_keys(client_id);
|
||||||
|
|
||||||
CREATE TABLE olm_sessions (
|
CREATE TABLE olm_sessions (
|
||||||
client_id INTEGER NOT NULL REFERENCES clients(client_id),
|
client_id INTEGER NOT NULL REFERENCES clients(client_id),
|
||||||
identity_key TEXT NOT NULL,
|
identity_key TEXT NOT NULL,
|
||||||
|
@ -154,6 +167,7 @@ updateClientKeys: UPDATE clients SET olm_account = :olm_account WHERE client_id
|
||||||
storePrevBatch: UPDATE clients SET prev_batch = :prev_batch WHERE client_id = :client_id;
|
storePrevBatch: UPDATE clients SET prev_batch = :prev_batch WHERE client_id = :client_id;
|
||||||
getAllUserDeviceKeys: SELECT * FROM user_device_keys WHERE client_id = :client_id;
|
getAllUserDeviceKeys: SELECT * FROM user_device_keys WHERE client_id = :client_id;
|
||||||
getAllUserDeviceKeysKeys: SELECT * FROM user_device_keys_key WHERE client_id = :client_id;
|
getAllUserDeviceKeysKeys: SELECT * FROM user_device_keys_key WHERE client_id = :client_id;
|
||||||
|
getAllUserCrossSigningKeys: SELECT * FROM user_cross_signing_keys WHERE client_id = :client_id;
|
||||||
getAllOlmSessions: SELECT * FROM olm_sessions WHERE client_id = :client_id;
|
getAllOlmSessions: SELECT * FROM olm_sessions WHERE client_id = :client_id;
|
||||||
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) VALUES (:client_id, :identitiy_key, :session_id, :pickle);
|
||||||
getAllOutboundGroupSessions: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id;
|
getAllOutboundGroupSessions: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id;
|
||||||
|
@ -168,8 +182,12 @@ updateInboundGroupSessionIndexes: UPDATE inbound_group_sessions SET indexes = :i
|
||||||
storeUserDeviceKeysInfo: INSERT OR REPLACE INTO user_device_keys (client_id, user_id, outdated) VALUES (:client_id, :user_id, :outdated);
|
storeUserDeviceKeysInfo: INSERT OR REPLACE INTO user_device_keys (client_id, user_id, outdated) VALUES (:client_id, :user_id, :outdated);
|
||||||
setVerifiedUserDeviceKey: UPDATE user_device_keys_key SET verified = :verified WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id;
|
setVerifiedUserDeviceKey: UPDATE user_device_keys_key SET verified = :verified WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id;
|
||||||
setBlockedUserDeviceKey: UPDATE user_device_keys_key SET blocked = :blocked WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id;
|
setBlockedUserDeviceKey: UPDATE user_device_keys_key SET blocked = :blocked WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id;
|
||||||
storeUserDeviceKey: INSERT OR REPLACE INTO user_device_keys_key (client_id, user_id, device_id, content, verified, blocked) VALUES (:client_id, :user_id, :device_id, :content, :verified, :blocked);
|
storeUserDeviceKey: INSERT OR REPLACE INTO user_device_keys_key (client_id, user_id, device_id, content, valid_signatures, verified, blocked) VALUES (:client_id, :user_id, :device_id, :content, :valid_signatures, :verified, :blocked);
|
||||||
removeUserDeviceKey: DELETE FROM user_device_keys_key WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id;
|
removeUserDeviceKey: DELETE FROM user_device_keys_key WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id;
|
||||||
|
setVerifiedUserCrossSigningKey: UPDATE user_cross_signing_keys SET verified = :verified WHERE client_id = :client_id AND user_id = :user_id AND public_key = :public_key;
|
||||||
|
setBlockedUserCrossSigningKey: UPDATE user_cross_signing_keys SET blocked = :blocked WHERE client_id = :client_id AND user_id = :user_id AND public_key = :public_key;
|
||||||
|
storeUserCrossSigningKey: INSERT OR REPLACE INTO user_cross_signing_keys (client_id, user_id, public_key, content, valid_signatures, verified, blocked) VALUES (:client_id, :user_id, :public_key, :content, :valid_signatures, :verified, :blocked);
|
||||||
|
removeUserCrossSigningKey: DELETE FROM user_cross_signing_keys WHERE client_id = :client_id AND user_id = :user_id AND public_key = :public_key;
|
||||||
insertClient: INSERT INTO clients (name, homeserver_url, token, user_id, device_id, device_name, prev_batch, olm_account) VALUES (:name, :homeserver_url, :token, :user_id, :device_id, :device_name, :prev_batch, :olm_account);
|
insertClient: INSERT INTO clients (name, homeserver_url, token, user_id, device_id, device_name, prev_batch, olm_account) VALUES (:name, :homeserver_url, :token, :user_id, :device_id, :device_name, :prev_batch, :olm_account);
|
||||||
ensureRoomExists: INSERT OR IGNORE INTO rooms (client_id, room_id, membership) VALUES (:client_id, :room_id, :membership);
|
ensureRoomExists: INSERT OR IGNORE INTO rooms (client_id, room_id, membership) VALUES (:client_id, :room_id, :membership);
|
||||||
setRoomPrevBatch: UPDATE rooms SET prev_batch = :prev_batch WHERE client_id = :client_id AND room_id = :room_id;
|
setRoomPrevBatch: UPDATE rooms SET prev_batch = :prev_batch WHERE client_id = :client_id AND room_id = :room_id;
|
||||||
|
|
|
@ -1,36 +1,50 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:canonical_json/canonical_json.dart';
|
||||||
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../client.dart';
|
import '../client.dart';
|
||||||
import '../database/database.dart' show DbUserDeviceKey, DbUserDeviceKeysKey;
|
import '../database/database.dart' show DbUserDeviceKey, DbUserDeviceKeysKey, DbUserCrossSigningKey;
|
||||||
import '../event.dart';
|
import '../event.dart';
|
||||||
import 'key_verification.dart';
|
import 'key_verification.dart';
|
||||||
|
|
||||||
class DeviceKeysList {
|
class DeviceKeysList {
|
||||||
|
Client client;
|
||||||
String userId;
|
String userId;
|
||||||
bool outdated = true;
|
bool outdated = true;
|
||||||
Map<String, DeviceKeys> deviceKeys = {};
|
Map<String, DeviceKeys> deviceKeys = {};
|
||||||
|
Map<String, CrossSigningKey> crossSigningKeys = {};
|
||||||
|
|
||||||
DeviceKeysList.fromDb(DbUserDeviceKey dbEntry, List<DbUserDeviceKeysKey> childEntries) {
|
DeviceKeysList.fromDb(DbUserDeviceKey dbEntry, List<DbUserDeviceKeysKey> childEntries, List<DbUserCrossSigningKey> crossSigningEntries, Client cl) {
|
||||||
|
client = cl;
|
||||||
userId = dbEntry.userId;
|
userId = dbEntry.userId;
|
||||||
outdated = dbEntry.outdated;
|
outdated = dbEntry.outdated;
|
||||||
deviceKeys = {};
|
deviceKeys = {};
|
||||||
for (final childEntry in childEntries) {
|
for (final childEntry in childEntries) {
|
||||||
final entry = DeviceKeys.fromDb(childEntry);
|
final entry = DeviceKeys.fromDb(childEntry, client);
|
||||||
if (entry.isValid) {
|
if (entry.isValid) {
|
||||||
deviceKeys[childEntry.deviceId] = entry;
|
deviceKeys[childEntry.deviceId] = entry;
|
||||||
} else {
|
} else {
|
||||||
outdated = true;
|
outdated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (final crossSigningEntry in crossSigningEntries) {
|
||||||
|
final entry = CrossSigningKey.fromDb(crossSigningEntry, client);
|
||||||
|
if (entry.isValid) {
|
||||||
|
crossSigningKeys[crossSigningEntry.publicKey] = entry;
|
||||||
|
} else {
|
||||||
|
outdated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceKeysList.fromJson(Map<String, dynamic> json) {
|
DeviceKeysList.fromJson(Map<String, dynamic> json, Client cl) {
|
||||||
|
client = cl;
|
||||||
userId = json['user_id'];
|
userId = json['user_id'];
|
||||||
outdated = json['outdated'];
|
outdated = json['outdated'];
|
||||||
deviceKeys = {};
|
deviceKeys = {};
|
||||||
for (final rawDeviceKeyEntry in json['device_keys'].entries) {
|
for (final rawDeviceKeyEntry in json['device_keys'].entries) {
|
||||||
deviceKeys[rawDeviceKeyEntry.key] =
|
deviceKeys[rawDeviceKeyEntry.key] =
|
||||||
DeviceKeys.fromJson(rawDeviceKeyEntry.value);
|
DeviceKeys.fromJson(rawDeviceKeyEntry.value, client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,27 +68,199 @@ class DeviceKeysList {
|
||||||
DeviceKeysList(this.userId);
|
DeviceKeysList(this.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeviceKeys {
|
abstract class _SignedKey {
|
||||||
|
Client client;
|
||||||
String userId;
|
String userId;
|
||||||
String deviceId;
|
String identifier;
|
||||||
List<String> algorithms;
|
Map<String, dynamic> content;
|
||||||
Map<String, String> keys;
|
Map<String, String> keys;
|
||||||
Map<String, dynamic> signatures;
|
Map<String, dynamic> signatures;
|
||||||
Map<String, dynamic> unsigned;
|
Map<String, dynamic> validSignatures;
|
||||||
bool verified;
|
bool verified;
|
||||||
bool blocked;
|
bool blocked;
|
||||||
|
|
||||||
|
String get ed25519Key => keys['ed25519:$identifier'];
|
||||||
|
|
||||||
|
bool get crossVerified {
|
||||||
|
try {
|
||||||
|
return hasValidSignatureChain();
|
||||||
|
} catch (err, stacktrace) {
|
||||||
|
print('[Cross Signing] Error during trying to determine signature chain: ' + err.toString());
|
||||||
|
print(stacktrace);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getSigningContent() {
|
||||||
|
final data = Map<String, dynamic>.from(content);
|
||||||
|
data.remove('verified');
|
||||||
|
data.remove('blocked');
|
||||||
|
data.remove('unsigned');
|
||||||
|
data.remove('signatures');
|
||||||
|
return String.fromCharCodes(canonicalJson.encode(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _verifySignature(String pubKey, String signature) {
|
||||||
|
final olmutil = olm.Utility();
|
||||||
|
var valid = false;
|
||||||
|
try {
|
||||||
|
olmutil.ed25519_verify(pubKey, _getSigningContent(), signature);
|
||||||
|
valid = true;
|
||||||
|
} finally {
|
||||||
|
olmutil.free();
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasValidSignatureChain({Set<String> visited}) {
|
||||||
|
if (visited == null) {
|
||||||
|
visited = Set<String>();
|
||||||
|
}
|
||||||
|
final setKey = '${userId};${identifier}';
|
||||||
|
if (visited.contains(setKey)) {
|
||||||
|
return false; // prevent recursion
|
||||||
|
}
|
||||||
|
visited.add(setKey);
|
||||||
|
for (final signatureEntries in signatures.entries) {
|
||||||
|
final otherUserId = signatureEntries.key;
|
||||||
|
if (!(signatureEntries.value is Map) || !client.userDeviceKeys.containsKey(otherUserId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (final signatureEntry in signatureEntries.value.entries) {
|
||||||
|
final fullKeyId = signatureEntry.key;
|
||||||
|
final signature = signatureEntry.value;
|
||||||
|
if (!(fullKeyId is String) || !(signature is String)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final keyId = fullKeyId.substring('ed25519:'.length);
|
||||||
|
_SignedKey key;
|
||||||
|
if (client.userDeviceKeys[otherUserId].deviceKeys.containsKey(keyId)) {
|
||||||
|
key = client.userDeviceKeys[otherUserId].deviceKeys[keyId];
|
||||||
|
} else if (client.userDeviceKeys[otherUserId].crossSigningKeys.containsKey(keyId)) {
|
||||||
|
key = client.userDeviceKeys[otherUserId].crossSigningKeys[keyId];
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (key.blocked) {
|
||||||
|
continue; // we can't be bothered about this keys signatures
|
||||||
|
}
|
||||||
|
var haveValidSignature = false;
|
||||||
|
var gotSignatureFromCache = false;
|
||||||
|
if (validSignatures != null && validSignatures.containsKey(otherUserId) && validSignatures[otherUserId].containsKey(fullKeyId)) {
|
||||||
|
if (validSignatures[otherUserId][fullKeyId] == true) {
|
||||||
|
haveValidSignature = true;
|
||||||
|
gotSignatureFromCache = true;
|
||||||
|
} else if (validSignatures[otherUserId][fullKeyId] == false) {
|
||||||
|
gotSignatureFromCache = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!gotSignatureFromCache) {
|
||||||
|
// validate the signature manually
|
||||||
|
haveValidSignature = _verifySignature(key.ed25519Key, signature);
|
||||||
|
}
|
||||||
|
if (!haveValidSignature) {
|
||||||
|
// no valid signature, this key is useless
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.verified) {
|
||||||
|
return true; // we verified this key and it is valid...all checks out!
|
||||||
|
}
|
||||||
|
// or else we just recurse into that key and chack if it works out
|
||||||
|
final haveChain = key.hasValidSignatureChain(visited: visited);
|
||||||
|
if (haveChain) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CrossSigningKey extends _SignedKey {
|
||||||
|
String get publicKey => identifier;
|
||||||
|
List<String> usage;
|
||||||
|
|
||||||
|
bool get isValid => userId != null && publicKey != null && keys != null && ed25519Key != null;
|
||||||
|
|
||||||
|
Future<void> setVerified(bool newVerified) {
|
||||||
|
verified = newVerified;
|
||||||
|
return client.database?.setVerifiedUserCrossSigningKey(newVerified, client.id, userId, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setBlocked(bool newBlocked) {
|
||||||
|
blocked = newBlocked;
|
||||||
|
return client.database?.setBlockedUserCrossSigningKey(newBlocked, client.id, userId, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
CrossSigningKey.fromDb(DbUserCrossSigningKey dbEntry, Client cl) {
|
||||||
|
client = cl;
|
||||||
|
final json = Event.getMapFromPayload(dbEntry.content);
|
||||||
|
content = Map<String, dynamic>.from(json);
|
||||||
|
userId = dbEntry.userId;
|
||||||
|
identifier = dbEntry.publicKey;
|
||||||
|
usage = json['usage'].cast<String>();
|
||||||
|
keys = json['keys'] != null ? Map<String, String>.from(json['keys']) : null;
|
||||||
|
signatures = json['signatures'] != null ? Map<String, dynamic>.from(json['signatures']) : null;
|
||||||
|
validSignatures = null;
|
||||||
|
if (dbEntry.validSignatures != null) {
|
||||||
|
final validSignaturesContent = Event.getMapFromPayload(dbEntry.validSignatures);
|
||||||
|
if (validSignaturesContent is Map) {
|
||||||
|
validSignatures = validSignaturesContent.cast<String, dynamic>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verified = dbEntry.verified;
|
||||||
|
blocked = dbEntry.blocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
CrossSigningKey.fromJson(Map<String, dynamic> json, Client cl) {
|
||||||
|
client = cl;
|
||||||
|
content = Map<String, dynamic>.from(json);
|
||||||
|
userId = json['user_id'];
|
||||||
|
usage = json['usage'].cast<String>();
|
||||||
|
keys = json['keys'] != null ? Map<String, String>.from(json['keys']) : null;
|
||||||
|
signatures = json['signatures'] != null
|
||||||
|
? Map<String, dynamic>.from(json['signatures'])
|
||||||
|
: null;
|
||||||
|
validSignatures = null;
|
||||||
|
verified = json['verified'] ?? false;
|
||||||
|
blocked = json['blocked'] ?? false;
|
||||||
|
if (keys != null) {
|
||||||
|
identifier = keys.values.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final data = Map<String, dynamic>.from(content);
|
||||||
|
data['user_id'] = userId;
|
||||||
|
data['usage'] = usage;
|
||||||
|
if (keys != null) {
|
||||||
|
data['keys'] = keys;
|
||||||
|
}
|
||||||
|
if (signatures != null) {
|
||||||
|
data['signatures'] = signatures;
|
||||||
|
}
|
||||||
|
data['verified'] = verified;
|
||||||
|
data['blocked'] = blocked;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeviceKeys extends _SignedKey {
|
||||||
|
String get deviceId => identifier;
|
||||||
|
List<String> algorithms;
|
||||||
|
Map<String, dynamic> unsigned;
|
||||||
|
|
||||||
String get curve25519Key => keys['curve25519:$deviceId'];
|
String get curve25519Key => keys['curve25519:$deviceId'];
|
||||||
String get ed25519Key => keys['ed25519:$deviceId'];
|
|
||||||
|
|
||||||
bool get isValid => userId != null && deviceId != null && curve25519Key != null && ed25519Key != null;
|
bool get isValid => userId != null && deviceId != null && keys != null && curve25519Key != null && ed25519Key != null;
|
||||||
|
|
||||||
Future<void> setVerified(bool newVerified, Client client) {
|
Future<void> setVerified(bool newVerified) {
|
||||||
verified = newVerified;
|
verified = newVerified;
|
||||||
return client.database?.setVerifiedUserDeviceKey(newVerified, client.id, userId, deviceId);
|
return client.database?.setVerifiedUserDeviceKey(newVerified, client.id, userId, deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setBlocked(bool newBlocked, Client client) {
|
Future<void> setBlocked(bool newBlocked) {
|
||||||
blocked = newBlocked;
|
blocked = newBlocked;
|
||||||
for (var room in client.rooms) {
|
for (var room in client.rooms) {
|
||||||
if (!room.encrypted) continue;
|
if (!room.encrypted) continue;
|
||||||
|
@ -85,36 +271,36 @@ class DeviceKeys {
|
||||||
return client.database?.setBlockedUserDeviceKey(newBlocked, client.id, userId, deviceId);
|
return client.database?.setBlockedUserDeviceKey(newBlocked, client.id, userId, deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceKeys({
|
DeviceKeys.fromDb(DbUserDeviceKeysKey dbEntry, Client cl) {
|
||||||
this.userId,
|
client = cl;
|
||||||
this.deviceId,
|
final json = Event.getMapFromPayload(dbEntry.content);
|
||||||
this.algorithms,
|
content = Map<String, dynamic>.from(json);
|
||||||
this.keys,
|
|
||||||
this.signatures,
|
|
||||||
this.unsigned,
|
|
||||||
this.verified,
|
|
||||||
this.blocked,
|
|
||||||
});
|
|
||||||
|
|
||||||
DeviceKeys.fromDb(DbUserDeviceKeysKey dbEntry) {
|
|
||||||
final content = Event.getMapFromPayload(dbEntry.content);
|
|
||||||
userId = dbEntry.userId;
|
userId = dbEntry.userId;
|
||||||
deviceId = dbEntry.deviceId;
|
identifier = dbEntry.deviceId;
|
||||||
algorithms = content['algorithms'].cast<String>();
|
algorithms = json['algorithms'].cast<String>();
|
||||||
keys = content['keys'] != null ? Map<String, String>.from(content['keys']) : null;
|
keys = json['keys'] != null ? Map<String, String>.from(json['keys']) : null;
|
||||||
signatures = content['signatures'] != null
|
signatures = json['signatures'] != null
|
||||||
? Map<String, dynamic>.from(content['signatures'])
|
? Map<String, dynamic>.from(json['signatures'])
|
||||||
: null;
|
: null;
|
||||||
unsigned = content['unsigned'] != null
|
unsigned = json['unsigned'] != null
|
||||||
? Map<String, dynamic>.from(content['unsigned'])
|
? Map<String, dynamic>.from(json['unsigned'])
|
||||||
: null;
|
: null;
|
||||||
|
validSignatures = null;
|
||||||
|
if (dbEntry.validSignatures != null) {
|
||||||
|
final validSignaturesContent = Event.getMapFromPayload(dbEntry.validSignatures);
|
||||||
|
if (validSignaturesContent is Map) {
|
||||||
|
validSignatures = validSignaturesContent.cast<String, dynamic>();
|
||||||
|
}
|
||||||
|
}
|
||||||
verified = dbEntry.verified;
|
verified = dbEntry.verified;
|
||||||
blocked = dbEntry.blocked;
|
blocked = dbEntry.blocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceKeys.fromJson(Map<String, dynamic> json) {
|
DeviceKeys.fromJson(Map<String, dynamic> json, Client cl) {
|
||||||
|
client = cl;
|
||||||
|
content = Map<String, dynamic>.from(json);
|
||||||
userId = json['user_id'];
|
userId = json['user_id'];
|
||||||
deviceId = json['device_id'];
|
identifier = json['device_id'];
|
||||||
algorithms = json['algorithms'].cast<String>();
|
algorithms = json['algorithms'].cast<String>();
|
||||||
keys = json['keys'] != null ? Map<String, String>.from(json['keys']) : null;
|
keys = json['keys'] != null ? Map<String, String>.from(json['keys']) : null;
|
||||||
signatures = json['signatures'] != null
|
signatures = json['signatures'] != null
|
||||||
|
@ -128,7 +314,7 @@ class DeviceKeys {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final data = <String, dynamic>{};
|
final data = Map<String, dynamic>.from(content);
|
||||||
data['user_id'] = userId;
|
data['user_id'] = userId;
|
||||||
data['device_id'] = deviceId;
|
data['device_id'] = deviceId;
|
||||||
data['algorithms'] = algorithms;
|
data['algorithms'] = algorithms;
|
||||||
|
@ -146,7 +332,7 @@ class DeviceKeys {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyVerification startVerification(Client client) {
|
KeyVerification startVerification() {
|
||||||
final request = KeyVerification(client: client, userId: userId, deviceId: deviceId);
|
final request = KeyVerification(client: client, userId: userId, deviceId: deviceId);
|
||||||
request.start();
|
request.start();
|
||||||
client.addKeyVerificationRequest(request);
|
client.addKeyVerificationRequest(request);
|
||||||
|
|
|
@ -271,7 +271,7 @@ class KeyVerification {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> verifyKeys(Map<String, String> keys, Future<bool> Function(String, DeviceKeys) verifier) async {
|
Future<void> verifyKeys(Map<String, String> keys, Future<bool> Function(String, dynamic) verifier) async {
|
||||||
final verifiedDevices = <String>[];
|
final verifiedDevices = <String>[];
|
||||||
|
|
||||||
if (!client.userDeviceKeys.containsKey(userId)) {
|
if (!client.userDeviceKeys.containsKey(userId)) {
|
||||||
|
@ -288,14 +288,23 @@ class KeyVerification {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
verifiedDevices.add(verifyDeviceId);
|
verifiedDevices.add(verifyDeviceId);
|
||||||
} else {
|
} else if (client.userDeviceKeys[userId].crossSigningKeys.containsKey(verifyDeviceId)) {
|
||||||
// TODO: we would check here if what we are verifying is actually a
|
// this is a cross signing key!
|
||||||
// cross-signing key and not a "normal" device key
|
if (!(await verifier(keyInfo, client.userDeviceKeys[userId].crossSigningKeys[verifyDeviceId]))) {
|
||||||
|
await cancel('m.key_mismatch');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
verifiedDevices.add(verifyDeviceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// okay, we reached this far, so all the devices are verified!
|
// okay, we reached this far, so all the devices are verified!
|
||||||
for (final verifyDeviceId in verifiedDevices) {
|
for (final verifyDeviceId in verifiedDevices) {
|
||||||
await client.userDeviceKeys[userId].deviceKeys[verifyDeviceId].setVerified(true, client);
|
if (client.userDeviceKeys[userId].deviceKeys.containsKey(verifyDeviceId)) {
|
||||||
|
await client.userDeviceKeys[userId].deviceKeys[verifyDeviceId].setVerified(true);
|
||||||
|
} else if (client.userDeviceKeys[userId].crossSigningKeys.containsKey(verifyDeviceId)) {
|
||||||
|
await client.userDeviceKeys[userId].crossSigningKeys[verifyDeviceId].setVerified(true);
|
||||||
|
// TODO: sign the other persons master key
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,8 +654,13 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod {
|
||||||
mac[entry.key] = entry.value;
|
mac[entry.key] = entry.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await request.verifyKeys(mac, (String mac, DeviceKeys device) async {
|
await request.verifyKeys(mac, (String mac, dynamic device) async {
|
||||||
|
if (device is DeviceKeys) {
|
||||||
return mac == _calculateMac(device.ed25519Key, baseInfo + 'ed25519:' + device.deviceId);
|
return mac == _calculateMac(device.ed25519Key, baseInfo + 'ed25519:' + device.deviceId);
|
||||||
|
} else if (device is CrossSigningKey) {
|
||||||
|
return mac == _calculateMac(device.ed25519Key, baseInfo + 'ed25519:' + device.publicKey);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
await request.send('m.key.verification.done', {});
|
await request.send('m.key.verification.done', {});
|
||||||
if (request.state != KeyVerificationState.error) {
|
if (request.state != KeyVerificationState.error) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ class RoomKeyRequest extends ToDeviceEvent {
|
||||||
for (final key in session.forwardingCurve25519KeyChain) {
|
for (final key in session.forwardingCurve25519KeyChain) {
|
||||||
forwardedKeys.add(key);
|
forwardedKeys.add(key);
|
||||||
}
|
}
|
||||||
await requestingDevice?.setVerified(true, client);
|
await requestingDevice?.setVerified(true);
|
||||||
var message = session.content;
|
var message = session.content;
|
||||||
message['forwarding_curve25519_key_chain'] = forwardedKeys;
|
message['forwarding_curve25519_key_chain'] = forwardedKeys;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue