invitation work

This commit is contained in:
Christien Rioux 2023-07-29 15:27:35 -04:00
parent 96e3251b3b
commit bb8a3df281
14 changed files with 1036 additions and 3 deletions

47
doc/invitations.md Normal file
View File

@ -0,0 +1,47 @@
## Sending an invitation
1. Generate writer keypair to share with new contact
2. Encrypt secret with requested encryption type
3. Create Local Chat DHT record (no content yet, will be encrypted with DH of contact identity key)
4. Create ContactRequestPrivate and encrypt with the writer secret
5. Create ContactRequest and embed possibly encrypted ContactRequestPrivate
6. Create DHT unicast inbox for ContactRequest and store ContactRequest in owner subkey
7. Create ContactInvitation and add invitation record to local table
8. Create SignedContactInvitation embedding ContactInvitation
9. Render SignedContactInvitation to shareable encoding (qr code, text blob, etc)
10. Share SignedContactInvitation out of band to desired contact, along with password somehow if used
## Receiving an invitation
1. Receive SignedContactInvitation from out of band, and the password somehow if used
2. Get the ContactRequest record unicastinbox DHT record owner subkey from the network
3. Decrypt the writer secret with the password if necessary
4. Decrypt the ContactRequestPrivate chunk with the writer secret
5. Get the contact's AccountMaster record key
6. Verify identity signature on the SignedContactInvitation
7. Verify expiration
8. Display the profile and ask if the user wants to accept or reject the invitation
## Accepting an invitation
1. Create a Local Chat DHT record (no content yet, will be encrypted with DH of contact identity key)
2. Create ContactAccept with chat dht record and account master
3. Create SignedContactResponse with accept=true signed with identity
4. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
5. Add a local contact with the remote chat dht record, updating from the remote profile in it
## Rejecting an invitation
1. Create ContactReject with account master
2. Create SignedContactResponse with accept=false signed with identity
3. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
## Receiving an accept/reject
1. Decrypt with writer secret
2. Get DHT record for contact's AccountMaster
3. Validate the SignedContactResponse signature
If accept == false:
1. Announce rejection
2. Delete local invitation from table
If accept == true:
1. Add a local contact with the remote chat dht record, updating from the remote profile in it.
2. Delete local invitation from table

View File

@ -1122,6 +1122,7 @@ class Account extends $pb.GeneratedMessage {
..aOB(2, _omitFieldNames ? '' : 'invisible') ..aOB(2, _omitFieldNames ? '' : 'invisible')
..a<$core.int>(3, _omitFieldNames ? '' : 'autoAwayTimeoutSec', $pb.PbFieldType.OU3) ..a<$core.int>(3, _omitFieldNames ? '' : 'autoAwayTimeoutSec', $pb.PbFieldType.OU3)
..aOM<TypedKey>(4, _omitFieldNames ? '' : 'contactList', subBuilder: TypedKey.create) ..aOM<TypedKey>(4, _omitFieldNames ? '' : 'contactList', subBuilder: TypedKey.create)
..aOM<TypedKey>(5, _omitFieldNames ? '' : 'contactRequests', subBuilder: TypedKey.create)
..hasRequiredFields = false ..hasRequiredFields = false
; ;
@ -1185,6 +1186,497 @@ class Account extends $pb.GeneratedMessage {
void clearContactList() => clearField(4); void clearContactList() => clearField(4);
@$pb.TagNumber(4) @$pb.TagNumber(4)
TypedKey ensureContactList() => $_ensure(3); TypedKey ensureContactList() => $_ensure(3);
@$pb.TagNumber(5)
TypedKey get contactRequests => $_getN(4);
@$pb.TagNumber(5)
set contactRequests(TypedKey v) { setField(5, v); }
@$pb.TagNumber(5)
$core.bool hasContactRequests() => $_has(4);
@$pb.TagNumber(5)
void clearContactRequests() => clearField(5);
@$pb.TagNumber(5)
TypedKey ensureContactRequests() => $_ensure(4);
}
class ContactInvitation extends $pb.GeneratedMessage {
factory ContactInvitation() => create();
ContactInvitation._() : super();
factory ContactInvitation.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory ContactInvitation.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ContactInvitation', createEmptyInstance: create)
..aOM<TypedKey>(1, _omitFieldNames ? '' : 'contactRequestRecordKey', subBuilder: TypedKey.create)
..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'writerSecret', $pb.PbFieldType.OY)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
ContactInvitation clone() => ContactInvitation()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
ContactInvitation copyWith(void Function(ContactInvitation) updates) => super.copyWith((message) => updates(message as ContactInvitation)) as ContactInvitation;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ContactInvitation create() => ContactInvitation._();
ContactInvitation createEmptyInstance() => create();
static $pb.PbList<ContactInvitation> createRepeated() => $pb.PbList<ContactInvitation>();
@$core.pragma('dart2js:noInline')
static ContactInvitation getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ContactInvitation>(create);
static ContactInvitation? _defaultInstance;
@$pb.TagNumber(1)
TypedKey get contactRequestRecordKey => $_getN(0);
@$pb.TagNumber(1)
set contactRequestRecordKey(TypedKey v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasContactRequestRecordKey() => $_has(0);
@$pb.TagNumber(1)
void clearContactRequestRecordKey() => clearField(1);
@$pb.TagNumber(1)
TypedKey ensureContactRequestRecordKey() => $_ensure(0);
@$pb.TagNumber(2)
$core.List<$core.int> get writerSecret => $_getN(1);
@$pb.TagNumber(2)
set writerSecret($core.List<$core.int> v) { $_setBytes(1, v); }
@$pb.TagNumber(2)
$core.bool hasWriterSecret() => $_has(1);
@$pb.TagNumber(2)
void clearWriterSecret() => clearField(2);
}
class SignedContactInvitation extends $pb.GeneratedMessage {
factory SignedContactInvitation() => create();
SignedContactInvitation._() : super();
factory SignedContactInvitation.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory SignedContactInvitation.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SignedContactInvitation', createEmptyInstance: create)
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'contactInvitation', $pb.PbFieldType.OY)
..aOM<Signature>(2, _omitFieldNames ? '' : 'identitySignature', subBuilder: Signature.create)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
SignedContactInvitation clone() => SignedContactInvitation()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
SignedContactInvitation copyWith(void Function(SignedContactInvitation) updates) => super.copyWith((message) => updates(message as SignedContactInvitation)) as SignedContactInvitation;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static SignedContactInvitation create() => SignedContactInvitation._();
SignedContactInvitation createEmptyInstance() => create();
static $pb.PbList<SignedContactInvitation> createRepeated() => $pb.PbList<SignedContactInvitation>();
@$core.pragma('dart2js:noInline')
static SignedContactInvitation getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SignedContactInvitation>(create);
static SignedContactInvitation? _defaultInstance;
@$pb.TagNumber(1)
$core.List<$core.int> get contactInvitation => $_getN(0);
@$pb.TagNumber(1)
set contactInvitation($core.List<$core.int> v) { $_setBytes(0, v); }
@$pb.TagNumber(1)
$core.bool hasContactInvitation() => $_has(0);
@$pb.TagNumber(1)
void clearContactInvitation() => clearField(1);
@$pb.TagNumber(2)
Signature get identitySignature => $_getN(1);
@$pb.TagNumber(2)
set identitySignature(Signature v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasIdentitySignature() => $_has(1);
@$pb.TagNumber(2)
void clearIdentitySignature() => clearField(2);
@$pb.TagNumber(2)
Signature ensureIdentitySignature() => $_ensure(1);
}
class ContactRequest extends $pb.GeneratedMessage {
factory ContactRequest() => create();
ContactRequest._() : super();
factory ContactRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory ContactRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ContactRequest', createEmptyInstance: create)
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'writerSalt', $pb.PbFieldType.OY)
..e<EncryptionKind>(2, _omitFieldNames ? '' : 'encryptionKeyType', $pb.PbFieldType.OE, defaultOrMaker: EncryptionKind.ENCRYPTION_KIND_UNSPECIFIED, valueOf: EncryptionKind.valueOf, enumValues: EncryptionKind.values)
..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'private', $pb.PbFieldType.OY)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
ContactRequest clone() => ContactRequest()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
ContactRequest copyWith(void Function(ContactRequest) updates) => super.copyWith((message) => updates(message as ContactRequest)) as ContactRequest;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ContactRequest create() => ContactRequest._();
ContactRequest createEmptyInstance() => create();
static $pb.PbList<ContactRequest> createRepeated() => $pb.PbList<ContactRequest>();
@$core.pragma('dart2js:noInline')
static ContactRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ContactRequest>(create);
static ContactRequest? _defaultInstance;
@$pb.TagNumber(1)
$core.List<$core.int> get writerSalt => $_getN(0);
@$pb.TagNumber(1)
set writerSalt($core.List<$core.int> v) { $_setBytes(0, v); }
@$pb.TagNumber(1)
$core.bool hasWriterSalt() => $_has(0);
@$pb.TagNumber(1)
void clearWriterSalt() => clearField(1);
@$pb.TagNumber(2)
EncryptionKind get encryptionKeyType => $_getN(1);
@$pb.TagNumber(2)
set encryptionKeyType(EncryptionKind v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasEncryptionKeyType() => $_has(1);
@$pb.TagNumber(2)
void clearEncryptionKeyType() => clearField(2);
@$pb.TagNumber(3)
$core.List<$core.int> get private => $_getN(2);
@$pb.TagNumber(3)
set private($core.List<$core.int> v) { $_setBytes(2, v); }
@$pb.TagNumber(3)
$core.bool hasPrivate() => $_has(2);
@$pb.TagNumber(3)
void clearPrivate() => clearField(3);
}
class ContactRequestPrivate extends $pb.GeneratedMessage {
factory ContactRequestPrivate() => create();
ContactRequestPrivate._() : super();
factory ContactRequestPrivate.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory ContactRequestPrivate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ContactRequestPrivate', createEmptyInstance: create)
..aOM<CryptoKey>(1, _omitFieldNames ? '' : 'writerKey', subBuilder: CryptoKey.create)
..aOM<Profile>(2, _omitFieldNames ? '' : 'profile', subBuilder: Profile.create)
..aOM<TypedKey>(3, _omitFieldNames ? '' : 'accountMasterRecordKey', subBuilder: TypedKey.create)
..aOM<TypedKey>(4, _omitFieldNames ? '' : 'chatRecordKey', subBuilder: TypedKey.create)
..a<$fixnum.Int64>(5, _omitFieldNames ? '' : 'expiration', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
ContactRequestPrivate clone() => ContactRequestPrivate()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
ContactRequestPrivate copyWith(void Function(ContactRequestPrivate) updates) => super.copyWith((message) => updates(message as ContactRequestPrivate)) as ContactRequestPrivate;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ContactRequestPrivate create() => ContactRequestPrivate._();
ContactRequestPrivate createEmptyInstance() => create();
static $pb.PbList<ContactRequestPrivate> createRepeated() => $pb.PbList<ContactRequestPrivate>();
@$core.pragma('dart2js:noInline')
static ContactRequestPrivate getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ContactRequestPrivate>(create);
static ContactRequestPrivate? _defaultInstance;
@$pb.TagNumber(1)
CryptoKey get writerKey => $_getN(0);
@$pb.TagNumber(1)
set writerKey(CryptoKey v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasWriterKey() => $_has(0);
@$pb.TagNumber(1)
void clearWriterKey() => clearField(1);
@$pb.TagNumber(1)
CryptoKey ensureWriterKey() => $_ensure(0);
@$pb.TagNumber(2)
Profile get profile => $_getN(1);
@$pb.TagNumber(2)
set profile(Profile v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasProfile() => $_has(1);
@$pb.TagNumber(2)
void clearProfile() => clearField(2);
@$pb.TagNumber(2)
Profile ensureProfile() => $_ensure(1);
@$pb.TagNumber(3)
TypedKey get accountMasterRecordKey => $_getN(2);
@$pb.TagNumber(3)
set accountMasterRecordKey(TypedKey v) { setField(3, v); }
@$pb.TagNumber(3)
$core.bool hasAccountMasterRecordKey() => $_has(2);
@$pb.TagNumber(3)
void clearAccountMasterRecordKey() => clearField(3);
@$pb.TagNumber(3)
TypedKey ensureAccountMasterRecordKey() => $_ensure(2);
@$pb.TagNumber(4)
TypedKey get chatRecordKey => $_getN(3);
@$pb.TagNumber(4)
set chatRecordKey(TypedKey v) { setField(4, v); }
@$pb.TagNumber(4)
$core.bool hasChatRecordKey() => $_has(3);
@$pb.TagNumber(4)
void clearChatRecordKey() => clearField(4);
@$pb.TagNumber(4)
TypedKey ensureChatRecordKey() => $_ensure(3);
@$pb.TagNumber(5)
$fixnum.Int64 get expiration => $_getI64(4);
@$pb.TagNumber(5)
set expiration($fixnum.Int64 v) { $_setInt64(4, v); }
@$pb.TagNumber(5)
$core.bool hasExpiration() => $_has(4);
@$pb.TagNumber(5)
void clearExpiration() => clearField(5);
}
class ContactResponse extends $pb.GeneratedMessage {
factory ContactResponse() => create();
ContactResponse._() : super();
factory ContactResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory ContactResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ContactResponse', createEmptyInstance: create)
..aOB(1, _omitFieldNames ? '' : 'accept')
..aOM<TypedKey>(2, _omitFieldNames ? '' : 'accountMasterRecordKey', subBuilder: TypedKey.create)
..aOM<TypedKey>(3, _omitFieldNames ? '' : 'chatRecordKey', subBuilder: TypedKey.create)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
ContactResponse clone() => ContactResponse()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
ContactResponse copyWith(void Function(ContactResponse) updates) => super.copyWith((message) => updates(message as ContactResponse)) as ContactResponse;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ContactResponse create() => ContactResponse._();
ContactResponse createEmptyInstance() => create();
static $pb.PbList<ContactResponse> createRepeated() => $pb.PbList<ContactResponse>();
@$core.pragma('dart2js:noInline')
static ContactResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ContactResponse>(create);
static ContactResponse? _defaultInstance;
@$pb.TagNumber(1)
$core.bool get accept => $_getBF(0);
@$pb.TagNumber(1)
set accept($core.bool v) { $_setBool(0, v); }
@$pb.TagNumber(1)
$core.bool hasAccept() => $_has(0);
@$pb.TagNumber(1)
void clearAccept() => clearField(1);
@$pb.TagNumber(2)
TypedKey get accountMasterRecordKey => $_getN(1);
@$pb.TagNumber(2)
set accountMasterRecordKey(TypedKey v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasAccountMasterRecordKey() => $_has(1);
@$pb.TagNumber(2)
void clearAccountMasterRecordKey() => clearField(2);
@$pb.TagNumber(2)
TypedKey ensureAccountMasterRecordKey() => $_ensure(1);
@$pb.TagNumber(3)
TypedKey get chatRecordKey => $_getN(2);
@$pb.TagNumber(3)
set chatRecordKey(TypedKey v) { setField(3, v); }
@$pb.TagNumber(3)
$core.bool hasChatRecordKey() => $_has(2);
@$pb.TagNumber(3)
void clearChatRecordKey() => clearField(3);
@$pb.TagNumber(3)
TypedKey ensureChatRecordKey() => $_ensure(2);
}
class SignedContactResponse extends $pb.GeneratedMessage {
factory SignedContactResponse() => create();
SignedContactResponse._() : super();
factory SignedContactResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory SignedContactResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SignedContactResponse', createEmptyInstance: create)
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'contactResponse', $pb.PbFieldType.OY)
..aOM<Signature>(2, _omitFieldNames ? '' : 'identitySignature', subBuilder: Signature.create)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
SignedContactResponse clone() => SignedContactResponse()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
SignedContactResponse copyWith(void Function(SignedContactResponse) updates) => super.copyWith((message) => updates(message as SignedContactResponse)) as SignedContactResponse;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static SignedContactResponse create() => SignedContactResponse._();
SignedContactResponse createEmptyInstance() => create();
static $pb.PbList<SignedContactResponse> createRepeated() => $pb.PbList<SignedContactResponse>();
@$core.pragma('dart2js:noInline')
static SignedContactResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SignedContactResponse>(create);
static SignedContactResponse? _defaultInstance;
@$pb.TagNumber(1)
$core.List<$core.int> get contactResponse => $_getN(0);
@$pb.TagNumber(1)
set contactResponse($core.List<$core.int> v) { $_setBytes(0, v); }
@$pb.TagNumber(1)
$core.bool hasContactResponse() => $_has(0);
@$pb.TagNumber(1)
void clearContactResponse() => clearField(1);
@$pb.TagNumber(2)
Signature get identitySignature => $_getN(1);
@$pb.TagNumber(2)
set identitySignature(Signature v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasIdentitySignature() => $_has(1);
@$pb.TagNumber(2)
void clearIdentitySignature() => clearField(2);
@$pb.TagNumber(2)
Signature ensureIdentitySignature() => $_ensure(1);
}
class ContactRequestRecord extends $pb.GeneratedMessage {
factory ContactRequestRecord() => create();
ContactRequestRecord._() : super();
factory ContactRequestRecord.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory ContactRequestRecord.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ContactRequestRecord', createEmptyInstance: create)
..aOM<TypedKey>(1, _omitFieldNames ? '' : 'contactRequestRecordKey', subBuilder: TypedKey.create)
..aOM<CryptoKey>(2, _omitFieldNames ? '' : 'writerKey', subBuilder: CryptoKey.create)
..aOM<CryptoKey>(3, _omitFieldNames ? '' : 'writerSecret', subBuilder: CryptoKey.create)
..aOM<TypedKey>(4, _omitFieldNames ? '' : 'chatRecordKey', subBuilder: TypedKey.create)
..a<$fixnum.Int64>(5, _omitFieldNames ? '' : 'expiration', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
..a<$core.List<$core.int>>(6, _omitFieldNames ? '' : 'invitation', $pb.PbFieldType.OY)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
ContactRequestRecord clone() => ContactRequestRecord()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
ContactRequestRecord copyWith(void Function(ContactRequestRecord) updates) => super.copyWith((message) => updates(message as ContactRequestRecord)) as ContactRequestRecord;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ContactRequestRecord create() => ContactRequestRecord._();
ContactRequestRecord createEmptyInstance() => create();
static $pb.PbList<ContactRequestRecord> createRepeated() => $pb.PbList<ContactRequestRecord>();
@$core.pragma('dart2js:noInline')
static ContactRequestRecord getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ContactRequestRecord>(create);
static ContactRequestRecord? _defaultInstance;
@$pb.TagNumber(1)
TypedKey get contactRequestRecordKey => $_getN(0);
@$pb.TagNumber(1)
set contactRequestRecordKey(TypedKey v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasContactRequestRecordKey() => $_has(0);
@$pb.TagNumber(1)
void clearContactRequestRecordKey() => clearField(1);
@$pb.TagNumber(1)
TypedKey ensureContactRequestRecordKey() => $_ensure(0);
@$pb.TagNumber(2)
CryptoKey get writerKey => $_getN(1);
@$pb.TagNumber(2)
set writerKey(CryptoKey v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasWriterKey() => $_has(1);
@$pb.TagNumber(2)
void clearWriterKey() => clearField(2);
@$pb.TagNumber(2)
CryptoKey ensureWriterKey() => $_ensure(1);
@$pb.TagNumber(3)
CryptoKey get writerSecret => $_getN(2);
@$pb.TagNumber(3)
set writerSecret(CryptoKey v) { setField(3, v); }
@$pb.TagNumber(3)
$core.bool hasWriterSecret() => $_has(2);
@$pb.TagNumber(3)
void clearWriterSecret() => clearField(3);
@$pb.TagNumber(3)
CryptoKey ensureWriterSecret() => $_ensure(2);
@$pb.TagNumber(4)
TypedKey get chatRecordKey => $_getN(3);
@$pb.TagNumber(4)
set chatRecordKey(TypedKey v) { setField(4, v); }
@$pb.TagNumber(4)
$core.bool hasChatRecordKey() => $_has(3);
@$pb.TagNumber(4)
void clearChatRecordKey() => clearField(4);
@$pb.TagNumber(4)
TypedKey ensureChatRecordKey() => $_ensure(3);
@$pb.TagNumber(5)
$fixnum.Int64 get expiration => $_getI64(4);
@$pb.TagNumber(5)
set expiration($fixnum.Int64 v) { $_setInt64(4, v); }
@$pb.TagNumber(5)
$core.bool hasExpiration() => $_has(4);
@$pb.TagNumber(5)
void clearExpiration() => clearField(5);
@$pb.TagNumber(6)
$core.List<$core.int> get invitation => $_getN(5);
@$pb.TagNumber(6)
set invitation($core.List<$core.int> v) { $_setBytes(5, v); }
@$pb.TagNumber(6)
$core.bool hasInvitation() => $_has(5);
@$pb.TagNumber(6)
void clearInvitation() => clearField(6);
} }

View File

@ -51,5 +51,22 @@ class Availability extends $pb.ProtobufEnum {
const Availability._($core.int v, $core.String n) : super(v, n); const Availability._($core.int v, $core.String n) : super(v, n);
} }
class EncryptionKind extends $pb.ProtobufEnum {
static const EncryptionKind ENCRYPTION_KIND_UNSPECIFIED = EncryptionKind._(0, _omitEnumNames ? '' : 'ENCRYPTION_KIND_UNSPECIFIED');
static const EncryptionKind ENCRYPTION_KIND_PIN = EncryptionKind._(1, _omitEnumNames ? '' : 'ENCRYPTION_KIND_PIN');
static const EncryptionKind ENCRYPTION_KIND_PASSWORD = EncryptionKind._(2, _omitEnumNames ? '' : 'ENCRYPTION_KIND_PASSWORD');
static const $core.List<EncryptionKind> values = <EncryptionKind> [
ENCRYPTION_KIND_UNSPECIFIED,
ENCRYPTION_KIND_PIN,
ENCRYPTION_KIND_PASSWORD,
];
static final $core.Map<$core.int, EncryptionKind> _byValue = $pb.ProtobufEnum.initByValue(values);
static EncryptionKind? valueOf($core.int value) => _byValue[value];
const EncryptionKind._($core.int v, $core.String n) : super(v, n);
}
const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names');

View File

@ -46,6 +46,21 @@ final $typed_data.Uint8List availabilityDescriptor = $convert.base64Decode(
'lMSVRZX09GRkxJTkUQARIVChFBVkFJTEFCSUxJVFlfRlJFRRACEhUKEUFWQUlMQUJJTElUWV9C' 'lMSVRZX09GRkxJTkUQARIVChFBVkFJTEFCSUxJVFlfRlJFRRACEhUKEUFWQUlMQUJJTElUWV9C'
'VVNZEAMSFQoRQVZBSUxBQklMSVRZX0FXQVkQBA=='); 'VVNZEAMSFQoRQVZBSUxBQklMSVRZX0FXQVkQBA==');
@$core.Deprecated('Use encryptionKindDescriptor instead')
const EncryptionKind$json = {
'1': 'EncryptionKind',
'2': [
{'1': 'ENCRYPTION_KIND_UNSPECIFIED', '2': 0},
{'1': 'ENCRYPTION_KIND_PIN', '2': 1},
{'1': 'ENCRYPTION_KIND_PASSWORD', '2': 2},
],
};
/// Descriptor for `EncryptionKind`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List encryptionKindDescriptor = $convert.base64Decode(
'Cg5FbmNyeXB0aW9uS2luZBIfChtFTkNSWVBUSU9OX0tJTkRfVU5TUEVDSUZJRUQQABIXChNFTk'
'NSWVBUSU9OX0tJTkRfUElOEAESHAoYRU5DUllQVElPTl9LSU5EX1BBU1NXT1JEEAI=');
@$core.Deprecated('Use cryptoKeyDescriptor instead') @$core.Deprecated('Use cryptoKeyDescriptor instead')
const CryptoKey$json = { const CryptoKey$json = {
'1': 'CryptoKey', '1': 'CryptoKey',
@ -301,6 +316,7 @@ const Account$json = {
{'1': 'invisible', '3': 2, '4': 1, '5': 8, '10': 'invisible'}, {'1': 'invisible', '3': 2, '4': 1, '5': 8, '10': 'invisible'},
{'1': 'auto_away_timeout_sec', '3': 3, '4': 1, '5': 13, '10': 'autoAwayTimeoutSec'}, {'1': 'auto_away_timeout_sec', '3': 3, '4': 1, '5': 13, '10': 'autoAwayTimeoutSec'},
{'1': 'contact_list', '3': 4, '4': 1, '5': 11, '6': '.TypedKey', '10': 'contactList'}, {'1': 'contact_list', '3': 4, '4': 1, '5': 11, '6': '.TypedKey', '10': 'contactList'},
{'1': 'contact_requests', '3': 5, '4': 1, '5': 11, '6': '.TypedKey', '10': 'contactRequests'},
], ],
}; };
@ -309,5 +325,125 @@ final $typed_data.Uint8List accountDescriptor = $convert.base64Decode(
'CgdBY2NvdW50EiIKB3Byb2ZpbGUYASABKAsyCC5Qcm9maWxlUgdwcm9maWxlEhwKCWludmlzaW' 'CgdBY2NvdW50EiIKB3Byb2ZpbGUYASABKAsyCC5Qcm9maWxlUgdwcm9maWxlEhwKCWludmlzaW'
'JsZRgCIAEoCFIJaW52aXNpYmxlEjEKFWF1dG9fYXdheV90aW1lb3V0X3NlYxgDIAEoDVISYXV0' 'JsZRgCIAEoCFIJaW52aXNpYmxlEjEKFWF1dG9fYXdheV90aW1lb3V0X3NlYxgDIAEoDVISYXV0'
'b0F3YXlUaW1lb3V0U2VjEiwKDGNvbnRhY3RfbGlzdBgEIAEoCzIJLlR5cGVkS2V5Ugtjb250YW' 'b0F3YXlUaW1lb3V0U2VjEiwKDGNvbnRhY3RfbGlzdBgEIAEoCzIJLlR5cGVkS2V5Ugtjb250YW'
'N0TGlzdA=='); 'N0TGlzdBI0ChBjb250YWN0X3JlcXVlc3RzGAUgASgLMgkuVHlwZWRLZXlSD2NvbnRhY3RSZXF1'
'ZXN0cw==');
@$core.Deprecated('Use contactInvitationDescriptor instead')
const ContactInvitation$json = {
'1': 'ContactInvitation',
'2': [
{'1': 'contact_request_record_key', '3': 1, '4': 1, '5': 11, '6': '.TypedKey', '10': 'contactRequestRecordKey'},
{'1': 'writer_secret', '3': 2, '4': 1, '5': 12, '10': 'writerSecret'},
],
};
/// Descriptor for `ContactInvitation`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List contactInvitationDescriptor = $convert.base64Decode(
'ChFDb250YWN0SW52aXRhdGlvbhJGChpjb250YWN0X3JlcXVlc3RfcmVjb3JkX2tleRgBIAEoCz'
'IJLlR5cGVkS2V5Uhdjb250YWN0UmVxdWVzdFJlY29yZEtleRIjCg13cml0ZXJfc2VjcmV0GAIg'
'ASgMUgx3cml0ZXJTZWNyZXQ=');
@$core.Deprecated('Use signedContactInvitationDescriptor instead')
const SignedContactInvitation$json = {
'1': 'SignedContactInvitation',
'2': [
{'1': 'contact_invitation', '3': 1, '4': 1, '5': 12, '10': 'contactInvitation'},
{'1': 'identity_signature', '3': 2, '4': 1, '5': 11, '6': '.Signature', '10': 'identitySignature'},
],
};
/// Descriptor for `SignedContactInvitation`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List signedContactInvitationDescriptor = $convert.base64Decode(
'ChdTaWduZWRDb250YWN0SW52aXRhdGlvbhItChJjb250YWN0X2ludml0YXRpb24YASABKAxSEW'
'NvbnRhY3RJbnZpdGF0aW9uEjkKEmlkZW50aXR5X3NpZ25hdHVyZRgCIAEoCzIKLlNpZ25hdHVy'
'ZVIRaWRlbnRpdHlTaWduYXR1cmU=');
@$core.Deprecated('Use contactRequestDescriptor instead')
const ContactRequest$json = {
'1': 'ContactRequest',
'2': [
{'1': 'writer_salt', '3': 1, '4': 1, '5': 12, '10': 'writerSalt'},
{'1': 'encryption_key_type', '3': 2, '4': 1, '5': 14, '6': '.EncryptionKind', '10': 'encryptionKeyType'},
{'1': 'private', '3': 3, '4': 1, '5': 12, '10': 'private'},
],
};
/// Descriptor for `ContactRequest`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List contactRequestDescriptor = $convert.base64Decode(
'Cg5Db250YWN0UmVxdWVzdBIfCgt3cml0ZXJfc2FsdBgBIAEoDFIKd3JpdGVyU2FsdBI/ChNlbm'
'NyeXB0aW9uX2tleV90eXBlGAIgASgOMg8uRW5jcnlwdGlvbktpbmRSEWVuY3J5cHRpb25LZXlU'
'eXBlEhgKB3ByaXZhdGUYAyABKAxSB3ByaXZhdGU=');
@$core.Deprecated('Use contactRequestPrivateDescriptor instead')
const ContactRequestPrivate$json = {
'1': 'ContactRequestPrivate',
'2': [
{'1': 'writer_key', '3': 1, '4': 1, '5': 11, '6': '.CryptoKey', '10': 'writerKey'},
{'1': 'profile', '3': 2, '4': 1, '5': 11, '6': '.Profile', '10': 'profile'},
{'1': 'account_master_record_key', '3': 3, '4': 1, '5': 11, '6': '.TypedKey', '10': 'accountMasterRecordKey'},
{'1': 'chat_record_key', '3': 4, '4': 1, '5': 11, '6': '.TypedKey', '10': 'chatRecordKey'},
{'1': 'expiration', '3': 5, '4': 1, '5': 4, '10': 'expiration'},
],
};
/// Descriptor for `ContactRequestPrivate`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List contactRequestPrivateDescriptor = $convert.base64Decode(
'ChVDb250YWN0UmVxdWVzdFByaXZhdGUSKQoKd3JpdGVyX2tleRgBIAEoCzIKLkNyeXB0b0tleV'
'IJd3JpdGVyS2V5EiIKB3Byb2ZpbGUYAiABKAsyCC5Qcm9maWxlUgdwcm9maWxlEkQKGWFjY291'
'bnRfbWFzdGVyX3JlY29yZF9rZXkYAyABKAsyCS5UeXBlZEtleVIWYWNjb3VudE1hc3RlclJlY2'
'9yZEtleRIxCg9jaGF0X3JlY29yZF9rZXkYBCABKAsyCS5UeXBlZEtleVINY2hhdFJlY29yZEtl'
'eRIeCgpleHBpcmF0aW9uGAUgASgEUgpleHBpcmF0aW9u');
@$core.Deprecated('Use contactResponseDescriptor instead')
const ContactResponse$json = {
'1': 'ContactResponse',
'2': [
{'1': 'accept', '3': 1, '4': 1, '5': 8, '10': 'accept'},
{'1': 'account_master_record_key', '3': 2, '4': 1, '5': 11, '6': '.TypedKey', '10': 'accountMasterRecordKey'},
{'1': 'chat_record_key', '3': 3, '4': 1, '5': 11, '6': '.TypedKey', '10': 'chatRecordKey'},
],
};
/// Descriptor for `ContactResponse`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List contactResponseDescriptor = $convert.base64Decode(
'Cg9Db250YWN0UmVzcG9uc2USFgoGYWNjZXB0GAEgASgIUgZhY2NlcHQSRAoZYWNjb3VudF9tYX'
'N0ZXJfcmVjb3JkX2tleRgCIAEoCzIJLlR5cGVkS2V5UhZhY2NvdW50TWFzdGVyUmVjb3JkS2V5'
'EjEKD2NoYXRfcmVjb3JkX2tleRgDIAEoCzIJLlR5cGVkS2V5Ug1jaGF0UmVjb3JkS2V5');
@$core.Deprecated('Use signedContactResponseDescriptor instead')
const SignedContactResponse$json = {
'1': 'SignedContactResponse',
'2': [
{'1': 'contact_response', '3': 1, '4': 1, '5': 12, '10': 'contactResponse'},
{'1': 'identity_signature', '3': 2, '4': 1, '5': 11, '6': '.Signature', '10': 'identitySignature'},
],
};
/// Descriptor for `SignedContactResponse`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List signedContactResponseDescriptor = $convert.base64Decode(
'ChVTaWduZWRDb250YWN0UmVzcG9uc2USKQoQY29udGFjdF9yZXNwb25zZRgBIAEoDFIPY29udG'
'FjdFJlc3BvbnNlEjkKEmlkZW50aXR5X3NpZ25hdHVyZRgCIAEoCzIKLlNpZ25hdHVyZVIRaWRl'
'bnRpdHlTaWduYXR1cmU=');
@$core.Deprecated('Use contactRequestRecordDescriptor instead')
const ContactRequestRecord$json = {
'1': 'ContactRequestRecord',
'2': [
{'1': 'contact_request_record_key', '3': 1, '4': 1, '5': 11, '6': '.TypedKey', '10': 'contactRequestRecordKey'},
{'1': 'writer_key', '3': 2, '4': 1, '5': 11, '6': '.CryptoKey', '10': 'writerKey'},
{'1': 'writer_secret', '3': 3, '4': 1, '5': 11, '6': '.CryptoKey', '10': 'writerSecret'},
{'1': 'chat_record_key', '3': 4, '4': 1, '5': 11, '6': '.TypedKey', '10': 'chatRecordKey'},
{'1': 'expiration', '3': 5, '4': 1, '5': 4, '10': 'expiration'},
{'1': 'invitation', '3': 6, '4': 1, '5': 12, '10': 'invitation'},
],
};
/// Descriptor for `ContactRequestRecord`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List contactRequestRecordDescriptor = $convert.base64Decode(
'ChRDb250YWN0UmVxdWVzdFJlY29yZBJGChpjb250YWN0X3JlcXVlc3RfcmVjb3JkX2tleRgBIA'
'EoCzIJLlR5cGVkS2V5Uhdjb250YWN0UmVxdWVzdFJlY29yZEtleRIpCgp3cml0ZXJfa2V5GAIg'
'ASgLMgouQ3J5cHRvS2V5Ugl3cml0ZXJLZXkSLwoNd3JpdGVyX3NlY3JldBgDIAEoCzIKLkNyeX'
'B0b0tleVIMd3JpdGVyU2VjcmV0EjEKD2NoYXRfcmVjb3JkX2tleRgEIAEoCzIJLlR5cGVkS2V5'
'Ug1jaGF0UmVjb3JkS2V5Eh4KCmV4cGlyYXRpb24YBSABKARSCmV4cGlyYXRpb24SHgoKaW52aX'
'RhdGlvbhgGIAEoDFIKaW52aXRhdGlvbg==');

View File

@ -61,6 +61,7 @@ message TypedKey {
message DHTData { message DHTData {
// Other keys to concatenate // Other keys to concatenate
// Uses the same writer as this DHTList with SMPL schema
repeated TypedKey keys = 1; repeated TypedKey keys = 1;
// Hash of reassembled data to verify contents // Hash of reassembled data to verify contents
TypedKey hash = 2; TypedKey hash = 2;
@ -80,6 +81,7 @@ message DHTData {
// Keys must use writable schema in order to make this list mutable // Keys must use writable schema in order to make this list mutable
message DHTList { message DHTList {
// Other keys to concatenate // Other keys to concatenate
// Uses the same writer as this DHTList with SMPL schema
repeated TypedKey keys = 1; repeated TypedKey keys = 1;
// Item position index // Item position index
// Actual item location is: // Actual item location is:
@ -223,6 +225,16 @@ message Profile {
optional TypedKey avatar = 5; optional TypedKey avatar = 5;
} }
// A pointer to an owned DHT record
message OwnedDHTRecordPointer {
// DHT Record key
TypedKey record_key = 1;
// DHT record owner key
CryptoKey owner_key = 2;
// DHT record owner secret
CryptoKey owner_secret = 3;
}
// A record of an individual account // A record of an individual account
// Pointed to by the identity account map in the identity key // Pointed to by the identity account map in the identity key
// //
@ -236,6 +248,95 @@ message Account {
// Auto-away sets 'away' mode after an inactivity time // Auto-away sets 'away' mode after an inactivity time
uint32 auto_away_timeout_sec = 3; uint32 auto_away_timeout_sec = 3;
// The contacts DHTList for this account // The contacts DHTList for this account
// DHT Private: accountSecretKey // DHT Private
TypedKey contact_list = 4; OwnedDHTRecordPointer contact_list = 4;
// The contact requests DHTList for this account
// DHT Private
OwnedDHTRecordPointer contact_requests = 5;
}
// EncryptionKind
// Encryption of secret
enum EncryptionKind {
ENCRYPTION_KIND_UNSPECIFIED = 0;
ENCRYPTION_KIND_PIN = 1;
ENCRYPTION_KIND_PASSWORD =2;
}
// Invitation that is shared for VeilidChat contact connections
// serialized to QR code or data blob, not send over DHT, out of band.
// Writer secret is unique to this invitation
message ContactInvitation {
// Contact request DHT record key
TypedKey contact_request_record_key = 1;
// Writer secret key bytes possibly encrypted
bytes writer_secret = 2;
}
// Signature of invitation with identity
message SignedContactInvitation {
// The serialized bytes for the contact invitation
bytes contact_invitation = 1;
// The signature of the contact_invitation bytes with the identity
Signature identity_signature = 2;
}
// Contact request unicastinbox on the DHT
// DHTSchema: SMPL 2 owner key, 1 writer key symmetrically encrypted with writer secret
message ContactRequest {
// The salt for the encryption used on the unicastinbox writer secret
bytes writer_salt = 1;
// The kind of encryption used on the unicastinbox writer key
EncryptionKind encryption_key_type = 2;
// The private part encoded and symmetrically encrypted with the unicastinbox writer secret
bytes private = 3;
}
// The private part of a possibly encrypted contact request
// Symmetrically encrypted with writer secret
message ContactRequestPrivate {
// Writer public key for signing writes to contact request unicastinbox
CryptoKey writer_key = 1;
// Snapshot of profile
Profile profile = 2;
// Account master dht key
TypedKey account_master_record_key = 3;
// Local chat DHT record key
TypedKey chat_record_key = 4;
// Expiration timestamp
uint64 expiration = 5;
}
// To accept or reject a contact request, fill this out and send to the ContactRequest unicastinbox
message ContactResponse {
// Accept or reject
bool accept = 1;
// Account master record key
TypedKey account_master_record_key = 2;
// Local chat DHT record key if accepted
TypedKey chat_record_key = 3;
}
// Signature of response with identity
// Symmetrically encrypted with writer secret
message SignedContactResponse {
// Serialized bytes for ContactResponse
bytes contact_response = 1;
// Signature of the contact_accept bytes with the identity
Signature identity_signature = 2;
}
// Contact request record kept in Account DHTList to keep track of extant contact invitations
message ContactRequestRecord {
// Contact request unicastinbox DHT record key
TypedKey contact_request_record_key = 1;
// Unencrypted writer key for this request
CryptoKey writer_key = 2;
CryptoKey writer_secret = 3;
// Local chat DHT record key
TypedKey chat_record_key = 4;
// Expiration timestamp
uint64 expiration = 5;
// A copy of the raw invitation bytes post-encryption
bytes invitation = 6;
} }

View File

@ -0,0 +1,48 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:veilid/veilid.dart';
import '../entities/entities.dart';
import '../entities/proto.dart' as proto;
import '../tools/tools.dart';
import '../veilid_support/veilid_support.dart';
import 'logins.dart';
part 'contact_request_records.g.dart';
// Contact invitation records stored in Account
class ContactRequestRecords extends DHTList<proto.ContactRequestRecord> {
//
Future<proto.ContactRequestRecord> newContactRequest(
proto.EncryptionKind encryptionKind,
String encryptionKey,
) async {
//
}
}
class ContactRequestRecordsParams {
ContactRequestRecordsParams({required this.contactRequestsDHTListKey});
TypedKey contactRequestsDHTListKey;
}
@riverpod
Future<ContactRequestRecords?> fetchContactRequestRecords(
FetchContactRequestRecordsRef ref,
{required ContactRequestRecordsParams params}) async {
// final localAccounts = await ref.watch(localAccountsProvider.future);
// try {
// return localAccounts.firstWhere(
// (e) => e.identityMaster.masterRecordKey == accountMasterRecordKey);
// } on Exception catch (e) {
// if (e is StateError) {
// return null;
// }
// rethrow;
// }
}

View File

@ -0,0 +1,117 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'contact_request_records.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$fetchContactRequestRecordsHash() =>
r'603c6d81b22d1cb4fd26cf32b98d3206ff6bc38c';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
typedef FetchContactRequestRecordsRef
= AutoDisposeFutureProviderRef<ContactRequestRecords?>;
/// See also [fetchContactRequestRecords].
@ProviderFor(fetchContactRequestRecords)
const fetchContactRequestRecordsProvider = FetchContactRequestRecordsFamily();
/// See also [fetchContactRequestRecords].
class FetchContactRequestRecordsFamily
extends Family<AsyncValue<ContactRequestRecords?>> {
/// See also [fetchContactRequestRecords].
const FetchContactRequestRecordsFamily();
/// See also [fetchContactRequestRecords].
FetchContactRequestRecordsProvider call({
required ContactRequestRecordsParams params,
}) {
return FetchContactRequestRecordsProvider(
params: params,
);
}
@override
FetchContactRequestRecordsProvider getProviderOverride(
covariant FetchContactRequestRecordsProvider provider,
) {
return call(
params: provider.params,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'fetchContactRequestRecordsProvider';
}
/// See also [fetchContactRequestRecords].
class FetchContactRequestRecordsProvider
extends AutoDisposeFutureProvider<ContactRequestRecords?> {
/// See also [fetchContactRequestRecords].
FetchContactRequestRecordsProvider({
required this.params,
}) : super.internal(
(ref) => fetchContactRequestRecords(
ref,
params: params,
),
from: fetchContactRequestRecordsProvider,
name: r'fetchContactRequestRecordsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$fetchContactRequestRecordsHash,
dependencies: FetchContactRequestRecordsFamily._dependencies,
allTransitiveDependencies:
FetchContactRequestRecordsFamily._allTransitiveDependencies,
);
final ContactRequestRecordsParams params;
@override
bool operator ==(Object other) {
return other is FetchContactRequestRecordsProvider &&
other.params == params;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, params.hashCode);
return _SystemHash.finish(hash);
}
}
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions

View File

@ -0,0 +1,43 @@
import 'dart:typed_data';
import 'package:protobuf/protobuf.dart';
import 'package:veilid/veilid.dart';
import '../tools/tools.dart';
import 'veilid_support.dart';
class DHTList {
DHTList({required DHTRecord dhtRecord}) : _dhtRecord = dhtRecord;
final DHTRecord _dhtRecord;
static Future<DHTList> create(VeilidRoutingContext dhtctx,
{DHTRecordCrypto? crypto}) async {
final dhtRecord = await DHTRecord.create(dhtctx, crypto: crypto);
final dhtList = DHTList(dhtRecord: dhtRecord);
return dhtList;
}
static Future<DHTList> openRead(
VeilidRoutingContext dhtctx, TypedKey dhtRecordKey,
{DHTRecordCrypto? crypto}) async {
final dhtRecord =
await DHTRecord.openRead(dhtctx, dhtRecordKey, crypto: crypto);
final dhtList = DHTList(dhtRecord: dhtRecord);
return dhtList;
}
static Future<DHTList> openWrite(
VeilidRoutingContext dhtctx,
TypedKey dhtRecordKey,
KeyPair writer, {
DHTRecordCrypto? crypto,
}) async {
final dhtRecord =
await DHTRecord.openWrite(dhtctx, dhtRecordKey, writer, crypto: crypto);
final dhtList = DHTList(dhtRecord: dhtRecord);
return dhtList;
}
////////////////////////////////////////////////////////////////
}

View File

@ -3,6 +3,7 @@ import 'dart:typed_data';
import 'package:protobuf/protobuf.dart'; import 'package:protobuf/protobuf.dart';
import 'package:veilid/veilid.dart'; import 'package:veilid/veilid.dart';
import '../entities/proto.dart' as proto;
import '../tools/tools.dart'; import '../tools/tools.dart';
import 'veilid_support.dart'; import 'veilid_support.dart';

View File

@ -7,6 +7,7 @@ import Foundation
import path_provider_foundation import path_provider_foundation
import screen_retriever import screen_retriever
import share_plus
import shared_preferences_foundation import shared_preferences_foundation
import sqflite import sqflite
import url_launcher_macos import url_launcher_macos
@ -16,6 +17,7 @@ import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))

View File

@ -281,6 +281,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.1" version: "3.1.1"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9"
url: "https://pub.dev"
source: hosted
version: "0.3.3+4"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -981,6 +989,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.1"
share_plus:
dependency: "direct main"
description:
name: share_plus
sha256: ed3fcea4f789ed95913328e629c0c53e69e80e08b6c24542f1b3576046c614e8
url: "https://pub.dev"
source: hosted
version: "7.0.2"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
sha256: "0c6e61471bd71b04a138b8b588fa388e66d8b005e6f2deda63371c5c505a0981"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -47,6 +47,7 @@ dependencies:
radix_colors: ^1.0.4 radix_colors: ^1.0.4
reorderable_grid: ^1.0.7 reorderable_grid: ^1.0.7
riverpod_annotation: ^2.1.1 riverpod_annotation: ^2.1.1
share_plus: ^7.0.2
shared_preferences: ^2.0.15 shared_preferences: ^2.0.15
signal_strength_indicator: ^0.4.1 signal_strength_indicator: ^0.4.1
split_view: ^3.2.1 split_view: ^3.2.1

View File

@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <screen_retriever/screen_retriever_plugin.h> #include <screen_retriever/screen_retriever_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
#include <veilid/veilid_plugin.h> #include <veilid/veilid_plugin.h>
#include <window_manager/window_manager_plugin.h> #include <window_manager/window_manager_plugin.h>
@ -14,6 +15,8 @@
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
ScreenRetrieverPluginRegisterWithRegistrar( ScreenRetrieverPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
SharePlusWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows")); registry->GetRegistrarForPlugin("UrlLauncherWindows"));
VeilidPluginRegisterWithRegistrar( VeilidPluginRegisterWithRegistrar(

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
screen_retriever screen_retriever
share_plus
url_launcher_windows url_launcher_windows
veilid veilid
window_manager window_manager