mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-08-03 11:49:04 -04:00
switch to smpl key
This commit is contained in:
parent
e68cbb26eb
commit
ee80dbf3a5
13 changed files with 192 additions and 136 deletions
|
@ -17,8 +17,8 @@ Future<void> createContact({
|
|||
required ActiveAccountInfo activeAccountInfo,
|
||||
required proto.Profile profile,
|
||||
required IdentityMaster remoteIdentity,
|
||||
required TypedKey remoteConversationKey,
|
||||
required OwnedDHTRecordPointer localConversation,
|
||||
required TypedKey remoteConversationRecordKey,
|
||||
required TypedKey localConversationRecordKey,
|
||||
}) async {
|
||||
final accountRecordKey =
|
||||
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
|
@ -32,8 +32,8 @@ Future<void> createContact({
|
|||
kind: remoteIdentity.identityRecordKey.kind,
|
||||
value: remoteIdentity.identityPublicKey)
|
||||
.toProto()
|
||||
..remoteConversationKey = remoteConversationKey.toProto()
|
||||
..localConversation = localConversation.toProto()
|
||||
..remoteConversationRecordKey = remoteConversationRecordKey.toProto()
|
||||
..localConversationRecordKey = localConversationRecordKey.toProto()
|
||||
..showAvailability = false;
|
||||
|
||||
// Add Contact to account's list
|
||||
|
@ -56,7 +56,7 @@ Future<void> deleteContact(
|
|||
final accountRecordKey =
|
||||
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
final remoteConversationKey =
|
||||
proto.TypedKeyProto.fromProto(contact.remoteConversationKey);
|
||||
proto.TypedKeyProto.fromProto(contact.remoteConversationRecordKey);
|
||||
|
||||
// Remove any chats for this contact
|
||||
await deleteChat(
|
||||
|
@ -75,14 +75,14 @@ Future<void> deleteContact(
|
|||
if (item == null) {
|
||||
throw Exception('Failed to get contact');
|
||||
}
|
||||
if (item.remoteConversationKey == contact.remoteConversationKey) {
|
||||
if (item.remoteConversationRecordKey ==
|
||||
contact.remoteConversationRecordKey) {
|
||||
await contactList.tryRemoveItem(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
await (await pool.openOwned(
|
||||
proto.OwnedDHTRecordPointerProto.fromProto(
|
||||
contact.localConversation),
|
||||
await (await pool.openRead(
|
||||
proto.TypedKeyProto.fromProto(contact.localConversationRecordKey),
|
||||
parent: accountRecordKey))
|
||||
.delete();
|
||||
await (await pool.openRead(remoteConversationKey, parent: accountRecordKey))
|
||||
|
|
|
@ -28,14 +28,14 @@ class AcceptedContact {
|
|||
AcceptedContact({
|
||||
required this.profile,
|
||||
required this.remoteIdentity,
|
||||
required this.remoteConversationKey,
|
||||
required this.localConversation,
|
||||
required this.remoteConversationRecordKey,
|
||||
required this.localConversationRecordKey,
|
||||
});
|
||||
|
||||
proto.Profile profile;
|
||||
IdentityMaster remoteIdentity;
|
||||
TypedKey remoteConversationKey;
|
||||
OwnedDHTRecordPointer localConversation;
|
||||
TypedKey remoteConversationRecordKey;
|
||||
TypedKey localConversationRecordKey;
|
||||
}
|
||||
|
||||
class AcceptedOrRejectedContact {
|
||||
|
@ -92,33 +92,33 @@ Future<AcceptedOrRejectedContact?> checkAcceptRejectContact(
|
|||
contactResponseBytes, signature);
|
||||
|
||||
// Pull profile from remote conversation key
|
||||
final remoteConversationKey =
|
||||
proto.TypedKeyProto.fromProto(contactResponse.remoteConversationKey);
|
||||
final remoteConversationRecordKey = proto.TypedKeyProto.fromProto(
|
||||
contactResponse.remoteConversationRecordKey);
|
||||
final remoteConversation = await readRemoteConversation(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey:
|
||||
contactIdentityMaster.identityPublicTypedKey(),
|
||||
remoteConversationKey: remoteConversationKey);
|
||||
remoteConversationRecordKey: remoteConversationRecordKey);
|
||||
if (remoteConversation == null) {
|
||||
log.info('Remote conversation could not be read. Waiting...');
|
||||
return null;
|
||||
}
|
||||
// Complete the local conversation now that we have the remote profile
|
||||
final localConversationOwned = proto.OwnedDHTRecordPointerProto.fromProto(
|
||||
contactInvitationRecord.localConversation);
|
||||
final localConversationRecordKey = proto.TypedKeyProto.fromProto(
|
||||
contactInvitationRecord.localConversationRecordKey);
|
||||
return createConversation(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey:
|
||||
contactIdentityMaster.identityPublicTypedKey(),
|
||||
existingConversationOwned: localConversationOwned,
|
||||
existingConversationRecordKey: localConversationRecordKey,
|
||||
// ignore: prefer_expression_function_bodies
|
||||
callback: (localConversation) async {
|
||||
return AcceptedOrRejectedContact(
|
||||
acceptedContact: AcceptedContact(
|
||||
profile: remoteConversation.profile,
|
||||
remoteIdentity: contactIdentityMaster,
|
||||
remoteConversationKey: remoteConversationKey,
|
||||
localConversation: localConversationOwned));
|
||||
remoteConversationRecordKey: remoteConversationRecordKey,
|
||||
localConversationRecordKey: localConversationRecordKey));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -182,9 +182,9 @@ Future<void> deleteContactInvitation(
|
|||
await contactRequestInbox.delete();
|
||||
});
|
||||
if (!accepted) {
|
||||
await (await pool.openOwned(
|
||||
proto.OwnedDHTRecordPointerProto.fromProto(
|
||||
contactInvitationRecord.localConversation),
|
||||
await (await pool.openRead(
|
||||
proto.TypedKeyProto.fromProto(
|
||||
contactInvitationRecord.localConversationRecordKey),
|
||||
parent: accountRecordKey))
|
||||
.delete();
|
||||
}
|
||||
|
@ -206,11 +206,13 @@ Future<Uint8List> createContactInvitation(
|
|||
|
||||
// Generate writer keypair to share with new contact
|
||||
final cs = await pool.veilid.bestCryptoSystem();
|
||||
final writer = await cs.generateKeyPair();
|
||||
final contactRequestWriter = await cs.generateKeyPair();
|
||||
final conversationWriter =
|
||||
getConversationWriter(activeAccountInfo: activeAccountInfo);
|
||||
|
||||
// Encrypt the writer secret with the encryption key
|
||||
final encryptedSecret = await encryptSecretToBytes(
|
||||
secret: writer.secret,
|
||||
secret: contactRequestWriter.secret,
|
||||
cryptoKind: cs.kind(),
|
||||
encryptionKey: encryptionKey,
|
||||
encryptionKeyType: encryptionKeyType);
|
||||
|
@ -220,19 +222,24 @@ Future<Uint8List> createContactInvitation(
|
|||
// to and it will be eventually encrypted with the DH of the contact's
|
||||
// identity key
|
||||
late final Uint8List signedContactInvitationBytes;
|
||||
await (await pool.create(parent: accountRecordKey))
|
||||
await (await pool.create(
|
||||
parent: accountRecordKey,
|
||||
schema: DHTSchema.smpl(oCnt: 0, members: [
|
||||
DHTSchemaMember(mKey: conversationWriter.key, mCnt: 1)
|
||||
])))
|
||||
.deleteScope((localConversation) async {
|
||||
// dont bother reopening localConversation with writer
|
||||
// Make ContactRequestPrivate and encrypt with the writer secret
|
||||
final crpriv = ContactRequestPrivate()
|
||||
..writerKey = writer.key.toProto()
|
||||
..writerKey = contactRequestWriter.key.toProto()
|
||||
..profile = activeAccountInfo.account.profile
|
||||
..identityMasterRecordKey =
|
||||
activeAccountInfo.userLogin.accountMasterRecordKey.toProto()
|
||||
..chatRecordKey = localConversation.key.toProto()
|
||||
..expiration = expiration?.toInt64() ?? Int64.ZERO;
|
||||
final crprivbytes = crpriv.writeToBuffer();
|
||||
final encryptedContactRequestPrivate =
|
||||
await cs.encryptNoAuthWithNonce(crprivbytes, writer.secret);
|
||||
final encryptedContactRequestPrivate = await cs.encryptNoAuthWithNonce(
|
||||
crprivbytes, contactRequestWriter.secret);
|
||||
|
||||
// Create ContactRequest and embed contactrequestprivate
|
||||
final creq = ContactRequest()
|
||||
|
@ -242,8 +249,9 @@ Future<Uint8List> createContactInvitation(
|
|||
// Create DHT unicast inbox for ContactRequest
|
||||
await (await pool.create(
|
||||
parent: accountRecordKey,
|
||||
schema: DHTSchema.smpl(
|
||||
oCnt: 1, members: [DHTSchemaMember(mCnt: 1, mKey: writer.key)]),
|
||||
schema: DHTSchema.smpl(oCnt: 1, members: [
|
||||
DHTSchemaMember(mCnt: 1, mKey: contactRequestWriter.key)
|
||||
]),
|
||||
crypto: const DHTRecordCryptoPublic()))
|
||||
.deleteScope((contactRequestInbox) async {
|
||||
// Store ContactRequest in owner subkey
|
||||
|
@ -264,9 +272,9 @@ Future<Uint8List> createContactInvitation(
|
|||
final cinvrec = ContactInvitationRecord()
|
||||
..contactRequestInbox =
|
||||
contactRequestInbox.ownedDHTRecordPointer.toProto()
|
||||
..writerKey = writer.key.toProto()
|
||||
..writerSecret = writer.secret.toProto()
|
||||
..localConversation = localConversation.ownedDHTRecordPointer.toProto()
|
||||
..writerKey = contactRequestWriter.key.toProto()
|
||||
..writerSecret = contactRequestWriter.secret.toProto()
|
||||
..localConversationRecordKey = localConversation.key.toProto()
|
||||
..expiration = expiration?.toInt64() ?? Int64.ZERO
|
||||
..invitation = signedContactInvitationBytes
|
||||
..message = message;
|
||||
|
@ -390,7 +398,7 @@ Future<AcceptedContact?> acceptContactInvitation(
|
|||
callback: (localConversation) async {
|
||||
final contactResponse = ContactResponse()
|
||||
..accept = true
|
||||
..remoteConversationKey = localConversation.key.toProto()
|
||||
..remoteConversationRecordKey = localConversation.key.toProto()
|
||||
..identityMasterRecordKey = activeAccountInfo
|
||||
.localAccount.identityMaster.masterRecordKey
|
||||
.toProto();
|
||||
|
@ -418,9 +426,9 @@ Future<AcceptedContact?> acceptContactInvitation(
|
|||
return AcceptedContact(
|
||||
profile: validContactInvitation.contactRequestPrivate.profile,
|
||||
remoteIdentity: validContactInvitation.contactIdentityMaster,
|
||||
remoteConversationKey: proto.TypedKeyProto.fromProto(
|
||||
remoteConversationRecordKey: proto.TypedKeyProto.fromProto(
|
||||
validContactInvitation.contactRequestPrivate.chatRecordKey),
|
||||
localConversation: localConversation.ownedDHTRecordPointer,
|
||||
localConversationRecordKey: localConversation.key,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,6 +24,15 @@ Future<DHTRecordCrypto> getConversationCrypto({
|
|||
return DHTRecordCryptoPrivate.fromSecret(identitySecret.kind, sharedSecret);
|
||||
}
|
||||
|
||||
KeyPair getConversationWriter({
|
||||
required ActiveAccountInfo activeAccountInfo,
|
||||
}) {
|
||||
final identityKey =
|
||||
activeAccountInfo.localAccount.identityMaster.identityPublicKey;
|
||||
final identitySecret = activeAccountInfo.userLogin.identitySecret;
|
||||
return KeyPair(key: identityKey, secret: identitySecret.value);
|
||||
}
|
||||
|
||||
// Create a conversation
|
||||
// If we were the initator of the conversation there may be an
|
||||
// incomplete 'existingConversationRecord' that we need to fill
|
||||
|
@ -32,7 +41,7 @@ Future<T> createConversation<T>(
|
|||
{required ActiveAccountInfo activeAccountInfo,
|
||||
required TypedKey remoteIdentityPublicKey,
|
||||
required FutureOr<T> Function(DHTRecord) callback,
|
||||
OwnedDHTRecordPointer? existingConversationOwned}) async {
|
||||
TypedKey? existingConversationRecordKey}) async {
|
||||
final pool = await DHTRecordPool.instance();
|
||||
final accountRecordKey =
|
||||
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
|
@ -40,28 +49,38 @@ Future<T> createConversation<T>(
|
|||
final crypto = await getConversationCrypto(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey);
|
||||
final writer = getConversationWriter(activeAccountInfo: activeAccountInfo);
|
||||
|
||||
// Open with SMPL scheme for identity writer
|
||||
late final DHTRecord localConversationRecord;
|
||||
if (existingConversationOwned != null) {
|
||||
localConversationRecord = await pool.openOwned(existingConversationOwned,
|
||||
if (existingConversationRecordKey != null) {
|
||||
localConversationRecord = await pool.openWrite(
|
||||
existingConversationRecordKey, writer,
|
||||
parent: accountRecordKey, crypto: crypto);
|
||||
} else {
|
||||
localConversationRecord =
|
||||
await pool.create(parent: accountRecordKey, crypto: crypto);
|
||||
final localConversationRecordCreate = await pool.create(
|
||||
parent: accountRecordKey,
|
||||
crypto: crypto,
|
||||
schema: DHTSchema.smpl(
|
||||
oCnt: 0, members: [DHTSchemaMember(mKey: writer.key, mCnt: 1)]));
|
||||
await localConversationRecordCreate.close();
|
||||
localConversationRecord = await pool.openWrite(
|
||||
localConversationRecordCreate.key, writer,
|
||||
parent: accountRecordKey, crypto: crypto);
|
||||
}
|
||||
return localConversationRecord
|
||||
// ignore: prefer_expression_function_bodies
|
||||
.deleteScope((localConversation) async {
|
||||
// Make messages log
|
||||
return (await DHTShortArray.create(
|
||||
parent: localConversation.key, crypto: crypto))
|
||||
parent: localConversation.key, crypto: crypto, smplWriter: writer))
|
||||
.deleteScope((messages) async {
|
||||
// Write local conversation key
|
||||
final conversation = Conversation()
|
||||
..profile = activeAccountInfo.account.profile
|
||||
..identityMasterJson =
|
||||
jsonEncode(activeAccountInfo.localAccount.identityMaster.toJson())
|
||||
..messages = messages.record.ownedDHTRecordPointer.toProto();
|
||||
..messages = messages.record.key.toProto();
|
||||
|
||||
//
|
||||
final update = await localConversation.tryWriteProtobuf(
|
||||
|
@ -76,8 +95,8 @@ Future<T> createConversation<T>(
|
|||
|
||||
Future<Conversation?> readRemoteConversation({
|
||||
required ActiveAccountInfo activeAccountInfo,
|
||||
required TypedKey remoteConversationRecordKey,
|
||||
required TypedKey remoteIdentityPublicKey,
|
||||
required TypedKey remoteConversationKey,
|
||||
}) async {
|
||||
final accountRecordKey =
|
||||
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
|
@ -86,7 +105,7 @@ Future<Conversation?> readRemoteConversation({
|
|||
final crypto = await getConversationCrypto(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey);
|
||||
return (await pool.openRead(remoteConversationKey,
|
||||
return (await pool.openRead(remoteConversationRecordKey,
|
||||
parent: accountRecordKey, crypto: crypto))
|
||||
.scope((remoteConversation) async {
|
||||
//
|
||||
|
@ -98,7 +117,7 @@ Future<Conversation?> readRemoteConversation({
|
|||
|
||||
Future<Conversation?> writeLocalConversation({
|
||||
required ActiveAccountInfo activeAccountInfo,
|
||||
required OwnedDHTRecordPointer localConversationOwned,
|
||||
required TypedKey localConversationRecordKey,
|
||||
required TypedKey remoteIdentityPublicKey,
|
||||
required Conversation conversation,
|
||||
}) async {
|
||||
|
@ -109,8 +128,9 @@ Future<Conversation?> writeLocalConversation({
|
|||
final crypto = await getConversationCrypto(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey);
|
||||
final writer = getConversationWriter(activeAccountInfo: activeAccountInfo);
|
||||
|
||||
return (await pool.openOwned(localConversationOwned,
|
||||
return (await pool.openWrite(localConversationRecordKey, writer,
|
||||
parent: accountRecordKey, crypto: crypto))
|
||||
.scope((localConversation) async {
|
||||
//
|
||||
|
@ -125,7 +145,7 @@ Future<Conversation?> writeLocalConversation({
|
|||
|
||||
Future<Conversation?> readLocalConversation({
|
||||
required ActiveAccountInfo activeAccountInfo,
|
||||
required OwnedDHTRecordPointer localConversationOwned,
|
||||
required TypedKey localConversationRecordKey,
|
||||
required TypedKey remoteIdentityPublicKey,
|
||||
}) async {
|
||||
final accountRecordKey =
|
||||
|
@ -136,7 +156,7 @@ Future<Conversation?> readLocalConversation({
|
|||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey);
|
||||
|
||||
return (await pool.openOwned(localConversationOwned,
|
||||
return (await pool.openRead(localConversationRecordKey,
|
||||
parent: accountRecordKey, crypto: crypto))
|
||||
.scope((localConversation) async {
|
||||
//
|
||||
|
@ -150,24 +170,25 @@ Future<Conversation?> readLocalConversation({
|
|||
|
||||
Future<void> addLocalConversationMessage(
|
||||
{required ActiveAccountInfo activeAccountInfo,
|
||||
required OwnedDHTRecordPointer localConversationOwned,
|
||||
required TypedKey localConversationRecordKey,
|
||||
required TypedKey remoteIdentityPublicKey,
|
||||
required proto.Message message}) async {
|
||||
final conversation = await readLocalConversation(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
localConversationOwned: localConversationOwned,
|
||||
localConversationRecordKey: localConversationRecordKey,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey);
|
||||
if (conversation == null) {
|
||||
return;
|
||||
}
|
||||
final messagesOwned =
|
||||
proto.OwnedDHTRecordPointerProto.fromProto(conversation.messages);
|
||||
final messagesRecordKey =
|
||||
proto.TypedKeyProto.fromProto(conversation.messages);
|
||||
final crypto = await getConversationCrypto(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey);
|
||||
final writer = getConversationWriter(activeAccountInfo: activeAccountInfo);
|
||||
|
||||
await (await DHTShortArray.openOwned(messagesOwned,
|
||||
parent: localConversationOwned.recordKey, crypto: crypto))
|
||||
await (await DHTShortArray.openWrite(messagesRecordKey, writer,
|
||||
parent: localConversationRecordKey, crypto: crypto))
|
||||
.scope((messages) async {
|
||||
await messages.tryAddItem(message.writeToBuffer());
|
||||
});
|
||||
|
@ -175,24 +196,24 @@ Future<void> addLocalConversationMessage(
|
|||
|
||||
Future<IList<proto.Message>?> getLocalConversationMessages({
|
||||
required ActiveAccountInfo activeAccountInfo,
|
||||
required OwnedDHTRecordPointer localConversationOwned,
|
||||
required TypedKey localConversationRecordKey,
|
||||
required TypedKey remoteIdentityPublicKey,
|
||||
}) async {
|
||||
final conversation = await readLocalConversation(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
localConversationOwned: localConversationOwned,
|
||||
localConversationRecordKey: localConversationRecordKey,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey);
|
||||
if (conversation == null) {
|
||||
return null;
|
||||
}
|
||||
final messagesOwned =
|
||||
proto.OwnedDHTRecordPointerProto.fromProto(conversation.messages);
|
||||
final messagesRecordKey =
|
||||
proto.TypedKeyProto.fromProto(conversation.messages);
|
||||
final crypto = await getConversationCrypto(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey);
|
||||
|
||||
return (await DHTShortArray.openOwned(messagesOwned,
|
||||
parent: localConversationOwned.recordKey, crypto: crypto))
|
||||
return (await DHTShortArray.openRead(messagesRecordKey,
|
||||
parent: localConversationRecordKey, crypto: crypto))
|
||||
.scope((messages) async {
|
||||
var out = IList<proto.Message>();
|
||||
for (var i = 0; i < messages.length; i++) {
|
||||
|
@ -208,24 +229,24 @@ Future<IList<proto.Message>?> getLocalConversationMessages({
|
|||
|
||||
Future<IList<proto.Message>?> getRemoteConversationMessages({
|
||||
required ActiveAccountInfo activeAccountInfo,
|
||||
required TypedKey remoteConversationKey,
|
||||
required TypedKey remoteConversationRecordKey,
|
||||
required TypedKey remoteIdentityPublicKey,
|
||||
}) async {
|
||||
final conversation = await readRemoteConversation(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteConversationKey: remoteConversationKey,
|
||||
remoteConversationRecordKey: remoteConversationRecordKey,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey);
|
||||
if (conversation == null) {
|
||||
return null;
|
||||
}
|
||||
final messagesOwned =
|
||||
proto.OwnedDHTRecordPointerProto.fromProto(conversation.messages);
|
||||
final messagesRecordKey =
|
||||
proto.TypedKeyProto.fromProto(conversation.messages);
|
||||
final crypto = await getConversationCrypto(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey);
|
||||
|
||||
return (await DHTShortArray.openOwned(messagesOwned,
|
||||
parent: localConversationOwned.recordKey, crypto: crypto))
|
||||
return (await DHTShortArray.openRead(messagesRecordKey,
|
||||
parent: remoteConversationRecordKey, crypto: crypto))
|
||||
.scope((messages) async {
|
||||
var out = IList<proto.Message>();
|
||||
for (var i = 0; i < messages.length; i++) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue