This commit is contained in:
Christien Rioux 2024-02-16 09:46:42 -07:00
parent fcccacfafa
commit f936cb069e
5 changed files with 61 additions and 27 deletions

View File

@ -299,17 +299,18 @@ class AccountRepository {
Future<bool> _decryptedLogin(
IdentityMaster identityMaster, SecretKey identitySecret) async {
final cs = await Veilid.instance
.getCryptoSystem(identityMaster.identityRecordKey.kind);
final keyOk = await cs.validateKeyPair(
identityMaster.identityPublicKey, identitySecret);
if (!keyOk) {
throw Exception('Identity is corrupted');
}
// Verify identity secret works and return the valid cryptosystem
final cs = await identityMaster.validateIdentitySecret(identitySecret);
// Read the identity key to get the account keys
final accountRecordInfo = await identityMaster.readAccountFromIdentity(
final accountRecordInfoList = await identityMaster.readAccountsFromIdentity(
identitySecret: identitySecret, accountKey: veilidChatAccountKey);
if (accountRecordInfoList.length > 1) {
throw IdentityException.limitExceeded;
} else if (accountRecordInfoList.isEmpty) {
throw IdentityException.noAccount;
}
final accountRecordInfo = accountRecordInfoList.single;
// Add to user logins and select it
final userLogins = await _userLogins.get();

View File

@ -199,7 +199,7 @@ class DHTRecord {
// See if the encrypted data returned is exactly the same
// if so, shortcut and don't bother decrypting it
if (newValueData.data == encryptedNewValue) {
if (newValueData.data.equals(encryptedNewValue)) {
if (isUpdated) {
addLocalValueChange(newValue, subkey);
}
@ -243,7 +243,7 @@ class DHTRecord {
// The encrypted data returned should be exactly the same
// as what we are trying to set,
// otherwise we still need to keep trying to set the value
} while (newValueData.data != encryptedNewValue);
} while (!newValueData.data.equals(encryptedNewValue));
final isUpdated = newValueData.seq != lastSeq;
if (isUpdated) {

View File

@ -10,6 +10,20 @@ import '../dht_support/dht_support.dart';
part 'identity.freezed.dart';
part 'identity.g.dart';
// Identity errors
enum IdentityException implements Exception {
readError('identity could not be read'),
noAccount('no account record info'),
limitExceeded('too many items for the limit'),
invalid('identity is corrupted or secret is invalid');
const IdentityException(this.message);
final String message;
@override
String toString() => 'IdentityException($name): $message';
}
// AccountOwnerInfo is the key and owner info for the account dht key that is
// stored in the identity key
@freezed
@ -82,6 +96,12 @@ extension IdentityMasterExtension on IdentityMaster {
await (await pool.openRead(masterRecordKey)).delete();
}
Future<VeilidCryptoSystem> get identityCrypto =>
Veilid.instance.getCryptoSystem(identityRecordKey.kind);
Future<VeilidCryptoSystem> get masterCrypto =>
Veilid.instance.getCryptoSystem(masterRecordKey.kind);
KeyPair identityWriter(SecretKey secret) =>
KeyPair(key: identityPublicKey, secret: secret);
@ -91,7 +111,17 @@ extension IdentityMasterExtension on IdentityMaster {
TypedKey identityPublicTypedKey() =>
TypedKey(kind: identityRecordKey.kind, value: identityPublicKey);
Future<AccountRecordInfo> readAccountFromIdentity(
Future<VeilidCryptoSystem> validateIdentitySecret(
SecretKey identitySecret) async {
final cs = await identityCrypto;
final keyOk = await cs.validateKeyPair(identityPublicKey, identitySecret);
if (!keyOk) {
throw IdentityException.invalid;
}
return cs;
}
Future<List<AccountRecordInfo>> readAccountsFromIdentity(
{required SharedSecret identitySecret,
required String accountKey}) async {
// Read the identity key to get the account keys
@ -100,23 +130,19 @@ extension IdentityMasterExtension on IdentityMaster {
final identityRecordCrypto = await DHTRecordCryptoPrivate.fromSecret(
identityRecordKey.kind, identitySecret);
late final AccountRecordInfo accountRecordInfo;
late final List<AccountRecordInfo> accountRecordInfo;
await (await pool.openRead(identityRecordKey,
parent: masterRecordKey, crypto: identityRecordCrypto))
.scope((identityRec) async {
final identity = await identityRec.getJson(Identity.fromJson);
if (identity == null) {
// Identity could not be read or decrypted from DHT
throw StateError('identity could not be read');
throw IdentityException.readError;
}
final accountRecords = IMapOfSets.from(identity.accountRecords);
final vcAccounts = accountRecords.get(accountKey);
if (vcAccounts.length != 1) {
// No account, or multiple accounts somehow associated with identity
throw StateError('no single account record info');
}
accountRecordInfo = vcAccounts.first;
accountRecordInfo = vcAccounts.toList();
});
return accountRecordInfo;
@ -128,6 +154,7 @@ extension IdentityMasterExtension on IdentityMaster {
required SharedSecret identitySecret,
required String accountKey,
required Future<T> Function(TypedKey parent) createAccountCallback,
int maxAccounts = 1,
}) async {
final pool = DHTRecordPool.instance;
@ -153,11 +180,14 @@ extension IdentityMasterExtension on IdentityMaster {
await identityRec.eventualUpdateJson(Identity.fromJson,
(oldIdentity) async {
if (oldIdentity == null) {
throw IdentityException.readError;
}
final oldAccountRecords =
IMapOfSets.from(oldIdentity.accountRecords);
// Only allow one account per identity for veilidchat
if (oldAccountRecords.get(accountKey).isNotEmpty) {
throw StateError('Only one account per key in identity');
if (oldAccountRecords.get(accountKey).length >= maxAccounts) {
throw IdentityException.limitExceeded;
}
final accountRecords = oldAccountRecords
.add(accountKey, newAccountRecordInfo)

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:async_tools/async_tools.dart';
import 'package:veilid/veilid.dart';
Future<T> tableScope<T>(
@ -67,25 +68,26 @@ class TableDBValue<T> extends TableDBBacked<T> {
_tableKeyName = tableKeyName,
_streamController = StreamController<T>.broadcast();
T? get value => _value;
T get requireValue => _value!;
AsyncData<T>? get value => _value;
T get requireValue => _value!.value;
Stream<T> get stream => _streamController.stream;
Future<T> get() async {
final val = _value;
if (val != null) {
return val;
return val.value;
}
final loadedValue = await load();
return _value = loadedValue;
_value = AsyncData(loadedValue);
return loadedValue;
}
Future<void> set(T newVal) async {
_value = await store(newVal);
_value = AsyncData(await store(newVal));
_streamController.add(newVal);
}
T? _value;
AsyncData<T>? _value;
final String _tableName;
final String _tableKeyName;
final T Function(Object? obj) _valueFromJson;

View File

@ -9,6 +9,7 @@ export 'dht_support/dht_support.dart';
export 'src/config.dart';
export 'src/identity.dart';
export 'src/json_tools.dart';
export 'src/memory_tools.dart';
export 'src/protobuf_tools.dart';
export 'src/table_db.dart';
export 'src/veilid_log.dart';