mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2024-10-01 06:55:46 -04:00
big identity refactor
This commit is contained in:
parent
bae58d5f5c
commit
ddc02f6771
@ -31,7 +31,8 @@
|
|||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"accept": "Accept",
|
"accept": "Accept",
|
||||||
"reject": "Reject"
|
"reject": "Reject",
|
||||||
|
"waiting_for_network": "Waiting For Network"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
|
@ -16,27 +16,27 @@
|
|||||||
2. Get the ContactRequest record unicastinbox DHT record owner subkey from the network
|
2. Get the ContactRequest record unicastinbox DHT record owner subkey from the network
|
||||||
3. Decrypt the writer secret with the password if necessary
|
3. Decrypt the writer secret with the password if necessary
|
||||||
4. Decrypt the ContactRequestPrivate chunk with the writer secret
|
4. Decrypt the ContactRequestPrivate chunk with the writer secret
|
||||||
5. Get the contact's AccountMaster record key
|
5. Get the contact's SuperIdentity record key
|
||||||
6. Verify identity signature on the SignedContactInvitation
|
6. Verify identity signature on the SignedContactInvitation
|
||||||
7. Verify expiration
|
7. Verify expiration
|
||||||
8. Display the profile and ask if the user wants to accept or reject the invitation
|
8. Display the profile and ask if the user wants to accept or reject the invitation
|
||||||
|
|
||||||
## Accepting an invitation
|
## Accepting an invitation
|
||||||
1. Create a Local Chat DHT record (no content yet, will be encrypted with DH of contact identity key)
|
1. Create a Local Chat DHT record (no content yet, will be encrypted with DH of contact identity key)
|
||||||
2. Create ContactResponse with chat dht record and account master
|
2. Create ContactResponse with chat dht record and superidentity
|
||||||
3. Create SignedContactResponse with accept=true signed with identity
|
3. Create SignedContactResponse with accept=true signed with identity
|
||||||
4. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
|
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
|
5. Add a local contact with the remote chat dht record, updating from the remote profile in it
|
||||||
|
|
||||||
## Rejecting an invitation
|
## Rejecting an invitation
|
||||||
1. Create ContactResponse with account master
|
1. Create ContactResponse with superidentity
|
||||||
2. Create SignedContactResponse with accept=false signed with identity
|
2. Create SignedContactResponse with accept=false signed with identity
|
||||||
3. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
|
3. Set ContactRequest unicastinbox DHT record writer subkey with SignedContactResponse, encrypted with writer secret
|
||||||
|
|
||||||
## Receiving an accept/reject
|
## Receiving an accept/reject
|
||||||
1. Open and get SignedContactResponse from ContactRequest unicastinbox DHT record
|
1. Open and get SignedContactResponse from ContactRequest unicastinbox DHT record
|
||||||
2. Decrypt with writer secret
|
2. Decrypt with writer secret
|
||||||
3. Get DHT record for contact's AccountMaster
|
3. Get DHT record for contact's SuperIdentity
|
||||||
4. Validate the SignedContactResponse signature
|
4. Validate the SignedContactResponse signature
|
||||||
|
|
||||||
If accept == false:
|
If accept == false:
|
||||||
|
@ -14,14 +14,18 @@ class ActiveAccountInfo {
|
|||||||
});
|
});
|
||||||
//
|
//
|
||||||
|
|
||||||
|
TypedKey get superIdentityRecordKey => localAccount.superIdentity.recordKey;
|
||||||
TypedKey get accountRecordKey =>
|
TypedKey get accountRecordKey =>
|
||||||
userLogin.accountRecordInfo.accountRecord.recordKey;
|
userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||||
|
TypedKey get identityTypedPublicKey =>
|
||||||
KeyPair get conversationWriter {
|
localAccount.superIdentity.currentInstance.typedPublicKey;
|
||||||
final identityKey = localAccount.identityMaster.identityPublicKey;
|
PublicKey get identityPublicKey =>
|
||||||
final identitySecret = userLogin.identitySecret;
|
localAccount.superIdentity.currentInstance.publicKey;
|
||||||
return KeyPair(key: identityKey, secret: identitySecret.value);
|
SecretKey get identitySecretKey => userLogin.identitySecret.value;
|
||||||
}
|
KeyPair get identityWriter =>
|
||||||
|
KeyPair(key: identityPublicKey, secret: identitySecretKey);
|
||||||
|
Future<VeilidCryptoSystem> get identityCryptoSystem =>
|
||||||
|
localAccount.superIdentity.currentInstance.cryptoSystem;
|
||||||
|
|
||||||
Future<VeilidCrypto> makeConversationCrypto(
|
Future<VeilidCrypto> makeConversationCrypto(
|
||||||
TypedKey remoteIdentityPublicKey) async {
|
TypedKey remoteIdentityPublicKey) async {
|
||||||
|
@ -11,25 +11,31 @@ part 'local_account.freezed.dart';
|
|||||||
// Local Accounts are stored in a table locally and not backed by a DHT key
|
// Local Accounts are stored in a table locally and not backed by a DHT key
|
||||||
// and represents the accounts that have been added/imported
|
// and represents the accounts that have been added/imported
|
||||||
// on the current device.
|
// on the current device.
|
||||||
// Stores a copy of the IdentityMaster associated with the account
|
// Stores a copy of the most recent SuperIdentity associated with the account
|
||||||
// and the identitySecretKey optionally encrypted by an unlock code
|
// and the identitySecretKey optionally encrypted by an unlock code
|
||||||
// This is the root of the account information tree for VeilidChat
|
// This is the root of the account information tree for VeilidChat
|
||||||
//
|
//
|
||||||
@freezed
|
@freezed
|
||||||
class LocalAccount with _$LocalAccount {
|
class LocalAccount with _$LocalAccount {
|
||||||
const factory LocalAccount({
|
const factory LocalAccount({
|
||||||
// The master key record for the account, containing the identityPublicKey
|
// The super identity key record for the account,
|
||||||
required IdentityMaster identityMaster,
|
// containing the publicKey in the currentIdentity
|
||||||
// The encrypted identity secret that goes with
|
required SuperIdentity superIdentity,
|
||||||
|
|
||||||
|
// The encrypted currentIdentity secret that goes with
|
||||||
// the identityPublicKey with appended salt
|
// the identityPublicKey with appended salt
|
||||||
@Uint8ListJsonConverter() required Uint8List identitySecretBytes,
|
@Uint8ListJsonConverter() required Uint8List identitySecretBytes,
|
||||||
|
|
||||||
// The kind of encryption input used on the account
|
// The kind of encryption input used on the account
|
||||||
required EncryptionKeyType encryptionKeyType,
|
required EncryptionKeyType encryptionKeyType,
|
||||||
|
|
||||||
// If account is not hidden, password can be retrieved via
|
// If account is not hidden, password can be retrieved via
|
||||||
required bool biometricsEnabled,
|
required bool biometricsEnabled,
|
||||||
|
|
||||||
// Keep account hidden unless account password is entered
|
// Keep account hidden unless account password is entered
|
||||||
// (tries all hidden accounts with auth method (no biometrics))
|
// (tries all hidden accounts with auth method (no biometrics))
|
||||||
required bool hiddenAccount,
|
required bool hiddenAccount,
|
||||||
|
|
||||||
// Display name for account until it is unlocked
|
// Display name for account until it is unlocked
|
||||||
required String name,
|
required String name,
|
||||||
}) = _LocalAccount;
|
}) = _LocalAccount;
|
||||||
|
@ -20,9 +20,10 @@ LocalAccount _$LocalAccountFromJson(Map<String, dynamic> json) {
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$LocalAccount {
|
mixin _$LocalAccount {
|
||||||
// The master key record for the account, containing the identityPublicKey
|
// The super identity key record for the account,
|
||||||
IdentityMaster get identityMaster =>
|
// containing the publicKey in the currentIdentity
|
||||||
throw _privateConstructorUsedError; // The encrypted identity secret that goes with
|
SuperIdentity get superIdentity =>
|
||||||
|
throw _privateConstructorUsedError; // The encrypted currentIdentity secret that goes with
|
||||||
// the identityPublicKey with appended salt
|
// the identityPublicKey with appended salt
|
||||||
@Uint8ListJsonConverter()
|
@Uint8ListJsonConverter()
|
||||||
Uint8List get identitySecretBytes =>
|
Uint8List get identitySecretBytes =>
|
||||||
@ -49,14 +50,14 @@ abstract class $LocalAccountCopyWith<$Res> {
|
|||||||
_$LocalAccountCopyWithImpl<$Res, LocalAccount>;
|
_$LocalAccountCopyWithImpl<$Res, LocalAccount>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{IdentityMaster identityMaster,
|
{SuperIdentity superIdentity,
|
||||||
@Uint8ListJsonConverter() Uint8List identitySecretBytes,
|
@Uint8ListJsonConverter() Uint8List identitySecretBytes,
|
||||||
EncryptionKeyType encryptionKeyType,
|
EncryptionKeyType encryptionKeyType,
|
||||||
bool biometricsEnabled,
|
bool biometricsEnabled,
|
||||||
bool hiddenAccount,
|
bool hiddenAccount,
|
||||||
String name});
|
String name});
|
||||||
|
|
||||||
$IdentityMasterCopyWith<$Res> get identityMaster;
|
$SuperIdentityCopyWith<$Res> get superIdentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@ -72,7 +73,7 @@ class _$LocalAccountCopyWithImpl<$Res, $Val extends LocalAccount>
|
|||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
Object? identityMaster = null,
|
Object? superIdentity = null,
|
||||||
Object? identitySecretBytes = null,
|
Object? identitySecretBytes = null,
|
||||||
Object? encryptionKeyType = null,
|
Object? encryptionKeyType = null,
|
||||||
Object? biometricsEnabled = null,
|
Object? biometricsEnabled = null,
|
||||||
@ -80,10 +81,10 @@ class _$LocalAccountCopyWithImpl<$Res, $Val extends LocalAccount>
|
|||||||
Object? name = null,
|
Object? name = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
identityMaster: null == identityMaster
|
superIdentity: null == superIdentity
|
||||||
? _value.identityMaster
|
? _value.superIdentity
|
||||||
: identityMaster // ignore: cast_nullable_to_non_nullable
|
: superIdentity // ignore: cast_nullable_to_non_nullable
|
||||||
as IdentityMaster,
|
as SuperIdentity,
|
||||||
identitySecretBytes: null == identitySecretBytes
|
identitySecretBytes: null == identitySecretBytes
|
||||||
? _value.identitySecretBytes
|
? _value.identitySecretBytes
|
||||||
: identitySecretBytes // ignore: cast_nullable_to_non_nullable
|
: identitySecretBytes // ignore: cast_nullable_to_non_nullable
|
||||||
@ -109,9 +110,9 @@ class _$LocalAccountCopyWithImpl<$Res, $Val extends LocalAccount>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
$IdentityMasterCopyWith<$Res> get identityMaster {
|
$SuperIdentityCopyWith<$Res> get superIdentity {
|
||||||
return $IdentityMasterCopyWith<$Res>(_value.identityMaster, (value) {
|
return $SuperIdentityCopyWith<$Res>(_value.superIdentity, (value) {
|
||||||
return _then(_value.copyWith(identityMaster: value) as $Val);
|
return _then(_value.copyWith(superIdentity: value) as $Val);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,7 +126,7 @@ abstract class _$$LocalAccountImplCopyWith<$Res>
|
|||||||
@override
|
@override
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{IdentityMaster identityMaster,
|
{SuperIdentity superIdentity,
|
||||||
@Uint8ListJsonConverter() Uint8List identitySecretBytes,
|
@Uint8ListJsonConverter() Uint8List identitySecretBytes,
|
||||||
EncryptionKeyType encryptionKeyType,
|
EncryptionKeyType encryptionKeyType,
|
||||||
bool biometricsEnabled,
|
bool biometricsEnabled,
|
||||||
@ -133,7 +134,7 @@ abstract class _$$LocalAccountImplCopyWith<$Res>
|
|||||||
String name});
|
String name});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
$IdentityMasterCopyWith<$Res> get identityMaster;
|
$SuperIdentityCopyWith<$Res> get superIdentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@ -147,7 +148,7 @@ class __$$LocalAccountImplCopyWithImpl<$Res>
|
|||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
Object? identityMaster = null,
|
Object? superIdentity = null,
|
||||||
Object? identitySecretBytes = null,
|
Object? identitySecretBytes = null,
|
||||||
Object? encryptionKeyType = null,
|
Object? encryptionKeyType = null,
|
||||||
Object? biometricsEnabled = null,
|
Object? biometricsEnabled = null,
|
||||||
@ -155,10 +156,10 @@ class __$$LocalAccountImplCopyWithImpl<$Res>
|
|||||||
Object? name = null,
|
Object? name = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$LocalAccountImpl(
|
return _then(_$LocalAccountImpl(
|
||||||
identityMaster: null == identityMaster
|
superIdentity: null == superIdentity
|
||||||
? _value.identityMaster
|
? _value.superIdentity
|
||||||
: identityMaster // ignore: cast_nullable_to_non_nullable
|
: superIdentity // ignore: cast_nullable_to_non_nullable
|
||||||
as IdentityMaster,
|
as SuperIdentity,
|
||||||
identitySecretBytes: null == identitySecretBytes
|
identitySecretBytes: null == identitySecretBytes
|
||||||
? _value.identitySecretBytes
|
? _value.identitySecretBytes
|
||||||
: identitySecretBytes // ignore: cast_nullable_to_non_nullable
|
: identitySecretBytes // ignore: cast_nullable_to_non_nullable
|
||||||
@ -187,7 +188,7 @@ class __$$LocalAccountImplCopyWithImpl<$Res>
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class _$LocalAccountImpl implements _LocalAccount {
|
class _$LocalAccountImpl implements _LocalAccount {
|
||||||
const _$LocalAccountImpl(
|
const _$LocalAccountImpl(
|
||||||
{required this.identityMaster,
|
{required this.superIdentity,
|
||||||
@Uint8ListJsonConverter() required this.identitySecretBytes,
|
@Uint8ListJsonConverter() required this.identitySecretBytes,
|
||||||
required this.encryptionKeyType,
|
required this.encryptionKeyType,
|
||||||
required this.biometricsEnabled,
|
required this.biometricsEnabled,
|
||||||
@ -197,10 +198,11 @@ class _$LocalAccountImpl implements _LocalAccount {
|
|||||||
factory _$LocalAccountImpl.fromJson(Map<String, dynamic> json) =>
|
factory _$LocalAccountImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
_$$LocalAccountImplFromJson(json);
|
_$$LocalAccountImplFromJson(json);
|
||||||
|
|
||||||
// The master key record for the account, containing the identityPublicKey
|
// The super identity key record for the account,
|
||||||
|
// containing the publicKey in the currentIdentity
|
||||||
@override
|
@override
|
||||||
final IdentityMaster identityMaster;
|
final SuperIdentity superIdentity;
|
||||||
// The encrypted identity secret that goes with
|
// The encrypted currentIdentity secret that goes with
|
||||||
// the identityPublicKey with appended salt
|
// the identityPublicKey with appended salt
|
||||||
@override
|
@override
|
||||||
@Uint8ListJsonConverter()
|
@Uint8ListJsonConverter()
|
||||||
@ -221,7 +223,7 @@ class _$LocalAccountImpl implements _LocalAccount {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'LocalAccount(identityMaster: $identityMaster, identitySecretBytes: $identitySecretBytes, encryptionKeyType: $encryptionKeyType, biometricsEnabled: $biometricsEnabled, hiddenAccount: $hiddenAccount, name: $name)';
|
return 'LocalAccount(superIdentity: $superIdentity, identitySecretBytes: $identitySecretBytes, encryptionKeyType: $encryptionKeyType, biometricsEnabled: $biometricsEnabled, hiddenAccount: $hiddenAccount, name: $name)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -229,8 +231,8 @@ class _$LocalAccountImpl implements _LocalAccount {
|
|||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType &&
|
(other.runtimeType == runtimeType &&
|
||||||
other is _$LocalAccountImpl &&
|
other is _$LocalAccountImpl &&
|
||||||
(identical(other.identityMaster, identityMaster) ||
|
(identical(other.superIdentity, superIdentity) ||
|
||||||
other.identityMaster == identityMaster) &&
|
other.superIdentity == superIdentity) &&
|
||||||
const DeepCollectionEquality()
|
const DeepCollectionEquality()
|
||||||
.equals(other.identitySecretBytes, identitySecretBytes) &&
|
.equals(other.identitySecretBytes, identitySecretBytes) &&
|
||||||
(identical(other.encryptionKeyType, encryptionKeyType) ||
|
(identical(other.encryptionKeyType, encryptionKeyType) ||
|
||||||
@ -246,7 +248,7 @@ class _$LocalAccountImpl implements _LocalAccount {
|
|||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(
|
int get hashCode => Object.hash(
|
||||||
runtimeType,
|
runtimeType,
|
||||||
identityMaster,
|
superIdentity,
|
||||||
const DeepCollectionEquality().hash(identitySecretBytes),
|
const DeepCollectionEquality().hash(identitySecretBytes),
|
||||||
encryptionKeyType,
|
encryptionKeyType,
|
||||||
biometricsEnabled,
|
biometricsEnabled,
|
||||||
@ -269,7 +271,7 @@ class _$LocalAccountImpl implements _LocalAccount {
|
|||||||
|
|
||||||
abstract class _LocalAccount implements LocalAccount {
|
abstract class _LocalAccount implements LocalAccount {
|
||||||
const factory _LocalAccount(
|
const factory _LocalAccount(
|
||||||
{required final IdentityMaster identityMaster,
|
{required final SuperIdentity superIdentity,
|
||||||
@Uint8ListJsonConverter() required final Uint8List identitySecretBytes,
|
@Uint8ListJsonConverter() required final Uint8List identitySecretBytes,
|
||||||
required final EncryptionKeyType encryptionKeyType,
|
required final EncryptionKeyType encryptionKeyType,
|
||||||
required final bool biometricsEnabled,
|
required final bool biometricsEnabled,
|
||||||
@ -279,9 +281,10 @@ abstract class _LocalAccount implements LocalAccount {
|
|||||||
factory _LocalAccount.fromJson(Map<String, dynamic> json) =
|
factory _LocalAccount.fromJson(Map<String, dynamic> json) =
|
||||||
_$LocalAccountImpl.fromJson;
|
_$LocalAccountImpl.fromJson;
|
||||||
|
|
||||||
@override // The master key record for the account, containing the identityPublicKey
|
@override // The super identity key record for the account,
|
||||||
IdentityMaster get identityMaster;
|
// containing the publicKey in the currentIdentity
|
||||||
@override // The encrypted identity secret that goes with
|
SuperIdentity get superIdentity;
|
||||||
|
@override // The encrypted currentIdentity secret that goes with
|
||||||
// the identityPublicKey with appended salt
|
// the identityPublicKey with appended salt
|
||||||
@Uint8ListJsonConverter()
|
@Uint8ListJsonConverter()
|
||||||
Uint8List get identitySecretBytes;
|
Uint8List get identitySecretBytes;
|
||||||
|
@ -8,7 +8,7 @@ part of 'local_account.dart';
|
|||||||
|
|
||||||
_$LocalAccountImpl _$$LocalAccountImplFromJson(Map<String, dynamic> json) =>
|
_$LocalAccountImpl _$$LocalAccountImplFromJson(Map<String, dynamic> json) =>
|
||||||
_$LocalAccountImpl(
|
_$LocalAccountImpl(
|
||||||
identityMaster: IdentityMaster.fromJson(json['identity_master']),
|
superIdentity: SuperIdentity.fromJson(json['super_identity']),
|
||||||
identitySecretBytes: const Uint8ListJsonConverter()
|
identitySecretBytes: const Uint8ListJsonConverter()
|
||||||
.fromJson(json['identity_secret_bytes']),
|
.fromJson(json['identity_secret_bytes']),
|
||||||
encryptionKeyType:
|
encryptionKeyType:
|
||||||
@ -20,7 +20,7 @@ _$LocalAccountImpl _$$LocalAccountImplFromJson(Map<String, dynamic> json) =>
|
|||||||
|
|
||||||
Map<String, dynamic> _$$LocalAccountImplToJson(_$LocalAccountImpl instance) =>
|
Map<String, dynamic> _$$LocalAccountImplToJson(_$LocalAccountImpl instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'identity_master': instance.identityMaster.toJson(),
|
'super_identity': instance.superIdentity.toJson(),
|
||||||
'identity_secret_bytes':
|
'identity_secret_bytes':
|
||||||
const Uint8ListJsonConverter().toJson(instance.identitySecretBytes),
|
const Uint8ListJsonConverter().toJson(instance.identitySecretBytes),
|
||||||
'encryption_key_type': instance.encryptionKeyType.toJson(),
|
'encryption_key_type': instance.encryptionKeyType.toJson(),
|
||||||
|
@ -7,12 +7,13 @@ part 'user_login.g.dart';
|
|||||||
|
|
||||||
// Represents a currently logged in account
|
// Represents a currently logged in account
|
||||||
// User logins are stored in the user_logins tablestore table
|
// User logins are stored in the user_logins tablestore table
|
||||||
// indexed by the accountMasterKey
|
// indexed by the accountSuperIdentityRecordKey
|
||||||
@freezed
|
@freezed
|
||||||
class UserLogin with _$UserLogin {
|
class UserLogin with _$UserLogin {
|
||||||
const factory UserLogin({
|
const factory UserLogin({
|
||||||
// Master record key for the user used to index the local accounts table
|
// SuperIdentity record key for the user
|
||||||
required TypedKey accountMasterRecordKey,
|
// used to index the local accounts table
|
||||||
|
required TypedKey superIdentityRecordKey,
|
||||||
// The identity secret as unlocked from the local accounts table
|
// The identity secret as unlocked from the local accounts table
|
||||||
required TypedSecret identitySecret,
|
required TypedSecret identitySecret,
|
||||||
// The account record key, owner key and secret pulled from the identity
|
// The account record key, owner key and secret pulled from the identity
|
||||||
|
@ -20,8 +20,9 @@ UserLogin _$UserLoginFromJson(Map<String, dynamic> json) {
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$UserLogin {
|
mixin _$UserLogin {
|
||||||
// Master record key for the user used to index the local accounts table
|
// SuperIdentity record key for the user
|
||||||
Typed<FixedEncodedString43> get accountMasterRecordKey =>
|
// used to index the local accounts table
|
||||||
|
Typed<FixedEncodedString43> get superIdentityRecordKey =>
|
||||||
throw _privateConstructorUsedError; // The identity secret as unlocked from the local accounts table
|
throw _privateConstructorUsedError; // The identity secret as unlocked from the local accounts table
|
||||||
Typed<FixedEncodedString43> get identitySecret =>
|
Typed<FixedEncodedString43> get identitySecret =>
|
||||||
throw _privateConstructorUsedError; // The account record key, owner key and secret pulled from the identity
|
throw _privateConstructorUsedError; // The account record key, owner key and secret pulled from the identity
|
||||||
@ -41,7 +42,7 @@ abstract class $UserLoginCopyWith<$Res> {
|
|||||||
_$UserLoginCopyWithImpl<$Res, UserLogin>;
|
_$UserLoginCopyWithImpl<$Res, UserLogin>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{Typed<FixedEncodedString43> accountMasterRecordKey,
|
{Typed<FixedEncodedString43> superIdentityRecordKey,
|
||||||
Typed<FixedEncodedString43> identitySecret,
|
Typed<FixedEncodedString43> identitySecret,
|
||||||
AccountRecordInfo accountRecordInfo,
|
AccountRecordInfo accountRecordInfo,
|
||||||
Timestamp lastActive});
|
Timestamp lastActive});
|
||||||
@ -62,15 +63,15 @@ class _$UserLoginCopyWithImpl<$Res, $Val extends UserLogin>
|
|||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
Object? accountMasterRecordKey = null,
|
Object? superIdentityRecordKey = null,
|
||||||
Object? identitySecret = null,
|
Object? identitySecret = null,
|
||||||
Object? accountRecordInfo = null,
|
Object? accountRecordInfo = null,
|
||||||
Object? lastActive = null,
|
Object? lastActive = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
accountMasterRecordKey: null == accountMasterRecordKey
|
superIdentityRecordKey: null == superIdentityRecordKey
|
||||||
? _value.accountMasterRecordKey
|
? _value.superIdentityRecordKey
|
||||||
: accountMasterRecordKey // ignore: cast_nullable_to_non_nullable
|
: superIdentityRecordKey // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as Typed<FixedEncodedString43>,
|
||||||
identitySecret: null == identitySecret
|
identitySecret: null == identitySecret
|
||||||
? _value.identitySecret
|
? _value.identitySecret
|
||||||
@ -105,7 +106,7 @@ abstract class _$$UserLoginImplCopyWith<$Res>
|
|||||||
@override
|
@override
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{Typed<FixedEncodedString43> accountMasterRecordKey,
|
{Typed<FixedEncodedString43> superIdentityRecordKey,
|
||||||
Typed<FixedEncodedString43> identitySecret,
|
Typed<FixedEncodedString43> identitySecret,
|
||||||
AccountRecordInfo accountRecordInfo,
|
AccountRecordInfo accountRecordInfo,
|
||||||
Timestamp lastActive});
|
Timestamp lastActive});
|
||||||
@ -125,15 +126,15 @@ class __$$UserLoginImplCopyWithImpl<$Res>
|
|||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
Object? accountMasterRecordKey = null,
|
Object? superIdentityRecordKey = null,
|
||||||
Object? identitySecret = null,
|
Object? identitySecret = null,
|
||||||
Object? accountRecordInfo = null,
|
Object? accountRecordInfo = null,
|
||||||
Object? lastActive = null,
|
Object? lastActive = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$UserLoginImpl(
|
return _then(_$UserLoginImpl(
|
||||||
accountMasterRecordKey: null == accountMasterRecordKey
|
superIdentityRecordKey: null == superIdentityRecordKey
|
||||||
? _value.accountMasterRecordKey
|
? _value.superIdentityRecordKey
|
||||||
: accountMasterRecordKey // ignore: cast_nullable_to_non_nullable
|
: superIdentityRecordKey // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as Typed<FixedEncodedString43>,
|
||||||
identitySecret: null == identitySecret
|
identitySecret: null == identitySecret
|
||||||
? _value.identitySecret
|
? _value.identitySecret
|
||||||
@ -155,7 +156,7 @@ class __$$UserLoginImplCopyWithImpl<$Res>
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class _$UserLoginImpl implements _UserLogin {
|
class _$UserLoginImpl implements _UserLogin {
|
||||||
const _$UserLoginImpl(
|
const _$UserLoginImpl(
|
||||||
{required this.accountMasterRecordKey,
|
{required this.superIdentityRecordKey,
|
||||||
required this.identitySecret,
|
required this.identitySecret,
|
||||||
required this.accountRecordInfo,
|
required this.accountRecordInfo,
|
||||||
required this.lastActive});
|
required this.lastActive});
|
||||||
@ -163,9 +164,10 @@ class _$UserLoginImpl implements _UserLogin {
|
|||||||
factory _$UserLoginImpl.fromJson(Map<String, dynamic> json) =>
|
factory _$UserLoginImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
_$$UserLoginImplFromJson(json);
|
_$$UserLoginImplFromJson(json);
|
||||||
|
|
||||||
// Master record key for the user used to index the local accounts table
|
// SuperIdentity record key for the user
|
||||||
|
// used to index the local accounts table
|
||||||
@override
|
@override
|
||||||
final Typed<FixedEncodedString43> accountMasterRecordKey;
|
final Typed<FixedEncodedString43> superIdentityRecordKey;
|
||||||
// The identity secret as unlocked from the local accounts table
|
// The identity secret as unlocked from the local accounts table
|
||||||
@override
|
@override
|
||||||
final Typed<FixedEncodedString43> identitySecret;
|
final Typed<FixedEncodedString43> identitySecret;
|
||||||
@ -178,7 +180,7 @@ class _$UserLoginImpl implements _UserLogin {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'UserLogin(accountMasterRecordKey: $accountMasterRecordKey, identitySecret: $identitySecret, accountRecordInfo: $accountRecordInfo, lastActive: $lastActive)';
|
return 'UserLogin(superIdentityRecordKey: $superIdentityRecordKey, identitySecret: $identitySecret, accountRecordInfo: $accountRecordInfo, lastActive: $lastActive)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -186,8 +188,8 @@ class _$UserLoginImpl implements _UserLogin {
|
|||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType &&
|
(other.runtimeType == runtimeType &&
|
||||||
other is _$UserLoginImpl &&
|
other is _$UserLoginImpl &&
|
||||||
(identical(other.accountMasterRecordKey, accountMasterRecordKey) ||
|
(identical(other.superIdentityRecordKey, superIdentityRecordKey) ||
|
||||||
other.accountMasterRecordKey == accountMasterRecordKey) &&
|
other.superIdentityRecordKey == superIdentityRecordKey) &&
|
||||||
(identical(other.identitySecret, identitySecret) ||
|
(identical(other.identitySecret, identitySecret) ||
|
||||||
other.identitySecret == identitySecret) &&
|
other.identitySecret == identitySecret) &&
|
||||||
(identical(other.accountRecordInfo, accountRecordInfo) ||
|
(identical(other.accountRecordInfo, accountRecordInfo) ||
|
||||||
@ -198,7 +200,7 @@ class _$UserLoginImpl implements _UserLogin {
|
|||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType, accountMasterRecordKey,
|
int get hashCode => Object.hash(runtimeType, superIdentityRecordKey,
|
||||||
identitySecret, accountRecordInfo, lastActive);
|
identitySecret, accountRecordInfo, lastActive);
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@ -217,7 +219,7 @@ class _$UserLoginImpl implements _UserLogin {
|
|||||||
|
|
||||||
abstract class _UserLogin implements UserLogin {
|
abstract class _UserLogin implements UserLogin {
|
||||||
const factory _UserLogin(
|
const factory _UserLogin(
|
||||||
{required final Typed<FixedEncodedString43> accountMasterRecordKey,
|
{required final Typed<FixedEncodedString43> superIdentityRecordKey,
|
||||||
required final Typed<FixedEncodedString43> identitySecret,
|
required final Typed<FixedEncodedString43> identitySecret,
|
||||||
required final AccountRecordInfo accountRecordInfo,
|
required final AccountRecordInfo accountRecordInfo,
|
||||||
required final Timestamp lastActive}) = _$UserLoginImpl;
|
required final Timestamp lastActive}) = _$UserLoginImpl;
|
||||||
@ -225,8 +227,9 @@ abstract class _UserLogin implements UserLogin {
|
|||||||
factory _UserLogin.fromJson(Map<String, dynamic> json) =
|
factory _UserLogin.fromJson(Map<String, dynamic> json) =
|
||||||
_$UserLoginImpl.fromJson;
|
_$UserLoginImpl.fromJson;
|
||||||
|
|
||||||
@override // Master record key for the user used to index the local accounts table
|
@override // SuperIdentity record key for the user
|
||||||
Typed<FixedEncodedString43> get accountMasterRecordKey;
|
// used to index the local accounts table
|
||||||
|
Typed<FixedEncodedString43> get superIdentityRecordKey;
|
||||||
@override // The identity secret as unlocked from the local accounts table
|
@override // The identity secret as unlocked from the local accounts table
|
||||||
Typed<FixedEncodedString43> get identitySecret;
|
Typed<FixedEncodedString43> get identitySecret;
|
||||||
@override // The account record key, owner key and secret pulled from the identity
|
@override // The account record key, owner key and secret pulled from the identity
|
||||||
|
@ -8,8 +8,8 @@ part of 'user_login.dart';
|
|||||||
|
|
||||||
_$UserLoginImpl _$$UserLoginImplFromJson(Map<String, dynamic> json) =>
|
_$UserLoginImpl _$$UserLoginImplFromJson(Map<String, dynamic> json) =>
|
||||||
_$UserLoginImpl(
|
_$UserLoginImpl(
|
||||||
accountMasterRecordKey: Typed<FixedEncodedString43>.fromJson(
|
superIdentityRecordKey: Typed<FixedEncodedString43>.fromJson(
|
||||||
json['account_master_record_key']),
|
json['super_identity_record_key']),
|
||||||
identitySecret:
|
identitySecret:
|
||||||
Typed<FixedEncodedString43>.fromJson(json['identity_secret']),
|
Typed<FixedEncodedString43>.fromJson(json['identity_secret']),
|
||||||
accountRecordInfo:
|
accountRecordInfo:
|
||||||
@ -19,7 +19,7 @@ _$UserLoginImpl _$$UserLoginImplFromJson(Map<String, dynamic> json) =>
|
|||||||
|
|
||||||
Map<String, dynamic> _$$UserLoginImplToJson(_$UserLoginImpl instance) =>
|
Map<String, dynamic> _$$UserLoginImplToJson(_$UserLoginImpl instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'account_master_record_key': instance.accountMasterRecordKey.toJson(),
|
'super_identity_record_key': instance.superIdentityRecordKey.toJson(),
|
||||||
'identity_secret': instance.identitySecret.toJson(),
|
'identity_secret': instance.identitySecret.toJson(),
|
||||||
'account_record_info': instance.accountRecordInfo.toJson(),
|
'account_record_info': instance.accountRecordInfo.toJson(),
|
||||||
'last_active': instance.lastActive.toJson(),
|
'last_active': instance.lastActive.toJson(),
|
||||||
|
@ -87,30 +87,30 @@ class AccountRepository {
|
|||||||
: fetchUserLogin(activeLocalAccount);
|
: fetchUserLogin(activeLocalAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalAccount? fetchLocalAccount(TypedKey accountMasterRecordKey) {
|
LocalAccount? fetchLocalAccount(TypedKey accountSuperIdentityRecordKey) {
|
||||||
final localAccounts = _localAccounts.value;
|
final localAccounts = _localAccounts.value;
|
||||||
final idx = localAccounts.indexWhere(
|
final idx = localAccounts.indexWhere(
|
||||||
(e) => e.identityMaster.masterRecordKey == accountMasterRecordKey);
|
(e) => e.superIdentity.recordKey == accountSuperIdentityRecordKey);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return localAccounts[idx];
|
return localAccounts[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
UserLogin? fetchUserLogin(TypedKey accountMasterRecordKey) {
|
UserLogin? fetchUserLogin(TypedKey superIdentityRecordKey) {
|
||||||
final userLogins = _userLogins.value;
|
final userLogins = _userLogins.value;
|
||||||
final idx = userLogins
|
final idx = userLogins
|
||||||
.indexWhere((e) => e.accountMasterRecordKey == accountMasterRecordKey);
|
.indexWhere((e) => e.superIdentityRecordKey == superIdentityRecordKey);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return userLogins[idx];
|
return userLogins[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountInfo getAccountInfo(TypedKey? accountMasterRecordKey) {
|
AccountInfo getAccountInfo(TypedKey? superIdentityRecordKey) {
|
||||||
// Get active account if we have one
|
// Get active account if we have one
|
||||||
final activeLocalAccount = getActiveLocalAccount();
|
final activeLocalAccount = getActiveLocalAccount();
|
||||||
if (accountMasterRecordKey == null) {
|
if (superIdentityRecordKey == null) {
|
||||||
if (activeLocalAccount == null) {
|
if (activeLocalAccount == null) {
|
||||||
// No user logged in
|
// No user logged in
|
||||||
return const AccountInfo(
|
return const AccountInfo(
|
||||||
@ -118,12 +118,12 @@ class AccountRepository {
|
|||||||
active: false,
|
active: false,
|
||||||
activeAccountInfo: null);
|
activeAccountInfo: null);
|
||||||
}
|
}
|
||||||
accountMasterRecordKey = activeLocalAccount;
|
superIdentityRecordKey = activeLocalAccount;
|
||||||
}
|
}
|
||||||
final active = accountMasterRecordKey == activeLocalAccount;
|
final active = superIdentityRecordKey == activeLocalAccount;
|
||||||
|
|
||||||
// Get which local account we want to fetch the profile for
|
// Get which local account we want to fetch the profile for
|
||||||
final localAccount = fetchLocalAccount(accountMasterRecordKey);
|
final localAccount = fetchLocalAccount(superIdentityRecordKey);
|
||||||
if (localAccount == null) {
|
if (localAccount == null) {
|
||||||
// account does not exist
|
// account does not exist
|
||||||
return AccountInfo(
|
return AccountInfo(
|
||||||
@ -133,7 +133,7 @@ class AccountRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See if we've logged into this account or if it is locked
|
// See if we've logged into this account or if it is locked
|
||||||
final userLogin = fetchUserLogin(accountMasterRecordKey);
|
final userLogin = fetchUserLogin(superIdentityRecordKey);
|
||||||
if (userLogin == null) {
|
if (userLogin == null) {
|
||||||
// Account was locked
|
// Account was locked
|
||||||
return AccountInfo(
|
return AccountInfo(
|
||||||
@ -165,33 +165,32 @@ class AccountRepository {
|
|||||||
_streamController.add(AccountRepositoryChange.localAccounts);
|
_streamController.add(AccountRepositoryChange.localAccounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new master identity, an account associated with the master
|
/// Creates a new super identity, an identity instance, an account associated
|
||||||
/// identity, stores the account in the identity key and then logs into
|
/// with the identity instance, stores the account in the identity key and
|
||||||
/// that account with no password set at this time
|
/// then logs into that account with no password set at this time
|
||||||
Future<void> createWithNewMasterIdentity(
|
Future<void> createWithNewSuperIdentity(NewProfileSpec newProfileSpec) async {
|
||||||
NewProfileSpec newProfileSpec) async {
|
log.debug('Creating super identity');
|
||||||
log.debug('Creating master identity');
|
final wsi = await WritableSuperIdentity.create();
|
||||||
final imws = await IdentityMasterWithSecrets.create();
|
|
||||||
try {
|
try {
|
||||||
final localAccount = await _newLocalAccount(
|
final localAccount = await _newLocalAccount(
|
||||||
identityMaster: imws.identityMaster,
|
superIdentity: wsi.superIdentity,
|
||||||
identitySecret: imws.identitySecret,
|
identitySecret: wsi.identitySecret,
|
||||||
newProfileSpec: newProfileSpec);
|
newProfileSpec: newProfileSpec);
|
||||||
|
|
||||||
// Log in the new account by default with no pin
|
// Log in the new account by default with no pin
|
||||||
final ok = await login(localAccount.identityMaster.masterRecordKey,
|
final ok = await login(
|
||||||
EncryptionKeyType.none, '');
|
localAccount.superIdentity.recordKey, EncryptionKeyType.none, '');
|
||||||
assert(ok, 'login with none should never fail');
|
assert(ok, 'login with none should never fail');
|
||||||
} on Exception catch (_) {
|
} on Exception catch (_) {
|
||||||
await imws.delete();
|
await wsi.delete();
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Account associated with master identity
|
/// Creates a new Account associated with the current instance of the identity
|
||||||
/// Adds a logged-out LocalAccount to track its existence on this device
|
/// Adds a logged-out LocalAccount to track its existence on this device
|
||||||
Future<LocalAccount> _newLocalAccount(
|
Future<LocalAccount> _newLocalAccount(
|
||||||
{required IdentityMaster identityMaster,
|
{required SuperIdentity superIdentity,
|
||||||
required SecretKey identitySecret,
|
required SecretKey identitySecret,
|
||||||
required NewProfileSpec newProfileSpec,
|
required NewProfileSpec newProfileSpec,
|
||||||
EncryptionKeyType encryptionKeyType = EncryptionKeyType.none,
|
EncryptionKeyType encryptionKeyType = EncryptionKeyType.none,
|
||||||
@ -201,8 +200,9 @@ class AccountRepository {
|
|||||||
final localAccounts = await _localAccounts.get();
|
final localAccounts = await _localAccounts.get();
|
||||||
|
|
||||||
// Add account with profile to DHT
|
// Add account with profile to DHT
|
||||||
await identityMaster.addAccountToIdentity(
|
await superIdentity.currentInstance.addAccount(
|
||||||
identitySecret: identitySecret,
|
superRecordKey: superIdentity.recordKey,
|
||||||
|
secretKey: identitySecret,
|
||||||
accountKey: veilidChatAccountKey,
|
accountKey: veilidChatAccountKey,
|
||||||
createAccountCallback: (parent) async {
|
createAccountCallback: (parent) async {
|
||||||
// Make empty contact list
|
// Make empty contact list
|
||||||
@ -235,13 +235,13 @@ class AccountRepository {
|
|||||||
..contactList = contactList.toProto()
|
..contactList = contactList.toProto()
|
||||||
..contactInvitationRecords = contactInvitationRecords.toProto()
|
..contactInvitationRecords = contactInvitationRecords.toProto()
|
||||||
..chatList = chatRecords.toProto();
|
..chatList = chatRecords.toProto();
|
||||||
return account;
|
return account.writeToBuffer();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Encrypt identitySecret with key
|
// Encrypt identitySecret with key
|
||||||
final identitySecretBytes = await encryptionKeyType.encryptSecretToBytes(
|
final identitySecretBytes = await encryptionKeyType.encryptSecretToBytes(
|
||||||
secret: identitySecret,
|
secret: identitySecret,
|
||||||
cryptoKind: identityMaster.identityRecordKey.kind,
|
cryptoKind: superIdentity.currentInstance.recordKey.kind,
|
||||||
encryptionKey: encryptionKey,
|
encryptionKey: encryptionKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -250,7 +250,7 @@ class AccountRepository {
|
|||||||
// as that is not to be persisted, and only pulled from the identity key
|
// as that is not to be persisted, and only pulled from the identity key
|
||||||
// and optionally decrypted with the unlock password
|
// and optionally decrypted with the unlock password
|
||||||
final localAccount = LocalAccount(
|
final localAccount = LocalAccount(
|
||||||
identityMaster: identityMaster,
|
superIdentity: superIdentity,
|
||||||
identitySecretBytes: identitySecretBytes,
|
identitySecretBytes: identitySecretBytes,
|
||||||
encryptionKeyType: encryptionKeyType,
|
encryptionKeyType: encryptionKeyType,
|
||||||
biometricsEnabled: false,
|
biometricsEnabled: false,
|
||||||
@ -269,12 +269,12 @@ class AccountRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Remove an account and wipe the messages for this account from this device
|
/// Remove an account and wipe the messages for this account from this device
|
||||||
Future<bool> deleteLocalAccount(TypedKey accountMasterRecordKey) async {
|
Future<bool> deleteLocalAccount(TypedKey superIdentityRecordKey) async {
|
||||||
await logout(accountMasterRecordKey);
|
await logout(superIdentityRecordKey);
|
||||||
|
|
||||||
final localAccounts = await _localAccounts.get();
|
final localAccounts = await _localAccounts.get();
|
||||||
final newLocalAccounts = localAccounts.removeWhere(
|
final newLocalAccounts = localAccounts.removeWhere(
|
||||||
(la) => la.identityMaster.masterRecordKey == accountMasterRecordKey);
|
(la) => la.superIdentity.recordKey == superIdentityRecordKey);
|
||||||
|
|
||||||
await _localAccounts.set(newLocalAccounts);
|
await _localAccounts.set(newLocalAccounts);
|
||||||
_streamController.add(AccountRepositoryChange.localAccounts);
|
_streamController.add(AccountRepositoryChange.localAccounts);
|
||||||
@ -290,31 +290,35 @@ class AccountRepository {
|
|||||||
|
|
||||||
/// Delete an account from all devices
|
/// Delete an account from all devices
|
||||||
|
|
||||||
Future<void> switchToAccount(TypedKey? accountMasterRecordKey) async {
|
Future<void> switchToAccount(TypedKey? superIdentityRecordKey) async {
|
||||||
final activeLocalAccount = await _activeLocalAccount.get();
|
final activeLocalAccount = await _activeLocalAccount.get();
|
||||||
|
|
||||||
if (activeLocalAccount == accountMasterRecordKey) {
|
if (activeLocalAccount == superIdentityRecordKey) {
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accountMasterRecordKey != null) {
|
if (superIdentityRecordKey != null) {
|
||||||
// Assert the specified record key can be found, will throw if not
|
// Assert the specified record key can be found, will throw if not
|
||||||
final _ = _userLogins.value.firstWhere(
|
final _ = _userLogins.value.firstWhere(
|
||||||
(ul) => ul.accountMasterRecordKey == accountMasterRecordKey);
|
(ul) => ul.superIdentityRecordKey == superIdentityRecordKey);
|
||||||
}
|
}
|
||||||
await _activeLocalAccount.set(accountMasterRecordKey);
|
await _activeLocalAccount.set(superIdentityRecordKey);
|
||||||
_streamController.add(AccountRepositoryChange.activeLocalAccount);
|
_streamController.add(AccountRepositoryChange.activeLocalAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _decryptedLogin(
|
Future<bool> _decryptedLogin(
|
||||||
IdentityMaster identityMaster, SecretKey identitySecret) async {
|
SuperIdentity superIdentity, SecretKey identitySecret) async {
|
||||||
// Verify identity secret works and return the valid cryptosystem
|
// Verify identity secret works and return the valid cryptosystem
|
||||||
final cs = await identityMaster.validateIdentitySecret(identitySecret);
|
final cs = await superIdentity.currentInstance
|
||||||
|
.validateIdentitySecret(identitySecret);
|
||||||
|
|
||||||
// Read the identity key to get the account keys
|
// Read the identity key to get the account keys
|
||||||
final accountRecordInfoList = await identityMaster.readAccountsFromIdentity(
|
final accountRecordInfoList = await superIdentity.currentInstance
|
||||||
identitySecret: identitySecret, accountKey: veilidChatAccountKey);
|
.readAccount(
|
||||||
|
superRecordKey: superIdentity.recordKey,
|
||||||
|
secretKey: identitySecret,
|
||||||
|
accountKey: veilidChatAccountKey);
|
||||||
if (accountRecordInfoList.length > 1) {
|
if (accountRecordInfoList.length > 1) {
|
||||||
throw IdentityException.limitExceeded;
|
throw IdentityException.limitExceeded;
|
||||||
} else if (accountRecordInfoList.isEmpty) {
|
} else if (accountRecordInfoList.isEmpty) {
|
||||||
@ -326,11 +330,11 @@ class AccountRepository {
|
|||||||
final userLogins = await _userLogins.get();
|
final userLogins = await _userLogins.get();
|
||||||
final now = Veilid.instance.now();
|
final now = Veilid.instance.now();
|
||||||
final newUserLogins = userLogins.replaceFirstWhere(
|
final newUserLogins = userLogins.replaceFirstWhere(
|
||||||
(ul) => ul.accountMasterRecordKey == identityMaster.masterRecordKey,
|
(ul) => ul.superIdentityRecordKey == superIdentity.recordKey,
|
||||||
(ul) => ul != null
|
(ul) => ul != null
|
||||||
? ul.copyWith(lastActive: now)
|
? ul.copyWith(lastActive: now)
|
||||||
: UserLogin(
|
: UserLogin(
|
||||||
accountMasterRecordKey: identityMaster.masterRecordKey,
|
superIdentityRecordKey: superIdentity.recordKey,
|
||||||
identitySecret:
|
identitySecret:
|
||||||
TypedSecret(kind: cs.kind(), value: identitySecret),
|
TypedSecret(kind: cs.kind(), value: identitySecret),
|
||||||
accountRecordInfo: accountRecordInfo,
|
accountRecordInfo: accountRecordInfo,
|
||||||
@ -338,7 +342,7 @@ class AccountRepository {
|
|||||||
addIfNotFound: true);
|
addIfNotFound: true);
|
||||||
|
|
||||||
await _userLogins.set(newUserLogins);
|
await _userLogins.set(newUserLogins);
|
||||||
await _activeLocalAccount.set(identityMaster.masterRecordKey);
|
await _activeLocalAccount.set(superIdentity.recordKey);
|
||||||
|
|
||||||
_streamController
|
_streamController
|
||||||
..add(AccountRepositoryChange.userLogins)
|
..add(AccountRepositoryChange.userLogins)
|
||||||
@ -347,13 +351,13 @@ class AccountRepository {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> login(TypedKey accountMasterRecordKey,
|
Future<bool> login(TypedKey accountSuperRecordKey,
|
||||||
EncryptionKeyType encryptionKeyType, String encryptionKey) async {
|
EncryptionKeyType encryptionKeyType, String encryptionKey) async {
|
||||||
final localAccounts = await _localAccounts.get();
|
final localAccounts = await _localAccounts.get();
|
||||||
|
|
||||||
// Get account, throws if not found
|
// Get account, throws if not found
|
||||||
final localAccount = localAccounts.firstWhere(
|
final localAccount = localAccounts.firstWhere(
|
||||||
(la) => la.identityMaster.masterRecordKey == accountMasterRecordKey);
|
(la) => la.superIdentity.recordKey == accountSuperRecordKey);
|
||||||
|
|
||||||
// Log in with this local account
|
// Log in with this local account
|
||||||
|
|
||||||
@ -365,12 +369,12 @@ class AccountRepository {
|
|||||||
final identitySecret =
|
final identitySecret =
|
||||||
await localAccount.encryptionKeyType.decryptSecretFromBytes(
|
await localAccount.encryptionKeyType.decryptSecretFromBytes(
|
||||||
secretBytes: localAccount.identitySecretBytes,
|
secretBytes: localAccount.identitySecretBytes,
|
||||||
cryptoKind: localAccount.identityMaster.identityRecordKey.kind,
|
cryptoKind: localAccount.superIdentity.currentInstance.recordKey.kind,
|
||||||
encryptionKey: encryptionKey,
|
encryptionKey: encryptionKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Validate this secret with the identity public key and log in
|
// Validate this secret with the identity public key and log in
|
||||||
return _decryptedLogin(localAccount.identityMaster, identitySecret);
|
return _decryptedLogin(localAccount.superIdentity, identitySecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> logout(TypedKey? accountMasterRecordKey) async {
|
Future<void> logout(TypedKey? accountMasterRecordKey) async {
|
||||||
@ -390,20 +394,20 @@ class AccountRepository {
|
|||||||
|
|
||||||
// Remove user from active logins list
|
// Remove user from active logins list
|
||||||
final newUserLogins = (await _userLogins.get())
|
final newUserLogins = (await _userLogins.get())
|
||||||
.removeWhere((ul) => ul.accountMasterRecordKey == logoutUser);
|
.removeWhere((ul) => ul.superIdentityRecordKey == logoutUser);
|
||||||
await _userLogins.set(newUserLogins);
|
await _userLogins.set(newUserLogins);
|
||||||
_streamController.add(AccountRepositoryChange.userLogins);
|
_streamController.add(AccountRepositoryChange.userLogins);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<DHTRecord> openAccountRecord(UserLogin userLogin) async {
|
Future<DHTRecord> openAccountRecord(UserLogin userLogin) async {
|
||||||
final localAccount = fetchLocalAccount(userLogin.accountMasterRecordKey)!;
|
final localAccount = fetchLocalAccount(userLogin.superIdentityRecordKey)!;
|
||||||
|
|
||||||
// Record not yet open, do it
|
// Record not yet open, do it
|
||||||
final pool = DHTRecordPool.instance;
|
final pool = DHTRecordPool.instance;
|
||||||
final record = await pool.openRecordOwned(
|
final record = await pool.openRecordOwned(
|
||||||
userLogin.accountRecordInfo.accountRecord,
|
userLogin.accountRecordInfo.accountRecord,
|
||||||
debugName: 'AccountRepository::openAccountRecord::AccountRecord',
|
debugName: 'AccountRepository::openAccountRecord::AccountRecord',
|
||||||
parent: localAccount.identityMaster.identityRecordKey);
|
parent: localAccount.superIdentity.currentInstance.recordKey);
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import '../../../layout/default_app_bar.dart';
|
import '../../layout/default_app_bar.dart';
|
||||||
import '../../../theme/theme.dart';
|
import '../../theme/theme.dart';
|
||||||
import '../../../tools/tools.dart';
|
import '../../tools/tools.dart';
|
||||||
import '../../../veilid_processor/veilid_processor.dart';
|
import '../../veilid_processor/veilid_processor.dart';
|
||||||
import '../../account_manager.dart';
|
import '../account_manager.dart';
|
||||||
|
|
||||||
class NewAccountPage extends StatefulWidget {
|
class NewAccountPage extends StatefulWidget {
|
||||||
const NewAccountPage({super.key});
|
const NewAccountPage({super.key});
|
||||||
@ -36,63 +37,76 @@ class NewAccountPageState extends State<NewAccountPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _newAccountForm(BuildContext context,
|
Widget _newAccountForm(BuildContext context,
|
||||||
{required Future<void> Function(GlobalKey<FormBuilderState>)
|
{required Future<void> Function(GlobalKey<FormBuilderState>) onSubmit}) {
|
||||||
onSubmit}) =>
|
final networkReady = context
|
||||||
FormBuilder(
|
.watch<ConnectionStateCubit>()
|
||||||
key: _formKey,
|
.state
|
||||||
child: ListView(
|
.asData
|
||||||
children: [
|
?.value
|
||||||
Text(translate('new_account_page.header'))
|
.isPublicInternetReady ??
|
||||||
.textStyle(context.headlineSmall)
|
false;
|
||||||
.paddingSymmetric(vertical: 16),
|
final canSubmit = networkReady;
|
||||||
FormBuilderTextField(
|
|
||||||
autofocus: true,
|
return FormBuilder(
|
||||||
name: formFieldName,
|
key: _formKey,
|
||||||
decoration:
|
child: ListView(
|
||||||
InputDecoration(labelText: translate('account.form_name')),
|
children: [
|
||||||
maxLength: 64,
|
Text(translate('new_account_page.header'))
|
||||||
// The validator receives the text that the user has entered.
|
.textStyle(context.headlineSmall)
|
||||||
validator: FormBuilderValidators.compose([
|
.paddingSymmetric(vertical: 16),
|
||||||
FormBuilderValidators.required(),
|
FormBuilderTextField(
|
||||||
]),
|
autofocus: true,
|
||||||
textInputAction: TextInputAction.next,
|
name: formFieldName,
|
||||||
),
|
decoration:
|
||||||
FormBuilderTextField(
|
InputDecoration(labelText: translate('account.form_name')),
|
||||||
name: formFieldPronouns,
|
maxLength: 64,
|
||||||
maxLength: 64,
|
// The validator receives the text that the user has entered.
|
||||||
decoration: InputDecoration(
|
validator: FormBuilderValidators.compose([
|
||||||
labelText: translate('account.form_pronouns')),
|
FormBuilderValidators.required(),
|
||||||
textInputAction: TextInputAction.next,
|
]),
|
||||||
),
|
textInputAction: TextInputAction.next,
|
||||||
Row(children: [
|
),
|
||||||
const Spacer(),
|
FormBuilderTextField(
|
||||||
Text(translate('new_account_page.instructions'))
|
name: formFieldPronouns,
|
||||||
.toCenter()
|
maxLength: 64,
|
||||||
.flexible(flex: 6),
|
decoration:
|
||||||
const Spacer(),
|
InputDecoration(labelText: translate('account.form_pronouns')),
|
||||||
]).paddingSymmetric(vertical: 4),
|
textInputAction: TextInputAction.next,
|
||||||
ElevatedButton(
|
),
|
||||||
onPressed: () async {
|
Row(children: [
|
||||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
const Spacer(),
|
||||||
setState(() {
|
Text(translate('new_account_page.instructions'))
|
||||||
isInAsyncCall = true;
|
.toCenter()
|
||||||
});
|
.flexible(flex: 6),
|
||||||
try {
|
const Spacer(),
|
||||||
await onSubmit(_formKey);
|
]).paddingSymmetric(vertical: 4),
|
||||||
} finally {
|
ElevatedButton(
|
||||||
if (mounted) {
|
onPressed: !canSubmit
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||||
setState(() {
|
setState(() {
|
||||||
isInAsyncCall = false;
|
isInAsyncCall = true;
|
||||||
});
|
});
|
||||||
|
try {
|
||||||
|
await onSubmit(_formKey);
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
isInAsyncCall = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
child: Text(translate(!networkReady
|
||||||
},
|
? 'button.waiting_for_network'
|
||||||
child: Text(translate('new_account_page.create')),
|
: 'new_account_page.create')),
|
||||||
).paddingSymmetric(vertical: 4).alignAtCenterRight(),
|
).paddingSymmetric(vertical: 4).alignAtCenterRight(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -127,7 +141,7 @@ class NewAccountPageState extends State<NewAccountPage> {
|
|||||||
NewProfileSpec(name: name, pronouns: pronouns);
|
NewProfileSpec(name: name, pronouns: pronouns);
|
||||||
|
|
||||||
await AccountRepository.instance
|
await AccountRepository.instance
|
||||||
.createWithNewMasterIdentity(newProfileSpec);
|
.createWithNewSuperIdentity(newProfileSpec);
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
await showErrorModal(context, translate('new_account_page.error'),
|
await showErrorModal(context, translate('new_account_page.error'),
|
@ -1,2 +1,2 @@
|
|||||||
export 'new_account_page/new_account_page.dart';
|
export 'new_account_page.dart';
|
||||||
export 'profile_widget.dart';
|
export 'profile_widget.dart';
|
||||||
|
35
lib/app.dart
35
lib/app.dart
@ -1,4 +1,5 @@
|
|||||||
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
||||||
|
import 'package:async_tools/async_tools.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -7,6 +8,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
|
|||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
import 'account_manager/account_manager.dart';
|
import 'account_manager/account_manager.dart';
|
||||||
import 'init.dart';
|
import 'init.dart';
|
||||||
@ -22,6 +24,10 @@ class ReloadThemeIntent extends Intent {
|
|||||||
const ReloadThemeIntent();
|
const ReloadThemeIntent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AttachDetachThemeIntent extends Intent {
|
||||||
|
const AttachDetachThemeIntent();
|
||||||
|
}
|
||||||
|
|
||||||
class VeilidChatApp extends StatelessWidget {
|
class VeilidChatApp extends StatelessWidget {
|
||||||
const VeilidChatApp({
|
const VeilidChatApp({
|
||||||
required this.initialThemeData,
|
required this.initialThemeData,
|
||||||
@ -37,6 +43,29 @@ class VeilidChatApp extends StatelessWidget {
|
|||||||
final theme =
|
final theme =
|
||||||
PreferencesRepository.instance.value.themePreferences.themeData();
|
PreferencesRepository.instance.value.themePreferences.themeData();
|
||||||
ThemeSwitcher.of(context).changeTheme(theme: theme);
|
ThemeSwitcher.of(context).changeTheme(theme: theme);
|
||||||
|
|
||||||
|
// Hack to reload translations
|
||||||
|
final localizationDelegate = LocalizedApp.of(context).delegate;
|
||||||
|
singleFuture(this, () async {
|
||||||
|
await LocalizationDelegate.create(
|
||||||
|
fallbackLocale: localizationDelegate.fallbackLocale.toString(),
|
||||||
|
supportedLocales: localizationDelegate.supportedLocales
|
||||||
|
.map((x) => x.toString())
|
||||||
|
.toList());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _attachDetachTheme(BuildContext context) {
|
||||||
|
singleFuture(this, () async {
|
||||||
|
if (ProcessorRepository.instance.processorConnectionState.isAttached) {
|
||||||
|
log.info('Detaching');
|
||||||
|
await Veilid.instance.detach();
|
||||||
|
} else if (ProcessorRepository
|
||||||
|
.instance.processorConnectionState.isDetached) {
|
||||||
|
log.info('Attaching');
|
||||||
|
await Veilid.instance.attach();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildShortcuts(
|
Widget _buildShortcuts(
|
||||||
@ -48,10 +77,16 @@ class VeilidChatApp extends StatelessWidget {
|
|||||||
LogicalKeySet(
|
LogicalKeySet(
|
||||||
LogicalKeyboardKey.alt, LogicalKeyboardKey.keyR):
|
LogicalKeyboardKey.alt, LogicalKeyboardKey.keyR):
|
||||||
const ReloadThemeIntent(),
|
const ReloadThemeIntent(),
|
||||||
|
LogicalKeySet(
|
||||||
|
LogicalKeyboardKey.alt, LogicalKeyboardKey.keyD):
|
||||||
|
const AttachDetachThemeIntent(),
|
||||||
},
|
},
|
||||||
child: Actions(actions: <Type, Action<Intent>>{
|
child: Actions(actions: <Type, Action<Intent>>{
|
||||||
ReloadThemeIntent: CallbackAction<ReloadThemeIntent>(
|
ReloadThemeIntent: CallbackAction<ReloadThemeIntent>(
|
||||||
onInvoke: (intent) => _reloadTheme(context)),
|
onInvoke: (intent) => _reloadTheme(context)),
|
||||||
|
AttachDetachThemeIntent:
|
||||||
|
CallbackAction<AttachDetachThemeIntent>(
|
||||||
|
onInvoke: (intent) => _attachDetachTheme(context)),
|
||||||
}, child: Focus(autofocus: true, child: builder(context)))));
|
}, child: Focus(autofocus: true, child: builder(context)))));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -49,8 +49,7 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
|||||||
required ActiveConversationState activeConversationState,
|
required ActiveConversationState activeConversationState,
|
||||||
required SingleContactMessagesCubit messagesCubit}) {
|
required SingleContactMessagesCubit messagesCubit}) {
|
||||||
// Make local 'User'
|
// Make local 'User'
|
||||||
final localUserIdentityKey =
|
final localUserIdentityKey = activeAccountInfo.identityTypedPublicKey;
|
||||||
activeAccountInfo.localAccount.identityMaster.identityPublicTypedKey();
|
|
||||||
final localUser = types.User(
|
final localUser = types.User(
|
||||||
id: localUserIdentityKey.toString(),
|
id: localUserIdentityKey.toString(),
|
||||||
firstName: accountRecordInfo.profile.name,
|
firstName: accountRecordInfo.profile.name,
|
||||||
|
@ -114,13 +114,12 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
_conversationCrypto = await _activeAccountInfo
|
_conversationCrypto = await _activeAccountInfo
|
||||||
.makeConversationCrypto(_remoteIdentityPublicKey);
|
.makeConversationCrypto(_remoteIdentityPublicKey);
|
||||||
_senderMessageIntegrity = await MessageIntegrity.create(
|
_senderMessageIntegrity = await MessageIntegrity.create(
|
||||||
author: _activeAccountInfo.localAccount.identityMaster
|
author: _activeAccountInfo.identityTypedPublicKey);
|
||||||
.identityPublicTypedKey());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open local messages key
|
// Open local messages key
|
||||||
Future<void> _initSentMessagesCubit() async {
|
Future<void> _initSentMessagesCubit() async {
|
||||||
final writer = _activeAccountInfo.conversationWriter;
|
final writer = _activeAccountInfo.identityWriter;
|
||||||
|
|
||||||
_sentMessagesCubit = DHTLogCubit(
|
_sentMessagesCubit = DHTLogCubit(
|
||||||
open: () async => DHTLog.openWrite(_localMessagesRecordKey, writer,
|
open: () async => DHTLog.openWrite(_localMessagesRecordKey, writer,
|
||||||
@ -190,7 +189,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
{int? tail, int? count, bool? follow, bool forceRefresh = false}) async {
|
{int? tail, int? count, bool? follow, bool forceRefresh = false}) async {
|
||||||
await _initWait();
|
await _initWait();
|
||||||
|
|
||||||
print('setWindow: tail=$tail count=$count, follow=$follow');
|
// print('setWindow: tail=$tail count=$count, follow=$follow');
|
||||||
|
|
||||||
await _reconciledMessagesCubit!.setWindow(
|
await _reconciledMessagesCubit!.setWindow(
|
||||||
tail: tail, count: count, follow: follow, forceRefresh: forceRefresh);
|
tail: tail, count: count, follow: follow, forceRefresh: forceRefresh);
|
||||||
@ -241,10 +240,8 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_reconciliation.reconcileMessages(
|
_reconciliation.reconcileMessages(_activeAccountInfo.identityTypedPublicKey,
|
||||||
_activeAccountInfo.localAccount.identityMaster.identityPublicTypedKey(),
|
sentMessages, _sentMessagesCubit!);
|
||||||
sentMessages,
|
|
||||||
_sentMessagesCubit!);
|
|
||||||
|
|
||||||
// Update the view
|
// Update the view
|
||||||
_renderState();
|
_renderState();
|
||||||
@ -281,7 +278,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
|
|
||||||
// Now sign it
|
// Now sign it
|
||||||
await _senderMessageIntegrity.signMessage(
|
await _senderMessageIntegrity.signMessage(
|
||||||
message, _activeAccountInfo.userLogin.identitySecret.value);
|
message, _activeAccountInfo.identitySecretKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Async process to send messages in the background
|
// Async process to send messages in the background
|
||||||
@ -334,8 +331,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
|
|
||||||
for (final m in reconciledMessages.windowElements) {
|
for (final m in reconciledMessages.windowElements) {
|
||||||
final isLocal = m.content.author.toVeilid() ==
|
final isLocal = m.content.author.toVeilid() ==
|
||||||
_activeAccountInfo.localAccount.identityMaster
|
_activeAccountInfo.identityTypedPublicKey;
|
||||||
.identityPublicTypedKey();
|
|
||||||
final reconciledTimestamp = Timestamp.fromInt64(m.reconciledTime);
|
final reconciledTimestamp = Timestamp.fromInt64(m.reconciledTime);
|
||||||
final sm =
|
final sm =
|
||||||
isLocal ? sentMessagesMap[m.content.authorUniqueIdString] : null;
|
isLocal ? sentMessagesMap[m.content.authorUniqueIdString] : null;
|
||||||
@ -373,9 +369,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
// Add common fields
|
// Add common fields
|
||||||
// id and signature will get set by _processMessageToSend
|
// id and signature will get set by _processMessageToSend
|
||||||
message
|
message
|
||||||
..author = _activeAccountInfo.localAccount.identityMaster
|
..author = _activeAccountInfo.identityTypedPublicKey.toProto()
|
||||||
.identityPublicTypedKey()
|
|
||||||
.toProto()
|
|
||||||
..timestamp = Veilid.instance.now().toInt64();
|
..timestamp = Veilid.instance.now().toInt64();
|
||||||
|
|
||||||
// Put in the queue
|
// Put in the queue
|
||||||
|
@ -68,14 +68,16 @@ class ContactInvitationListCubit
|
|||||||
final pool = DHTRecordPool.instance;
|
final pool = DHTRecordPool.instance;
|
||||||
|
|
||||||
// Generate writer keypair to share with new contact
|
// Generate writer keypair to share with new contact
|
||||||
final cs = await pool.veilid.bestCryptoSystem();
|
final crcs = await pool.veilid.bestCryptoSystem();
|
||||||
final contactRequestWriter = await cs.generateKeyPair();
|
final contactRequestWriter = await crcs.generateKeyPair();
|
||||||
final conversationWriter = _activeAccountInfo.conversationWriter;
|
|
||||||
|
final idcs = await _activeAccountInfo.identityCryptoSystem;
|
||||||
|
final identityWriter = _activeAccountInfo.identityWriter;
|
||||||
|
|
||||||
// Encrypt the writer secret with the encryption key
|
// Encrypt the writer secret with the encryption key
|
||||||
final encryptedSecret = await encryptionKeyType.encryptSecretToBytes(
|
final encryptedSecret = await encryptionKeyType.encryptSecretToBytes(
|
||||||
secret: contactRequestWriter.secret,
|
secret: contactRequestWriter.secret,
|
||||||
cryptoKind: cs.kind(),
|
cryptoKind: crcs.kind(),
|
||||||
encryptionKey: encryptionKey,
|
encryptionKey: encryptionKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -89,21 +91,21 @@ class ContactInvitationListCubit
|
|||||||
debugName: 'ContactInvitationListCubit::createInvitation::'
|
debugName: 'ContactInvitationListCubit::createInvitation::'
|
||||||
'LocalConversation',
|
'LocalConversation',
|
||||||
parent: _activeAccountInfo.accountRecordKey,
|
parent: _activeAccountInfo.accountRecordKey,
|
||||||
schema: DHTSchema.smpl(oCnt: 0, members: [
|
schema: DHTSchema.smpl(
|
||||||
DHTSchemaMember(mKey: conversationWriter.key, mCnt: 1)
|
oCnt: 0,
|
||||||
])))
|
members: [DHTSchemaMember(mKey: identityWriter.key, mCnt: 1)])))
|
||||||
.deleteScope((localConversation) async {
|
.deleteScope((localConversation) async {
|
||||||
// dont bother reopening localConversation with writer
|
// dont bother reopening localConversation with writer
|
||||||
// Make ContactRequestPrivate and encrypt with the writer secret
|
// Make ContactRequestPrivate and encrypt with the writer secret
|
||||||
final crpriv = proto.ContactRequestPrivate()
|
final crpriv = proto.ContactRequestPrivate()
|
||||||
..writerKey = contactRequestWriter.key.toProto()
|
..writerKey = contactRequestWriter.key.toProto()
|
||||||
..profile = _account.profile
|
..profile = _account.profile
|
||||||
..identityMasterRecordKey =
|
..superIdentityRecordKey =
|
||||||
_activeAccountInfo.userLogin.accountMasterRecordKey.toProto()
|
_activeAccountInfo.userLogin.superIdentityRecordKey.toProto()
|
||||||
..chatRecordKey = localConversation.key.toProto()
|
..chatRecordKey = localConversation.key.toProto()
|
||||||
..expiration = expiration?.toInt64() ?? Int64.ZERO;
|
..expiration = expiration?.toInt64() ?? Int64.ZERO;
|
||||||
final crprivbytes = crpriv.writeToBuffer();
|
final crprivbytes = crpriv.writeToBuffer();
|
||||||
final encryptedContactRequestPrivate = await cs.encryptAeadWithNonce(
|
final encryptedContactRequestPrivate = await crcs.encryptAeadWithNonce(
|
||||||
crprivbytes, contactRequestWriter.secret);
|
crprivbytes, contactRequestWriter.secret);
|
||||||
|
|
||||||
// Create ContactRequest and embed contactrequestprivate
|
// Create ContactRequest and embed contactrequestprivate
|
||||||
@ -140,9 +142,8 @@ class ContactInvitationListCubit
|
|||||||
final cinvbytes = cinv.writeToBuffer();
|
final cinvbytes = cinv.writeToBuffer();
|
||||||
final scinv = proto.SignedContactInvitation()
|
final scinv = proto.SignedContactInvitation()
|
||||||
..contactInvitation = cinvbytes
|
..contactInvitation = cinvbytes
|
||||||
..identitySignature = (await cs.sign(
|
..identitySignature =
|
||||||
conversationWriter.key, conversationWriter.secret, cinvbytes))
|
(await idcs.signWithKeyPair(identityWriter, cinvbytes)).toProto();
|
||||||
.toProto();
|
|
||||||
signedContactInvitationBytes = scinv.writeToBuffer();
|
signedContactInvitationBytes = scinv.writeToBuffer();
|
||||||
|
|
||||||
// Create ContactInvitationRecord
|
// Create ContactInvitationRecord
|
||||||
@ -237,8 +238,6 @@ class ContactInvitationListCubit
|
|||||||
|
|
||||||
ValidContactInvitation? out;
|
ValidContactInvitation? out;
|
||||||
|
|
||||||
final cs = await pool.veilid.getCryptoSystem(contactRequestInboxKey.kind);
|
|
||||||
|
|
||||||
// Compare the invitation's contact request
|
// Compare the invitation's contact request
|
||||||
// inbox with our list of extant invitations
|
// inbox with our list of extant invitations
|
||||||
// If we're chatting to ourselves,
|
// If we're chatting to ourselves,
|
||||||
@ -257,6 +256,8 @@ class ContactInvitationListCubit
|
|||||||
final contactRequest = await contactRequestInbox
|
final contactRequest = await contactRequestInbox
|
||||||
.getProtobuf(proto.ContactRequest.fromBuffer);
|
.getProtobuf(proto.ContactRequest.fromBuffer);
|
||||||
|
|
||||||
|
final cs = await pool.veilid.getCryptoSystem(contactRequestInboxKey.kind);
|
||||||
|
|
||||||
// Decrypt contact request private
|
// Decrypt contact request private
|
||||||
final encryptionKeyType =
|
final encryptionKeyType =
|
||||||
EncryptionKeyType.fromProto(contactRequest!.encryptionKeyType);
|
EncryptionKeyType.fromProto(contactRequest!.encryptionKeyType);
|
||||||
@ -276,16 +277,17 @@ class ContactInvitationListCubit
|
|||||||
|
|
||||||
final contactRequestPrivate =
|
final contactRequestPrivate =
|
||||||
proto.ContactRequestPrivate.fromBuffer(contactRequestPrivateBytes);
|
proto.ContactRequestPrivate.fromBuffer(contactRequestPrivateBytes);
|
||||||
final contactIdentityMasterRecordKey =
|
final contactSuperIdentityRecordKey =
|
||||||
contactRequestPrivate.identityMasterRecordKey.toVeilid();
|
contactRequestPrivate.superIdentityRecordKey.toVeilid();
|
||||||
|
|
||||||
// Fetch the account master
|
// Fetch the account master
|
||||||
final contactIdentityMaster = await openIdentityMaster(
|
final contactSuperIdentity = await SuperIdentity.open(
|
||||||
identityMasterRecordKey: contactIdentityMasterRecordKey);
|
superRecordKey: contactSuperIdentityRecordKey);
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
|
final idcs = await contactSuperIdentity.currentInstance.cryptoSystem;
|
||||||
final signature = signedContactInvitation.identitySignature.toVeilid();
|
final signature = signedContactInvitation.identitySignature.toVeilid();
|
||||||
await cs.verify(contactIdentityMaster.identityPublicKey,
|
await idcs.verify(contactSuperIdentity.currentInstance.publicKey,
|
||||||
contactInvitationBytes, signature);
|
contactInvitationBytes, signature);
|
||||||
|
|
||||||
final writer = KeyPair(
|
final writer = KeyPair(
|
||||||
@ -297,7 +299,7 @@ class ContactInvitationListCubit
|
|||||||
account: _account,
|
account: _account,
|
||||||
contactRequestInboxKey: contactRequestInboxKey,
|
contactRequestInboxKey: contactRequestInboxKey,
|
||||||
contactRequestPrivate: contactRequestPrivate,
|
contactRequestPrivate: contactRequestPrivate,
|
||||||
contactIdentityMaster: contactIdentityMaster,
|
contactSuperIdentity: contactSuperIdentity,
|
||||||
writer: writer);
|
writer: writer);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -43,23 +43,21 @@ class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
|
|||||||
if (signedContactResponse == null) {
|
if (signedContactResponse == null) {
|
||||||
return const AsyncValue.loading();
|
return const AsyncValue.loading();
|
||||||
}
|
}
|
||||||
final pool = DHTRecordPool.instance;
|
|
||||||
final contactResponseBytes =
|
final contactResponseBytes =
|
||||||
Uint8List.fromList(signedContactResponse.contactResponse);
|
Uint8List.fromList(signedContactResponse.contactResponse);
|
||||||
final contactResponse =
|
final contactResponse =
|
||||||
proto.ContactResponse.fromBuffer(contactResponseBytes);
|
proto.ContactResponse.fromBuffer(contactResponseBytes);
|
||||||
final contactIdentityMasterRecordKey =
|
final contactIdentityMasterRecordKey =
|
||||||
contactResponse.identityMasterRecordKey.toVeilid();
|
contactResponse.superIdentityRecordKey.toVeilid();
|
||||||
final cs =
|
|
||||||
await pool.veilid.getCryptoSystem(contactIdentityMasterRecordKey.kind);
|
|
||||||
|
|
||||||
// Fetch the remote contact's account master
|
// Fetch the remote contact's account master
|
||||||
final contactIdentityMaster = await openIdentityMaster(
|
final contactSuperIdentity = await SuperIdentity.open(
|
||||||
identityMasterRecordKey: contactIdentityMasterRecordKey);
|
superRecordKey: contactIdentityMasterRecordKey);
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
|
final idcs = await contactSuperIdentity.currentInstance.cryptoSystem;
|
||||||
final signature = signedContactResponse.identitySignature.toVeilid();
|
final signature = signedContactResponse.identitySignature.toVeilid();
|
||||||
await cs.verify(contactIdentityMaster.identityPublicKey,
|
await idcs.verify(contactSuperIdentity.currentInstance.publicKey,
|
||||||
contactResponseBytes, signature);
|
contactResponseBytes, signature);
|
||||||
|
|
||||||
// Check for rejection
|
// Check for rejection
|
||||||
@ -74,7 +72,8 @@ class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
|
|||||||
|
|
||||||
final conversation = ConversationCubit(
|
final conversation = ConversationCubit(
|
||||||
activeAccountInfo: activeAccountInfo,
|
activeAccountInfo: activeAccountInfo,
|
||||||
remoteIdentityPublicKey: contactIdentityMaster.identityPublicTypedKey(),
|
remoteIdentityPublicKey:
|
||||||
|
contactSuperIdentity.currentInstance.typedPublicKey,
|
||||||
remoteConversationRecordKey: remoteConversationRecordKey);
|
remoteConversationRecordKey: remoteConversationRecordKey);
|
||||||
|
|
||||||
// wait for remote conversation for up to 20 seconds
|
// wait for remote conversation for up to 20 seconds
|
||||||
@ -106,7 +105,7 @@ class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
|
|||||||
return AsyncValue.data(InvitationStatus(
|
return AsyncValue.data(InvitationStatus(
|
||||||
acceptedContact: AcceptedContact(
|
acceptedContact: AcceptedContact(
|
||||||
remoteProfile: remoteProfile,
|
remoteProfile: remoteProfile,
|
||||||
remoteIdentity: contactIdentityMaster,
|
remoteIdentity: contactSuperIdentity,
|
||||||
remoteConversationRecordKey: remoteConversationRecordKey,
|
remoteConversationRecordKey: remoteConversationRecordKey,
|
||||||
localConversationRecordKey: localConversationRecordKey)));
|
localConversationRecordKey: localConversationRecordKey)));
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,7 @@ class AcceptedContact extends Equatable {
|
|||||||
});
|
});
|
||||||
|
|
||||||
final proto.Profile remoteProfile;
|
final proto.Profile remoteProfile;
|
||||||
final IdentityMaster remoteIdentity;
|
final SuperIdentity remoteIdentity;
|
||||||
final TypedKey remoteConversationRecordKey;
|
final TypedKey remoteConversationRecordKey;
|
||||||
final TypedKey localConversationRecordKey;
|
final TypedKey localConversationRecordKey;
|
||||||
|
|
||||||
|
@ -17,13 +17,13 @@ class ValidContactInvitation {
|
|||||||
required proto.Account account,
|
required proto.Account account,
|
||||||
required TypedKey contactRequestInboxKey,
|
required TypedKey contactRequestInboxKey,
|
||||||
required proto.ContactRequestPrivate contactRequestPrivate,
|
required proto.ContactRequestPrivate contactRequestPrivate,
|
||||||
required IdentityMaster contactIdentityMaster,
|
required SuperIdentity contactSuperIdentity,
|
||||||
required KeyPair writer})
|
required KeyPair writer})
|
||||||
: _activeAccountInfo = activeAccountInfo,
|
: _activeAccountInfo = activeAccountInfo,
|
||||||
_account = account,
|
_account = account,
|
||||||
_contactRequestInboxKey = contactRequestInboxKey,
|
_contactRequestInboxKey = contactRequestInboxKey,
|
||||||
_contactRequestPrivate = contactRequestPrivate,
|
_contactRequestPrivate = contactRequestPrivate,
|
||||||
_contactIdentityMaster = contactIdentityMaster,
|
_contactSuperIdentity = contactSuperIdentity,
|
||||||
_writer = writer;
|
_writer = writer;
|
||||||
|
|
||||||
proto.Profile get remoteProfile => _contactRequestPrivate.profile;
|
proto.Profile get remoteProfile => _contactRequestPrivate.profile;
|
||||||
@ -33,8 +33,8 @@ class ValidContactInvitation {
|
|||||||
try {
|
try {
|
||||||
// Ensure we don't delete this if we're trying to chat to self
|
// Ensure we don't delete this if we're trying to chat to self
|
||||||
// The initiating side will delete the records in deleteInvitation()
|
// The initiating side will delete the records in deleteInvitation()
|
||||||
final isSelf = _contactIdentityMaster.identityPublicKey ==
|
final isSelf = _contactSuperIdentity.currentInstance.publicKey ==
|
||||||
_activeAccountInfo.localAccount.identityMaster.identityPublicKey;
|
_activeAccountInfo.identityPublicKey;
|
||||||
final accountRecordKey = _activeAccountInfo.accountRecordKey;
|
final accountRecordKey = _activeAccountInfo.accountRecordKey;
|
||||||
|
|
||||||
return (await pool.openRecordWrite(_contactRequestInboxKey, _writer,
|
return (await pool.openRecordWrite(_contactRequestInboxKey, _writer,
|
||||||
@ -48,24 +48,23 @@ class ValidContactInvitation {
|
|||||||
final conversation = ConversationCubit(
|
final conversation = ConversationCubit(
|
||||||
activeAccountInfo: _activeAccountInfo,
|
activeAccountInfo: _activeAccountInfo,
|
||||||
remoteIdentityPublicKey:
|
remoteIdentityPublicKey:
|
||||||
_contactIdentityMaster.identityPublicTypedKey());
|
_contactSuperIdentity.currentInstance.typedPublicKey);
|
||||||
return conversation.initLocalConversation(
|
return conversation.initLocalConversation(
|
||||||
profile: _account.profile,
|
profile: _account.profile,
|
||||||
callback: (localConversation) async {
|
callback: (localConversation) async {
|
||||||
final contactResponse = proto.ContactResponse()
|
final contactResponse = proto.ContactResponse()
|
||||||
..accept = true
|
..accept = true
|
||||||
..remoteConversationRecordKey = localConversation.key.toProto()
|
..remoteConversationRecordKey = localConversation.key.toProto()
|
||||||
..identityMasterRecordKey = _activeAccountInfo
|
..superIdentityRecordKey =
|
||||||
.localAccount.identityMaster.masterRecordKey
|
_activeAccountInfo.superIdentityRecordKey.toProto();
|
||||||
.toProto();
|
|
||||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||||
|
|
||||||
final cs = await pool.veilid
|
final cs = await pool.veilid
|
||||||
.getCryptoSystem(_contactRequestInboxKey.kind);
|
.getCryptoSystem(_contactRequestInboxKey.kind);
|
||||||
|
|
||||||
final identitySignature = await cs.sign(
|
final identitySignature = await cs.sign(
|
||||||
_activeAccountInfo.conversationWriter.key,
|
_activeAccountInfo.identityWriter.key,
|
||||||
_activeAccountInfo.conversationWriter.secret,
|
_activeAccountInfo.identityWriter.secret,
|
||||||
contactResponseBytes);
|
contactResponseBytes);
|
||||||
|
|
||||||
final signedContactResponse = proto.SignedContactResponse()
|
final signedContactResponse = proto.SignedContactResponse()
|
||||||
@ -78,7 +77,7 @@ class ValidContactInvitation {
|
|||||||
|
|
||||||
return AcceptedContact(
|
return AcceptedContact(
|
||||||
remoteProfile: _contactRequestPrivate.profile,
|
remoteProfile: _contactRequestPrivate.profile,
|
||||||
remoteIdentity: _contactIdentityMaster,
|
remoteIdentity: _contactSuperIdentity,
|
||||||
remoteConversationRecordKey:
|
remoteConversationRecordKey:
|
||||||
_contactRequestPrivate.chatRecordKey.toVeilid(),
|
_contactRequestPrivate.chatRecordKey.toVeilid(),
|
||||||
localConversationRecordKey: localConversation.key,
|
localConversationRecordKey: localConversation.key,
|
||||||
@ -95,10 +94,9 @@ class ValidContactInvitation {
|
|||||||
final pool = DHTRecordPool.instance;
|
final pool = DHTRecordPool.instance;
|
||||||
|
|
||||||
// Ensure we don't delete this if we're trying to chat to self
|
// Ensure we don't delete this if we're trying to chat to self
|
||||||
final isSelf = _contactIdentityMaster.identityPublicKey ==
|
final isSelf = _contactSuperIdentity.currentInstance.publicKey ==
|
||||||
_activeAccountInfo.localAccount.identityMaster.identityPublicKey;
|
_activeAccountInfo.identityPublicKey;
|
||||||
final accountRecordKey =
|
final accountRecordKey = _activeAccountInfo.accountRecordKey;
|
||||||
_activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
|
||||||
|
|
||||||
return (await pool.openRecordWrite(_contactRequestInboxKey, _writer,
|
return (await pool.openRecordWrite(_contactRequestInboxKey, _writer,
|
||||||
debugName: 'ValidContactInvitation::reject::'
|
debugName: 'ValidContactInvitation::reject::'
|
||||||
@ -110,14 +108,13 @@ class ValidContactInvitation {
|
|||||||
|
|
||||||
final contactResponse = proto.ContactResponse()
|
final contactResponse = proto.ContactResponse()
|
||||||
..accept = false
|
..accept = false
|
||||||
..identityMasterRecordKey = _activeAccountInfo
|
..superIdentityRecordKey =
|
||||||
.localAccount.identityMaster.masterRecordKey
|
_activeAccountInfo.superIdentityRecordKey.toProto();
|
||||||
.toProto();
|
|
||||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||||
|
|
||||||
final identitySignature = await cs.sign(
|
final identitySignature = await cs.sign(
|
||||||
_activeAccountInfo.conversationWriter.key,
|
_activeAccountInfo.identityWriter.key,
|
||||||
_activeAccountInfo.conversationWriter.secret,
|
_activeAccountInfo.identityWriter.secret,
|
||||||
contactResponseBytes);
|
contactResponseBytes);
|
||||||
|
|
||||||
final signedContactResponse = proto.SignedContactResponse()
|
final signedContactResponse = proto.SignedContactResponse()
|
||||||
@ -135,7 +132,7 @@ class ValidContactInvitation {
|
|||||||
final ActiveAccountInfo _activeAccountInfo;
|
final ActiveAccountInfo _activeAccountInfo;
|
||||||
final proto.Account _account;
|
final proto.Account _account;
|
||||||
final TypedKey _contactRequestInboxKey;
|
final TypedKey _contactRequestInboxKey;
|
||||||
final IdentityMaster _contactIdentityMaster;
|
final SuperIdentity _contactSuperIdentity;
|
||||||
final KeyPair _writer;
|
final KeyPair _writer;
|
||||||
final proto.ContactRequestPrivate _contactRequestPrivate;
|
final proto.ContactRequestPrivate _contactRequestPrivate;
|
||||||
}
|
}
|
||||||
|
@ -86,13 +86,12 @@ class InvitationDialogState extends State<InvitationDialog> {
|
|||||||
if (acceptedContact != null) {
|
if (acceptedContact != null) {
|
||||||
// initiator when accept is received will create
|
// initiator when accept is received will create
|
||||||
// contact in the case of a 'note to self'
|
// contact in the case of a 'note to self'
|
||||||
final isSelf =
|
final isSelf = activeAccountInfo.identityPublicKey ==
|
||||||
activeAccountInfo.localAccount.identityMaster.identityPublicKey ==
|
acceptedContact.remoteIdentity.currentInstance.publicKey;
|
||||||
acceptedContact.remoteIdentity.identityPublicKey;
|
|
||||||
if (!isSelf) {
|
if (!isSelf) {
|
||||||
await contactList.createContact(
|
await contactList.createContact(
|
||||||
remoteProfile: acceptedContact.remoteProfile,
|
remoteProfile: acceptedContact.remoteProfile,
|
||||||
remoteIdentity: acceptedContact.remoteIdentity,
|
remoteSuperIdentity: acceptedContact.remoteIdentity,
|
||||||
remoteConversationRecordKey:
|
remoteConversationRecordKey:
|
||||||
acceptedContact.remoteConversationRecordKey,
|
acceptedContact.remoteConversationRecordKey,
|
||||||
localConversationRecordKey:
|
localConversationRecordKey:
|
||||||
|
@ -36,7 +36,7 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
|
|||||||
|
|
||||||
Future<void> createContact({
|
Future<void> createContact({
|
||||||
required proto.Profile remoteProfile,
|
required proto.Profile remoteProfile,
|
||||||
required IdentityMaster remoteIdentity,
|
required SuperIdentity remoteSuperIdentity,
|
||||||
required TypedKey remoteConversationRecordKey,
|
required TypedKey remoteConversationRecordKey,
|
||||||
required TypedKey localConversationRecordKey,
|
required TypedKey localConversationRecordKey,
|
||||||
}) async {
|
}) async {
|
||||||
@ -44,11 +44,9 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
|
|||||||
final contact = proto.Contact()
|
final contact = proto.Contact()
|
||||||
..editedProfile = remoteProfile
|
..editedProfile = remoteProfile
|
||||||
..remoteProfile = remoteProfile
|
..remoteProfile = remoteProfile
|
||||||
..identityMasterJson = jsonEncode(remoteIdentity.toJson())
|
..superIdentityJson = jsonEncode(remoteSuperIdentity.toJson())
|
||||||
..identityPublicKey = TypedKey(
|
..identityPublicKey =
|
||||||
kind: remoteIdentity.identityRecordKey.kind,
|
remoteSuperIdentity.currentInstance.typedPublicKey.toProto()
|
||||||
value: remoteIdentity.identityPublicKey)
|
|
||||||
.toProto()
|
|
||||||
..remoteConversationRecordKey = remoteConversationRecordKey.toProto()
|
..remoteConversationRecordKey = remoteConversationRecordKey.toProto()
|
||||||
..localConversationRecordKey = localConversationRecordKey.toProto()
|
..localConversationRecordKey = localConversationRecordKey.toProto()
|
||||||
..showAvailability = false;
|
..showAvailability = false;
|
||||||
|
@ -47,7 +47,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
|||||||
// Open local record key if it is specified
|
// Open local record key if it is specified
|
||||||
final pool = DHTRecordPool.instance;
|
final pool = DHTRecordPool.instance;
|
||||||
final crypto = await _cachedConversationCrypto();
|
final crypto = await _cachedConversationCrypto();
|
||||||
final writer = _activeAccountInfo.conversationWriter;
|
final writer = _activeAccountInfo.identityWriter;
|
||||||
final record = await pool.openRecordWrite(
|
final record = await pool.openRecordWrite(
|
||||||
_localConversationRecordKey!, writer,
|
_localConversationRecordKey!, writer,
|
||||||
debugName: 'ConversationCubit::LocalConversation',
|
debugName: 'ConversationCubit::LocalConversation',
|
||||||
@ -221,7 +221,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
|||||||
_activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
_activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||||
|
|
||||||
final crypto = await _cachedConversationCrypto();
|
final crypto = await _cachedConversationCrypto();
|
||||||
final writer = _activeAccountInfo.conversationWriter;
|
final writer = _activeAccountInfo.identityWriter;
|
||||||
|
|
||||||
// Open with SMPL scheme for identity writer
|
// Open with SMPL scheme for identity writer
|
||||||
late final DHTRecord localConversationRecord;
|
late final DHTRecord localConversationRecord;
|
||||||
@ -254,8 +254,8 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
|||||||
// Create initial local conversation key contents
|
// Create initial local conversation key contents
|
||||||
final conversation = proto.Conversation()
|
final conversation = proto.Conversation()
|
||||||
..profile = profile
|
..profile = profile
|
||||||
..identityMasterJson = jsonEncode(
|
..superIdentityJson = jsonEncode(
|
||||||
_activeAccountInfo.localAccount.identityMaster.toJson())
|
_activeAccountInfo.localAccount.superIdentity.toJson())
|
||||||
..messages = messages.recordKey.toProto();
|
..messages = messages.recordKey.toProto();
|
||||||
|
|
||||||
// Write initial conversation to record
|
// Write initial conversation to record
|
||||||
@ -289,7 +289,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
|||||||
}) async {
|
}) async {
|
||||||
final crypto =
|
final crypto =
|
||||||
await activeAccountInfo.makeConversationCrypto(remoteIdentityPublicKey);
|
await activeAccountInfo.makeConversationCrypto(remoteIdentityPublicKey);
|
||||||
final writer = activeAccountInfo.conversationWriter;
|
final writer = activeAccountInfo.identityWriter;
|
||||||
|
|
||||||
return (await DHTLog.create(
|
return (await DHTLog.create(
|
||||||
debugName: 'ConversationCubit::initLocalMessages::LocalMessages',
|
debugName: 'ConversationCubit::initLocalMessages::LocalMessages',
|
||||||
|
@ -91,7 +91,7 @@ class HomeAccountReadyShellState extends State<HomeAccountReadyShell> {
|
|||||||
// Accept
|
// Accept
|
||||||
await contactListCubit.createContact(
|
await contactListCubit.createContact(
|
||||||
remoteProfile: acceptedContact.remoteProfile,
|
remoteProfile: acceptedContact.remoteProfile,
|
||||||
remoteIdentity: acceptedContact.remoteIdentity,
|
remoteSuperIdentity: acceptedContact.remoteIdentity,
|
||||||
remoteConversationRecordKey:
|
remoteConversationRecordKey:
|
||||||
acceptedContact.remoteConversationRecordKey,
|
acceptedContact.remoteConversationRecordKey,
|
||||||
localConversationRecordKey:
|
localConversationRecordKey:
|
||||||
|
@ -1026,7 +1026,7 @@ class Conversation extends $pb.GeneratedMessage {
|
|||||||
|
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Conversation', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Conversation', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
||||||
..aOM<Profile>(1, _omitFieldNames ? '' : 'profile', subBuilder: Profile.create)
|
..aOM<Profile>(1, _omitFieldNames ? '' : 'profile', subBuilder: Profile.create)
|
||||||
..aOS(2, _omitFieldNames ? '' : 'identityMasterJson')
|
..aOS(2, _omitFieldNames ? '' : 'superIdentityJson')
|
||||||
..aOM<$0.TypedKey>(3, _omitFieldNames ? '' : 'messages', subBuilder: $0.TypedKey.create)
|
..aOM<$0.TypedKey>(3, _omitFieldNames ? '' : 'messages', subBuilder: $0.TypedKey.create)
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
@ -1064,13 +1064,13 @@ class Conversation extends $pb.GeneratedMessage {
|
|||||||
Profile ensureProfile() => $_ensure(0);
|
Profile ensureProfile() => $_ensure(0);
|
||||||
|
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$core.String get identityMasterJson => $_getSZ(1);
|
$core.String get superIdentityJson => $_getSZ(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
set identityMasterJson($core.String v) { $_setString(1, v); }
|
set superIdentityJson($core.String v) { $_setString(1, v); }
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$core.bool hasIdentityMasterJson() => $_has(1);
|
$core.bool hasSuperIdentityJson() => $_has(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
void clearIdentityMasterJson() => clearField(2);
|
void clearSuperIdentityJson() => clearField(2);
|
||||||
|
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$0.TypedKey get messages => $_getN(2);
|
$0.TypedKey get messages => $_getN(2);
|
||||||
@ -1427,7 +1427,7 @@ class Contact extends $pb.GeneratedMessage {
|
|||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Contact', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Contact', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
||||||
..aOM<Profile>(1, _omitFieldNames ? '' : 'editedProfile', subBuilder: Profile.create)
|
..aOM<Profile>(1, _omitFieldNames ? '' : 'editedProfile', subBuilder: Profile.create)
|
||||||
..aOM<Profile>(2, _omitFieldNames ? '' : 'remoteProfile', subBuilder: Profile.create)
|
..aOM<Profile>(2, _omitFieldNames ? '' : 'remoteProfile', subBuilder: Profile.create)
|
||||||
..aOS(3, _omitFieldNames ? '' : 'identityMasterJson')
|
..aOS(3, _omitFieldNames ? '' : 'superIdentityJson')
|
||||||
..aOM<$0.TypedKey>(4, _omitFieldNames ? '' : 'identityPublicKey', subBuilder: $0.TypedKey.create)
|
..aOM<$0.TypedKey>(4, _omitFieldNames ? '' : 'identityPublicKey', subBuilder: $0.TypedKey.create)
|
||||||
..aOM<$0.TypedKey>(5, _omitFieldNames ? '' : 'remoteConversationRecordKey', subBuilder: $0.TypedKey.create)
|
..aOM<$0.TypedKey>(5, _omitFieldNames ? '' : 'remoteConversationRecordKey', subBuilder: $0.TypedKey.create)
|
||||||
..aOM<$0.TypedKey>(6, _omitFieldNames ? '' : 'localConversationRecordKey', subBuilder: $0.TypedKey.create)
|
..aOM<$0.TypedKey>(6, _omitFieldNames ? '' : 'localConversationRecordKey', subBuilder: $0.TypedKey.create)
|
||||||
@ -1479,13 +1479,13 @@ class Contact extends $pb.GeneratedMessage {
|
|||||||
Profile ensureRemoteProfile() => $_ensure(1);
|
Profile ensureRemoteProfile() => $_ensure(1);
|
||||||
|
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$core.String get identityMasterJson => $_getSZ(2);
|
$core.String get superIdentityJson => $_getSZ(2);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
set identityMasterJson($core.String v) { $_setString(2, v); }
|
set superIdentityJson($core.String v) { $_setString(2, v); }
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$core.bool hasIdentityMasterJson() => $_has(2);
|
$core.bool hasSuperIdentityJson() => $_has(2);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
void clearIdentityMasterJson() => clearField(3);
|
void clearSuperIdentityJson() => clearField(3);
|
||||||
|
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
$0.TypedKey get identityPublicKey => $_getN(3);
|
$0.TypedKey get identityPublicKey => $_getN(3);
|
||||||
@ -1699,7 +1699,7 @@ class ContactRequestPrivate extends $pb.GeneratedMessage {
|
|||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ContactRequestPrivate', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ContactRequestPrivate', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
||||||
..aOM<$0.CryptoKey>(1, _omitFieldNames ? '' : 'writerKey', subBuilder: $0.CryptoKey.create)
|
..aOM<$0.CryptoKey>(1, _omitFieldNames ? '' : 'writerKey', subBuilder: $0.CryptoKey.create)
|
||||||
..aOM<Profile>(2, _omitFieldNames ? '' : 'profile', subBuilder: Profile.create)
|
..aOM<Profile>(2, _omitFieldNames ? '' : 'profile', subBuilder: Profile.create)
|
||||||
..aOM<$0.TypedKey>(3, _omitFieldNames ? '' : 'identityMasterRecordKey', subBuilder: $0.TypedKey.create)
|
..aOM<$0.TypedKey>(3, _omitFieldNames ? '' : 'superIdentityRecordKey', subBuilder: $0.TypedKey.create)
|
||||||
..aOM<$0.TypedKey>(4, _omitFieldNames ? '' : 'chatRecordKey', subBuilder: $0.TypedKey.create)
|
..aOM<$0.TypedKey>(4, _omitFieldNames ? '' : 'chatRecordKey', subBuilder: $0.TypedKey.create)
|
||||||
..a<$fixnum.Int64>(5, _omitFieldNames ? '' : 'expiration', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
|
..a<$fixnum.Int64>(5, _omitFieldNames ? '' : 'expiration', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
@ -1749,15 +1749,15 @@ class ContactRequestPrivate extends $pb.GeneratedMessage {
|
|||||||
Profile ensureProfile() => $_ensure(1);
|
Profile ensureProfile() => $_ensure(1);
|
||||||
|
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$0.TypedKey get identityMasterRecordKey => $_getN(2);
|
$0.TypedKey get superIdentityRecordKey => $_getN(2);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
set identityMasterRecordKey($0.TypedKey v) { setField(3, v); }
|
set superIdentityRecordKey($0.TypedKey v) { setField(3, v); }
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$core.bool hasIdentityMasterRecordKey() => $_has(2);
|
$core.bool hasSuperIdentityRecordKey() => $_has(2);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
void clearIdentityMasterRecordKey() => clearField(3);
|
void clearSuperIdentityRecordKey() => clearField(3);
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$0.TypedKey ensureIdentityMasterRecordKey() => $_ensure(2);
|
$0.TypedKey ensureSuperIdentityRecordKey() => $_ensure(2);
|
||||||
|
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
$0.TypedKey get chatRecordKey => $_getN(3);
|
$0.TypedKey get chatRecordKey => $_getN(3);
|
||||||
@ -1788,7 +1788,7 @@ class ContactResponse extends $pb.GeneratedMessage {
|
|||||||
|
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ContactResponse', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ContactResponse', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
|
||||||
..aOB(1, _omitFieldNames ? '' : 'accept')
|
..aOB(1, _omitFieldNames ? '' : 'accept')
|
||||||
..aOM<$0.TypedKey>(2, _omitFieldNames ? '' : 'identityMasterRecordKey', subBuilder: $0.TypedKey.create)
|
..aOM<$0.TypedKey>(2, _omitFieldNames ? '' : 'superIdentityRecordKey', subBuilder: $0.TypedKey.create)
|
||||||
..aOM<$0.TypedKey>(3, _omitFieldNames ? '' : 'remoteConversationRecordKey', subBuilder: $0.TypedKey.create)
|
..aOM<$0.TypedKey>(3, _omitFieldNames ? '' : 'remoteConversationRecordKey', subBuilder: $0.TypedKey.create)
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
@ -1824,15 +1824,15 @@ class ContactResponse extends $pb.GeneratedMessage {
|
|||||||
void clearAccept() => clearField(1);
|
void clearAccept() => clearField(1);
|
||||||
|
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$0.TypedKey get identityMasterRecordKey => $_getN(1);
|
$0.TypedKey get superIdentityRecordKey => $_getN(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
set identityMasterRecordKey($0.TypedKey v) { setField(2, v); }
|
set superIdentityRecordKey($0.TypedKey v) { setField(2, v); }
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$core.bool hasIdentityMasterRecordKey() => $_has(1);
|
$core.bool hasSuperIdentityRecordKey() => $_has(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
void clearIdentityMasterRecordKey() => clearField(2);
|
void clearSuperIdentityRecordKey() => clearField(2);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$0.TypedKey ensureIdentityMasterRecordKey() => $_ensure(1);
|
$0.TypedKey ensureSuperIdentityRecordKey() => $_ensure(1);
|
||||||
|
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
$0.TypedKey get remoteConversationRecordKey => $_getN(2);
|
$0.TypedKey get remoteConversationRecordKey => $_getN(2);
|
||||||
|
@ -309,7 +309,7 @@ const Conversation$json = {
|
|||||||
'1': 'Conversation',
|
'1': 'Conversation',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'profile', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.Profile', '10': 'profile'},
|
{'1': 'profile', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.Profile', '10': 'profile'},
|
||||||
{'1': 'identity_master_json', '3': 2, '4': 1, '5': 9, '10': 'identityMasterJson'},
|
{'1': 'super_identity_json', '3': 2, '4': 1, '5': 9, '10': 'superIdentityJson'},
|
||||||
{'1': 'messages', '3': 3, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'messages'},
|
{'1': 'messages', '3': 3, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'messages'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -317,8 +317,8 @@ const Conversation$json = {
|
|||||||
/// Descriptor for `Conversation`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `Conversation`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List conversationDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List conversationDescriptor = $convert.base64Decode(
|
||||||
'CgxDb252ZXJzYXRpb24SLQoHcHJvZmlsZRgBIAEoCzITLnZlaWxpZGNoYXQuUHJvZmlsZVIHcH'
|
'CgxDb252ZXJzYXRpb24SLQoHcHJvZmlsZRgBIAEoCzITLnZlaWxpZGNoYXQuUHJvZmlsZVIHcH'
|
||||||
'JvZmlsZRIwChRpZGVudGl0eV9tYXN0ZXJfanNvbhgCIAEoCVISaWRlbnRpdHlNYXN0ZXJKc29u'
|
'JvZmlsZRIuChNzdXBlcl9pZGVudGl0eV9qc29uGAIgASgJUhFzdXBlcklkZW50aXR5SnNvbhIs'
|
||||||
'EiwKCG1lc3NhZ2VzGAMgASgLMhAudmVpbGlkLlR5cGVkS2V5UghtZXNzYWdlcw==');
|
'CghtZXNzYWdlcxgDIAEoCzIQLnZlaWxpZC5UeXBlZEtleVIIbWVzc2FnZXM=');
|
||||||
|
|
||||||
@$core.Deprecated('Use chatDescriptor instead')
|
@$core.Deprecated('Use chatDescriptor instead')
|
||||||
const Chat$json = {
|
const Chat$json = {
|
||||||
@ -411,7 +411,7 @@ const Contact$json = {
|
|||||||
'2': [
|
'2': [
|
||||||
{'1': 'edited_profile', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.Profile', '10': 'editedProfile'},
|
{'1': 'edited_profile', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.Profile', '10': 'editedProfile'},
|
||||||
{'1': 'remote_profile', '3': 2, '4': 1, '5': 11, '6': '.veilidchat.Profile', '10': 'remoteProfile'},
|
{'1': 'remote_profile', '3': 2, '4': 1, '5': 11, '6': '.veilidchat.Profile', '10': 'remoteProfile'},
|
||||||
{'1': 'identity_master_json', '3': 3, '4': 1, '5': 9, '10': 'identityMasterJson'},
|
{'1': 'super_identity_json', '3': 3, '4': 1, '5': 9, '10': 'superIdentityJson'},
|
||||||
{'1': 'identity_public_key', '3': 4, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'identityPublicKey'},
|
{'1': 'identity_public_key', '3': 4, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'identityPublicKey'},
|
||||||
{'1': 'remote_conversation_record_key', '3': 5, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteConversationRecordKey'},
|
{'1': 'remote_conversation_record_key', '3': 5, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteConversationRecordKey'},
|
||||||
{'1': 'local_conversation_record_key', '3': 6, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'localConversationRecordKey'},
|
{'1': 'local_conversation_record_key', '3': 6, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'localConversationRecordKey'},
|
||||||
@ -423,13 +423,13 @@ const Contact$json = {
|
|||||||
final $typed_data.Uint8List contactDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List contactDescriptor = $convert.base64Decode(
|
||||||
'CgdDb250YWN0EjoKDmVkaXRlZF9wcm9maWxlGAEgASgLMhMudmVpbGlkY2hhdC5Qcm9maWxlUg'
|
'CgdDb250YWN0EjoKDmVkaXRlZF9wcm9maWxlGAEgASgLMhMudmVpbGlkY2hhdC5Qcm9maWxlUg'
|
||||||
'1lZGl0ZWRQcm9maWxlEjoKDnJlbW90ZV9wcm9maWxlGAIgASgLMhMudmVpbGlkY2hhdC5Qcm9m'
|
'1lZGl0ZWRQcm9maWxlEjoKDnJlbW90ZV9wcm9maWxlGAIgASgLMhMudmVpbGlkY2hhdC5Qcm9m'
|
||||||
'aWxlUg1yZW1vdGVQcm9maWxlEjAKFGlkZW50aXR5X21hc3Rlcl9qc29uGAMgASgJUhJpZGVudG'
|
'aWxlUg1yZW1vdGVQcm9maWxlEi4KE3N1cGVyX2lkZW50aXR5X2pzb24YAyABKAlSEXN1cGVySW'
|
||||||
'l0eU1hc3Rlckpzb24SQAoTaWRlbnRpdHlfcHVibGljX2tleRgEIAEoCzIQLnZlaWxpZC5UeXBl'
|
'RlbnRpdHlKc29uEkAKE2lkZW50aXR5X3B1YmxpY19rZXkYBCABKAsyEC52ZWlsaWQuVHlwZWRL'
|
||||||
'ZEtleVIRaWRlbnRpdHlQdWJsaWNLZXkSVQoecmVtb3RlX2NvbnZlcnNhdGlvbl9yZWNvcmRfa2'
|
'ZXlSEWlkZW50aXR5UHVibGljS2V5ElUKHnJlbW90ZV9jb252ZXJzYXRpb25fcmVjb3JkX2tleR'
|
||||||
'V5GAUgASgLMhAudmVpbGlkLlR5cGVkS2V5UhtyZW1vdGVDb252ZXJzYXRpb25SZWNvcmRLZXkS'
|
'gFIAEoCzIQLnZlaWxpZC5UeXBlZEtleVIbcmVtb3RlQ29udmVyc2F0aW9uUmVjb3JkS2V5ElMK'
|
||||||
'UwodbG9jYWxfY29udmVyc2F0aW9uX3JlY29yZF9rZXkYBiABKAsyEC52ZWlsaWQuVHlwZWRLZX'
|
'HWxvY2FsX2NvbnZlcnNhdGlvbl9yZWNvcmRfa2V5GAYgASgLMhAudmVpbGlkLlR5cGVkS2V5Uh'
|
||||||
'lSGmxvY2FsQ29udmVyc2F0aW9uUmVjb3JkS2V5EisKEXNob3dfYXZhaWxhYmlsaXR5GAcgASgI'
|
'psb2NhbENvbnZlcnNhdGlvblJlY29yZEtleRIrChFzaG93X2F2YWlsYWJpbGl0eRgHIAEoCFIQ'
|
||||||
'UhBzaG93QXZhaWxhYmlsaXR5');
|
'c2hvd0F2YWlsYWJpbGl0eQ==');
|
||||||
|
|
||||||
@$core.Deprecated('Use contactInvitationDescriptor instead')
|
@$core.Deprecated('Use contactInvitationDescriptor instead')
|
||||||
const ContactInvitation$json = {
|
const ContactInvitation$json = {
|
||||||
@ -482,7 +482,7 @@ const ContactRequestPrivate$json = {
|
|||||||
'2': [
|
'2': [
|
||||||
{'1': 'writer_key', '3': 1, '4': 1, '5': 11, '6': '.veilid.CryptoKey', '10': 'writerKey'},
|
{'1': 'writer_key', '3': 1, '4': 1, '5': 11, '6': '.veilid.CryptoKey', '10': 'writerKey'},
|
||||||
{'1': 'profile', '3': 2, '4': 1, '5': 11, '6': '.veilidchat.Profile', '10': 'profile'},
|
{'1': 'profile', '3': 2, '4': 1, '5': 11, '6': '.veilidchat.Profile', '10': 'profile'},
|
||||||
{'1': 'identity_master_record_key', '3': 3, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'identityMasterRecordKey'},
|
{'1': 'super_identity_record_key', '3': 3, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'superIdentityRecordKey'},
|
||||||
{'1': 'chat_record_key', '3': 4, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'chatRecordKey'},
|
{'1': 'chat_record_key', '3': 4, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'chatRecordKey'},
|
||||||
{'1': 'expiration', '3': 5, '4': 1, '5': 4, '10': 'expiration'},
|
{'1': 'expiration', '3': 5, '4': 1, '5': 4, '10': 'expiration'},
|
||||||
],
|
],
|
||||||
@ -492,27 +492,27 @@ const ContactRequestPrivate$json = {
|
|||||||
final $typed_data.Uint8List contactRequestPrivateDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List contactRequestPrivateDescriptor = $convert.base64Decode(
|
||||||
'ChVDb250YWN0UmVxdWVzdFByaXZhdGUSMAoKd3JpdGVyX2tleRgBIAEoCzIRLnZlaWxpZC5Dcn'
|
'ChVDb250YWN0UmVxdWVzdFByaXZhdGUSMAoKd3JpdGVyX2tleRgBIAEoCzIRLnZlaWxpZC5Dcn'
|
||||||
'lwdG9LZXlSCXdyaXRlcktleRItCgdwcm9maWxlGAIgASgLMhMudmVpbGlkY2hhdC5Qcm9maWxl'
|
'lwdG9LZXlSCXdyaXRlcktleRItCgdwcm9maWxlGAIgASgLMhMudmVpbGlkY2hhdC5Qcm9maWxl'
|
||||||
'Ugdwcm9maWxlEk0KGmlkZW50aXR5X21hc3Rlcl9yZWNvcmRfa2V5GAMgASgLMhAudmVpbGlkLl'
|
'Ugdwcm9maWxlEksKGXN1cGVyX2lkZW50aXR5X3JlY29yZF9rZXkYAyABKAsyEC52ZWlsaWQuVH'
|
||||||
'R5cGVkS2V5UhdpZGVudGl0eU1hc3RlclJlY29yZEtleRI4Cg9jaGF0X3JlY29yZF9rZXkYBCAB'
|
'lwZWRLZXlSFnN1cGVySWRlbnRpdHlSZWNvcmRLZXkSOAoPY2hhdF9yZWNvcmRfa2V5GAQgASgL'
|
||||||
'KAsyEC52ZWlsaWQuVHlwZWRLZXlSDWNoYXRSZWNvcmRLZXkSHgoKZXhwaXJhdGlvbhgFIAEoBF'
|
'MhAudmVpbGlkLlR5cGVkS2V5Ug1jaGF0UmVjb3JkS2V5Eh4KCmV4cGlyYXRpb24YBSABKARSCm'
|
||||||
'IKZXhwaXJhdGlvbg==');
|
'V4cGlyYXRpb24=');
|
||||||
|
|
||||||
@$core.Deprecated('Use contactResponseDescriptor instead')
|
@$core.Deprecated('Use contactResponseDescriptor instead')
|
||||||
const ContactResponse$json = {
|
const ContactResponse$json = {
|
||||||
'1': 'ContactResponse',
|
'1': 'ContactResponse',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'accept', '3': 1, '4': 1, '5': 8, '10': 'accept'},
|
{'1': 'accept', '3': 1, '4': 1, '5': 8, '10': 'accept'},
|
||||||
{'1': 'identity_master_record_key', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'identityMasterRecordKey'},
|
{'1': 'super_identity_record_key', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'superIdentityRecordKey'},
|
||||||
{'1': 'remote_conversation_record_key', '3': 3, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteConversationRecordKey'},
|
{'1': 'remote_conversation_record_key', '3': 3, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteConversationRecordKey'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Descriptor for `ContactResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `ContactResponse`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List contactResponseDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List contactResponseDescriptor = $convert.base64Decode(
|
||||||
'Cg9Db250YWN0UmVzcG9uc2USFgoGYWNjZXB0GAEgASgIUgZhY2NlcHQSTQoaaWRlbnRpdHlfbW'
|
'Cg9Db250YWN0UmVzcG9uc2USFgoGYWNjZXB0GAEgASgIUgZhY2NlcHQSSwoZc3VwZXJfaWRlbn'
|
||||||
'FzdGVyX3JlY29yZF9rZXkYAiABKAsyEC52ZWlsaWQuVHlwZWRLZXlSF2lkZW50aXR5TWFzdGVy'
|
'RpdHlfcmVjb3JkX2tleRgCIAEoCzIQLnZlaWxpZC5UeXBlZEtleVIWc3VwZXJJZGVudGl0eVJl'
|
||||||
'UmVjb3JkS2V5ElUKHnJlbW90ZV9jb252ZXJzYXRpb25fcmVjb3JkX2tleRgDIAEoCzIQLnZlaW'
|
'Y29yZEtleRJVCh5yZW1vdGVfY29udmVyc2F0aW9uX3JlY29yZF9rZXkYAyABKAsyEC52ZWlsaW'
|
||||||
'xpZC5UeXBlZEtleVIbcmVtb3RlQ29udmVyc2F0aW9uUmVjb3JkS2V5');
|
'QuVHlwZWRLZXlSG3JlbW90ZUNvbnZlcnNhdGlvblJlY29yZEtleQ==');
|
||||||
|
|
||||||
@$core.Deprecated('Use signedContactResponseDescriptor instead')
|
@$core.Deprecated('Use signedContactResponseDescriptor instead')
|
||||||
const SignedContactResponse$json = {
|
const SignedContactResponse$json = {
|
||||||
|
@ -237,8 +237,8 @@ message ReconciledMessage {
|
|||||||
message Conversation {
|
message Conversation {
|
||||||
// Profile to publish to friend
|
// Profile to publish to friend
|
||||||
Profile profile = 1;
|
Profile profile = 1;
|
||||||
// Identity master (JSON) to publish to friend or chat room
|
// SuperIdentity (JSON) to publish to friend or chat room
|
||||||
string identity_master_json = 2;
|
string super_identity_json = 2;
|
||||||
// Messages DHTLog
|
// Messages DHTLog
|
||||||
veilid.TypedKey messages = 3;
|
veilid.TypedKey messages = 3;
|
||||||
}
|
}
|
||||||
@ -327,8 +327,8 @@ message Contact {
|
|||||||
Profile edited_profile = 1;
|
Profile edited_profile = 1;
|
||||||
// Copy of friend's profile from remote conversation
|
// Copy of friend's profile from remote conversation
|
||||||
Profile remote_profile = 2;
|
Profile remote_profile = 2;
|
||||||
// Copy of friend's IdentityMaster in JSON from remote conversation
|
// Copy of friend's SuperIdentity in JSON from remote conversation
|
||||||
string identity_master_json = 3;
|
string super_identity_json = 3;
|
||||||
// Copy of friend's most recent identity public key from their identityMaster
|
// Copy of friend's most recent identity public key from their identityMaster
|
||||||
veilid.TypedKey identity_public_key = 4;
|
veilid.TypedKey identity_public_key = 4;
|
||||||
// Remote conversation key to sync from friend
|
// Remote conversation key to sync from friend
|
||||||
@ -378,8 +378,8 @@ message ContactRequestPrivate {
|
|||||||
veilid.CryptoKey writer_key = 1;
|
veilid.CryptoKey writer_key = 1;
|
||||||
// Snapshot of profile
|
// Snapshot of profile
|
||||||
Profile profile = 2;
|
Profile profile = 2;
|
||||||
// Identity master DHT record key
|
// SuperIdentity DHT record key
|
||||||
veilid.TypedKey identity_master_record_key = 3;
|
veilid.TypedKey super_identity_record_key = 3;
|
||||||
// Local chat DHT record key
|
// Local chat DHT record key
|
||||||
veilid.TypedKey chat_record_key = 4;
|
veilid.TypedKey chat_record_key = 4;
|
||||||
// Expiration timestamp
|
// Expiration timestamp
|
||||||
@ -390,8 +390,8 @@ message ContactRequestPrivate {
|
|||||||
message ContactResponse {
|
message ContactResponse {
|
||||||
// Accept or reject
|
// Accept or reject
|
||||||
bool accept = 1;
|
bool accept = 1;
|
||||||
// Remote identity master DHT record key
|
// Remote SuperIdentity DHT record key
|
||||||
veilid.TypedKey identity_master_record_key = 2;
|
veilid.TypedKey super_identity_record_key = 2;
|
||||||
// Remote chat DHT record key if accepted
|
// Remote chat DHT record key if accepted
|
||||||
veilid.TypedKey remote_conversation_record_key = 3;
|
veilid.TypedKey remote_conversation_record_key = 3;
|
||||||
}
|
}
|
||||||
|
@ -15,5 +15,7 @@ class ProcessorConnectionState with _$ProcessorConnectionState {
|
|||||||
attachment.state == AttachmentState.detaching ||
|
attachment.state == AttachmentState.detaching ||
|
||||||
attachment.state == AttachmentState.attaching);
|
attachment.state == AttachmentState.attaching);
|
||||||
|
|
||||||
|
bool get isDetached => attachment.state == AttachmentState.detached;
|
||||||
|
|
||||||
bool get isPublicInternetReady => attachment.publicInternetReady;
|
bool get isPublicInternetReady => attachment.publicInternetReady;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
import '../veilid_support.dart';
|
||||||
|
|
||||||
|
part 'account_record_info.freezed.dart';
|
||||||
|
part 'account_record_info.g.dart';
|
||||||
|
|
||||||
|
/// AccountRecordInfo is the key and owner info for the account dht record that
|
||||||
|
/// is stored in the identity instance record
|
||||||
|
@freezed
|
||||||
|
class AccountRecordInfo with _$AccountRecordInfo {
|
||||||
|
const factory AccountRecordInfo({
|
||||||
|
// Top level account keys and secrets
|
||||||
|
required OwnedDHTRecordPointer accountRecord,
|
||||||
|
}) = _AccountRecordInfo;
|
||||||
|
|
||||||
|
factory AccountRecordInfo.fromJson(dynamic json) =>
|
||||||
|
_$AccountRecordInfoFromJson(json as Map<String, dynamic>);
|
||||||
|
}
|
@ -0,0 +1,170 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'account_record_info.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
AccountRecordInfo _$AccountRecordInfoFromJson(Map<String, dynamic> json) {
|
||||||
|
return _AccountRecordInfo.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$AccountRecordInfo {
|
||||||
|
// Top level account keys and secrets
|
||||||
|
OwnedDHTRecordPointer get accountRecord => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$AccountRecordInfoCopyWith<AccountRecordInfo> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $AccountRecordInfoCopyWith<$Res> {
|
||||||
|
factory $AccountRecordInfoCopyWith(
|
||||||
|
AccountRecordInfo value, $Res Function(AccountRecordInfo) then) =
|
||||||
|
_$AccountRecordInfoCopyWithImpl<$Res, AccountRecordInfo>;
|
||||||
|
@useResult
|
||||||
|
$Res call({OwnedDHTRecordPointer accountRecord});
|
||||||
|
|
||||||
|
$OwnedDHTRecordPointerCopyWith<$Res> get accountRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$AccountRecordInfoCopyWithImpl<$Res, $Val extends AccountRecordInfo>
|
||||||
|
implements $AccountRecordInfoCopyWith<$Res> {
|
||||||
|
_$AccountRecordInfoCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? accountRecord = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
accountRecord: null == accountRecord
|
||||||
|
? _value.accountRecord
|
||||||
|
: accountRecord // ignore: cast_nullable_to_non_nullable
|
||||||
|
as OwnedDHTRecordPointer,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$OwnedDHTRecordPointerCopyWith<$Res> get accountRecord {
|
||||||
|
return $OwnedDHTRecordPointerCopyWith<$Res>(_value.accountRecord, (value) {
|
||||||
|
return _then(_value.copyWith(accountRecord: value) as $Val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$AccountRecordInfoImplCopyWith<$Res>
|
||||||
|
implements $AccountRecordInfoCopyWith<$Res> {
|
||||||
|
factory _$$AccountRecordInfoImplCopyWith(_$AccountRecordInfoImpl value,
|
||||||
|
$Res Function(_$AccountRecordInfoImpl) then) =
|
||||||
|
__$$AccountRecordInfoImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({OwnedDHTRecordPointer accountRecord});
|
||||||
|
|
||||||
|
@override
|
||||||
|
$OwnedDHTRecordPointerCopyWith<$Res> get accountRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$AccountRecordInfoImplCopyWithImpl<$Res>
|
||||||
|
extends _$AccountRecordInfoCopyWithImpl<$Res, _$AccountRecordInfoImpl>
|
||||||
|
implements _$$AccountRecordInfoImplCopyWith<$Res> {
|
||||||
|
__$$AccountRecordInfoImplCopyWithImpl(_$AccountRecordInfoImpl _value,
|
||||||
|
$Res Function(_$AccountRecordInfoImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? accountRecord = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$AccountRecordInfoImpl(
|
||||||
|
accountRecord: null == accountRecord
|
||||||
|
? _value.accountRecord
|
||||||
|
: accountRecord // ignore: cast_nullable_to_non_nullable
|
||||||
|
as OwnedDHTRecordPointer,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$AccountRecordInfoImpl implements _AccountRecordInfo {
|
||||||
|
const _$AccountRecordInfoImpl({required this.accountRecord});
|
||||||
|
|
||||||
|
factory _$AccountRecordInfoImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$AccountRecordInfoImplFromJson(json);
|
||||||
|
|
||||||
|
// Top level account keys and secrets
|
||||||
|
@override
|
||||||
|
final OwnedDHTRecordPointer accountRecord;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AccountRecordInfo(accountRecord: $accountRecord)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$AccountRecordInfoImpl &&
|
||||||
|
(identical(other.accountRecord, accountRecord) ||
|
||||||
|
other.accountRecord == accountRecord));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, accountRecord);
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$AccountRecordInfoImplCopyWith<_$AccountRecordInfoImpl> get copyWith =>
|
||||||
|
__$$AccountRecordInfoImplCopyWithImpl<_$AccountRecordInfoImpl>(
|
||||||
|
this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$AccountRecordInfoImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _AccountRecordInfo implements AccountRecordInfo {
|
||||||
|
const factory _AccountRecordInfo(
|
||||||
|
{required final OwnedDHTRecordPointer accountRecord}) =
|
||||||
|
_$AccountRecordInfoImpl;
|
||||||
|
|
||||||
|
factory _AccountRecordInfo.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$AccountRecordInfoImpl.fromJson;
|
||||||
|
|
||||||
|
@override // Top level account keys and secrets
|
||||||
|
OwnedDHTRecordPointer get accountRecord;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$AccountRecordInfoImplCopyWith<_$AccountRecordInfoImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'account_record_info.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$AccountRecordInfoImpl _$$AccountRecordInfoImplFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$AccountRecordInfoImpl(
|
||||||
|
accountRecord: OwnedDHTRecordPointer.fromJson(json['account_record']),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$AccountRecordInfoImplToJson(
|
||||||
|
_$AccountRecordInfoImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'account_record': instance.accountRecord.toJson(),
|
||||||
|
};
|
13
packages/veilid_support/lib/identity_support/exceptions.dart
Normal file
13
packages/veilid_support/lib/identity_support/exceptions.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/// Identity errors
|
||||||
|
enum IdentityException implements Exception {
|
||||||
|
readError('identity could not be read'),
|
||||||
|
noAccount('no account record info'),
|
||||||
|
limitExceeded('too many items for the limit'),
|
||||||
|
invalid('identity is corrupted or secret is invalid');
|
||||||
|
|
||||||
|
const IdentityException(this.message);
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'IdentityException($name): $message';
|
||||||
|
}
|
25
packages/veilid_support/lib/identity_support/identity.dart
Normal file
25
packages/veilid_support/lib/identity_support/identity.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
import 'account_record_info.dart';
|
||||||
|
|
||||||
|
part 'identity.freezed.dart';
|
||||||
|
part 'identity.g.dart';
|
||||||
|
|
||||||
|
/// Identity points to accounts associated with this IdentityInstance
|
||||||
|
/// accountRecords field has a map of bundle id or uuid to account key pairs
|
||||||
|
/// DHT Schema: DFLT(1)
|
||||||
|
/// DHT Key (Private): IdentityInstance.recordKey
|
||||||
|
/// DHT Owner Key: IdentityInstance.publicKey
|
||||||
|
/// DHT Secret: IdentityInstance Secret Key (stored encrypted with unlock code
|
||||||
|
/// in local table store)
|
||||||
|
@freezed
|
||||||
|
class Identity with _$Identity {
|
||||||
|
const factory Identity({
|
||||||
|
// Top level account keys and secrets
|
||||||
|
required IMap<String, ISet<AccountRecordInfo>> accountRecords,
|
||||||
|
}) = _Identity;
|
||||||
|
|
||||||
|
factory Identity.fromJson(dynamic json) =>
|
||||||
|
_$IdentityFromJson(json as Map<String, dynamic>);
|
||||||
|
}
|
@ -0,0 +1,156 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'identity.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
Identity _$IdentityFromJson(Map<String, dynamic> json) {
|
||||||
|
return _Identity.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$Identity {
|
||||||
|
// Top level account keys and secrets
|
||||||
|
IMap<String, ISet<AccountRecordInfo>> get accountRecords =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$IdentityCopyWith<Identity> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $IdentityCopyWith<$Res> {
|
||||||
|
factory $IdentityCopyWith(Identity value, $Res Function(Identity) then) =
|
||||||
|
_$IdentityCopyWithImpl<$Res, Identity>;
|
||||||
|
@useResult
|
||||||
|
$Res call({IMap<String, ISet<AccountRecordInfo>> accountRecords});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$IdentityCopyWithImpl<$Res, $Val extends Identity>
|
||||||
|
implements $IdentityCopyWith<$Res> {
|
||||||
|
_$IdentityCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? accountRecords = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
accountRecords: null == accountRecords
|
||||||
|
? _value.accountRecords
|
||||||
|
: accountRecords // ignore: cast_nullable_to_non_nullable
|
||||||
|
as IMap<String, ISet<AccountRecordInfo>>,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$IdentityImplCopyWith<$Res>
|
||||||
|
implements $IdentityCopyWith<$Res> {
|
||||||
|
factory _$$IdentityImplCopyWith(
|
||||||
|
_$IdentityImpl value, $Res Function(_$IdentityImpl) then) =
|
||||||
|
__$$IdentityImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({IMap<String, ISet<AccountRecordInfo>> accountRecords});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$IdentityImplCopyWithImpl<$Res>
|
||||||
|
extends _$IdentityCopyWithImpl<$Res, _$IdentityImpl>
|
||||||
|
implements _$$IdentityImplCopyWith<$Res> {
|
||||||
|
__$$IdentityImplCopyWithImpl(
|
||||||
|
_$IdentityImpl _value, $Res Function(_$IdentityImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? accountRecords = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$IdentityImpl(
|
||||||
|
accountRecords: null == accountRecords
|
||||||
|
? _value.accountRecords
|
||||||
|
: accountRecords // ignore: cast_nullable_to_non_nullable
|
||||||
|
as IMap<String, ISet<AccountRecordInfo>>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$IdentityImpl implements _Identity {
|
||||||
|
const _$IdentityImpl({required this.accountRecords});
|
||||||
|
|
||||||
|
factory _$IdentityImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$IdentityImplFromJson(json);
|
||||||
|
|
||||||
|
// Top level account keys and secrets
|
||||||
|
@override
|
||||||
|
final IMap<String, ISet<AccountRecordInfo>> accountRecords;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'Identity(accountRecords: $accountRecords)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$IdentityImpl &&
|
||||||
|
(identical(other.accountRecords, accountRecords) ||
|
||||||
|
other.accountRecords == accountRecords));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, accountRecords);
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$IdentityImplCopyWith<_$IdentityImpl> get copyWith =>
|
||||||
|
__$$IdentityImplCopyWithImpl<_$IdentityImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$IdentityImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Identity implements Identity {
|
||||||
|
const factory _Identity(
|
||||||
|
{required final IMap<String, ISet<AccountRecordInfo>>
|
||||||
|
accountRecords}) = _$IdentityImpl;
|
||||||
|
|
||||||
|
factory _Identity.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$IdentityImpl.fromJson;
|
||||||
|
|
||||||
|
@override // Top level account keys and secrets
|
||||||
|
IMap<String, ISet<AccountRecordInfo>> get accountRecords;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$IdentityImplCopyWith<_$IdentityImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
26
packages/veilid_support/lib/identity_support/identity.g.dart
Normal file
26
packages/veilid_support/lib/identity_support/identity.g.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'identity.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$IdentityImpl _$$IdentityImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$IdentityImpl(
|
||||||
|
accountRecords: IMap<String, ISet<AccountRecordInfo>>.fromJson(
|
||||||
|
json['account_records'] as Map<String, dynamic>,
|
||||||
|
(value) => value as String,
|
||||||
|
(value) => ISet<AccountRecordInfo>.fromJson(
|
||||||
|
value, (value) => AccountRecordInfo.fromJson(value))),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$IdentityImplToJson(_$IdentityImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'account_records': instance.accountRecords.toJson(
|
||||||
|
(value) => value,
|
||||||
|
(value) => value.toJson(
|
||||||
|
(value) => value.toJson(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
@ -0,0 +1,274 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
import '../src/veilid_log.dart';
|
||||||
|
import '../veilid_support.dart';
|
||||||
|
import 'exceptions.dart';
|
||||||
|
|
||||||
|
part 'identity_instance.freezed.dart';
|
||||||
|
part 'identity_instance.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class IdentityInstance with _$IdentityInstance {
|
||||||
|
const factory IdentityInstance({
|
||||||
|
// Private DHT record storing identity account mapping
|
||||||
|
required TypedKey recordKey,
|
||||||
|
|
||||||
|
// Public key of identity instance
|
||||||
|
required PublicKey publicKey,
|
||||||
|
|
||||||
|
// Secret key of identity instance
|
||||||
|
// Encrypted with DH(publicKey, SuperIdentity.secret) with appended salt
|
||||||
|
// Used to recover accounts without generating a new instance
|
||||||
|
@Uint8ListJsonConverter() required Uint8List encryptedSecretKey,
|
||||||
|
|
||||||
|
// Signature of SuperInstance recordKey and SuperInstance publicKey
|
||||||
|
// by publicKey
|
||||||
|
required Signature superSignature,
|
||||||
|
|
||||||
|
// Signature of recordKey, publicKey, encryptedSecretKey, and superSignature
|
||||||
|
// by SuperIdentity publicKey
|
||||||
|
required Signature signature,
|
||||||
|
}) = _IdentityInstance;
|
||||||
|
|
||||||
|
factory IdentityInstance.fromJson(dynamic json) =>
|
||||||
|
_$IdentityInstanceFromJson(json as Map<String, dynamic>);
|
||||||
|
|
||||||
|
const IdentityInstance._();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Public interface
|
||||||
|
|
||||||
|
/// Delete this identity instance record
|
||||||
|
/// Only deletes from the local machine not the DHT
|
||||||
|
Future<void> delete() async {
|
||||||
|
final pool = DHTRecordPool.instance;
|
||||||
|
await pool.deleteRecord(recordKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<VeilidCryptoSystem> get cryptoSystem =>
|
||||||
|
Veilid.instance.getCryptoSystem(recordKey.kind);
|
||||||
|
|
||||||
|
Future<VeilidCrypto> getPrivateCrypto(SecretKey secretKey) async =>
|
||||||
|
DHTRecordPool.privateCryptoFromTypedSecret(
|
||||||
|
TypedKey(kind: recordKey.kind, value: secretKey));
|
||||||
|
|
||||||
|
KeyPair writer(SecretKey secret) => KeyPair(key: publicKey, secret: secret);
|
||||||
|
|
||||||
|
TypedKey get typedPublicKey =>
|
||||||
|
TypedKey(kind: recordKey.kind, value: publicKey);
|
||||||
|
|
||||||
|
Future<VeilidCryptoSystem> validateIdentitySecret(SecretKey secretKey) async {
|
||||||
|
final cs = await cryptoSystem;
|
||||||
|
final keyOk = await cs.validateKeyPair(publicKey, secretKey);
|
||||||
|
if (!keyOk) {
|
||||||
|
throw IdentityException.invalid;
|
||||||
|
}
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the account record info for a specific accountKey from the identity
|
||||||
|
/// instance record using the identity instance secret key to decrypt
|
||||||
|
Future<List<AccountRecordInfo>> readAccount(
|
||||||
|
{required TypedKey superRecordKey,
|
||||||
|
required SecretKey secretKey,
|
||||||
|
required String accountKey}) async {
|
||||||
|
// Read the identity key to get the account keys
|
||||||
|
final pool = DHTRecordPool.instance;
|
||||||
|
|
||||||
|
final identityRecordCrypto = await getPrivateCrypto(secretKey);
|
||||||
|
|
||||||
|
late final List<AccountRecordInfo> accountRecordInfo;
|
||||||
|
await (await pool.openRecordRead(recordKey,
|
||||||
|
debugName: 'IdentityInstance::readAccounts::IdentityRecord',
|
||||||
|
parent: superRecordKey,
|
||||||
|
crypto: identityRecordCrypto))
|
||||||
|
.scope((identityRec) async {
|
||||||
|
final identity = await identityRec.getJson(Identity.fromJson);
|
||||||
|
if (identity == null) {
|
||||||
|
// Identity could not be read or decrypted from DHT
|
||||||
|
throw IdentityException.readError;
|
||||||
|
}
|
||||||
|
final accountRecords = IMapOfSets.from(identity.accountRecords);
|
||||||
|
final vcAccounts = accountRecords.get(accountKey);
|
||||||
|
|
||||||
|
accountRecordInfo = vcAccounts.toList();
|
||||||
|
});
|
||||||
|
|
||||||
|
return accountRecordInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Account associated with super identity and store it in the
|
||||||
|
/// identity instance record.
|
||||||
|
Future<AccountRecordInfo> addAccount({
|
||||||
|
required TypedKey superRecordKey,
|
||||||
|
required SecretKey secretKey,
|
||||||
|
required String accountKey,
|
||||||
|
required Future<Uint8List> Function(TypedKey parent) createAccountCallback,
|
||||||
|
int maxAccounts = 1,
|
||||||
|
}) async {
|
||||||
|
final pool = DHTRecordPool.instance;
|
||||||
|
|
||||||
|
/////// Add account with profile to DHT
|
||||||
|
|
||||||
|
// Open identity key for writing
|
||||||
|
veilidLoggy.debug('Opening identity record');
|
||||||
|
return (await pool.openRecordWrite(recordKey, writer(secretKey),
|
||||||
|
debugName: 'IdentityInstance::addAccount::IdentityRecord',
|
||||||
|
parent: superRecordKey))
|
||||||
|
.scope((identityRec) async {
|
||||||
|
// Create new account to insert into identity
|
||||||
|
veilidLoggy.debug('Creating new account');
|
||||||
|
return (await pool.createRecord(
|
||||||
|
debugName:
|
||||||
|
'IdentityInstance::addAccount::IdentityRecord::AccountRecord',
|
||||||
|
parent: identityRec.key))
|
||||||
|
.deleteScope((accountRec) async {
|
||||||
|
final account = await createAccountCallback(accountRec.key);
|
||||||
|
// Write account key
|
||||||
|
veilidLoggy.debug('Writing account record');
|
||||||
|
await accountRec.eventualWriteBytes(account);
|
||||||
|
|
||||||
|
// Update identity key to include account
|
||||||
|
final newAccountRecordInfo = AccountRecordInfo(
|
||||||
|
accountRecord: OwnedDHTRecordPointer(
|
||||||
|
recordKey: accountRec.key, owner: accountRec.ownerKeyPair!));
|
||||||
|
|
||||||
|
veilidLoggy.debug('Updating identity with new account');
|
||||||
|
await identityRec.eventualUpdateJson(Identity.fromJson,
|
||||||
|
(oldIdentity) async {
|
||||||
|
if (oldIdentity == null) {
|
||||||
|
throw IdentityException.readError;
|
||||||
|
}
|
||||||
|
final oldAccountRecords = IMapOfSets.from(oldIdentity.accountRecords);
|
||||||
|
|
||||||
|
if (oldAccountRecords.get(accountKey).length >= maxAccounts) {
|
||||||
|
throw IdentityException.limitExceeded;
|
||||||
|
}
|
||||||
|
final accountRecords =
|
||||||
|
oldAccountRecords.add(accountKey, newAccountRecordInfo).asIMap();
|
||||||
|
return oldIdentity.copyWith(accountRecords: accountRecords);
|
||||||
|
});
|
||||||
|
|
||||||
|
return newAccountRecordInfo;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Internal implementation
|
||||||
|
|
||||||
|
Future<bool> validateIdentityInstance(
|
||||||
|
{required TypedKey superRecordKey,
|
||||||
|
required PublicKey superPublicKey}) async {
|
||||||
|
final sigValid = await IdentityInstance.validateIdentitySignature(
|
||||||
|
recordKey: recordKey,
|
||||||
|
publicKey: publicKey,
|
||||||
|
encryptedSecretKey: encryptedSecretKey,
|
||||||
|
superSignature: superSignature,
|
||||||
|
superPublicKey: superPublicKey,
|
||||||
|
signature: signature);
|
||||||
|
if (!sigValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final superSigValid = await IdentityInstance.validateSuperSignature(
|
||||||
|
superRecordKey: superRecordKey,
|
||||||
|
superPublicKey: superPublicKey,
|
||||||
|
publicKey: publicKey,
|
||||||
|
superSignature: superSignature);
|
||||||
|
if (!superSigValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint8List signatureBytes({
|
||||||
|
required TypedKey recordKey,
|
||||||
|
required PublicKey publicKey,
|
||||||
|
required Uint8List encryptedSecretKey,
|
||||||
|
required Signature superSignature,
|
||||||
|
}) {
|
||||||
|
final sigBuf = BytesBuilder()
|
||||||
|
..add(recordKey.decode())
|
||||||
|
..add(publicKey.decode())
|
||||||
|
..add(encryptedSecretKey)
|
||||||
|
..add(superSignature.decode());
|
||||||
|
return sigBuf.toBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> validateIdentitySignature({
|
||||||
|
required TypedKey recordKey,
|
||||||
|
required PublicKey publicKey,
|
||||||
|
required Uint8List encryptedSecretKey,
|
||||||
|
required Signature superSignature,
|
||||||
|
required PublicKey superPublicKey,
|
||||||
|
required Signature signature,
|
||||||
|
}) async {
|
||||||
|
final cs = await Veilid.instance.getCryptoSystem(recordKey.kind);
|
||||||
|
final identitySigBytes = IdentityInstance.signatureBytes(
|
||||||
|
recordKey: recordKey,
|
||||||
|
publicKey: publicKey,
|
||||||
|
encryptedSecretKey: encryptedSecretKey,
|
||||||
|
superSignature: superSignature);
|
||||||
|
return cs.verify(superPublicKey, identitySigBytes, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Signature> createIdentitySignature({
|
||||||
|
required TypedKey recordKey,
|
||||||
|
required PublicKey publicKey,
|
||||||
|
required Uint8List encryptedSecretKey,
|
||||||
|
required Signature superSignature,
|
||||||
|
required PublicKey superPublicKey,
|
||||||
|
required SecretKey superSecret,
|
||||||
|
}) async {
|
||||||
|
final cs = await Veilid.instance.getCryptoSystem(recordKey.kind);
|
||||||
|
final identitySigBytes = IdentityInstance.signatureBytes(
|
||||||
|
recordKey: recordKey,
|
||||||
|
publicKey: publicKey,
|
||||||
|
encryptedSecretKey: encryptedSecretKey,
|
||||||
|
superSignature: superSignature);
|
||||||
|
return cs.sign(superPublicKey, superSecret, identitySigBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint8List superSignatureBytes({
|
||||||
|
required TypedKey superRecordKey,
|
||||||
|
required PublicKey superPublicKey,
|
||||||
|
}) {
|
||||||
|
final superSigBuf = BytesBuilder()
|
||||||
|
..add(superRecordKey.decode())
|
||||||
|
..add(superPublicKey.decode());
|
||||||
|
return superSigBuf.toBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> validateSuperSignature({
|
||||||
|
required TypedKey superRecordKey,
|
||||||
|
required PublicKey superPublicKey,
|
||||||
|
required PublicKey publicKey,
|
||||||
|
required Signature superSignature,
|
||||||
|
}) async {
|
||||||
|
final cs = await Veilid.instance.getCryptoSystem(superRecordKey.kind);
|
||||||
|
final superSigBytes = IdentityInstance.superSignatureBytes(
|
||||||
|
superRecordKey: superRecordKey,
|
||||||
|
superPublicKey: superPublicKey,
|
||||||
|
);
|
||||||
|
return cs.verify(publicKey, superSigBytes, superSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Signature> createSuperSignature({
|
||||||
|
required TypedKey superRecordKey,
|
||||||
|
required PublicKey superPublicKey,
|
||||||
|
required PublicKey publicKey,
|
||||||
|
required SecretKey secretKey,
|
||||||
|
}) async {
|
||||||
|
final cs = await Veilid.instance.getCryptoSystem(superRecordKey.kind);
|
||||||
|
final superSigBytes = IdentityInstance.superSignatureBytes(
|
||||||
|
superRecordKey: superRecordKey,
|
||||||
|
superPublicKey: superPublicKey,
|
||||||
|
);
|
||||||
|
return cs.sign(publicKey, secretKey, superSigBytes);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,274 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'identity_instance.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
IdentityInstance _$IdentityInstanceFromJson(Map<String, dynamic> json) {
|
||||||
|
return _IdentityInstance.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$IdentityInstance {
|
||||||
|
// Private DHT record storing identity account mapping
|
||||||
|
Typed<FixedEncodedString43> get recordKey =>
|
||||||
|
throw _privateConstructorUsedError; // Public key of identity instance
|
||||||
|
FixedEncodedString43 get publicKey =>
|
||||||
|
throw _privateConstructorUsedError; // Secret key of identity instance
|
||||||
|
// Encrypted with DH(publicKey, SuperIdentity.secret) with appended salt
|
||||||
|
// Used to recover accounts without generating a new instance
|
||||||
|
@Uint8ListJsonConverter()
|
||||||
|
Uint8List get encryptedSecretKey =>
|
||||||
|
throw _privateConstructorUsedError; // Signature of SuperInstance recordKey and SuperInstance publicKey
|
||||||
|
// by publicKey
|
||||||
|
FixedEncodedString86 get superSignature =>
|
||||||
|
throw _privateConstructorUsedError; // Signature of recordKey, publicKey, encryptedSecretKey, and superSignature
|
||||||
|
// by SuperIdentity publicKey
|
||||||
|
FixedEncodedString86 get signature => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$IdentityInstanceCopyWith<IdentityInstance> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $IdentityInstanceCopyWith<$Res> {
|
||||||
|
factory $IdentityInstanceCopyWith(
|
||||||
|
IdentityInstance value, $Res Function(IdentityInstance) then) =
|
||||||
|
_$IdentityInstanceCopyWithImpl<$Res, IdentityInstance>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{Typed<FixedEncodedString43> recordKey,
|
||||||
|
FixedEncodedString43 publicKey,
|
||||||
|
@Uint8ListJsonConverter() Uint8List encryptedSecretKey,
|
||||||
|
FixedEncodedString86 superSignature,
|
||||||
|
FixedEncodedString86 signature});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$IdentityInstanceCopyWithImpl<$Res, $Val extends IdentityInstance>
|
||||||
|
implements $IdentityInstanceCopyWith<$Res> {
|
||||||
|
_$IdentityInstanceCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? recordKey = null,
|
||||||
|
Object? publicKey = null,
|
||||||
|
Object? encryptedSecretKey = null,
|
||||||
|
Object? superSignature = null,
|
||||||
|
Object? signature = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
recordKey: null == recordKey
|
||||||
|
? _value.recordKey
|
||||||
|
: recordKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Typed<FixedEncodedString43>,
|
||||||
|
publicKey: null == publicKey
|
||||||
|
? _value.publicKey
|
||||||
|
: publicKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString43,
|
||||||
|
encryptedSecretKey: null == encryptedSecretKey
|
||||||
|
? _value.encryptedSecretKey
|
||||||
|
: encryptedSecretKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Uint8List,
|
||||||
|
superSignature: null == superSignature
|
||||||
|
? _value.superSignature
|
||||||
|
: superSignature // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString86,
|
||||||
|
signature: null == signature
|
||||||
|
? _value.signature
|
||||||
|
: signature // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString86,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$IdentityInstanceImplCopyWith<$Res>
|
||||||
|
implements $IdentityInstanceCopyWith<$Res> {
|
||||||
|
factory _$$IdentityInstanceImplCopyWith(_$IdentityInstanceImpl value,
|
||||||
|
$Res Function(_$IdentityInstanceImpl) then) =
|
||||||
|
__$$IdentityInstanceImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{Typed<FixedEncodedString43> recordKey,
|
||||||
|
FixedEncodedString43 publicKey,
|
||||||
|
@Uint8ListJsonConverter() Uint8List encryptedSecretKey,
|
||||||
|
FixedEncodedString86 superSignature,
|
||||||
|
FixedEncodedString86 signature});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$IdentityInstanceImplCopyWithImpl<$Res>
|
||||||
|
extends _$IdentityInstanceCopyWithImpl<$Res, _$IdentityInstanceImpl>
|
||||||
|
implements _$$IdentityInstanceImplCopyWith<$Res> {
|
||||||
|
__$$IdentityInstanceImplCopyWithImpl(_$IdentityInstanceImpl _value,
|
||||||
|
$Res Function(_$IdentityInstanceImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? recordKey = null,
|
||||||
|
Object? publicKey = null,
|
||||||
|
Object? encryptedSecretKey = null,
|
||||||
|
Object? superSignature = null,
|
||||||
|
Object? signature = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$IdentityInstanceImpl(
|
||||||
|
recordKey: null == recordKey
|
||||||
|
? _value.recordKey
|
||||||
|
: recordKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Typed<FixedEncodedString43>,
|
||||||
|
publicKey: null == publicKey
|
||||||
|
? _value.publicKey
|
||||||
|
: publicKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString43,
|
||||||
|
encryptedSecretKey: null == encryptedSecretKey
|
||||||
|
? _value.encryptedSecretKey
|
||||||
|
: encryptedSecretKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Uint8List,
|
||||||
|
superSignature: null == superSignature
|
||||||
|
? _value.superSignature
|
||||||
|
: superSignature // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString86,
|
||||||
|
signature: null == signature
|
||||||
|
? _value.signature
|
||||||
|
: signature // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString86,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$IdentityInstanceImpl extends _IdentityInstance {
|
||||||
|
const _$IdentityInstanceImpl(
|
||||||
|
{required this.recordKey,
|
||||||
|
required this.publicKey,
|
||||||
|
@Uint8ListJsonConverter() required this.encryptedSecretKey,
|
||||||
|
required this.superSignature,
|
||||||
|
required this.signature})
|
||||||
|
: super._();
|
||||||
|
|
||||||
|
factory _$IdentityInstanceImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$IdentityInstanceImplFromJson(json);
|
||||||
|
|
||||||
|
// Private DHT record storing identity account mapping
|
||||||
|
@override
|
||||||
|
final Typed<FixedEncodedString43> recordKey;
|
||||||
|
// Public key of identity instance
|
||||||
|
@override
|
||||||
|
final FixedEncodedString43 publicKey;
|
||||||
|
// Secret key of identity instance
|
||||||
|
// Encrypted with DH(publicKey, SuperIdentity.secret) with appended salt
|
||||||
|
// Used to recover accounts without generating a new instance
|
||||||
|
@override
|
||||||
|
@Uint8ListJsonConverter()
|
||||||
|
final Uint8List encryptedSecretKey;
|
||||||
|
// Signature of SuperInstance recordKey and SuperInstance publicKey
|
||||||
|
// by publicKey
|
||||||
|
@override
|
||||||
|
final FixedEncodedString86 superSignature;
|
||||||
|
// Signature of recordKey, publicKey, encryptedSecretKey, and superSignature
|
||||||
|
// by SuperIdentity publicKey
|
||||||
|
@override
|
||||||
|
final FixedEncodedString86 signature;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'IdentityInstance(recordKey: $recordKey, publicKey: $publicKey, encryptedSecretKey: $encryptedSecretKey, superSignature: $superSignature, signature: $signature)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$IdentityInstanceImpl &&
|
||||||
|
(identical(other.recordKey, recordKey) ||
|
||||||
|
other.recordKey == recordKey) &&
|
||||||
|
(identical(other.publicKey, publicKey) ||
|
||||||
|
other.publicKey == publicKey) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other.encryptedSecretKey, encryptedSecretKey) &&
|
||||||
|
(identical(other.superSignature, superSignature) ||
|
||||||
|
other.superSignature == superSignature) &&
|
||||||
|
(identical(other.signature, signature) ||
|
||||||
|
other.signature == signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
recordKey,
|
||||||
|
publicKey,
|
||||||
|
const DeepCollectionEquality().hash(encryptedSecretKey),
|
||||||
|
superSignature,
|
||||||
|
signature);
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$IdentityInstanceImplCopyWith<_$IdentityInstanceImpl> get copyWith =>
|
||||||
|
__$$IdentityInstanceImplCopyWithImpl<_$IdentityInstanceImpl>(
|
||||||
|
this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$IdentityInstanceImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _IdentityInstance extends IdentityInstance {
|
||||||
|
const factory _IdentityInstance(
|
||||||
|
{required final Typed<FixedEncodedString43> recordKey,
|
||||||
|
required final FixedEncodedString43 publicKey,
|
||||||
|
@Uint8ListJsonConverter() required final Uint8List encryptedSecretKey,
|
||||||
|
required final FixedEncodedString86 superSignature,
|
||||||
|
required final FixedEncodedString86 signature}) = _$IdentityInstanceImpl;
|
||||||
|
const _IdentityInstance._() : super._();
|
||||||
|
|
||||||
|
factory _IdentityInstance.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$IdentityInstanceImpl.fromJson;
|
||||||
|
|
||||||
|
@override // Private DHT record storing identity account mapping
|
||||||
|
Typed<FixedEncodedString43> get recordKey;
|
||||||
|
@override // Public key of identity instance
|
||||||
|
FixedEncodedString43 get publicKey;
|
||||||
|
@override // Secret key of identity instance
|
||||||
|
// Encrypted with DH(publicKey, SuperIdentity.secret) with appended salt
|
||||||
|
// Used to recover accounts without generating a new instance
|
||||||
|
@Uint8ListJsonConverter()
|
||||||
|
Uint8List get encryptedSecretKey;
|
||||||
|
@override // Signature of SuperInstance recordKey and SuperInstance publicKey
|
||||||
|
// by publicKey
|
||||||
|
FixedEncodedString86 get superSignature;
|
||||||
|
@override // Signature of recordKey, publicKey, encryptedSecretKey, and superSignature
|
||||||
|
// by SuperIdentity publicKey
|
||||||
|
FixedEncodedString86 get signature;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$IdentityInstanceImplCopyWith<_$IdentityInstanceImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'identity_instance.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$IdentityInstanceImpl _$$IdentityInstanceImplFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$IdentityInstanceImpl(
|
||||||
|
recordKey: Typed<FixedEncodedString43>.fromJson(json['record_key']),
|
||||||
|
publicKey: FixedEncodedString43.fromJson(json['public_key']),
|
||||||
|
encryptedSecretKey:
|
||||||
|
const Uint8ListJsonConverter().fromJson(json['encrypted_secret_key']),
|
||||||
|
superSignature: FixedEncodedString86.fromJson(json['super_signature']),
|
||||||
|
signature: FixedEncodedString86.fromJson(json['signature']),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$IdentityInstanceImplToJson(
|
||||||
|
_$IdentityInstanceImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'record_key': instance.recordKey.toJson(),
|
||||||
|
'public_key': instance.publicKey.toJson(),
|
||||||
|
'encrypted_secret_key':
|
||||||
|
const Uint8ListJsonConverter().toJson(instance.encryptedSecretKey),
|
||||||
|
'super_signature': instance.superSignature.toJson(),
|
||||||
|
'signature': instance.signature.toJson(),
|
||||||
|
};
|
@ -0,0 +1,6 @@
|
|||||||
|
export 'account_record_info.dart';
|
||||||
|
export 'exceptions.dart';
|
||||||
|
export 'identity.dart';
|
||||||
|
export 'identity_instance.dart';
|
||||||
|
export 'super_identity.dart';
|
||||||
|
export 'writable_super_identity.dart';
|
174
packages/veilid_support/lib/identity_support/super_identity.dart
Normal file
174
packages/veilid_support/lib/identity_support/super_identity.dart
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
import '../veilid_support.dart';
|
||||||
|
|
||||||
|
part 'super_identity.freezed.dart';
|
||||||
|
part 'super_identity.g.dart';
|
||||||
|
|
||||||
|
/// SuperIdentity key structure for created account
|
||||||
|
///
|
||||||
|
/// SuperIdentity key allows for regeneration of identity DHT record
|
||||||
|
/// Bidirectional Super<->Instance signature allows for
|
||||||
|
/// chain of identity ownership for account recovery process
|
||||||
|
///
|
||||||
|
/// Backed by a DHT key at superRecordKey, the secret is kept
|
||||||
|
/// completely offline and only written to upon account recovery
|
||||||
|
///
|
||||||
|
/// DHT Schema: DFLT(1)
|
||||||
|
/// DHT Record Key (Public): SuperIdentity.recordKey
|
||||||
|
/// DHT Owner Key: SuperIdentity.publicKey
|
||||||
|
/// DHT Owner Secret: SuperIdentity Secret Key (kept offline)
|
||||||
|
/// Encryption: None
|
||||||
|
@freezed
|
||||||
|
class SuperIdentity with _$SuperIdentity {
|
||||||
|
const factory SuperIdentity({
|
||||||
|
/// Public DHT record storing this structure for account recovery
|
||||||
|
/// changing this can migrate/forward the SuperIdentity to a new DHT record
|
||||||
|
/// Instances should not hash this recordKey, rather the actual record
|
||||||
|
/// key used to store the superIdentity, as this may change.
|
||||||
|
required TypedKey recordKey,
|
||||||
|
|
||||||
|
/// Public key of the SuperIdentity used to sign identity keys for recovery
|
||||||
|
/// This must match the owner of the superRecord DHT record and can not be
|
||||||
|
/// changed without changing the record
|
||||||
|
required PublicKey publicKey,
|
||||||
|
|
||||||
|
/// Current identity instance
|
||||||
|
/// The most recently generated identity instance for this SuperIdentity
|
||||||
|
required IdentityInstance currentInstance,
|
||||||
|
|
||||||
|
/// Deprecated identity instances
|
||||||
|
/// These may be compromised and should not be considered valid for
|
||||||
|
/// new signatures, but may be used to validate old signatures
|
||||||
|
required List<IdentityInstance> deprecatedInstances,
|
||||||
|
|
||||||
|
/// Deprecated superRecords
|
||||||
|
/// These may be compromised and should not be considered valid for
|
||||||
|
/// new signatures, but may be used to validate old signatures
|
||||||
|
required List<TypedKey> deprecatedSuperRecordKeys,
|
||||||
|
|
||||||
|
/// Signature of recordKey, currentInstance signature,
|
||||||
|
/// signatures of deprecatedInstances, and deprecatedSuperRecordKeys
|
||||||
|
/// by publicKey
|
||||||
|
required Signature signature,
|
||||||
|
}) = _SuperIdentity;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
factory SuperIdentity.fromJson(dynamic json) =>
|
||||||
|
_$SuperIdentityFromJson(json as Map<String, dynamic>);
|
||||||
|
|
||||||
|
const SuperIdentity._();
|
||||||
|
|
||||||
|
/// Opens an existing super identity and validates it
|
||||||
|
static Future<SuperIdentity> open({required TypedKey superRecordKey}) async {
|
||||||
|
final pool = DHTRecordPool.instance;
|
||||||
|
|
||||||
|
// SuperIdentity DHT record is public/unencrypted
|
||||||
|
return (await pool.openRecordRead(superRecordKey,
|
||||||
|
debugName: 'SuperIdentity::openSuperIdentity::SuperIdentityRecord'))
|
||||||
|
.deleteScope((superRec) async {
|
||||||
|
final superIdentity = (await superRec.getJson(SuperIdentity.fromJson,
|
||||||
|
refreshMode: DHTRecordRefreshMode.network))!;
|
||||||
|
|
||||||
|
// Validate current IdentityInstance
|
||||||
|
if (!await superIdentity.currentInstance.validateIdentityInstance(
|
||||||
|
superRecordKey: superRecordKey,
|
||||||
|
superPublicKey: superIdentity.publicKey)) {
|
||||||
|
// Invalid current IdentityInstance signature(s)
|
||||||
|
throw IdentityException.invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate deprecated IdentityInstances
|
||||||
|
for (final deprecatedInstance in superIdentity.deprecatedInstances) {
|
||||||
|
if (!await deprecatedInstance.validateIdentityInstance(
|
||||||
|
superRecordKey: superRecordKey,
|
||||||
|
superPublicKey: superIdentity.publicKey)) {
|
||||||
|
// Invalid deprecated IdentityInstance signature(s)
|
||||||
|
throw IdentityException.invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate SuperIdentity
|
||||||
|
final deprecatedInstancesSignatures =
|
||||||
|
superIdentity.deprecatedInstances.map((x) => x.signature).toList();
|
||||||
|
if (!await _validateSuperIdentitySignature(
|
||||||
|
recordKey: superIdentity.recordKey,
|
||||||
|
currentInstanceSignature: superIdentity.currentInstance.signature,
|
||||||
|
deprecatedInstancesSignatures: deprecatedInstancesSignatures,
|
||||||
|
deprecatedSuperRecordKeys: superIdentity.deprecatedSuperRecordKeys,
|
||||||
|
publicKey: superIdentity.publicKey,
|
||||||
|
signature: superIdentity.signature)) {
|
||||||
|
// Invalid SuperIdentity signature
|
||||||
|
throw IdentityException.invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return superIdentity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Public Interface
|
||||||
|
|
||||||
|
/// Deletes a super identity and the identity instance records under it
|
||||||
|
/// Only deletes from the local machine not the DHT
|
||||||
|
Future<void> delete() async {
|
||||||
|
final pool = DHTRecordPool.instance;
|
||||||
|
await pool.deleteRecord(recordKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<VeilidCryptoSystem> get cryptoSystem =>
|
||||||
|
Veilid.instance.getCryptoSystem(recordKey.kind);
|
||||||
|
|
||||||
|
KeyPair writer(SecretKey secretKey) =>
|
||||||
|
KeyPair(key: publicKey, secret: secretKey);
|
||||||
|
|
||||||
|
TypedKey get typedPublicKey =>
|
||||||
|
TypedKey(kind: recordKey.kind, value: publicKey);
|
||||||
|
|
||||||
|
Future<VeilidCryptoSystem> validateSecret(SecretKey secretKey) async {
|
||||||
|
final cs = await cryptoSystem;
|
||||||
|
final keyOk = await cs.validateKeyPair(publicKey, secretKey);
|
||||||
|
if (!keyOk) {
|
||||||
|
throw IdentityException.invalid;
|
||||||
|
}
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Internal implementation
|
||||||
|
|
||||||
|
static Uint8List signatureBytes({
|
||||||
|
required TypedKey recordKey,
|
||||||
|
required Signature currentInstanceSignature,
|
||||||
|
required List<Signature> deprecatedInstancesSignatures,
|
||||||
|
required List<TypedKey> deprecatedSuperRecordKeys,
|
||||||
|
}) {
|
||||||
|
final sigBuf = BytesBuilder()
|
||||||
|
..add(recordKey.decode())
|
||||||
|
..add(currentInstanceSignature.decode())
|
||||||
|
..add(deprecatedInstancesSignatures.expand((s) => s.decode()).toList())
|
||||||
|
..add(deprecatedSuperRecordKeys.expand((s) => s.decode()).toList());
|
||||||
|
return sigBuf.toBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> _validateSuperIdentitySignature({
|
||||||
|
required TypedKey recordKey,
|
||||||
|
required Signature currentInstanceSignature,
|
||||||
|
required List<Signature> deprecatedInstancesSignatures,
|
||||||
|
required List<TypedKey> deprecatedSuperRecordKeys,
|
||||||
|
required PublicKey publicKey,
|
||||||
|
required Signature signature,
|
||||||
|
}) async {
|
||||||
|
final cs = await Veilid.instance.getCryptoSystem(recordKey.kind);
|
||||||
|
final sigBytes = SuperIdentity.signatureBytes(
|
||||||
|
recordKey: recordKey,
|
||||||
|
currentInstanceSignature: currentInstanceSignature,
|
||||||
|
deprecatedInstancesSignatures: deprecatedInstancesSignatures,
|
||||||
|
deprecatedSuperRecordKeys: deprecatedSuperRecordKeys);
|
||||||
|
return cs.verify(publicKey, sigBytes, signature);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,380 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'super_identity.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
SuperIdentity _$SuperIdentityFromJson(Map<String, dynamic> json) {
|
||||||
|
return _SuperIdentity.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SuperIdentity {
|
||||||
|
/// Public DHT record storing this structure for account recovery
|
||||||
|
/// changing this can migrate/forward the SuperIdentity to a new DHT record
|
||||||
|
/// Instances should not hash this recordKey, rather the actual record
|
||||||
|
/// key used to store the superIdentity, as this may change.
|
||||||
|
Typed<FixedEncodedString43> get recordKey =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Public key of the SuperIdentity used to sign identity keys for recovery
|
||||||
|
/// This must match the owner of the superRecord DHT record and can not be
|
||||||
|
/// changed without changing the record
|
||||||
|
FixedEncodedString43 get publicKey => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Current identity instance
|
||||||
|
/// The most recently generated identity instance for this SuperIdentity
|
||||||
|
IdentityInstance get currentInstance => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Deprecated identity instances
|
||||||
|
/// These may be compromised and should not be considered valid for
|
||||||
|
/// new signatures, but may be used to validate old signatures
|
||||||
|
List<IdentityInstance> get deprecatedInstances =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Deprecated superRecords
|
||||||
|
/// These may be compromised and should not be considered valid for
|
||||||
|
/// new signatures, but may be used to validate old signatures
|
||||||
|
List<Typed<FixedEncodedString43>> get deprecatedSuperRecordKeys =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Signature of recordKey, currentInstance signature,
|
||||||
|
/// signatures of deprecatedInstances, and deprecatedSuperRecordKeys
|
||||||
|
/// by publicKey
|
||||||
|
FixedEncodedString86 get signature => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$SuperIdentityCopyWith<SuperIdentity> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $SuperIdentityCopyWith<$Res> {
|
||||||
|
factory $SuperIdentityCopyWith(
|
||||||
|
SuperIdentity value, $Res Function(SuperIdentity) then) =
|
||||||
|
_$SuperIdentityCopyWithImpl<$Res, SuperIdentity>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{Typed<FixedEncodedString43> recordKey,
|
||||||
|
FixedEncodedString43 publicKey,
|
||||||
|
IdentityInstance currentInstance,
|
||||||
|
List<IdentityInstance> deprecatedInstances,
|
||||||
|
List<Typed<FixedEncodedString43>> deprecatedSuperRecordKeys,
|
||||||
|
FixedEncodedString86 signature});
|
||||||
|
|
||||||
|
$IdentityInstanceCopyWith<$Res> get currentInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$SuperIdentityCopyWithImpl<$Res, $Val extends SuperIdentity>
|
||||||
|
implements $SuperIdentityCopyWith<$Res> {
|
||||||
|
_$SuperIdentityCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? recordKey = null,
|
||||||
|
Object? publicKey = null,
|
||||||
|
Object? currentInstance = null,
|
||||||
|
Object? deprecatedInstances = null,
|
||||||
|
Object? deprecatedSuperRecordKeys = null,
|
||||||
|
Object? signature = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
recordKey: null == recordKey
|
||||||
|
? _value.recordKey
|
||||||
|
: recordKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Typed<FixedEncodedString43>,
|
||||||
|
publicKey: null == publicKey
|
||||||
|
? _value.publicKey
|
||||||
|
: publicKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString43,
|
||||||
|
currentInstance: null == currentInstance
|
||||||
|
? _value.currentInstance
|
||||||
|
: currentInstance // ignore: cast_nullable_to_non_nullable
|
||||||
|
as IdentityInstance,
|
||||||
|
deprecatedInstances: null == deprecatedInstances
|
||||||
|
? _value.deprecatedInstances
|
||||||
|
: deprecatedInstances // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<IdentityInstance>,
|
||||||
|
deprecatedSuperRecordKeys: null == deprecatedSuperRecordKeys
|
||||||
|
? _value.deprecatedSuperRecordKeys
|
||||||
|
: deprecatedSuperRecordKeys // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<Typed<FixedEncodedString43>>,
|
||||||
|
signature: null == signature
|
||||||
|
? _value.signature
|
||||||
|
: signature // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString86,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$IdentityInstanceCopyWith<$Res> get currentInstance {
|
||||||
|
return $IdentityInstanceCopyWith<$Res>(_value.currentInstance, (value) {
|
||||||
|
return _then(_value.copyWith(currentInstance: value) as $Val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$SuperIdentityImplCopyWith<$Res>
|
||||||
|
implements $SuperIdentityCopyWith<$Res> {
|
||||||
|
factory _$$SuperIdentityImplCopyWith(
|
||||||
|
_$SuperIdentityImpl value, $Res Function(_$SuperIdentityImpl) then) =
|
||||||
|
__$$SuperIdentityImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{Typed<FixedEncodedString43> recordKey,
|
||||||
|
FixedEncodedString43 publicKey,
|
||||||
|
IdentityInstance currentInstance,
|
||||||
|
List<IdentityInstance> deprecatedInstances,
|
||||||
|
List<Typed<FixedEncodedString43>> deprecatedSuperRecordKeys,
|
||||||
|
FixedEncodedString86 signature});
|
||||||
|
|
||||||
|
@override
|
||||||
|
$IdentityInstanceCopyWith<$Res> get currentInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$SuperIdentityImplCopyWithImpl<$Res>
|
||||||
|
extends _$SuperIdentityCopyWithImpl<$Res, _$SuperIdentityImpl>
|
||||||
|
implements _$$SuperIdentityImplCopyWith<$Res> {
|
||||||
|
__$$SuperIdentityImplCopyWithImpl(
|
||||||
|
_$SuperIdentityImpl _value, $Res Function(_$SuperIdentityImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? recordKey = null,
|
||||||
|
Object? publicKey = null,
|
||||||
|
Object? currentInstance = null,
|
||||||
|
Object? deprecatedInstances = null,
|
||||||
|
Object? deprecatedSuperRecordKeys = null,
|
||||||
|
Object? signature = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$SuperIdentityImpl(
|
||||||
|
recordKey: null == recordKey
|
||||||
|
? _value.recordKey
|
||||||
|
: recordKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Typed<FixedEncodedString43>,
|
||||||
|
publicKey: null == publicKey
|
||||||
|
? _value.publicKey
|
||||||
|
: publicKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString43,
|
||||||
|
currentInstance: null == currentInstance
|
||||||
|
? _value.currentInstance
|
||||||
|
: currentInstance // ignore: cast_nullable_to_non_nullable
|
||||||
|
as IdentityInstance,
|
||||||
|
deprecatedInstances: null == deprecatedInstances
|
||||||
|
? _value._deprecatedInstances
|
||||||
|
: deprecatedInstances // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<IdentityInstance>,
|
||||||
|
deprecatedSuperRecordKeys: null == deprecatedSuperRecordKeys
|
||||||
|
? _value._deprecatedSuperRecordKeys
|
||||||
|
: deprecatedSuperRecordKeys // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<Typed<FixedEncodedString43>>,
|
||||||
|
signature: null == signature
|
||||||
|
? _value.signature
|
||||||
|
: signature // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FixedEncodedString86,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$SuperIdentityImpl extends _SuperIdentity {
|
||||||
|
const _$SuperIdentityImpl(
|
||||||
|
{required this.recordKey,
|
||||||
|
required this.publicKey,
|
||||||
|
required this.currentInstance,
|
||||||
|
required final List<IdentityInstance> deprecatedInstances,
|
||||||
|
required final List<Typed<FixedEncodedString43>>
|
||||||
|
deprecatedSuperRecordKeys,
|
||||||
|
required this.signature})
|
||||||
|
: _deprecatedInstances = deprecatedInstances,
|
||||||
|
_deprecatedSuperRecordKeys = deprecatedSuperRecordKeys,
|
||||||
|
super._();
|
||||||
|
|
||||||
|
factory _$SuperIdentityImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$SuperIdentityImplFromJson(json);
|
||||||
|
|
||||||
|
/// Public DHT record storing this structure for account recovery
|
||||||
|
/// changing this can migrate/forward the SuperIdentity to a new DHT record
|
||||||
|
/// Instances should not hash this recordKey, rather the actual record
|
||||||
|
/// key used to store the superIdentity, as this may change.
|
||||||
|
@override
|
||||||
|
final Typed<FixedEncodedString43> recordKey;
|
||||||
|
|
||||||
|
/// Public key of the SuperIdentity used to sign identity keys for recovery
|
||||||
|
/// This must match the owner of the superRecord DHT record and can not be
|
||||||
|
/// changed without changing the record
|
||||||
|
@override
|
||||||
|
final FixedEncodedString43 publicKey;
|
||||||
|
|
||||||
|
/// Current identity instance
|
||||||
|
/// The most recently generated identity instance for this SuperIdentity
|
||||||
|
@override
|
||||||
|
final IdentityInstance currentInstance;
|
||||||
|
|
||||||
|
/// Deprecated identity instances
|
||||||
|
/// These may be compromised and should not be considered valid for
|
||||||
|
/// new signatures, but may be used to validate old signatures
|
||||||
|
final List<IdentityInstance> _deprecatedInstances;
|
||||||
|
|
||||||
|
/// Deprecated identity instances
|
||||||
|
/// These may be compromised and should not be considered valid for
|
||||||
|
/// new signatures, but may be used to validate old signatures
|
||||||
|
@override
|
||||||
|
List<IdentityInstance> get deprecatedInstances {
|
||||||
|
if (_deprecatedInstances is EqualUnmodifiableListView)
|
||||||
|
return _deprecatedInstances;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_deprecatedInstances);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deprecated superRecords
|
||||||
|
/// These may be compromised and should not be considered valid for
|
||||||
|
/// new signatures, but may be used to validate old signatures
|
||||||
|
final List<Typed<FixedEncodedString43>> _deprecatedSuperRecordKeys;
|
||||||
|
|
||||||
|
/// Deprecated superRecords
|
||||||
|
/// These may be compromised and should not be considered valid for
|
||||||
|
/// new signatures, but may be used to validate old signatures
|
||||||
|
@override
|
||||||
|
List<Typed<FixedEncodedString43>> get deprecatedSuperRecordKeys {
|
||||||
|
if (_deprecatedSuperRecordKeys is EqualUnmodifiableListView)
|
||||||
|
return _deprecatedSuperRecordKeys;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_deprecatedSuperRecordKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signature of recordKey, currentInstance signature,
|
||||||
|
/// signatures of deprecatedInstances, and deprecatedSuperRecordKeys
|
||||||
|
/// by publicKey
|
||||||
|
@override
|
||||||
|
final FixedEncodedString86 signature;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SuperIdentity(recordKey: $recordKey, publicKey: $publicKey, currentInstance: $currentInstance, deprecatedInstances: $deprecatedInstances, deprecatedSuperRecordKeys: $deprecatedSuperRecordKeys, signature: $signature)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$SuperIdentityImpl &&
|
||||||
|
(identical(other.recordKey, recordKey) ||
|
||||||
|
other.recordKey == recordKey) &&
|
||||||
|
(identical(other.publicKey, publicKey) ||
|
||||||
|
other.publicKey == publicKey) &&
|
||||||
|
(identical(other.currentInstance, currentInstance) ||
|
||||||
|
other.currentInstance == currentInstance) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._deprecatedInstances, _deprecatedInstances) &&
|
||||||
|
const DeepCollectionEquality().equals(
|
||||||
|
other._deprecatedSuperRecordKeys, _deprecatedSuperRecordKeys) &&
|
||||||
|
(identical(other.signature, signature) ||
|
||||||
|
other.signature == signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
recordKey,
|
||||||
|
publicKey,
|
||||||
|
currentInstance,
|
||||||
|
const DeepCollectionEquality().hash(_deprecatedInstances),
|
||||||
|
const DeepCollectionEquality().hash(_deprecatedSuperRecordKeys),
|
||||||
|
signature);
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$SuperIdentityImplCopyWith<_$SuperIdentityImpl> get copyWith =>
|
||||||
|
__$$SuperIdentityImplCopyWithImpl<_$SuperIdentityImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$SuperIdentityImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _SuperIdentity extends SuperIdentity {
|
||||||
|
const factory _SuperIdentity(
|
||||||
|
{required final Typed<FixedEncodedString43> recordKey,
|
||||||
|
required final FixedEncodedString43 publicKey,
|
||||||
|
required final IdentityInstance currentInstance,
|
||||||
|
required final List<IdentityInstance> deprecatedInstances,
|
||||||
|
required final List<Typed<FixedEncodedString43>>
|
||||||
|
deprecatedSuperRecordKeys,
|
||||||
|
required final FixedEncodedString86 signature}) = _$SuperIdentityImpl;
|
||||||
|
const _SuperIdentity._() : super._();
|
||||||
|
|
||||||
|
factory _SuperIdentity.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$SuperIdentityImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// Public DHT record storing this structure for account recovery
|
||||||
|
/// changing this can migrate/forward the SuperIdentity to a new DHT record
|
||||||
|
/// Instances should not hash this recordKey, rather the actual record
|
||||||
|
/// key used to store the superIdentity, as this may change.
|
||||||
|
Typed<FixedEncodedString43> get recordKey;
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// Public key of the SuperIdentity used to sign identity keys for recovery
|
||||||
|
/// This must match the owner of the superRecord DHT record and can not be
|
||||||
|
/// changed without changing the record
|
||||||
|
FixedEncodedString43 get publicKey;
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// Current identity instance
|
||||||
|
/// The most recently generated identity instance for this SuperIdentity
|
||||||
|
IdentityInstance get currentInstance;
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// Deprecated identity instances
|
||||||
|
/// These may be compromised and should not be considered valid for
|
||||||
|
/// new signatures, but may be used to validate old signatures
|
||||||
|
List<IdentityInstance> get deprecatedInstances;
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// Deprecated superRecords
|
||||||
|
/// These may be compromised and should not be considered valid for
|
||||||
|
/// new signatures, but may be used to validate old signatures
|
||||||
|
List<Typed<FixedEncodedString43>> get deprecatedSuperRecordKeys;
|
||||||
|
@override
|
||||||
|
|
||||||
|
/// Signature of recordKey, currentInstance signature,
|
||||||
|
/// signatures of deprecatedInstances, and deprecatedSuperRecordKeys
|
||||||
|
/// by publicKey
|
||||||
|
FixedEncodedString86 get signature;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$SuperIdentityImplCopyWith<_$SuperIdentityImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'super_identity.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$SuperIdentityImpl _$$SuperIdentityImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SuperIdentityImpl(
|
||||||
|
recordKey: Typed<FixedEncodedString43>.fromJson(json['record_key']),
|
||||||
|
publicKey: FixedEncodedString43.fromJson(json['public_key']),
|
||||||
|
currentInstance: IdentityInstance.fromJson(json['current_instance']),
|
||||||
|
deprecatedInstances: (json['deprecated_instances'] as List<dynamic>)
|
||||||
|
.map(IdentityInstance.fromJson)
|
||||||
|
.toList(),
|
||||||
|
deprecatedSuperRecordKeys:
|
||||||
|
(json['deprecated_super_record_keys'] as List<dynamic>)
|
||||||
|
.map(Typed<FixedEncodedString43>.fromJson)
|
||||||
|
.toList(),
|
||||||
|
signature: FixedEncodedString86.fromJson(json['signature']),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$SuperIdentityImplToJson(_$SuperIdentityImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'record_key': instance.recordKey.toJson(),
|
||||||
|
'public_key': instance.publicKey.toJson(),
|
||||||
|
'current_instance': instance.currentInstance.toJson(),
|
||||||
|
'deprecated_instances':
|
||||||
|
instance.deprecatedInstances.map((e) => e.toJson()).toList(),
|
||||||
|
'deprecated_super_record_keys':
|
||||||
|
instance.deprecatedSuperRecordKeys.map((e) => e.toJson()).toList(),
|
||||||
|
'signature': instance.signature.toJson(),
|
||||||
|
};
|
@ -0,0 +1,158 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||||
|
|
||||||
|
import '../src/veilid_log.dart';
|
||||||
|
import '../veilid_support.dart';
|
||||||
|
|
||||||
|
Uint8List identityCryptoDomain = utf8.encode('identity');
|
||||||
|
|
||||||
|
/// SuperIdentity creator with secret
|
||||||
|
/// Not freezed because we never persist this class in its entirety.
|
||||||
|
class WritableSuperIdentity {
|
||||||
|
WritableSuperIdentity._({
|
||||||
|
required this.superIdentity,
|
||||||
|
required this.superSecret,
|
||||||
|
required this.identitySecret,
|
||||||
|
});
|
||||||
|
|
||||||
|
static Future<WritableSuperIdentity> create() async {
|
||||||
|
final pool = DHTRecordPool.instance;
|
||||||
|
|
||||||
|
// SuperIdentity DHT record is public/unencrypted
|
||||||
|
veilidLoggy.debug('Creating super identity record');
|
||||||
|
return (await pool.createRecord(
|
||||||
|
debugName: 'WritableSuperIdentity::create::SuperIdentityRecord',
|
||||||
|
crypto: const VeilidCryptoPublic()))
|
||||||
|
.deleteScope((superRec) async {
|
||||||
|
final superRecordKey = superRec.key;
|
||||||
|
final superPublicKey = superRec.ownerKeyPair!.key;
|
||||||
|
final superSecret = superRec.ownerKeyPair!.secret;
|
||||||
|
|
||||||
|
return _createIdentityInstance(
|
||||||
|
superRecordKey: superRecordKey,
|
||||||
|
superPublicKey: superPublicKey,
|
||||||
|
superSecret: superSecret,
|
||||||
|
closure: (identityInstance, identitySecret) async {
|
||||||
|
final signature = await _createSuperIdentitySignature(
|
||||||
|
recordKey: superRecordKey,
|
||||||
|
publicKey: superPublicKey,
|
||||||
|
secretKey: superSecret,
|
||||||
|
currentInstanceSignature: identityInstance.signature,
|
||||||
|
deprecatedInstancesSignatures: [],
|
||||||
|
deprecatedSuperRecordKeys: [],
|
||||||
|
);
|
||||||
|
|
||||||
|
final superIdentity = SuperIdentity(
|
||||||
|
recordKey: superRecordKey,
|
||||||
|
publicKey: superPublicKey,
|
||||||
|
currentInstance: identityInstance,
|
||||||
|
deprecatedInstances: [],
|
||||||
|
deprecatedSuperRecordKeys: [],
|
||||||
|
signature: signature);
|
||||||
|
|
||||||
|
// Write superidentity to dht record
|
||||||
|
await superRec.eventualWriteJson(superIdentity);
|
||||||
|
|
||||||
|
return WritableSuperIdentity._(
|
||||||
|
superIdentity: superIdentity,
|
||||||
|
superSecret: superSecret,
|
||||||
|
identitySecret: identitySecret);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Public Interface
|
||||||
|
|
||||||
|
/// Delete a super identity with secrets
|
||||||
|
Future<void> delete() async => superIdentity.delete();
|
||||||
|
|
||||||
|
/// xxx: migration support, new identities, reveal identity secret etc
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Private Implementation
|
||||||
|
|
||||||
|
static Future<Signature> _createSuperIdentitySignature({
|
||||||
|
required TypedKey recordKey,
|
||||||
|
required Signature currentInstanceSignature,
|
||||||
|
required List<Signature> deprecatedInstancesSignatures,
|
||||||
|
required List<TypedKey> deprecatedSuperRecordKeys,
|
||||||
|
required PublicKey publicKey,
|
||||||
|
required SecretKey secretKey,
|
||||||
|
}) async {
|
||||||
|
final cs = await Veilid.instance.getCryptoSystem(recordKey.kind);
|
||||||
|
final sigBytes = SuperIdentity.signatureBytes(
|
||||||
|
recordKey: recordKey,
|
||||||
|
currentInstanceSignature: currentInstanceSignature,
|
||||||
|
deprecatedInstancesSignatures: deprecatedInstancesSignatures,
|
||||||
|
deprecatedSuperRecordKeys: deprecatedSuperRecordKeys);
|
||||||
|
return cs.sign(publicKey, secretKey, sigBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<T> _createIdentityInstance<T>({
|
||||||
|
required TypedKey superRecordKey,
|
||||||
|
required PublicKey superPublicKey,
|
||||||
|
required SecretKey superSecret,
|
||||||
|
required Future<T> Function(IdentityInstance, SecretKey) closure,
|
||||||
|
}) async {
|
||||||
|
final pool = DHTRecordPool.instance;
|
||||||
|
veilidLoggy.debug('Creating identity instance record');
|
||||||
|
// Identity record is private
|
||||||
|
return (await pool.createRecord(
|
||||||
|
debugName: 'SuperIdentityWithSecrets::create::IdentityRecord',
|
||||||
|
parent: superRecordKey))
|
||||||
|
.deleteScope((identityRec) async {
|
||||||
|
final identityRecordKey = identityRec.key;
|
||||||
|
assert(superRecordKey.kind == identityRecordKey.kind,
|
||||||
|
'new super and identity should have same cryptosystem');
|
||||||
|
final identityPublicKey = identityRec.ownerKeyPair!.key;
|
||||||
|
final identitySecretKey = identityRec.ownerKeyPair!.secret;
|
||||||
|
|
||||||
|
// Make encrypted secret key
|
||||||
|
final cs = await Veilid.instance.getCryptoSystem(identityRecordKey.kind);
|
||||||
|
|
||||||
|
final encryptionKey = await cs.generateSharedSecret(
|
||||||
|
identityPublicKey, superSecret, identityCryptoDomain);
|
||||||
|
final encryptedSecretKey = await cs.encryptNoAuthWithNonce(
|
||||||
|
identitySecretKey.decode(), encryptionKey);
|
||||||
|
|
||||||
|
// Make supersignature
|
||||||
|
final superSigBuf = BytesBuilder()
|
||||||
|
..add(superRecordKey.decode())
|
||||||
|
..add(superPublicKey.decode());
|
||||||
|
|
||||||
|
final superSignature = await cs.signWithKeyPair(
|
||||||
|
identityRec.ownerKeyPair!, superSigBuf.toBytes());
|
||||||
|
|
||||||
|
// Make signature
|
||||||
|
final signature = await IdentityInstance.createIdentitySignature(
|
||||||
|
recordKey: identityRecordKey,
|
||||||
|
publicKey: identityPublicKey,
|
||||||
|
encryptedSecretKey: encryptedSecretKey,
|
||||||
|
superSignature: superSignature,
|
||||||
|
superPublicKey: superPublicKey,
|
||||||
|
superSecret: superSecret);
|
||||||
|
|
||||||
|
// Make empty identity
|
||||||
|
const identity = Identity(accountRecords: IMapConst({}));
|
||||||
|
|
||||||
|
// Write empty identity to identity dht key
|
||||||
|
await identityRec.eventualWriteJson(identity);
|
||||||
|
|
||||||
|
final identityInstance = IdentityInstance(
|
||||||
|
recordKey: identityRecordKey,
|
||||||
|
publicKey: identityPublicKey,
|
||||||
|
encryptedSecretKey: encryptedSecretKey,
|
||||||
|
superSignature: superSignature,
|
||||||
|
signature: signature);
|
||||||
|
|
||||||
|
return closure(identityInstance, identitySecretKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SuperIdentity superIdentity;
|
||||||
|
SecretKey superSecret;
|
||||||
|
SecretKey identitySecret;
|
||||||
|
}
|
@ -1,333 +0,0 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
import 'package:protobuf/protobuf.dart';
|
|
||||||
|
|
||||||
import '../veilid_support.dart';
|
|
||||||
import 'veilid_log.dart';
|
|
||||||
|
|
||||||
part 'identity.freezed.dart';
|
|
||||||
part 'identity.g.dart';
|
|
||||||
|
|
||||||
// Identity errors
|
|
||||||
enum IdentityException implements Exception {
|
|
||||||
readError('identity could not be read'),
|
|
||||||
noAccount('no account record info'),
|
|
||||||
limitExceeded('too many items for the limit'),
|
|
||||||
invalid('identity is corrupted or secret is invalid');
|
|
||||||
|
|
||||||
const IdentityException(this.message);
|
|
||||||
final String message;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'IdentityException($name): $message';
|
|
||||||
}
|
|
||||||
|
|
||||||
// AccountOwnerInfo is the key and owner info for the account dht key that is
|
|
||||||
// stored in the identity key
|
|
||||||
@freezed
|
|
||||||
class AccountRecordInfo with _$AccountRecordInfo {
|
|
||||||
const factory AccountRecordInfo({
|
|
||||||
// Top level account keys and secrets
|
|
||||||
required OwnedDHTRecordPointer accountRecord,
|
|
||||||
}) = _AccountRecordInfo;
|
|
||||||
|
|
||||||
factory AccountRecordInfo.fromJson(dynamic json) =>
|
|
||||||
_$AccountRecordInfoFromJson(json as Map<String, dynamic>);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identity Key points to accounts associated with this identity
|
|
||||||
// accounts field has a map of bundle id or uuid to account key pairs
|
|
||||||
// DHT Schema: DFLT(1)
|
|
||||||
// DHT Key (Private): identityRecordKey
|
|
||||||
// DHT Owner Key: identityPublicKey
|
|
||||||
// DHT Secret: identitySecretKey (stored encrypted
|
|
||||||
// with unlock code in local table store)
|
|
||||||
@freezed
|
|
||||||
class Identity with _$Identity {
|
|
||||||
const factory Identity({
|
|
||||||
// Top level account keys and secrets
|
|
||||||
required IMap<String, ISet<AccountRecordInfo>> accountRecords,
|
|
||||||
}) = _Identity;
|
|
||||||
|
|
||||||
factory Identity.fromJson(dynamic json) =>
|
|
||||||
_$IdentityFromJson(json as Map<String, dynamic>);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identity Master key structure for created account
|
|
||||||
// Master key allows for regeneration of identity DHT record
|
|
||||||
// Bidirectional Master<->Identity signature allows for
|
|
||||||
// chain of identity ownership for account recovery process
|
|
||||||
//
|
|
||||||
// Backed by a DHT key at masterRecordKey, the secret is kept
|
|
||||||
// completely offline and only written to upon account recovery
|
|
||||||
//
|
|
||||||
// DHT Schema: DFLT(1)
|
|
||||||
// DHT Record Key (Public): masterRecordKey
|
|
||||||
// DHT Owner Key: masterPublicKey
|
|
||||||
// DHT Owner Secret: masterSecretKey (kept offline)
|
|
||||||
// Encryption: None
|
|
||||||
@freezed
|
|
||||||
class IdentityMaster with _$IdentityMaster {
|
|
||||||
const factory IdentityMaster(
|
|
||||||
{
|
|
||||||
// Private DHT record storing identity account mapping
|
|
||||||
required TypedKey identityRecordKey,
|
|
||||||
// Public key of identity
|
|
||||||
required PublicKey identityPublicKey,
|
|
||||||
// Public DHT record storing this structure for account recovery
|
|
||||||
required TypedKey masterRecordKey,
|
|
||||||
// Public key of master identity used to sign identity keys for recovery
|
|
||||||
required PublicKey masterPublicKey,
|
|
||||||
// Signature of identityRecordKey and identityPublicKey by masterPublicKey
|
|
||||||
required Signature identitySignature,
|
|
||||||
// Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
|
||||||
required Signature masterSignature}) = _IdentityMaster;
|
|
||||||
|
|
||||||
factory IdentityMaster.fromJson(dynamic json) =>
|
|
||||||
_$IdentityMasterFromJson(json as Map<String, dynamic>);
|
|
||||||
}
|
|
||||||
|
|
||||||
extension IdentityMasterExtension on IdentityMaster {
|
|
||||||
/// Deletes a master identity and the identity record under it
|
|
||||||
Future<void> delete() async {
|
|
||||||
final pool = DHTRecordPool.instance;
|
|
||||||
await pool.deleteRecord(masterRecordKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<VeilidCryptoSystem> get identityCrypto =>
|
|
||||||
Veilid.instance.getCryptoSystem(identityRecordKey.kind);
|
|
||||||
|
|
||||||
Future<VeilidCryptoSystem> get masterCrypto =>
|
|
||||||
Veilid.instance.getCryptoSystem(masterRecordKey.kind);
|
|
||||||
|
|
||||||
KeyPair identityWriter(SecretKey secret) =>
|
|
||||||
KeyPair(key: identityPublicKey, secret: secret);
|
|
||||||
|
|
||||||
KeyPair masterWriter(SecretKey secret) =>
|
|
||||||
KeyPair(key: masterPublicKey, secret: secret);
|
|
||||||
|
|
||||||
TypedKey identityPublicTypedKey() =>
|
|
||||||
TypedKey(kind: identityRecordKey.kind, value: identityPublicKey);
|
|
||||||
|
|
||||||
TypedKey masterPublicTypedKey() =>
|
|
||||||
TypedKey(kind: identityRecordKey.kind, value: masterPublicKey);
|
|
||||||
|
|
||||||
Future<VeilidCryptoSystem> validateIdentitySecret(
|
|
||||||
SecretKey identitySecret) async {
|
|
||||||
final cs = await identityCrypto;
|
|
||||||
final keyOk = await cs.validateKeyPair(identityPublicKey, identitySecret);
|
|
||||||
if (!keyOk) {
|
|
||||||
throw IdentityException.invalid;
|
|
||||||
}
|
|
||||||
return cs;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<AccountRecordInfo>> readAccountsFromIdentity(
|
|
||||||
{required SecretKey identitySecret, required String accountKey}) async {
|
|
||||||
// Read the identity key to get the account keys
|
|
||||||
final pool = DHTRecordPool.instance;
|
|
||||||
|
|
||||||
final identityRecordCrypto =
|
|
||||||
await DHTRecordPool.privateCryptoFromTypedSecret(
|
|
||||||
TypedKey(kind: identityRecordKey.kind, value: identitySecret),
|
|
||||||
);
|
|
||||||
|
|
||||||
late final List<AccountRecordInfo> accountRecordInfo;
|
|
||||||
await (await pool.openRecordRead(identityRecordKey,
|
|
||||||
debugName:
|
|
||||||
'IdentityMaster::readAccountsFromIdentity::IdentityRecord',
|
|
||||||
parent: masterRecordKey,
|
|
||||||
crypto: identityRecordCrypto))
|
|
||||||
.scope((identityRec) async {
|
|
||||||
final identity = await identityRec.getJson(Identity.fromJson);
|
|
||||||
if (identity == null) {
|
|
||||||
// Identity could not be read or decrypted from DHT
|
|
||||||
throw IdentityException.readError;
|
|
||||||
}
|
|
||||||
final accountRecords = IMapOfSets.from(identity.accountRecords);
|
|
||||||
final vcAccounts = accountRecords.get(accountKey);
|
|
||||||
|
|
||||||
accountRecordInfo = vcAccounts.toList();
|
|
||||||
});
|
|
||||||
|
|
||||||
return accountRecordInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new Account associated with master identity and store it in the
|
|
||||||
/// identity key.
|
|
||||||
Future<AccountRecordInfo> addAccountToIdentity<T extends GeneratedMessage>({
|
|
||||||
required SecretKey identitySecret,
|
|
||||||
required String accountKey,
|
|
||||||
required Future<T> Function(TypedKey parent) createAccountCallback,
|
|
||||||
int maxAccounts = 1,
|
|
||||||
}) async {
|
|
||||||
final pool = DHTRecordPool.instance;
|
|
||||||
|
|
||||||
/////// Add account with profile to DHT
|
|
||||||
|
|
||||||
// Open identity key for writing
|
|
||||||
veilidLoggy.debug('Opening identity record');
|
|
||||||
return (await pool.openRecordWrite(
|
|
||||||
identityRecordKey, identityWriter(identitySecret),
|
|
||||||
debugName: 'IdentityMaster::addAccountToIdentity::IdentityRecord',
|
|
||||||
parent: masterRecordKey))
|
|
||||||
.scope((identityRec) async {
|
|
||||||
// Create new account to insert into identity
|
|
||||||
veilidLoggy.debug('Creating new account');
|
|
||||||
return (await pool.createRecord(
|
|
||||||
debugName: 'IdentityMaster::addAccountToIdentity::AccountRecord',
|
|
||||||
parent: identityRec.key))
|
|
||||||
.deleteScope((accountRec) async {
|
|
||||||
final account = await createAccountCallback(accountRec.key);
|
|
||||||
// Write account key
|
|
||||||
veilidLoggy.debug('Writing account record');
|
|
||||||
await accountRec.eventualWriteProtobuf(account);
|
|
||||||
|
|
||||||
// Update identity key to include account
|
|
||||||
final newAccountRecordInfo = AccountRecordInfo(
|
|
||||||
accountRecord: OwnedDHTRecordPointer(
|
|
||||||
recordKey: accountRec.key, owner: accountRec.ownerKeyPair!));
|
|
||||||
|
|
||||||
veilidLoggy.debug('Updating identity with new account');
|
|
||||||
await identityRec.eventualUpdateJson(Identity.fromJson,
|
|
||||||
(oldIdentity) async {
|
|
||||||
if (oldIdentity == null) {
|
|
||||||
throw IdentityException.readError;
|
|
||||||
}
|
|
||||||
final oldAccountRecords = IMapOfSets.from(oldIdentity.accountRecords);
|
|
||||||
|
|
||||||
if (oldAccountRecords.get(accountKey).length >= maxAccounts) {
|
|
||||||
throw IdentityException.limitExceeded;
|
|
||||||
}
|
|
||||||
final accountRecords =
|
|
||||||
oldAccountRecords.add(accountKey, newAccountRecordInfo).asIMap();
|
|
||||||
return oldIdentity.copyWith(accountRecords: accountRecords);
|
|
||||||
});
|
|
||||||
|
|
||||||
return newAccountRecordInfo;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identity Master with secrets
|
|
||||||
// Not freezed because we never persist this class in its entirety
|
|
||||||
class IdentityMasterWithSecrets {
|
|
||||||
IdentityMasterWithSecrets._(
|
|
||||||
{required this.identityMaster,
|
|
||||||
required this.masterSecret,
|
|
||||||
required this.identitySecret});
|
|
||||||
IdentityMaster identityMaster;
|
|
||||||
SecretKey masterSecret;
|
|
||||||
SecretKey identitySecret;
|
|
||||||
|
|
||||||
/// Delete a master identity with secrets
|
|
||||||
Future<void> delete() async => identityMaster.delete();
|
|
||||||
|
|
||||||
/// Creates a new master identity and returns it with its secrets
|
|
||||||
static Future<IdentityMasterWithSecrets> create() async {
|
|
||||||
final pool = DHTRecordPool.instance;
|
|
||||||
|
|
||||||
// IdentityMaster DHT record is public/unencrypted
|
|
||||||
veilidLoggy.debug('Creating master identity record');
|
|
||||||
return (await pool.createRecord(
|
|
||||||
debugName:
|
|
||||||
'IdentityMasterWithSecrets::create::IdentityMasterRecord',
|
|
||||||
crypto: const VeilidCryptoPublic()))
|
|
||||||
.deleteScope((masterRec) async {
|
|
||||||
veilidLoggy.debug('Creating identity record');
|
|
||||||
// Identity record is private
|
|
||||||
return (await pool.createRecord(
|
|
||||||
debugName: 'IdentityMasterWithSecrets::create::IdentityRecord',
|
|
||||||
parent: masterRec.key))
|
|
||||||
.scope((identityRec) async {
|
|
||||||
// Make IdentityMaster
|
|
||||||
final masterRecordKey = masterRec.key;
|
|
||||||
final masterOwner = masterRec.ownerKeyPair!;
|
|
||||||
final masterSigBuf = BytesBuilder()
|
|
||||||
..add(masterRecordKey.decode())
|
|
||||||
..add(masterOwner.key.decode());
|
|
||||||
|
|
||||||
final identityRecordKey = identityRec.key;
|
|
||||||
final identityOwner = identityRec.ownerKeyPair!;
|
|
||||||
final identitySigBuf = BytesBuilder()
|
|
||||||
..add(identityRecordKey.decode())
|
|
||||||
..add(identityOwner.key.decode());
|
|
||||||
|
|
||||||
assert(masterRecordKey.kind == identityRecordKey.kind,
|
|
||||||
'new master and identity should have same cryptosystem');
|
|
||||||
final crypto = await pool.veilid.getCryptoSystem(masterRecordKey.kind);
|
|
||||||
|
|
||||||
final identitySignature =
|
|
||||||
await crypto.signWithKeyPair(masterOwner, identitySigBuf.toBytes());
|
|
||||||
final masterSignature =
|
|
||||||
await crypto.signWithKeyPair(identityOwner, masterSigBuf.toBytes());
|
|
||||||
|
|
||||||
final identityMaster = IdentityMaster(
|
|
||||||
identityRecordKey: identityRecordKey,
|
|
||||||
identityPublicKey: identityOwner.key,
|
|
||||||
masterRecordKey: masterRecordKey,
|
|
||||||
masterPublicKey: masterOwner.key,
|
|
||||||
identitySignature: identitySignature,
|
|
||||||
masterSignature: masterSignature);
|
|
||||||
|
|
||||||
// Write identity master to master dht key
|
|
||||||
await masterRec.eventualWriteJson(identityMaster);
|
|
||||||
|
|
||||||
// Make empty identity
|
|
||||||
const identity = Identity(accountRecords: IMapConst({}));
|
|
||||||
|
|
||||||
// Write empty identity to identity dht key
|
|
||||||
await identityRec.eventualWriteJson(identity);
|
|
||||||
|
|
||||||
return IdentityMasterWithSecrets._(
|
|
||||||
identityMaster: identityMaster,
|
|
||||||
masterSecret: masterOwner.secret,
|
|
||||||
identitySecret: identityOwner.secret);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Opens an existing master identity and validates it
|
|
||||||
Future<IdentityMaster> openIdentityMaster(
|
|
||||||
{required TypedKey identityMasterRecordKey}) async {
|
|
||||||
final pool = DHTRecordPool.instance;
|
|
||||||
|
|
||||||
// IdentityMaster DHT record is public/unencrypted
|
|
||||||
return (await pool.openRecordRead(identityMasterRecordKey,
|
|
||||||
debugName:
|
|
||||||
'IdentityMaster::openIdentityMaster::IdentityMasterRecord'))
|
|
||||||
.deleteScope((masterRec) async {
|
|
||||||
final identityMaster = (await masterRec.getJson(IdentityMaster.fromJson,
|
|
||||||
refreshMode: DHTRecordRefreshMode.network))!;
|
|
||||||
|
|
||||||
// Validate IdentityMaster
|
|
||||||
final masterRecordKey = masterRec.key;
|
|
||||||
final masterOwnerKey = masterRec.owner;
|
|
||||||
final masterSigBuf = BytesBuilder()
|
|
||||||
..add(masterRecordKey.decode())
|
|
||||||
..add(masterOwnerKey.decode());
|
|
||||||
final masterSignature = identityMaster.masterSignature;
|
|
||||||
|
|
||||||
final identityRecordKey = identityMaster.identityRecordKey;
|
|
||||||
final identityOwnerKey = identityMaster.identityPublicKey;
|
|
||||||
final identitySigBuf = BytesBuilder()
|
|
||||||
..add(identityRecordKey.decode())
|
|
||||||
..add(identityOwnerKey.decode());
|
|
||||||
final identitySignature = identityMaster.identitySignature;
|
|
||||||
|
|
||||||
assert(masterRecordKey.kind == identityRecordKey.kind,
|
|
||||||
'new master and identity should have same cryptosystem');
|
|
||||||
final crypto = await pool.veilid.getCryptoSystem(masterRecordKey.kind);
|
|
||||||
|
|
||||||
await crypto.verify(
|
|
||||||
masterOwnerKey, identitySigBuf.toBytes(), identitySignature);
|
|
||||||
await crypto.verify(
|
|
||||||
identityOwnerKey, masterSigBuf.toBytes(), masterSignature);
|
|
||||||
|
|
||||||
return identityMaster;
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,579 +0,0 @@
|
|||||||
// coverage:ignore-file
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
|
||||||
|
|
||||||
part of 'identity.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// FreezedGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
T _$identity<T>(T value) => value;
|
|
||||||
|
|
||||||
final _privateConstructorUsedError = UnsupportedError(
|
|
||||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
|
||||||
|
|
||||||
AccountRecordInfo _$AccountRecordInfoFromJson(Map<String, dynamic> json) {
|
|
||||||
return _AccountRecordInfo.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$AccountRecordInfo {
|
|
||||||
// Top level account keys and secrets
|
|
||||||
OwnedDHTRecordPointer get accountRecord => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
$AccountRecordInfoCopyWith<AccountRecordInfo> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $AccountRecordInfoCopyWith<$Res> {
|
|
||||||
factory $AccountRecordInfoCopyWith(
|
|
||||||
AccountRecordInfo value, $Res Function(AccountRecordInfo) then) =
|
|
||||||
_$AccountRecordInfoCopyWithImpl<$Res, AccountRecordInfo>;
|
|
||||||
@useResult
|
|
||||||
$Res call({OwnedDHTRecordPointer accountRecord});
|
|
||||||
|
|
||||||
$OwnedDHTRecordPointerCopyWith<$Res> get accountRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$AccountRecordInfoCopyWithImpl<$Res, $Val extends AccountRecordInfo>
|
|
||||||
implements $AccountRecordInfoCopyWith<$Res> {
|
|
||||||
_$AccountRecordInfoCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? accountRecord = null,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
accountRecord: null == accountRecord
|
|
||||||
? _value.accountRecord
|
|
||||||
: accountRecord // ignore: cast_nullable_to_non_nullable
|
|
||||||
as OwnedDHTRecordPointer,
|
|
||||||
) as $Val);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$OwnedDHTRecordPointerCopyWith<$Res> get accountRecord {
|
|
||||||
return $OwnedDHTRecordPointerCopyWith<$Res>(_value.accountRecord, (value) {
|
|
||||||
return _then(_value.copyWith(accountRecord: value) as $Val);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$AccountRecordInfoImplCopyWith<$Res>
|
|
||||||
implements $AccountRecordInfoCopyWith<$Res> {
|
|
||||||
factory _$$AccountRecordInfoImplCopyWith(_$AccountRecordInfoImpl value,
|
|
||||||
$Res Function(_$AccountRecordInfoImpl) then) =
|
|
||||||
__$$AccountRecordInfoImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({OwnedDHTRecordPointer accountRecord});
|
|
||||||
|
|
||||||
@override
|
|
||||||
$OwnedDHTRecordPointerCopyWith<$Res> get accountRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$AccountRecordInfoImplCopyWithImpl<$Res>
|
|
||||||
extends _$AccountRecordInfoCopyWithImpl<$Res, _$AccountRecordInfoImpl>
|
|
||||||
implements _$$AccountRecordInfoImplCopyWith<$Res> {
|
|
||||||
__$$AccountRecordInfoImplCopyWithImpl(_$AccountRecordInfoImpl _value,
|
|
||||||
$Res Function(_$AccountRecordInfoImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? accountRecord = null,
|
|
||||||
}) {
|
|
||||||
return _then(_$AccountRecordInfoImpl(
|
|
||||||
accountRecord: null == accountRecord
|
|
||||||
? _value.accountRecord
|
|
||||||
: accountRecord // ignore: cast_nullable_to_non_nullable
|
|
||||||
as OwnedDHTRecordPointer,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _$AccountRecordInfoImpl implements _AccountRecordInfo {
|
|
||||||
const _$AccountRecordInfoImpl({required this.accountRecord});
|
|
||||||
|
|
||||||
factory _$AccountRecordInfoImpl.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$$AccountRecordInfoImplFromJson(json);
|
|
||||||
|
|
||||||
// Top level account keys and secrets
|
|
||||||
@override
|
|
||||||
final OwnedDHTRecordPointer accountRecord;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'AccountRecordInfo(accountRecord: $accountRecord)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$AccountRecordInfoImpl &&
|
|
||||||
(identical(other.accountRecord, accountRecord) ||
|
|
||||||
other.accountRecord == accountRecord));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, accountRecord);
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$AccountRecordInfoImplCopyWith<_$AccountRecordInfoImpl> get copyWith =>
|
|
||||||
__$$AccountRecordInfoImplCopyWithImpl<_$AccountRecordInfoImpl>(
|
|
||||||
this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$$AccountRecordInfoImplToJson(
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _AccountRecordInfo implements AccountRecordInfo {
|
|
||||||
const factory _AccountRecordInfo(
|
|
||||||
{required final OwnedDHTRecordPointer accountRecord}) =
|
|
||||||
_$AccountRecordInfoImpl;
|
|
||||||
|
|
||||||
factory _AccountRecordInfo.fromJson(Map<String, dynamic> json) =
|
|
||||||
_$AccountRecordInfoImpl.fromJson;
|
|
||||||
|
|
||||||
@override // Top level account keys and secrets
|
|
||||||
OwnedDHTRecordPointer get accountRecord;
|
|
||||||
@override
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
_$$AccountRecordInfoImplCopyWith<_$AccountRecordInfoImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
Identity _$IdentityFromJson(Map<String, dynamic> json) {
|
|
||||||
return _Identity.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$Identity {
|
|
||||||
// Top level account keys and secrets
|
|
||||||
IMap<String, ISet<AccountRecordInfo>> get accountRecords =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
$IdentityCopyWith<Identity> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $IdentityCopyWith<$Res> {
|
|
||||||
factory $IdentityCopyWith(Identity value, $Res Function(Identity) then) =
|
|
||||||
_$IdentityCopyWithImpl<$Res, Identity>;
|
|
||||||
@useResult
|
|
||||||
$Res call({IMap<String, ISet<AccountRecordInfo>> accountRecords});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$IdentityCopyWithImpl<$Res, $Val extends Identity>
|
|
||||||
implements $IdentityCopyWith<$Res> {
|
|
||||||
_$IdentityCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? accountRecords = null,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
accountRecords: null == accountRecords
|
|
||||||
? _value.accountRecords
|
|
||||||
: accountRecords // ignore: cast_nullable_to_non_nullable
|
|
||||||
as IMap<String, ISet<AccountRecordInfo>>,
|
|
||||||
) as $Val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$IdentityImplCopyWith<$Res>
|
|
||||||
implements $IdentityCopyWith<$Res> {
|
|
||||||
factory _$$IdentityImplCopyWith(
|
|
||||||
_$IdentityImpl value, $Res Function(_$IdentityImpl) then) =
|
|
||||||
__$$IdentityImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({IMap<String, ISet<AccountRecordInfo>> accountRecords});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$IdentityImplCopyWithImpl<$Res>
|
|
||||||
extends _$IdentityCopyWithImpl<$Res, _$IdentityImpl>
|
|
||||||
implements _$$IdentityImplCopyWith<$Res> {
|
|
||||||
__$$IdentityImplCopyWithImpl(
|
|
||||||
_$IdentityImpl _value, $Res Function(_$IdentityImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? accountRecords = null,
|
|
||||||
}) {
|
|
||||||
return _then(_$IdentityImpl(
|
|
||||||
accountRecords: null == accountRecords
|
|
||||||
? _value.accountRecords
|
|
||||||
: accountRecords // ignore: cast_nullable_to_non_nullable
|
|
||||||
as IMap<String, ISet<AccountRecordInfo>>,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _$IdentityImpl implements _Identity {
|
|
||||||
const _$IdentityImpl({required this.accountRecords});
|
|
||||||
|
|
||||||
factory _$IdentityImpl.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$$IdentityImplFromJson(json);
|
|
||||||
|
|
||||||
// Top level account keys and secrets
|
|
||||||
@override
|
|
||||||
final IMap<String, ISet<AccountRecordInfo>> accountRecords;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'Identity(accountRecords: $accountRecords)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$IdentityImpl &&
|
|
||||||
(identical(other.accountRecords, accountRecords) ||
|
|
||||||
other.accountRecords == accountRecords));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, accountRecords);
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$IdentityImplCopyWith<_$IdentityImpl> get copyWith =>
|
|
||||||
__$$IdentityImplCopyWithImpl<_$IdentityImpl>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$$IdentityImplToJson(
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _Identity implements Identity {
|
|
||||||
const factory _Identity(
|
|
||||||
{required final IMap<String, ISet<AccountRecordInfo>>
|
|
||||||
accountRecords}) = _$IdentityImpl;
|
|
||||||
|
|
||||||
factory _Identity.fromJson(Map<String, dynamic> json) =
|
|
||||||
_$IdentityImpl.fromJson;
|
|
||||||
|
|
||||||
@override // Top level account keys and secrets
|
|
||||||
IMap<String, ISet<AccountRecordInfo>> get accountRecords;
|
|
||||||
@override
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
_$$IdentityImplCopyWith<_$IdentityImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityMaster _$IdentityMasterFromJson(Map<String, dynamic> json) {
|
|
||||||
return _IdentityMaster.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$IdentityMaster {
|
|
||||||
// Private DHT record storing identity account mapping
|
|
||||||
Typed<FixedEncodedString43> get identityRecordKey =>
|
|
||||||
throw _privateConstructorUsedError; // Public key of identity
|
|
||||||
FixedEncodedString43 get identityPublicKey =>
|
|
||||||
throw _privateConstructorUsedError; // Public DHT record storing this structure for account recovery
|
|
||||||
Typed<FixedEncodedString43> get masterRecordKey =>
|
|
||||||
throw _privateConstructorUsedError; // Public key of master identity used to sign identity keys for recovery
|
|
||||||
FixedEncodedString43 get masterPublicKey =>
|
|
||||||
throw _privateConstructorUsedError; // Signature of identityRecordKey and identityPublicKey by masterPublicKey
|
|
||||||
FixedEncodedString86 get identitySignature =>
|
|
||||||
throw _privateConstructorUsedError; // Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
|
||||||
FixedEncodedString86 get masterSignature =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
$IdentityMasterCopyWith<IdentityMaster> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $IdentityMasterCopyWith<$Res> {
|
|
||||||
factory $IdentityMasterCopyWith(
|
|
||||||
IdentityMaster value, $Res Function(IdentityMaster) then) =
|
|
||||||
_$IdentityMasterCopyWithImpl<$Res, IdentityMaster>;
|
|
||||||
@useResult
|
|
||||||
$Res call(
|
|
||||||
{Typed<FixedEncodedString43> identityRecordKey,
|
|
||||||
FixedEncodedString43 identityPublicKey,
|
|
||||||
Typed<FixedEncodedString43> masterRecordKey,
|
|
||||||
FixedEncodedString43 masterPublicKey,
|
|
||||||
FixedEncodedString86 identitySignature,
|
|
||||||
FixedEncodedString86 masterSignature});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$IdentityMasterCopyWithImpl<$Res, $Val extends IdentityMaster>
|
|
||||||
implements $IdentityMasterCopyWith<$Res> {
|
|
||||||
_$IdentityMasterCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? identityRecordKey = null,
|
|
||||||
Object? identityPublicKey = null,
|
|
||||||
Object? masterRecordKey = null,
|
|
||||||
Object? masterPublicKey = null,
|
|
||||||
Object? identitySignature = null,
|
|
||||||
Object? masterSignature = null,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
identityRecordKey: null == identityRecordKey
|
|
||||||
? _value.identityRecordKey
|
|
||||||
: identityRecordKey // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Typed<FixedEncodedString43>,
|
|
||||||
identityPublicKey: null == identityPublicKey
|
|
||||||
? _value.identityPublicKey
|
|
||||||
: identityPublicKey // ignore: cast_nullable_to_non_nullable
|
|
||||||
as FixedEncodedString43,
|
|
||||||
masterRecordKey: null == masterRecordKey
|
|
||||||
? _value.masterRecordKey
|
|
||||||
: masterRecordKey // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Typed<FixedEncodedString43>,
|
|
||||||
masterPublicKey: null == masterPublicKey
|
|
||||||
? _value.masterPublicKey
|
|
||||||
: masterPublicKey // ignore: cast_nullable_to_non_nullable
|
|
||||||
as FixedEncodedString43,
|
|
||||||
identitySignature: null == identitySignature
|
|
||||||
? _value.identitySignature
|
|
||||||
: identitySignature // ignore: cast_nullable_to_non_nullable
|
|
||||||
as FixedEncodedString86,
|
|
||||||
masterSignature: null == masterSignature
|
|
||||||
? _value.masterSignature
|
|
||||||
: masterSignature // ignore: cast_nullable_to_non_nullable
|
|
||||||
as FixedEncodedString86,
|
|
||||||
) as $Val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$IdentityMasterImplCopyWith<$Res>
|
|
||||||
implements $IdentityMasterCopyWith<$Res> {
|
|
||||||
factory _$$IdentityMasterImplCopyWith(_$IdentityMasterImpl value,
|
|
||||||
$Res Function(_$IdentityMasterImpl) then) =
|
|
||||||
__$$IdentityMasterImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call(
|
|
||||||
{Typed<FixedEncodedString43> identityRecordKey,
|
|
||||||
FixedEncodedString43 identityPublicKey,
|
|
||||||
Typed<FixedEncodedString43> masterRecordKey,
|
|
||||||
FixedEncodedString43 masterPublicKey,
|
|
||||||
FixedEncodedString86 identitySignature,
|
|
||||||
FixedEncodedString86 masterSignature});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$IdentityMasterImplCopyWithImpl<$Res>
|
|
||||||
extends _$IdentityMasterCopyWithImpl<$Res, _$IdentityMasterImpl>
|
|
||||||
implements _$$IdentityMasterImplCopyWith<$Res> {
|
|
||||||
__$$IdentityMasterImplCopyWithImpl(
|
|
||||||
_$IdentityMasterImpl _value, $Res Function(_$IdentityMasterImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? identityRecordKey = null,
|
|
||||||
Object? identityPublicKey = null,
|
|
||||||
Object? masterRecordKey = null,
|
|
||||||
Object? masterPublicKey = null,
|
|
||||||
Object? identitySignature = null,
|
|
||||||
Object? masterSignature = null,
|
|
||||||
}) {
|
|
||||||
return _then(_$IdentityMasterImpl(
|
|
||||||
identityRecordKey: null == identityRecordKey
|
|
||||||
? _value.identityRecordKey
|
|
||||||
: identityRecordKey // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Typed<FixedEncodedString43>,
|
|
||||||
identityPublicKey: null == identityPublicKey
|
|
||||||
? _value.identityPublicKey
|
|
||||||
: identityPublicKey // ignore: cast_nullable_to_non_nullable
|
|
||||||
as FixedEncodedString43,
|
|
||||||
masterRecordKey: null == masterRecordKey
|
|
||||||
? _value.masterRecordKey
|
|
||||||
: masterRecordKey // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Typed<FixedEncodedString43>,
|
|
||||||
masterPublicKey: null == masterPublicKey
|
|
||||||
? _value.masterPublicKey
|
|
||||||
: masterPublicKey // ignore: cast_nullable_to_non_nullable
|
|
||||||
as FixedEncodedString43,
|
|
||||||
identitySignature: null == identitySignature
|
|
||||||
? _value.identitySignature
|
|
||||||
: identitySignature // ignore: cast_nullable_to_non_nullable
|
|
||||||
as FixedEncodedString86,
|
|
||||||
masterSignature: null == masterSignature
|
|
||||||
? _value.masterSignature
|
|
||||||
: masterSignature // ignore: cast_nullable_to_non_nullable
|
|
||||||
as FixedEncodedString86,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _$IdentityMasterImpl implements _IdentityMaster {
|
|
||||||
const _$IdentityMasterImpl(
|
|
||||||
{required this.identityRecordKey,
|
|
||||||
required this.identityPublicKey,
|
|
||||||
required this.masterRecordKey,
|
|
||||||
required this.masterPublicKey,
|
|
||||||
required this.identitySignature,
|
|
||||||
required this.masterSignature});
|
|
||||||
|
|
||||||
factory _$IdentityMasterImpl.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$$IdentityMasterImplFromJson(json);
|
|
||||||
|
|
||||||
// Private DHT record storing identity account mapping
|
|
||||||
@override
|
|
||||||
final Typed<FixedEncodedString43> identityRecordKey;
|
|
||||||
// Public key of identity
|
|
||||||
@override
|
|
||||||
final FixedEncodedString43 identityPublicKey;
|
|
||||||
// Public DHT record storing this structure for account recovery
|
|
||||||
@override
|
|
||||||
final Typed<FixedEncodedString43> masterRecordKey;
|
|
||||||
// Public key of master identity used to sign identity keys for recovery
|
|
||||||
@override
|
|
||||||
final FixedEncodedString43 masterPublicKey;
|
|
||||||
// Signature of identityRecordKey and identityPublicKey by masterPublicKey
|
|
||||||
@override
|
|
||||||
final FixedEncodedString86 identitySignature;
|
|
||||||
// Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
|
||||||
@override
|
|
||||||
final FixedEncodedString86 masterSignature;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'IdentityMaster(identityRecordKey: $identityRecordKey, identityPublicKey: $identityPublicKey, masterRecordKey: $masterRecordKey, masterPublicKey: $masterPublicKey, identitySignature: $identitySignature, masterSignature: $masterSignature)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$IdentityMasterImpl &&
|
|
||||||
(identical(other.identityRecordKey, identityRecordKey) ||
|
|
||||||
other.identityRecordKey == identityRecordKey) &&
|
|
||||||
(identical(other.identityPublicKey, identityPublicKey) ||
|
|
||||||
other.identityPublicKey == identityPublicKey) &&
|
|
||||||
(identical(other.masterRecordKey, masterRecordKey) ||
|
|
||||||
other.masterRecordKey == masterRecordKey) &&
|
|
||||||
(identical(other.masterPublicKey, masterPublicKey) ||
|
|
||||||
other.masterPublicKey == masterPublicKey) &&
|
|
||||||
(identical(other.identitySignature, identitySignature) ||
|
|
||||||
other.identitySignature == identitySignature) &&
|
|
||||||
(identical(other.masterSignature, masterSignature) ||
|
|
||||||
other.masterSignature == masterSignature));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
identityRecordKey,
|
|
||||||
identityPublicKey,
|
|
||||||
masterRecordKey,
|
|
||||||
masterPublicKey,
|
|
||||||
identitySignature,
|
|
||||||
masterSignature);
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$IdentityMasterImplCopyWith<_$IdentityMasterImpl> get copyWith =>
|
|
||||||
__$$IdentityMasterImplCopyWithImpl<_$IdentityMasterImpl>(
|
|
||||||
this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$$IdentityMasterImplToJson(
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _IdentityMaster implements IdentityMaster {
|
|
||||||
const factory _IdentityMaster(
|
|
||||||
{required final Typed<FixedEncodedString43> identityRecordKey,
|
|
||||||
required final FixedEncodedString43 identityPublicKey,
|
|
||||||
required final Typed<FixedEncodedString43> masterRecordKey,
|
|
||||||
required final FixedEncodedString43 masterPublicKey,
|
|
||||||
required final FixedEncodedString86 identitySignature,
|
|
||||||
required final FixedEncodedString86 masterSignature}) =
|
|
||||||
_$IdentityMasterImpl;
|
|
||||||
|
|
||||||
factory _IdentityMaster.fromJson(Map<String, dynamic> json) =
|
|
||||||
_$IdentityMasterImpl.fromJson;
|
|
||||||
|
|
||||||
@override // Private DHT record storing identity account mapping
|
|
||||||
Typed<FixedEncodedString43> get identityRecordKey;
|
|
||||||
@override // Public key of identity
|
|
||||||
FixedEncodedString43 get identityPublicKey;
|
|
||||||
@override // Public DHT record storing this structure for account recovery
|
|
||||||
Typed<FixedEncodedString43> get masterRecordKey;
|
|
||||||
@override // Public key of master identity used to sign identity keys for recovery
|
|
||||||
FixedEncodedString43 get masterPublicKey;
|
|
||||||
@override // Signature of identityRecordKey and identityPublicKey by masterPublicKey
|
|
||||||
FixedEncodedString86 get identitySignature;
|
|
||||||
@override // Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
|
||||||
FixedEncodedString86 get masterSignature;
|
|
||||||
@override
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
_$$IdentityMasterImplCopyWith<_$IdentityMasterImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'identity.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
_$AccountRecordInfoImpl _$$AccountRecordInfoImplFromJson(
|
|
||||||
Map<String, dynamic> json) =>
|
|
||||||
_$AccountRecordInfoImpl(
|
|
||||||
accountRecord: OwnedDHTRecordPointer.fromJson(json['account_record']),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$AccountRecordInfoImplToJson(
|
|
||||||
_$AccountRecordInfoImpl instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'account_record': instance.accountRecord.toJson(),
|
|
||||||
};
|
|
||||||
|
|
||||||
_$IdentityImpl _$$IdentityImplFromJson(Map<String, dynamic> json) =>
|
|
||||||
_$IdentityImpl(
|
|
||||||
accountRecords: IMap<String, ISet<AccountRecordInfo>>.fromJson(
|
|
||||||
json['account_records'] as Map<String, dynamic>,
|
|
||||||
(value) => value as String,
|
|
||||||
(value) => ISet<AccountRecordInfo>.fromJson(
|
|
||||||
value, (value) => AccountRecordInfo.fromJson(value))),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$IdentityImplToJson(_$IdentityImpl instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'account_records': instance.accountRecords.toJson(
|
|
||||||
(value) => value,
|
|
||||||
(value) => value.toJson(
|
|
||||||
(value) => value.toJson(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
_$IdentityMasterImpl _$$IdentityMasterImplFromJson(Map<String, dynamic> json) =>
|
|
||||||
_$IdentityMasterImpl(
|
|
||||||
identityRecordKey:
|
|
||||||
Typed<FixedEncodedString43>.fromJson(json['identity_record_key']),
|
|
||||||
identityPublicKey:
|
|
||||||
FixedEncodedString43.fromJson(json['identity_public_key']),
|
|
||||||
masterRecordKey:
|
|
||||||
Typed<FixedEncodedString43>.fromJson(json['master_record_key']),
|
|
||||||
masterPublicKey: FixedEncodedString43.fromJson(json['master_public_key']),
|
|
||||||
identitySignature:
|
|
||||||
FixedEncodedString86.fromJson(json['identity_signature']),
|
|
||||||
masterSignature: FixedEncodedString86.fromJson(json['master_signature']),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$IdentityMasterImplToJson(
|
|
||||||
_$IdentityMasterImpl instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'identity_record_key': instance.identityRecordKey.toJson(),
|
|
||||||
'identity_public_key': instance.identityPublicKey.toJson(),
|
|
||||||
'master_record_key': instance.masterRecordKey.toJson(),
|
|
||||||
'master_public_key': instance.masterPublicKey.toJson(),
|
|
||||||
'identity_signature': instance.identitySignature.toJson(),
|
|
||||||
'master_signature': instance.masterSignature.toJson(),
|
|
||||||
};
|
|
@ -6,8 +6,8 @@ library veilid_support;
|
|||||||
export 'package:veilid/veilid.dart';
|
export 'package:veilid/veilid.dart';
|
||||||
|
|
||||||
export 'dht_support/dht_support.dart';
|
export 'dht_support/dht_support.dart';
|
||||||
|
export 'identity_support/identity_support.dart';
|
||||||
export 'src/config.dart';
|
export 'src/config.dart';
|
||||||
export 'src/identity.dart';
|
|
||||||
export 'src/json_tools.dart';
|
export 'src/json_tools.dart';
|
||||||
export 'src/memory_tools.dart';
|
export 'src/memory_tools.dart';
|
||||||
export 'src/online_element_state.dart';
|
export 'src/online_element_state.dart';
|
||||||
|
Loading…
Reference in New Issue
Block a user