encryption and dht work

This commit is contained in:
Christien Rioux 2023-09-24 22:35:54 -04:00
parent 7831deabd3
commit c63eee26fd
9 changed files with 246 additions and 159 deletions

View file

@ -24,6 +24,11 @@ import 'conversation.dart';
part 'contact_invite.g.dart';
class ContactInviteInvalidKeyException implements Exception {
const ContactInviteInvalidKeyException(this.type) : super();
final EncryptionKeyType type;
}
class AcceptedContact {
AcceptedContact({
required this.profile,
@ -238,8 +243,8 @@ Future<Uint8List> createContactInvitation(
..chatRecordKey = localConversation.key.toProto()
..expiration = expiration?.toInt64() ?? Int64.ZERO;
final crprivbytes = crpriv.writeToBuffer();
final encryptedContactRequestPrivate = await cs.encryptNoAuthWithNonce(
crprivbytes, contactRequestWriter.secret);
final encryptedContactRequestPrivate =
await cs.encryptAeadWithNonce(crprivbytes, contactRequestWriter.secret);
// Create ContactRequest and embed contactrequestprivate
final creq = ContactRequest()
@ -315,11 +320,18 @@ class ValidContactInvitation {
KeyPair writer;
}
typedef GetEncryptionKeyCallback = Future<SecretKey> Function(
EncryptionKeyType encryptionKeyType, Uint8List encryptedSecret);
typedef GetEncryptionKeyCallback = Future<SecretKey?> Function(
VeilidCryptoSystem cs,
EncryptionKeyType encryptionKeyType,
Uint8List encryptedSecret);
Future<ValidContactInvitation?> validateContactInvitation(
{required ActiveAccountInfo activeAccountInfo,
required Uint8List inviteData,
required GetEncryptionKeyCallback getEncryptionKeyCallback}) async {
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
Future<ValidContactInvitation> validateContactInvitation(Uint8List inviteData,
GetEncryptionKeyCallback getEncryptionKeyCallback) async {
final signedContactInvitation =
proto.SignedContactInvitation.fromBuffer(inviteData);
@ -334,19 +346,27 @@ Future<ValidContactInvitation> validateContactInvitation(Uint8List inviteData,
late final ValidContactInvitation out;
final pool = await DHTRecordPool.instance();
await (await pool.openRead(contactRequestInboxKey))
.deleteScope((contactRequestInbox) async {
final cs = await pool.veilid.getCryptoSystem(contactRequestInboxKey.kind);
// See if we're chatting to ourselves, if so, don't delete it here
final ownKey = pool.getParentRecord(contactRequestInboxKey) != null;
await (await pool.openRead(contactRequestInboxKey, parent: accountRecordKey))
.maybeDeleteScope(!ownKey, (contactRequestInbox) async {
//
final contactRequest =
await contactRequestInbox.getProtobuf(proto.ContactRequest.fromBuffer);
// Decrypt contact request private
final encryptionKeyType =
EncryptionKeyType.fromProto(contactRequest!.encryptionKeyType);
final writerSecret = await getEncryptionKeyCallback(
encryptionKeyType, Uint8List.fromList(contactInvitation.writerSecret));
final writerSecret = await getEncryptionKeyCallback(cs, encryptionKeyType,
Uint8List.fromList(contactInvitation.writerSecret));
if (writerSecret == null) {
return null;
}
final cs = await pool.veilid.getCryptoSystem(contactRequestInboxKey.kind);
final contactRequestPrivateBytes = await cs.decryptNoAuthWithNonce(
final contactRequestPrivateBytes = await cs.decryptAeadWithNonce(
Uint8List.fromList(contactRequest.private), writerSecret);
final contactRequestPrivate =
proto.ContactRequestPrivate.fromBuffer(contactRequestPrivateBytes);
@ -360,8 +380,12 @@ Future<ValidContactInvitation> validateContactInvitation(Uint8List inviteData,
// Verify
final signature = proto.SignatureProto.fromProto(
signedContactInvitation.identitySignature);
await cs.verify(contactIdentityMaster.identityPublicKey,
contactInvitationBytes, signature);
try {
await cs.verify(contactIdentityMaster.identityPublicKey,
contactInvitationBytes, signature);
} on Exception catch (_) {
throw ContactInviteInvalidKeyException(encryptionKeyType);
}
final writer = KeyPair(
key: proto.CryptoKeyProto.fromProto(contactRequestPrivate.writerKey),
@ -385,10 +409,18 @@ Future<AcceptedContact?> acceptContactInvitation(
ValidContactInvitation validContactInvitation) async {
final pool = await DHTRecordPool.instance();
try {
// Ensure we don't delete this if we're trying to chat to self
final ownKey =
pool.getParentRecord(validContactInvitation.contactRequestInboxKey) !=
null;
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
return (await pool.openWrite(validContactInvitation.contactRequestInboxKey,
validContactInvitation.writer))
validContactInvitation.writer,
parent: accountRecordKey))
// ignore: prefer_expression_function_bodies
.deleteScope((contactRequestInbox) async {
.maybeDeleteScope(!ownKey, (contactRequestInbox) async {
// Create local conversation key for this
// contact and send via contact response
return createConversation(
@ -441,9 +473,18 @@ Future<AcceptedContact?> acceptContactInvitation(
Future<bool> rejectContactInvitation(ActiveAccountInfo activeAccountInfo,
ValidContactInvitation validContactInvitation) async {
final pool = await DHTRecordPool.instance();
// Ensure we don't delete this if we're trying to chat to self
final ownKey =
pool.getParentRecord(validContactInvitation.contactRequestInboxKey) !=
null;
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
return (await pool.openWrite(validContactInvitation.contactRequestInboxKey,
validContactInvitation.writer))
.deleteScope((contactRequestInbox) async {
validContactInvitation.writer,
parent: accountRecordKey))
.maybeDeleteScope(!ownKey, (contactRequestInbox) async {
final cs = await pool.veilid
.getCryptoSystem(validContactInvitation.contactRequestInboxKey.kind);

View file

@ -5,6 +5,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../entities/entities.dart';
import '../log/loggy.dart';
import '../tools/tools.dart';
import '../veilid_support/veilid_support.dart';
import 'local_accounts.dart';
@ -52,7 +53,7 @@ class Logins extends _$Logins with AsyncTableDBBacked<ActiveLogins> {
state = AsyncValue.data(updated);
}
Future<bool> _loginCommon(
Future<bool> _decryptedLogin(
IdentityMaster identityMaster, SecretKey identitySecret) async {
final veilid = await eventualVeilid.future;
final cs =
@ -89,7 +90,8 @@ class Logins extends _$Logins with AsyncTableDBBacked<ActiveLogins> {
return true;
}
Future<bool> loginWithNone(TypedKey accountMasterRecordKey) async {
Future<bool> login(TypedKey accountMasterRecordKey,
EncryptionKeyType encryptionKeyType, String encryptionKey) async {
final localAccounts = ref.read(localAccountsProvider).requireValue;
// Get account, throws if not found
@ -99,42 +101,19 @@ class Logins extends _$Logins with AsyncTableDBBacked<ActiveLogins> {
// Log in with this local account
// Derive key from password
if (localAccount.encryptionKeyType != EncryptionKeyType.none) {
if (localAccount.encryptionKeyType != encryptionKeyType) {
throw Exception('Wrong authentication type');
}
final identitySecret =
SecretKey.fromBytes(localAccount.identitySecretBytes);
final identitySecret = await decryptSecretFromBytes(
secretBytes: localAccount.identitySecretBytes,
cryptoKind: localAccount.identityMaster.identityRecordKey.kind,
encryptionKeyType: localAccount.encryptionKeyType,
encryptionKey: encryptionKey,
);
// Validate this secret with the identity public key and log in
return _loginCommon(localAccount.identityMaster, identitySecret);
}
Future<bool> loginWithPasswordOrPin(
TypedKey accountMasterRecordKey, String encryptionKey) async {
final veilid = await eventualVeilid.future;
final localAccounts = ref.read(localAccountsProvider).requireValue;
// Get account, throws if not found
final localAccount = localAccounts.firstWhere(
(la) => la.identityMaster.masterRecordKey == accountMasterRecordKey);
// Log in with this local account
// Derive key from password
if (localAccount.encryptionKeyType != EncryptionKeyType.password ||
localAccount.encryptionKeyType != EncryptionKeyType.pin) {
throw Exception('Wrong authentication type');
}
final cs = await veilid
.getCryptoSystem(localAccount.identityMaster.identityRecordKey.kind);
final identitySecret = SecretKey.fromBytes(
await cs.decryptNoAuthWithPassword(
localAccount.identitySecretBytes, encryptionKey));
// Validate this secret with the identity public key and log in
return _loginCommon(localAccount.identityMaster, identitySecret);
return _decryptedLogin(localAccount.identityMaster, identitySecret);
}
Future<void> logout(TypedKey? accountMasterRecordKey) async {