switch to smpl key

This commit is contained in:
Christien Rioux 2023-08-07 11:02:29 -04:00
parent e68cbb26eb
commit ee80dbf3a5
13 changed files with 192 additions and 136 deletions

View file

@ -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))

View file

@ -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,
);
});
});

View file

@ -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++) {