feat(base): SimpleLogin accounts' keys without SRP, signing up in one call (ADR 0020, step 3)
An account that signs in with SimpleLogin keeps its keys under a password that signs nobody in (D19): `account_password` keeps the password's own proof key (Ed25519, `passwordProofKey`) in place of SRP's verifier, which becomes nullable. The migration adds the column and drops the NOT NULL, nothing else. - `simpleLogin.signUp(attempt, handle, setup)`: someone new whose identity waits under the attempt gets, in one transaction, an account (identified), the link, a profile with the handle and the keys; then a session (D18). Refused, the identity still waiting, for a setup out of range and a handle refused as saving a profile refuses it. - `simpleLogin.checkHandle(attempt, handle)`: whether someone new could take a handle, at most 30 an hour per attempt and 60 per network. - `simpleLogin.complete` says, for an account, whether it has its keys and its handle, and keeps the sign-in ten minutes under its attempt, the account as its caller: D20's confirmation for the changes to come. - `keys.begin`, `keys.unlock`: the keys for a signed-in device once the password is proven with its proof key, for a nonce, bound to the base URL the device addressed; wrong proofs counted per session as ADR 0017's. - An authenticator is enrolled only for a password that signs in. - Members ask the register for all of it; `RemoteRegister` and the instance API carry the four new operations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
c6831b0b7b
commit
090d901e92
63 changed files with 46254 additions and 78 deletions
|
|
@ -20,6 +20,7 @@ abstract class SignInWays
|
|||
required this.email,
|
||||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -28,6 +29,7 @@ abstract class SignInWays
|
|||
this.accountResetAt,
|
||||
}) : simpleLogin = simpleLogin ?? false,
|
||||
password = password ?? false,
|
||||
keys = keys ?? false,
|
||||
authenticator = authenticator ?? false,
|
||||
passwordOnly = passwordOnly ?? false,
|
||||
secondFactorWorks = secondFactorWorks ?? false;
|
||||
|
|
@ -37,6 +39,7 @@ abstract class SignInWays
|
|||
required bool email,
|
||||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -55,6 +58,9 @@ abstract class SignInWays
|
|||
password: jsonSerialization['password'] == null
|
||||
? null
|
||||
: _isc.BoolJsonExtension.fromJson(jsonSerialization['password']),
|
||||
keys: jsonSerialization['keys'] == null
|
||||
? null
|
||||
: _isc.BoolJsonExtension.fromJson(jsonSerialization['keys']),
|
||||
authenticator: jsonSerialization['authenticator'] == null
|
||||
? null
|
||||
: _isc.BoolJsonExtension.fromJson(jsonSerialization['authenticator']),
|
||||
|
|
@ -93,9 +99,14 @@ abstract class SignInWays
|
|||
/// instance's SimpleLogin app.
|
||||
bool simpleLogin;
|
||||
|
||||
/// A password (ADR 0017), which an address needs for resets.
|
||||
/// A password that signs in (ADR 0017, SRP's), which an address needs
|
||||
/// for resets.
|
||||
bool password;
|
||||
|
||||
/// The account's keys, under a password that signs in (ADR 0017) or one
|
||||
/// that only opens them, as for SimpleLogin (ADR 0020, D19).
|
||||
bool keys;
|
||||
|
||||
/// A confirmed authenticator: the password signs in only with one.
|
||||
bool authenticator;
|
||||
|
||||
|
|
@ -130,6 +141,7 @@ abstract class SignInWays
|
|||
bool? email,
|
||||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -145,6 +157,7 @@ abstract class SignInWays
|
|||
'email': email,
|
||||
'simpleLogin': simpleLogin,
|
||||
'password': password,
|
||||
'keys': keys,
|
||||
'authenticator': authenticator,
|
||||
'passwordOnly': passwordOnly,
|
||||
'secondFactorWorks': secondFactorWorks,
|
||||
|
|
@ -164,6 +177,7 @@ abstract class SignInWays
|
|||
'email': email,
|
||||
'simpleLogin': simpleLogin,
|
||||
'password': password,
|
||||
'keys': keys,
|
||||
'authenticator': authenticator,
|
||||
'passwordOnly': passwordOnly,
|
||||
'secondFactorWorks': secondFactorWorks,
|
||||
|
|
@ -189,6 +203,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
required bool email,
|
||||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -200,6 +215,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
email: email,
|
||||
simpleLogin: simpleLogin,
|
||||
password: password,
|
||||
keys: keys,
|
||||
authenticator: authenticator,
|
||||
passwordOnly: passwordOnly,
|
||||
secondFactorWorks: secondFactorWorks,
|
||||
|
|
@ -217,6 +233,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
bool? email,
|
||||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -229,6 +246,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
email: email ?? this.email,
|
||||
simpleLogin: simpleLogin ?? this.simpleLogin,
|
||||
password: password ?? this.password,
|
||||
keys: keys ?? this.keys,
|
||||
authenticator: authenticator ?? this.authenticator,
|
||||
passwordOnly: passwordOnly ?? this.passwordOnly,
|
||||
secondFactorWorks: secondFactorWorks ?? this.secondFactorWorks,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,14 @@ import 'package:halkyon_base_client/src/protocol/auth/sign_in_ways.dart'
|
|||
as _ifld8s4r;
|
||||
import 'package:halkyon_base_client/src/protocol/instance/instance_basics.dart'
|
||||
as _isnyur2i;
|
||||
import 'package:halkyon_base_client/src/protocol/keys/device_keys.dart'
|
||||
as _invv3u3l;
|
||||
import 'package:halkyon_base_client/src/protocol/keys/keys_challenge.dart'
|
||||
as _ivkzjgkp;
|
||||
import 'package:halkyon_base_client/src/protocol/keys/keys_proof.dart'
|
||||
as _iznbn55x;
|
||||
import 'package:halkyon_base_client/src/protocol/keys/keys_setup.dart'
|
||||
as _iec2fqr0;
|
||||
import 'package:halkyon_base_client/src/protocol/password/authenticator.dart'
|
||||
as _ivfmflch;
|
||||
import 'package:halkyon_base_client/src/protocol/password/authenticator_enrolment.dart'
|
||||
|
|
@ -222,6 +230,43 @@ class EndpointInstance extends _isc.EndpointRef {
|
|||
);
|
||||
}
|
||||
|
||||
/// The keys of an account that signs in with SimpleLogin (Halkyon Learn's
|
||||
/// ADR 0020, 7 and D19), for its signed-in devices: the password proven
|
||||
/// with its own proof key, which the instance's register checks (ADR 0016).
|
||||
/// Refusals are `PasswordRefusedException`s.
|
||||
/// {@category Endpoint}
|
||||
class EndpointKeys extends _isc.EndpointRef {
|
||||
EndpointKeys(_isc.EndpointCaller caller) : super(caller);
|
||||
|
||||
@override
|
||||
String get name => 'halkyon_base.keys';
|
||||
|
||||
/// The first step of proving your password: the salt and costs your
|
||||
/// device derives the password's keys with, and a nonce to sign with its
|
||||
/// proof key, within two minutes, once. Refused for an account without
|
||||
/// such keys (`PasswordRefusal.noPassword`).
|
||||
_ida.Future<_ivkzjgkp.KeysChallenge> begin() =>
|
||||
caller.callServerEndpoint<_ivkzjgkp.KeysChallenge>(
|
||||
'halkyon_base.keys',
|
||||
'begin',
|
||||
{},
|
||||
);
|
||||
|
||||
/// Your keys for this device, once [proof] proved your password: your
|
||||
/// public keys and your private keys wrapped under the password's key,
|
||||
/// which the device opens. The proof is bound to the base URL the device
|
||||
/// addressed here, and counts against this session: the tenth wrong in a
|
||||
/// row ends it (`PasswordRefusal.sessionEnded`), and you are warned.
|
||||
/// Refused for a wrong proof (`PasswordRefusal.wrongCredentials`) and a
|
||||
/// challenge unknown, used or too old (`PasswordRefusal.challengeExpired`).
|
||||
_ida.Future<_invv3u3l.DeviceKeys> unlock(_iznbn55x.KeysProof proof) =>
|
||||
caller.callServerEndpoint<_invv3u3l.DeviceKeys>(
|
||||
'halkyon_base.keys',
|
||||
'unlock',
|
||||
{'proof': proof},
|
||||
);
|
||||
}
|
||||
|
||||
/// Getting back into an account (ADR 0017): a lost authenticator, a lost
|
||||
/// password with the recovery phrase, a lost password without it. Called
|
||||
/// without a session, as the person can't sign in. The instance's register
|
||||
|
|
@ -761,10 +806,14 @@ class EndpointSimpleLogin extends _isc.EndpointRef {
|
|||
|
||||
/// Ends [attempt] with SimpleLogin's [code], as the caller began it:
|
||||
/// signed out, a session for the account with that identity, or for
|
||||
/// someone new the handle suggested (their account comes next); signed
|
||||
/// in, SimpleLogin added to the caller's account. Serverpod issues no
|
||||
/// session for another person inside a signed-in call, and nobody is
|
||||
/// switched to another account. Refusals as `completeSimpleLogin`'s.
|
||||
/// someone new the handle suggested (their account comes with [signUp]);
|
||||
/// signed in, SimpleLogin added to the caller's account. Serverpod issues
|
||||
/// no session for another person inside a signed-in call, and nobody is
|
||||
/// switched to another account. For an account, whether it has its keys
|
||||
/// (`keys.unlock` opens them; one without makes them) and its handle; and
|
||||
/// for ten minutes the attempt's ID stands for this sign-in, for a change
|
||||
/// the device asks for (ADR 0020, D20). Refusals as
|
||||
/// `completeSimpleLogin`'s.
|
||||
_ida.Future<_ianfx7qj.SimpleLoginCompleted> complete({
|
||||
required _isc.UuidValue attempt,
|
||||
required String code,
|
||||
|
|
@ -776,6 +825,48 @@ class EndpointSimpleLogin extends _isc.EndpointRef {
|
|||
'code': code,
|
||||
},
|
||||
);
|
||||
|
||||
/// Whether someone new, whose identity waits under [attempt] ([complete]
|
||||
/// said `newcomer`), could take [handle]: null where they could, else why
|
||||
/// not, as saving a profile would say. For the app to say so while it is
|
||||
/// typed; [signUp] checks again. At most 30 for an attempt and 60 from a
|
||||
/// network in an hour (`ProfileRefusal.tooManyHandleChecks`, thrown):
|
||||
/// each answer tells which handles exist. Refused
|
||||
/// (`OidcRefusal.unknownAttempt`) where no identity waits.
|
||||
_ida.Future<_ih722tf9.ProfileRefusal?> checkHandle({
|
||||
required _isc.UuidValue attempt,
|
||||
required String handle,
|
||||
}) => caller.callServerEndpoint<_ih722tf9.ProfileRefusal?>(
|
||||
'halkyon_base.simpleLogin',
|
||||
'checkHandle',
|
||||
{
|
||||
'attempt': attempt,
|
||||
'handle': handle,
|
||||
},
|
||||
);
|
||||
|
||||
/// Signs up someone new, whose identity waits under [attempt] for half an
|
||||
/// hour after [complete] said `newcomer` (ADR 0020, D16 and D18): an
|
||||
/// account with [handle], private until its person goes public, and the
|
||||
/// keys [setup] describes, all made on the device beforehand; a session
|
||||
/// for it here. Refused, the identity still waiting: for a setup out of
|
||||
/// range (`PasswordRefusal.invalidSetup`) and a handle refused
|
||||
/// (`ProfileRefusedException`, as saving a profile refuses it); for an
|
||||
/// attempt unknown, taken or past its time (`OidcRefusal.unknownAttempt`),
|
||||
/// and called signed in (`OidcRefusal.wrongCaller`).
|
||||
_ida.Future<_iacc.AuthSuccess> signUp({
|
||||
required _isc.UuidValue attempt,
|
||||
required String handle,
|
||||
required _iec2fqr0.KeysSetup setup,
|
||||
}) => caller.callServerEndpoint<_iacc.AuthSuccess>(
|
||||
'halkyon_base.simpleLogin',
|
||||
'signUp',
|
||||
{
|
||||
'attempt': attempt,
|
||||
'handle': handle,
|
||||
'setup': setup,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class Caller extends _isc.ModuleEndpointCaller {
|
||||
|
|
@ -786,6 +877,7 @@ class Caller extends _isc.ModuleEndpointCaller {
|
|||
gitHubSignIn = EndpointGitHubSignIn(this);
|
||||
gitHubUpgrade = EndpointGitHubUpgrade(this);
|
||||
instance = EndpointInstance(this);
|
||||
keys = EndpointKeys(this);
|
||||
accountRecovery = EndpointAccountRecovery(this);
|
||||
authenticator = EndpointAuthenticator(this);
|
||||
password = EndpointPassword(this);
|
||||
|
|
@ -807,6 +899,8 @@ class Caller extends _isc.ModuleEndpointCaller {
|
|||
|
||||
late final EndpointInstance instance;
|
||||
|
||||
late final EndpointKeys keys;
|
||||
|
||||
late final EndpointAccountRecovery accountRecovery;
|
||||
|
||||
late final EndpointAuthenticator authenticator;
|
||||
|
|
@ -829,6 +923,7 @@ class Caller extends _isc.ModuleEndpointCaller {
|
|||
'halkyon_base.gitHubSignIn': gitHubSignIn,
|
||||
'halkyon_base.gitHubUpgrade': gitHubUpgrade,
|
||||
'halkyon_base.instance': instance,
|
||||
'halkyon_base.keys': keys,
|
||||
'halkyon_base.accountRecovery': accountRecovery,
|
||||
'halkyon_base.authenticator': authenticator,
|
||||
'halkyon_base.password': password,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:halkyon_base_client/src/protocol/protocol.dart' as _iwgbpyo5;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import '../password/wrapped_keys.dart' as _i2rxumte;
|
||||
|
||||
/// The account's keys as a device gets them once it proved the password
|
||||
/// (Halkyon Learn's ADR 0020, 7 and D19): its public keys, and its private
|
||||
/// keys wrapped under the password's key, which the device opens.
|
||||
abstract class DeviceKeys
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
DeviceKeys._({
|
||||
required this.encryptionPublicKey,
|
||||
required this.signingPublicKey,
|
||||
required this.passwordKeys,
|
||||
});
|
||||
|
||||
factory DeviceKeys({
|
||||
required _idt.ByteData encryptionPublicKey,
|
||||
required _idt.ByteData signingPublicKey,
|
||||
required _i2rxumte.WrappedKeys passwordKeys,
|
||||
}) = _DeviceKeysImpl;
|
||||
|
||||
factory DeviceKeys.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return DeviceKeys(
|
||||
encryptionPublicKey: _isc.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['encryptionPublicKey'],
|
||||
),
|
||||
signingPublicKey: _isc.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['signingPublicKey'],
|
||||
),
|
||||
passwordKeys: _iwgbpyo5.Protocol().deserialize<_i2rxumte.WrappedKeys>(
|
||||
jsonSerialization['passwordKeys'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_idt.ByteData encryptionPublicKey;
|
||||
|
||||
_idt.ByteData signingPublicKey;
|
||||
|
||||
_i2rxumte.WrappedKeys passwordKeys;
|
||||
|
||||
/// Returns a shallow copy of this [DeviceKeys]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
DeviceKeys copyWith({
|
||||
_idt.ByteData? encryptionPublicKey,
|
||||
_idt.ByteData? signingPublicKey,
|
||||
_i2rxumte.WrappedKeys? passwordKeys,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.DeviceKeys',
|
||||
'encryptionPublicKey': encryptionPublicKey.toJson(),
|
||||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeys': passwordKeys.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.DeviceKeys',
|
||||
'encryptionPublicKey': encryptionPublicKey.toJson(),
|
||||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeys': passwordKeys.toJsonForProtocol(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _DeviceKeysImpl extends DeviceKeys {
|
||||
_DeviceKeysImpl({
|
||||
required _idt.ByteData encryptionPublicKey,
|
||||
required _idt.ByteData signingPublicKey,
|
||||
required _i2rxumte.WrappedKeys passwordKeys,
|
||||
}) : super._(
|
||||
encryptionPublicKey: encryptionPublicKey,
|
||||
signingPublicKey: signingPublicKey,
|
||||
passwordKeys: passwordKeys,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DeviceKeys]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
DeviceKeys copyWith({
|
||||
_idt.ByteData? encryptionPublicKey,
|
||||
_idt.ByteData? signingPublicKey,
|
||||
_i2rxumte.WrappedKeys? passwordKeys,
|
||||
}) {
|
||||
return DeviceKeys(
|
||||
encryptionPublicKey:
|
||||
encryptionPublicKey ?? this.encryptionPublicKey.clone(),
|
||||
signingPublicKey: signingPublicKey ?? this.signingPublicKey.clone(),
|
||||
passwordKeys: passwordKeys ?? this.passwordKeys.copyWith(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:halkyon_base_client/src/protocol/protocol.dart' as _iwgbpyo5;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import '../password/password_costs.dart' as _ioe6tior;
|
||||
|
||||
/// What a signed-in device needs to prove the password of an account that
|
||||
/// signs in with SimpleLogin (Halkyon Learn's ADR 0020, D19): the salt and
|
||||
/// costs it derives the password's keys with, and a nonce it signs with the
|
||||
/// password's proof key, within two minutes, once.
|
||||
abstract class KeysChallenge
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
KeysChallenge._({
|
||||
required this.challenge,
|
||||
required this.nonce,
|
||||
required this.salt,
|
||||
required this.costs,
|
||||
});
|
||||
|
||||
factory KeysChallenge({
|
||||
required _isc.UuidValue challenge,
|
||||
required _idt.ByteData nonce,
|
||||
required _idt.ByteData salt,
|
||||
required _ioe6tior.PasswordCosts costs,
|
||||
}) = _KeysChallengeImpl;
|
||||
|
||||
factory KeysChallenge.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return KeysChallenge(
|
||||
challenge: _isc.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['challenge'],
|
||||
),
|
||||
nonce: _isc.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
|
||||
salt: _isc.ByteDataJsonExtension.fromJson(jsonSerialization['salt']),
|
||||
costs: _iwgbpyo5.Protocol().deserialize<_ioe6tior.PasswordCosts>(
|
||||
jsonSerialization['costs'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_isc.UuidValue challenge;
|
||||
|
||||
_idt.ByteData nonce;
|
||||
|
||||
_idt.ByteData salt;
|
||||
|
||||
_ioe6tior.PasswordCosts costs;
|
||||
|
||||
/// Returns a shallow copy of this [KeysChallenge]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
KeysChallenge copyWith({
|
||||
_isc.UuidValue? challenge,
|
||||
_idt.ByteData? nonce,
|
||||
_idt.ByteData? salt,
|
||||
_ioe6tior.PasswordCosts? costs,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysChallenge',
|
||||
'challenge': challenge.toJson(),
|
||||
'nonce': nonce.toJson(),
|
||||
'salt': salt.toJson(),
|
||||
'costs': costs.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysChallenge',
|
||||
'challenge': challenge.toJson(),
|
||||
'nonce': nonce.toJson(),
|
||||
'salt': salt.toJson(),
|
||||
'costs': costs.toJsonForProtocol(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _KeysChallengeImpl extends KeysChallenge {
|
||||
_KeysChallengeImpl({
|
||||
required _isc.UuidValue challenge,
|
||||
required _idt.ByteData nonce,
|
||||
required _idt.ByteData salt,
|
||||
required _ioe6tior.PasswordCosts costs,
|
||||
}) : super._(
|
||||
challenge: challenge,
|
||||
nonce: nonce,
|
||||
salt: salt,
|
||||
costs: costs,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [KeysChallenge]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
KeysChallenge copyWith({
|
||||
_isc.UuidValue? challenge,
|
||||
_idt.ByteData? nonce,
|
||||
_idt.ByteData? salt,
|
||||
_ioe6tior.PasswordCosts? costs,
|
||||
}) {
|
||||
return KeysChallenge(
|
||||
challenge: challenge ?? this.challenge,
|
||||
nonce: nonce ?? this.nonce.clone(),
|
||||
salt: salt ?? this.salt.clone(),
|
||||
costs: costs ?? this.costs.copyWith(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
|
||||
/// A proof of the password of an account that signs in with SimpleLogin
|
||||
/// (Halkyon Learn's ADR 0020, D19): the signature by the password's proof
|
||||
/// key over the challenge's nonce, bound to the base URL the device
|
||||
/// addressed and to what it is for (`keyProofMessage`).
|
||||
abstract class KeysProof
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
KeysProof._({
|
||||
required this.challenge,
|
||||
required this.signature,
|
||||
required this.instanceUrl,
|
||||
});
|
||||
|
||||
factory KeysProof({
|
||||
required _isc.UuidValue challenge,
|
||||
required _idt.ByteData signature,
|
||||
required String instanceUrl,
|
||||
}) = _KeysProofImpl;
|
||||
|
||||
factory KeysProof.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return KeysProof(
|
||||
challenge: _isc.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['challenge'],
|
||||
),
|
||||
signature: _isc.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['signature'],
|
||||
),
|
||||
instanceUrl: jsonSerialization['instanceUrl'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
_isc.UuidValue challenge;
|
||||
|
||||
/// Ed25519's signature, 64 bytes.
|
||||
_idt.ByteData signature;
|
||||
|
||||
/// The instance's base URL the device addressed (scheme, host, port,
|
||||
/// path): the server it reached checks it is its own before the
|
||||
/// register binds the proof to it.
|
||||
String instanceUrl;
|
||||
|
||||
/// Returns a shallow copy of this [KeysProof]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
KeysProof copyWith({
|
||||
_isc.UuidValue? challenge,
|
||||
_idt.ByteData? signature,
|
||||
String? instanceUrl,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysProof',
|
||||
'challenge': challenge.toJson(),
|
||||
'signature': signature.toJson(),
|
||||
'instanceUrl': instanceUrl,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysProof',
|
||||
'challenge': challenge.toJson(),
|
||||
'signature': signature.toJson(),
|
||||
'instanceUrl': instanceUrl,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _KeysProofImpl extends KeysProof {
|
||||
_KeysProofImpl({
|
||||
required _isc.UuidValue challenge,
|
||||
required _idt.ByteData signature,
|
||||
required String instanceUrl,
|
||||
}) : super._(
|
||||
challenge: challenge,
|
||||
signature: signature,
|
||||
instanceUrl: instanceUrl,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [KeysProof]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
KeysProof copyWith({
|
||||
_isc.UuidValue? challenge,
|
||||
_idt.ByteData? signature,
|
||||
String? instanceUrl,
|
||||
}) {
|
||||
return KeysProof(
|
||||
challenge: challenge ?? this.challenge,
|
||||
signature: signature ?? this.signature.clone(),
|
||||
instanceUrl: instanceUrl ?? this.instanceUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:halkyon_base_client/src/protocol/protocol.dart' as _iwgbpyo5;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import '../password/password_costs.dart' as _ioe6tior;
|
||||
import '../password/wrapped_keys.dart' as _i2rxumte;
|
||||
|
||||
/// What a device sends to give an account that signs in with SimpleLogin
|
||||
/// its keys (Halkyon Learn's ADR 0020, 6 and D19), all made on the device:
|
||||
/// the salt and costs it derived the password's keys with, the public half
|
||||
/// of the password's proof key, the account's public keys, and its private
|
||||
/// keys wrapped under the password's key and the recovery phrase's, with
|
||||
/// the phrase's proof key. As ADR 0017's `PasswordSetup`, without SRP's
|
||||
/// verifier: nothing of it signs in, and nothing decrypts without the
|
||||
/// password or the phrase.
|
||||
abstract class KeysSetup
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
KeysSetup._({
|
||||
required this.salt,
|
||||
required this.costs,
|
||||
required this.passwordProofKey,
|
||||
required this.encryptionPublicKey,
|
||||
required this.signingPublicKey,
|
||||
required this.passwordKeys,
|
||||
required this.recoveryKeys,
|
||||
required this.recoveryProofKey,
|
||||
});
|
||||
|
||||
factory KeysSetup({
|
||||
required _idt.ByteData salt,
|
||||
required _ioe6tior.PasswordCosts costs,
|
||||
required _idt.ByteData passwordProofKey,
|
||||
required _idt.ByteData encryptionPublicKey,
|
||||
required _idt.ByteData signingPublicKey,
|
||||
required _i2rxumte.WrappedKeys passwordKeys,
|
||||
required _i2rxumte.WrappedKeys recoveryKeys,
|
||||
required _idt.ByteData recoveryProofKey,
|
||||
}) = _KeysSetupImpl;
|
||||
|
||||
factory KeysSetup.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return KeysSetup(
|
||||
salt: _isc.ByteDataJsonExtension.fromJson(jsonSerialization['salt']),
|
||||
costs: _iwgbpyo5.Protocol().deserialize<_ioe6tior.PasswordCosts>(
|
||||
jsonSerialization['costs'],
|
||||
),
|
||||
passwordProofKey: _isc.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['passwordProofKey'],
|
||||
),
|
||||
encryptionPublicKey: _isc.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['encryptionPublicKey'],
|
||||
),
|
||||
signingPublicKey: _isc.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['signingPublicKey'],
|
||||
),
|
||||
passwordKeys: _iwgbpyo5.Protocol().deserialize<_i2rxumte.WrappedKeys>(
|
||||
jsonSerialization['passwordKeys'],
|
||||
),
|
||||
recoveryKeys: _iwgbpyo5.Protocol().deserialize<_i2rxumte.WrappedKeys>(
|
||||
jsonSerialization['recoveryKeys'],
|
||||
),
|
||||
recoveryProofKey: _isc.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['recoveryProofKey'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_idt.ByteData salt;
|
||||
|
||||
_ioe6tior.PasswordCosts costs;
|
||||
|
||||
/// The public half of the password's proof key (Ed25519).
|
||||
_idt.ByteData passwordProofKey;
|
||||
|
||||
_idt.ByteData encryptionPublicKey;
|
||||
|
||||
_idt.ByteData signingPublicKey;
|
||||
|
||||
_i2rxumte.WrappedKeys passwordKeys;
|
||||
|
||||
_i2rxumte.WrappedKeys recoveryKeys;
|
||||
|
||||
/// The public half of the recovery phrase's proof key (Ed25519, from the
|
||||
/// phrase alone).
|
||||
_idt.ByteData recoveryProofKey;
|
||||
|
||||
/// Returns a shallow copy of this [KeysSetup]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
KeysSetup copyWith({
|
||||
_idt.ByteData? salt,
|
||||
_ioe6tior.PasswordCosts? costs,
|
||||
_idt.ByteData? passwordProofKey,
|
||||
_idt.ByteData? encryptionPublicKey,
|
||||
_idt.ByteData? signingPublicKey,
|
||||
_i2rxumte.WrappedKeys? passwordKeys,
|
||||
_i2rxumte.WrappedKeys? recoveryKeys,
|
||||
_idt.ByteData? recoveryProofKey,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysSetup',
|
||||
'salt': salt.toJson(),
|
||||
'costs': costs.toJson(),
|
||||
'passwordProofKey': passwordProofKey.toJson(),
|
||||
'encryptionPublicKey': encryptionPublicKey.toJson(),
|
||||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeys': passwordKeys.toJson(),
|
||||
'recoveryKeys': recoveryKeys.toJson(),
|
||||
'recoveryProofKey': recoveryProofKey.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysSetup',
|
||||
'salt': salt.toJson(),
|
||||
'costs': costs.toJsonForProtocol(),
|
||||
'passwordProofKey': passwordProofKey.toJson(),
|
||||
'encryptionPublicKey': encryptionPublicKey.toJson(),
|
||||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeys': passwordKeys.toJsonForProtocol(),
|
||||
'recoveryKeys': recoveryKeys.toJsonForProtocol(),
|
||||
'recoveryProofKey': recoveryProofKey.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _KeysSetupImpl extends KeysSetup {
|
||||
_KeysSetupImpl({
|
||||
required _idt.ByteData salt,
|
||||
required _ioe6tior.PasswordCosts costs,
|
||||
required _idt.ByteData passwordProofKey,
|
||||
required _idt.ByteData encryptionPublicKey,
|
||||
required _idt.ByteData signingPublicKey,
|
||||
required _i2rxumte.WrappedKeys passwordKeys,
|
||||
required _i2rxumte.WrappedKeys recoveryKeys,
|
||||
required _idt.ByteData recoveryProofKey,
|
||||
}) : super._(
|
||||
salt: salt,
|
||||
costs: costs,
|
||||
passwordProofKey: passwordProofKey,
|
||||
encryptionPublicKey: encryptionPublicKey,
|
||||
signingPublicKey: signingPublicKey,
|
||||
passwordKeys: passwordKeys,
|
||||
recoveryKeys: recoveryKeys,
|
||||
recoveryProofKey: recoveryProofKey,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [KeysSetup]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
KeysSetup copyWith({
|
||||
_idt.ByteData? salt,
|
||||
_ioe6tior.PasswordCosts? costs,
|
||||
_idt.ByteData? passwordProofKey,
|
||||
_idt.ByteData? encryptionPublicKey,
|
||||
_idt.ByteData? signingPublicKey,
|
||||
_i2rxumte.WrappedKeys? passwordKeys,
|
||||
_i2rxumte.WrappedKeys? recoveryKeys,
|
||||
_idt.ByteData? recoveryProofKey,
|
||||
}) {
|
||||
return KeysSetup(
|
||||
salt: salt ?? this.salt.clone(),
|
||||
costs: costs ?? this.costs.copyWith(),
|
||||
passwordProofKey: passwordProofKey ?? this.passwordProofKey.clone(),
|
||||
encryptionPublicKey:
|
||||
encryptionPublicKey ?? this.encryptionPublicKey.clone(),
|
||||
signingPublicKey: signingPublicKey ?? this.signingPublicKey.clone(),
|
||||
passwordKeys: passwordKeys ?? this.passwordKeys.copyWith(),
|
||||
recoveryKeys: recoveryKeys ?? this.recoveryKeys.copyWith(),
|
||||
recoveryProofKey: recoveryProofKey ?? this.recoveryProofKey.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,13 @@ enum SignInChallengePurpose implements _isc.SerializableModel {
|
|||
/// Proving the password while signed in, to change it, set a new phrase
|
||||
/// or delete the account: counted per account, never toward the
|
||||
/// address's proofs, so strangers' guesses hold no signed-in owner back.
|
||||
change;
|
||||
change,
|
||||
|
||||
/// Proving the password of an account without SRP's verifier, signed in,
|
||||
/// by signing a nonce with the password's proof key (Halkyon Learn's ADR
|
||||
/// 0020, D19): to get the keys on a device, or for a change. Counted per
|
||||
/// session (`asOwnerProofTry`).
|
||||
passwordProof;
|
||||
|
||||
static SignInChallengePurpose fromJson(String name) {
|
||||
switch (name) {
|
||||
|
|
@ -33,6 +39,8 @@ enum SignInChallengePurpose implements _isc.SerializableModel {
|
|||
return SignInChallengePurpose.keyProof;
|
||||
case 'change':
|
||||
return SignInChallengePurpose.change;
|
||||
case 'passwordProof':
|
||||
return SignInChallengePurpose.passwordProof;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Value "$name" cannot be converted to "SignInChallengePurpose"',
|
||||
|
|
|
|||
|
|
@ -43,7 +43,11 @@ enum ProfileRefusal implements _isc.SerializableModel {
|
|||
avatarInvalid,
|
||||
|
||||
/// The links were checked too often in the last hour.
|
||||
tooManyLinkChecks;
|
||||
tooManyLinkChecks,
|
||||
|
||||
/// The handles were checked too often in the last hour: their answers
|
||||
/// tell which handles exist.
|
||||
tooManyHandleChecks;
|
||||
|
||||
static ProfileRefusal fromJson(String name) {
|
||||
switch (name) {
|
||||
|
|
@ -67,6 +71,8 @@ enum ProfileRefusal implements _isc.SerializableModel {
|
|||
return ProfileRefusal.avatarInvalid;
|
||||
case 'tooManyLinkChecks':
|
||||
return ProfileRefusal.tooManyLinkChecks;
|
||||
case 'tooManyHandleChecks':
|
||||
return ProfileRefusal.tooManyHandleChecks;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Value "$name" cannot be converted to "ProfileRefusal"',
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ import 'auth/github_sign_in_refusal.dart' as _ir1gd9p5;
|
|||
import 'auth/github_sign_in_refused_exception.dart' as _i0xfcu5n;
|
||||
import 'auth/sign_in_ways.dart' as _ie6tbh85;
|
||||
import 'instance/instance_basics.dart' as _i8jixqx6;
|
||||
import 'keys/device_keys.dart' as _iz50cwqw;
|
||||
import 'keys/keys_challenge.dart' as _itebbsfc;
|
||||
import 'keys/keys_proof.dart' as _iviximyh;
|
||||
import 'keys/keys_setup.dart' as _iqdhlx3t;
|
||||
import 'password/account_reset_kind.dart' as _i91ka1it;
|
||||
import 'password/authenticator.dart' as _icpzuvvi;
|
||||
import 'password/authenticator_enrolment.dart' as _iv7tra7v;
|
||||
|
|
@ -57,6 +61,10 @@ export 'auth/github_sign_in_refusal.dart';
|
|||
export 'auth/github_sign_in_refused_exception.dart';
|
||||
export 'auth/sign_in_ways.dart';
|
||||
export 'instance/instance_basics.dart';
|
||||
export 'keys/device_keys.dart';
|
||||
export 'keys/keys_challenge.dart';
|
||||
export 'keys/keys_proof.dart';
|
||||
export 'keys/keys_setup.dart';
|
||||
export 'password/account_reset_kind.dart';
|
||||
export 'password/authenticator.dart';
|
||||
export 'password/authenticator_enrolment.dart';
|
||||
|
|
@ -149,6 +157,18 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (t == _i8jixqx6.InstanceBasics) {
|
||||
return _i8jixqx6.InstanceBasics.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iz50cwqw.DeviceKeys) {
|
||||
return _iz50cwqw.DeviceKeys.fromJson(data) as T;
|
||||
}
|
||||
if (t == _itebbsfc.KeysChallenge) {
|
||||
return _itebbsfc.KeysChallenge.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iviximyh.KeysProof) {
|
||||
return _iviximyh.KeysProof.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iqdhlx3t.KeysSetup) {
|
||||
return _iqdhlx3t.KeysSetup.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i91ka1it.AccountResetKind) {
|
||||
return _i91ka1it.AccountResetKind.fromJson(data) as T;
|
||||
}
|
||||
|
|
@ -254,6 +274,19 @@ class Protocol extends _isc.SerializationManager {
|
|||
return (data != null ? _i8jixqx6.InstanceBasics.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_iz50cwqw.DeviceKeys?>()) {
|
||||
return (data != null ? _iz50cwqw.DeviceKeys.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_itebbsfc.KeysChallenge?>()) {
|
||||
return (data != null ? _itebbsfc.KeysChallenge.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_iviximyh.KeysProof?>()) {
|
||||
return (data != null ? _iviximyh.KeysProof.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_iqdhlx3t.KeysSetup?>()) {
|
||||
return (data != null ? _iqdhlx3t.KeysSetup.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_i91ka1it.AccountResetKind?>()) {
|
||||
return (data != null ? _i91ka1it.AccountResetKind.fromJson(data) : null)
|
||||
as T;
|
||||
|
|
@ -401,6 +434,10 @@ class Protocol extends _isc.SerializationManager {
|
|||
_i0xfcu5n.GitHubSignInRefusedException => 'GitHubSignInRefusedException',
|
||||
_ie6tbh85.SignInWays => 'SignInWays',
|
||||
_i8jixqx6.InstanceBasics => 'InstanceBasics',
|
||||
_iz50cwqw.DeviceKeys => 'DeviceKeys',
|
||||
_itebbsfc.KeysChallenge => 'KeysChallenge',
|
||||
_iviximyh.KeysProof => 'KeysProof',
|
||||
_iqdhlx3t.KeysSetup => 'KeysSetup',
|
||||
_i91ka1it.AccountResetKind => 'AccountResetKind',
|
||||
_icpzuvvi.Authenticator => 'Authenticator',
|
||||
_iv7tra7v.AuthenticatorEnrolment => 'AuthenticatorEnrolment',
|
||||
|
|
@ -456,6 +493,14 @@ class Protocol extends _isc.SerializationManager {
|
|||
return 'SignInWays';
|
||||
case _i8jixqx6.InstanceBasics():
|
||||
return 'InstanceBasics';
|
||||
case _iz50cwqw.DeviceKeys():
|
||||
return 'DeviceKeys';
|
||||
case _itebbsfc.KeysChallenge():
|
||||
return 'KeysChallenge';
|
||||
case _iviximyh.KeysProof():
|
||||
return 'KeysProof';
|
||||
case _iqdhlx3t.KeysSetup():
|
||||
return 'KeysSetup';
|
||||
case _i91ka1it.AccountResetKind():
|
||||
return 'AccountResetKind';
|
||||
case _icpzuvvi.Authenticator():
|
||||
|
|
@ -535,6 +580,18 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (dataClassName == 'InstanceBasics') {
|
||||
return deserialize<_i8jixqx6.InstanceBasics>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'DeviceKeys') {
|
||||
return deserialize<_iz50cwqw.DeviceKeys>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'KeysChallenge') {
|
||||
return deserialize<_itebbsfc.KeysChallenge>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'KeysProof') {
|
||||
return deserialize<_iviximyh.KeysProof>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'KeysSetup') {
|
||||
return deserialize<_iqdhlx3t.KeysSetup>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'AccountResetKind') {
|
||||
return deserialize<_i91ka1it.AccountResetKind>(data['data']);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,12 +26,16 @@ abstract class SimpleLoginCompleted
|
|||
required this.outcome,
|
||||
this.session,
|
||||
this.suggestedHandle,
|
||||
});
|
||||
bool? hasKeys,
|
||||
this.handle,
|
||||
}) : hasKeys = hasKeys ?? false;
|
||||
|
||||
factory SimpleLoginCompleted({
|
||||
required _i2lb2chw.SimpleLoginOutcome outcome,
|
||||
_iacc.AuthSuccess? session,
|
||||
String? suggestedHandle,
|
||||
bool? hasKeys,
|
||||
String? handle,
|
||||
}) = _SimpleLoginCompletedImpl;
|
||||
|
||||
factory SimpleLoginCompleted.fromJson(
|
||||
|
|
@ -47,6 +51,10 @@ abstract class SimpleLoginCompleted
|
|||
jsonSerialization['session'],
|
||||
),
|
||||
suggestedHandle: jsonSerialization['suggestedHandle'] as String?,
|
||||
hasKeys: jsonSerialization['hasKeys'] == null
|
||||
? null
|
||||
: _isc.BoolJsonExtension.fromJson(jsonSerialization['hasKeys']),
|
||||
handle: jsonSerialization['handle'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +64,15 @@ abstract class SimpleLoginCompleted
|
|||
|
||||
String? suggestedHandle;
|
||||
|
||||
/// Whether the account signed in to, or SimpleLogin was added to, has its
|
||||
/// keys (`keys.unlock` opens them); one without (an account of before,
|
||||
/// adding SimpleLogin) makes them next.
|
||||
bool hasKeys;
|
||||
|
||||
/// The account's handle; null for one without (it chooses one with its
|
||||
/// keys).
|
||||
String? handle;
|
||||
|
||||
/// Returns a shallow copy of this [SimpleLoginCompleted]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
|
|
@ -63,6 +80,8 @@ abstract class SimpleLoginCompleted
|
|||
_i2lb2chw.SimpleLoginOutcome? outcome,
|
||||
_iacc.AuthSuccess? session,
|
||||
String? suggestedHandle,
|
||||
bool? hasKeys,
|
||||
String? handle,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -71,6 +90,8 @@ abstract class SimpleLoginCompleted
|
|||
'outcome': outcome.toJson(),
|
||||
if (session != null) 'session': session?.toJson(),
|
||||
if (suggestedHandle != null) 'suggestedHandle': suggestedHandle,
|
||||
'hasKeys': hasKeys,
|
||||
if (handle != null) 'handle': handle,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -81,6 +102,8 @@ abstract class SimpleLoginCompleted
|
|||
'outcome': outcome.toJson(),
|
||||
if (session != null) 'session': session?.toJson(),
|
||||
if (suggestedHandle != null) 'suggestedHandle': suggestedHandle,
|
||||
'hasKeys': hasKeys,
|
||||
if (handle != null) 'handle': handle,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -97,10 +120,14 @@ class _SimpleLoginCompletedImpl extends SimpleLoginCompleted {
|
|||
required _i2lb2chw.SimpleLoginOutcome outcome,
|
||||
_iacc.AuthSuccess? session,
|
||||
String? suggestedHandle,
|
||||
bool? hasKeys,
|
||||
String? handle,
|
||||
}) : super._(
|
||||
outcome: outcome,
|
||||
session: session,
|
||||
suggestedHandle: suggestedHandle,
|
||||
hasKeys: hasKeys,
|
||||
handle: handle,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [SimpleLoginCompleted]
|
||||
|
|
@ -111,6 +138,8 @@ class _SimpleLoginCompletedImpl extends SimpleLoginCompleted {
|
|||
_i2lb2chw.SimpleLoginOutcome? outcome,
|
||||
Object? session = _Undefined,
|
||||
Object? suggestedHandle = _Undefined,
|
||||
bool? hasKeys,
|
||||
Object? handle = _Undefined,
|
||||
}) {
|
||||
return SimpleLoginCompleted(
|
||||
outcome: outcome ?? this.outcome,
|
||||
|
|
@ -120,6 +149,8 @@ class _SimpleLoginCompletedImpl extends SimpleLoginCompleted {
|
|||
suggestedHandle: suggestedHandle is String?
|
||||
? suggestedHandle
|
||||
: this.suggestedHandle,
|
||||
hasKeys: hasKeys ?? this.hasKeys,
|
||||
handle: handle is String? ? handle : this.handle,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,8 @@ String canonicalInstanceUrl(String url) {
|
|||
return '$scheme://$shownHost$port$path';
|
||||
}
|
||||
|
||||
/// What proving the recovery phrase is for, by the words the message
|
||||
/// What proving the recovery phrase, or the password with its own proof key
|
||||
/// (Halkyon Learn's ADR 0020, D19), is for, by the words the message
|
||||
/// carries ([label]).
|
||||
enum KeyProofPurpose(final String label) {
|
||||
/// Asking for the reset of a lost authenticator.
|
||||
|
|
@ -96,6 +97,21 @@ enum KeyProofPurpose(final String label) {
|
|||
|
||||
/// Setting a new password, the keys staying.
|
||||
passwordReset('password reset'),
|
||||
|
||||
/// Getting the account's keys on a device, by the password.
|
||||
unlockKeys('unlock keys'),
|
||||
|
||||
/// A new password while signed in, by the old one.
|
||||
newPassword('new password'),
|
||||
|
||||
/// A new recovery phrase while signed in, by the password.
|
||||
newPhrase('new phrase'),
|
||||
|
||||
/// Deleting the account, by the password.
|
||||
deleteAccount('delete account'),
|
||||
|
||||
/// Linking a new SimpleLogin ID in place of a lost one, by the phrase.
|
||||
mendLink('mend link'),
|
||||
}
|
||||
|
||||
/// What the device signs with the recovery phrase's proof key to prove the
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
| `passwordSignIn` | `begin`, `complete`: SRP-6a's two steps with a code from an authenticator (or a recovery code; a setup never finished, a code by mail); a session. `signUp`: an account at the address a mailed code proves, with a password; a session | without a session |
|
||||
| `authenticator` | `enrol`, `confirm` (the first brings ten recovery codes; another takes a code from one confirmed before), `list`, `remove`, `newRecoveryCodes` | signed in |
|
||||
| `accountRecovery` | `resetAuthenticator`, `beginKeyProof`, `resetPassword` (with the recovery phrase), `startAccountReset`, `completeAccountReset` | without a session |
|
||||
| `simpleLogin` | `begin(platform)`: an attempt and SimpleLogin's address to open, at most 30 an hour per network; `complete(attempt, code)`: signed out, a session for the account with that SimpleLogin ID, or for someone new the handle suggested from their name (their account comes with step 3); signed in, SimpleLogin added to the caller's account, who becomes `identified` | anyone |
|
||||
| `simpleLogin` | `begin(platform)`: an attempt and SimpleLogin's address to open, at most 30 an hour per network; `complete(attempt, code)`: signed out, a session for the account with that SimpleLogin ID, or for someone new the handle suggested from their name; signed in, SimpleLogin added to the caller's account, who becomes `identified`; for an account, whether it has its keys and its handle, the attempt standing ten minutes for the sign-in; `checkHandle(attempt, handle)`: whether someone new could take a handle, at most 30 an hour per attempt and 60 per network; `signUp(attempt, handle, setup)`, without a session: someone new's account, `identified`, with the handle and the keys the device made; a session | anyone |
|
||||
| `keys` | `begin`, `unlock`: the caller's keys for their device, once the password is proven with its own proof key (an account that signs in with SimpleLogin, Halkyon Learn's ADR 0020, D19); the tenth wrong proof in a row ends the session, as ADR 0017's do | signed in |
|
||||
| `gitHubUpgrade` | `upgrade`: links a GitHub account to the caller, who becomes `identified`; refused where passwords or SimpleLogin are offered (deprecated) | signed in |
|
||||
| `gitHubSignIn` | `signIn`: a session for whoever has that GitHub account, unless they sign in with a password; none once `GITHUB_SIGN_IN_ENDS` has come | without a session |
|
||||
| `emailCode` | `sendCode`: a code by mail, which proves the address and signs nobody in | anyone |
|
||||
|
|
@ -53,7 +54,13 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
(`halkyon_oidc_identity`, the provider `simplelogin`): one per account, each an
|
||||
account's. Someone new's identity waits 30 minutes under the attempt's ID, with a
|
||||
handle suggested from the name SimpleLogin shared (`handleFromName`: `anna_mueller`,
|
||||
a number added where taken), which nothing keeps.
|
||||
a number added where taken), which nothing keeps. Signing up (`signUp`, D18) takes
|
||||
the identity and makes the account in one transaction: `identified`, linked, a
|
||||
profile with the handle (refused as saving a profile refuses it) and the keys, the
|
||||
password's own proof key (Ed25519) in place of SRP's verifier (D19). The password
|
||||
opens the keys and signs nobody in, so such an account has no authenticator (`enrol`
|
||||
refuses it `noPassword`). A sign-in into an account keeps its attempt ten minutes
|
||||
more, the account its caller: it confirms a change the device asks for next (D20).
|
||||
- SimpleLogin's forwarding page (Halkyon Learn's ADR 0020; `addSimpleLoginCallbackRoute`,
|
||||
on the register, on halkyon_oidc's `addOidcCallbackForwarding`, ADR 0023):
|
||||
`/simplelogin-callback`, the one address the instance's SimpleLogin
|
||||
|
|
@ -198,7 +205,8 @@ Built on the three parts above, on the register (members forward every step thro
|
|||
|
||||
- **Tables** (`serverOnly`, each row deleted with its person by `deletePerson`):
|
||||
`account_password` (one per account: Argon2id's salt and costs, SRP's verifier padded
|
||||
to N, the public keys, the private keys wrapped under the password and under the
|
||||
to N, or for an account that signs in with SimpleLogin the password's proof key in its
|
||||
place, the public keys, the private keys wrapped under the password and under the
|
||||
recovery phrase), `totp_authenticator` (the secret sealed with XChaCha20-Poly1305 under
|
||||
a key derived from `totpKey`, bound to the row's account and ID; `confirmedAt`;
|
||||
`lastStep`), `recovery_code` (keyed hashes), `sign_in_challenge` (a step begun: SRP's b
|
||||
|
|
|
|||
|
|
@ -6,8 +6,12 @@ fields:
|
|||
### SimpleLogin (ADR 0020): the instance's register keeps its ID for the
|
||||
### instance's SimpleLogin app.
|
||||
simpleLogin: bool, default=false
|
||||
### A password (ADR 0017), which an address needs for resets.
|
||||
### A password that signs in (ADR 0017, SRP's), which an address needs
|
||||
### for resets.
|
||||
password: bool, default=false
|
||||
### The account's keys, under a password that signs in (ADR 0017) or one
|
||||
### that only opens them, as for SimpleLogin (ADR 0020, D19).
|
||||
keys: bool, default=false
|
||||
### A confirmed authenticator: the password signs in only with one.
|
||||
authenticator: bool, default=false
|
||||
### The account signs in with its password alone: it had a confirmed
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ abstract class SignInWays
|
|||
required this.email,
|
||||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -28,6 +29,7 @@ abstract class SignInWays
|
|||
this.accountResetAt,
|
||||
}) : simpleLogin = simpleLogin ?? false,
|
||||
password = password ?? false,
|
||||
keys = keys ?? false,
|
||||
authenticator = authenticator ?? false,
|
||||
passwordOnly = passwordOnly ?? false,
|
||||
secondFactorWorks = secondFactorWorks ?? false;
|
||||
|
|
@ -37,6 +39,7 @@ abstract class SignInWays
|
|||
required bool email,
|
||||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -55,6 +58,9 @@ abstract class SignInWays
|
|||
password: jsonSerialization['password'] == null
|
||||
? null
|
||||
: _is.BoolJsonExtension.fromJson(jsonSerialization['password']),
|
||||
keys: jsonSerialization['keys'] == null
|
||||
? null
|
||||
: _is.BoolJsonExtension.fromJson(jsonSerialization['keys']),
|
||||
authenticator: jsonSerialization['authenticator'] == null
|
||||
? null
|
||||
: _is.BoolJsonExtension.fromJson(jsonSerialization['authenticator']),
|
||||
|
|
@ -93,9 +99,14 @@ abstract class SignInWays
|
|||
/// instance's SimpleLogin app.
|
||||
bool simpleLogin;
|
||||
|
||||
/// A password (ADR 0017), which an address needs for resets.
|
||||
/// A password that signs in (ADR 0017, SRP's), which an address needs
|
||||
/// for resets.
|
||||
bool password;
|
||||
|
||||
/// The account's keys, under a password that signs in (ADR 0017) or one
|
||||
/// that only opens them, as for SimpleLogin (ADR 0020, D19).
|
||||
bool keys;
|
||||
|
||||
/// A confirmed authenticator: the password signs in only with one.
|
||||
bool authenticator;
|
||||
|
||||
|
|
@ -130,6 +141,7 @@ abstract class SignInWays
|
|||
bool? email,
|
||||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -145,6 +157,7 @@ abstract class SignInWays
|
|||
'email': email,
|
||||
'simpleLogin': simpleLogin,
|
||||
'password': password,
|
||||
'keys': keys,
|
||||
'authenticator': authenticator,
|
||||
'passwordOnly': passwordOnly,
|
||||
'secondFactorWorks': secondFactorWorks,
|
||||
|
|
@ -164,6 +177,7 @@ abstract class SignInWays
|
|||
'email': email,
|
||||
'simpleLogin': simpleLogin,
|
||||
'password': password,
|
||||
'keys': keys,
|
||||
'authenticator': authenticator,
|
||||
'passwordOnly': passwordOnly,
|
||||
'secondFactorWorks': secondFactorWorks,
|
||||
|
|
@ -189,6 +203,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
required bool email,
|
||||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -200,6 +215,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
email: email,
|
||||
simpleLogin: simpleLogin,
|
||||
password: password,
|
||||
keys: keys,
|
||||
authenticator: authenticator,
|
||||
passwordOnly: passwordOnly,
|
||||
secondFactorWorks: secondFactorWorks,
|
||||
|
|
@ -217,6 +233,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
bool? email,
|
||||
bool? simpleLogin,
|
||||
bool? password,
|
||||
bool? keys,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
|
|
@ -229,6 +246,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
email: email ?? this.email,
|
||||
simpleLogin: simpleLogin ?? this.simpleLogin,
|
||||
password: password ?? this.password,
|
||||
keys: keys ?? this.keys,
|
||||
authenticator: authenticator ?? this.authenticator,
|
||||
passwordOnly: passwordOnly ?? this.passwordOnly,
|
||||
secondFactorWorks: secondFactorWorks ?? this.secondFactorWorks,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@
|
|||
import 'dart:typed_data' as _idt;
|
||||
import 'package:halkyon_base_server/src/generated/auth/email_code_purpose.dart'
|
||||
as _i5775c0l;
|
||||
import 'package:halkyon_base_server/src/generated/keys/keys_proof.dart'
|
||||
as _iy592cuk;
|
||||
import 'package:halkyon_base_server/src/generated/keys/keys_setup.dart'
|
||||
as _ii64ejy6;
|
||||
import 'package:halkyon_base_server/src/generated/password/deletion_proof.dart'
|
||||
as _i619l6vv;
|
||||
import 'package:halkyon_base_server/src/generated/password/new_password.dart'
|
||||
|
|
@ -37,6 +41,7 @@ import '../auth/email_code_endpoint.dart' as _in3cimlf;
|
|||
import '../auth/github_sign_in_endpoint.dart' as _iasw79m5;
|
||||
import '../auth/github_upgrade_endpoint.dart' as _ivnugbf2;
|
||||
import '../instance/instance_endpoint.dart' as _i3g8bfc2;
|
||||
import '../keys/keys_endpoint.dart' as _icowoccn;
|
||||
import '../password/account_recovery_endpoint.dart' as _ivnbjhab;
|
||||
import '../password/authenticator_endpoint.dart' as _i3xqawxj;
|
||||
import '../password/password_endpoint.dart' as _ig7fyg1b;
|
||||
|
|
@ -84,6 +89,12 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
'instance',
|
||||
'halkyon_base',
|
||||
),
|
||||
'keys': _icowoccn.KeysEndpoint()
|
||||
..initialize(
|
||||
server,
|
||||
'keys',
|
||||
'halkyon_base',
|
||||
),
|
||||
'accountRecovery': _ivnbjhab.AccountRecoveryEndpoint()
|
||||
..initialize(
|
||||
server,
|
||||
|
|
@ -318,6 +329,40 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
),
|
||||
},
|
||||
);
|
||||
connectors['keys'] = _is.EndpointConnector(
|
||||
name: 'keys',
|
||||
endpoint: endpoints['keys']!,
|
||||
methodConnectors: {
|
||||
'begin': _is.MethodConnector(
|
||||
name: 'begin',
|
||||
params: {},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['keys'] as _icowoccn.KeysEndpoint).begin(session),
|
||||
),
|
||||
'unlock': _is.MethodConnector(
|
||||
name: 'unlock',
|
||||
params: {
|
||||
'proof': _is.ParameterDescription(
|
||||
name: 'proof',
|
||||
type: _is.getType<_iy592cuk.KeysProof>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async => (endpoints['keys'] as _icowoccn.KeysEndpoint).unlock(
|
||||
session,
|
||||
params['proof'],
|
||||
),
|
||||
),
|
||||
},
|
||||
);
|
||||
connectors['accountRecovery'] = _is.EndpointConnector(
|
||||
name: 'accountRecovery',
|
||||
endpoint: endpoints['accountRecovery']!,
|
||||
|
|
@ -1150,6 +1195,64 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
code: params['code'],
|
||||
),
|
||||
),
|
||||
'checkHandle': _is.MethodConnector(
|
||||
name: 'checkHandle',
|
||||
params: {
|
||||
'attempt': _is.ParameterDescription(
|
||||
name: 'attempt',
|
||||
type: _is.getType<_is.UuidValue>(),
|
||||
nullable: false,
|
||||
),
|
||||
'handle': _is.ParameterDescription(
|
||||
name: 'handle',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['simpleLogin'] as _ia8c9alv.SimpleLoginEndpoint)
|
||||
.checkHandle(
|
||||
session,
|
||||
attempt: params['attempt'],
|
||||
handle: params['handle'],
|
||||
),
|
||||
),
|
||||
'signUp': _is.MethodConnector(
|
||||
name: 'signUp',
|
||||
params: {
|
||||
'attempt': _is.ParameterDescription(
|
||||
name: 'attempt',
|
||||
type: _is.getType<_is.UuidValue>(),
|
||||
nullable: false,
|
||||
),
|
||||
'handle': _is.ParameterDescription(
|
||||
name: 'handle',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'setup': _is.ParameterDescription(
|
||||
name: 'setup',
|
||||
type: _is.getType<_ii64ejy6.KeysSetup>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['simpleLogin'] as _ia8c9alv.SimpleLoginEndpoint)
|
||||
.signUp(
|
||||
session,
|
||||
attempt: params['attempt'],
|
||||
handle: params['handle'],
|
||||
setup: params['setup'],
|
||||
),
|
||||
),
|
||||
},
|
||||
);
|
||||
modules['halkyon_oidc'] = _ipoqfiqr.Endpoints()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:halkyon_base_server/src/generated/protocol.dart' as _ibewtqgw;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import '../password/wrapped_keys.dart' as _i2rxumte;
|
||||
|
||||
/// The account's keys as a device gets them once it proved the password
|
||||
/// (Halkyon Learn's ADR 0020, 7 and D19): its public keys, and its private
|
||||
/// keys wrapped under the password's key, which the device opens.
|
||||
abstract class DeviceKeys
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
DeviceKeys._({
|
||||
required this.encryptionPublicKey,
|
||||
required this.signingPublicKey,
|
||||
required this.passwordKeys,
|
||||
});
|
||||
|
||||
factory DeviceKeys({
|
||||
required _idt.ByteData encryptionPublicKey,
|
||||
required _idt.ByteData signingPublicKey,
|
||||
required _i2rxumte.WrappedKeys passwordKeys,
|
||||
}) = _DeviceKeysImpl;
|
||||
|
||||
factory DeviceKeys.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return DeviceKeys(
|
||||
encryptionPublicKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['encryptionPublicKey'],
|
||||
),
|
||||
signingPublicKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['signingPublicKey'],
|
||||
),
|
||||
passwordKeys: _ibewtqgw.Protocol().deserialize<_i2rxumte.WrappedKeys>(
|
||||
jsonSerialization['passwordKeys'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_idt.ByteData encryptionPublicKey;
|
||||
|
||||
_idt.ByteData signingPublicKey;
|
||||
|
||||
_i2rxumte.WrappedKeys passwordKeys;
|
||||
|
||||
/// Returns a shallow copy of this [DeviceKeys]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
DeviceKeys copyWith({
|
||||
_idt.ByteData? encryptionPublicKey,
|
||||
_idt.ByteData? signingPublicKey,
|
||||
_i2rxumte.WrappedKeys? passwordKeys,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.DeviceKeys',
|
||||
'encryptionPublicKey': encryptionPublicKey.toJson(),
|
||||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeys': passwordKeys.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.DeviceKeys',
|
||||
'encryptionPublicKey': encryptionPublicKey.toJson(),
|
||||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeys': passwordKeys.toJsonForProtocol(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _DeviceKeysImpl extends DeviceKeys {
|
||||
_DeviceKeysImpl({
|
||||
required _idt.ByteData encryptionPublicKey,
|
||||
required _idt.ByteData signingPublicKey,
|
||||
required _i2rxumte.WrappedKeys passwordKeys,
|
||||
}) : super._(
|
||||
encryptionPublicKey: encryptionPublicKey,
|
||||
signingPublicKey: signingPublicKey,
|
||||
passwordKeys: passwordKeys,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DeviceKeys]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
DeviceKeys copyWith({
|
||||
_idt.ByteData? encryptionPublicKey,
|
||||
_idt.ByteData? signingPublicKey,
|
||||
_i2rxumte.WrappedKeys? passwordKeys,
|
||||
}) {
|
||||
return DeviceKeys(
|
||||
encryptionPublicKey:
|
||||
encryptionPublicKey ?? this.encryptionPublicKey.clone(),
|
||||
signingPublicKey: signingPublicKey ?? this.signingPublicKey.clone(),
|
||||
passwordKeys: passwordKeys ?? this.passwordKeys.copyWith(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:halkyon_base_server/src/generated/protocol.dart' as _ibewtqgw;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import '../password/password_costs.dart' as _ioe6tior;
|
||||
|
||||
/// What a signed-in device needs to prove the password of an account that
|
||||
/// signs in with SimpleLogin (Halkyon Learn's ADR 0020, D19): the salt and
|
||||
/// costs it derives the password's keys with, and a nonce it signs with the
|
||||
/// password's proof key, within two minutes, once.
|
||||
abstract class KeysChallenge
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
KeysChallenge._({
|
||||
required this.challenge,
|
||||
required this.nonce,
|
||||
required this.salt,
|
||||
required this.costs,
|
||||
});
|
||||
|
||||
factory KeysChallenge({
|
||||
required _is.UuidValue challenge,
|
||||
required _idt.ByteData nonce,
|
||||
required _idt.ByteData salt,
|
||||
required _ioe6tior.PasswordCosts costs,
|
||||
}) = _KeysChallengeImpl;
|
||||
|
||||
factory KeysChallenge.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return KeysChallenge(
|
||||
challenge: _is.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['challenge'],
|
||||
),
|
||||
nonce: _is.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
|
||||
salt: _is.ByteDataJsonExtension.fromJson(jsonSerialization['salt']),
|
||||
costs: _ibewtqgw.Protocol().deserialize<_ioe6tior.PasswordCosts>(
|
||||
jsonSerialization['costs'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_is.UuidValue challenge;
|
||||
|
||||
_idt.ByteData nonce;
|
||||
|
||||
_idt.ByteData salt;
|
||||
|
||||
_ioe6tior.PasswordCosts costs;
|
||||
|
||||
/// Returns a shallow copy of this [KeysChallenge]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
KeysChallenge copyWith({
|
||||
_is.UuidValue? challenge,
|
||||
_idt.ByteData? nonce,
|
||||
_idt.ByteData? salt,
|
||||
_ioe6tior.PasswordCosts? costs,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysChallenge',
|
||||
'challenge': challenge.toJson(),
|
||||
'nonce': nonce.toJson(),
|
||||
'salt': salt.toJson(),
|
||||
'costs': costs.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysChallenge',
|
||||
'challenge': challenge.toJson(),
|
||||
'nonce': nonce.toJson(),
|
||||
'salt': salt.toJson(),
|
||||
'costs': costs.toJsonForProtocol(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _KeysChallengeImpl extends KeysChallenge {
|
||||
_KeysChallengeImpl({
|
||||
required _is.UuidValue challenge,
|
||||
required _idt.ByteData nonce,
|
||||
required _idt.ByteData salt,
|
||||
required _ioe6tior.PasswordCosts costs,
|
||||
}) : super._(
|
||||
challenge: challenge,
|
||||
nonce: nonce,
|
||||
salt: salt,
|
||||
costs: costs,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [KeysChallenge]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
KeysChallenge copyWith({
|
||||
_is.UuidValue? challenge,
|
||||
_idt.ByteData? nonce,
|
||||
_idt.ByteData? salt,
|
||||
_ioe6tior.PasswordCosts? costs,
|
||||
}) {
|
||||
return KeysChallenge(
|
||||
challenge: challenge ?? this.challenge,
|
||||
nonce: nonce ?? this.nonce.clone(),
|
||||
salt: salt ?? this.salt.clone(),
|
||||
costs: costs ?? this.costs.copyWith(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A proof of the password of an account that signs in with SimpleLogin
|
||||
/// (Halkyon Learn's ADR 0020, D19): the signature by the password's proof
|
||||
/// key over the challenge's nonce, bound to the base URL the device
|
||||
/// addressed and to what it is for (`keyProofMessage`).
|
||||
abstract class KeysProof
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
KeysProof._({
|
||||
required this.challenge,
|
||||
required this.signature,
|
||||
required this.instanceUrl,
|
||||
});
|
||||
|
||||
factory KeysProof({
|
||||
required _is.UuidValue challenge,
|
||||
required _idt.ByteData signature,
|
||||
required String instanceUrl,
|
||||
}) = _KeysProofImpl;
|
||||
|
||||
factory KeysProof.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return KeysProof(
|
||||
challenge: _is.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['challenge'],
|
||||
),
|
||||
signature: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['signature'],
|
||||
),
|
||||
instanceUrl: jsonSerialization['instanceUrl'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
_is.UuidValue challenge;
|
||||
|
||||
/// Ed25519's signature, 64 bytes.
|
||||
_idt.ByteData signature;
|
||||
|
||||
/// The instance's base URL the device addressed (scheme, host, port,
|
||||
/// path): the server it reached checks it is its own before the
|
||||
/// register binds the proof to it.
|
||||
String instanceUrl;
|
||||
|
||||
/// Returns a shallow copy of this [KeysProof]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
KeysProof copyWith({
|
||||
_is.UuidValue? challenge,
|
||||
_idt.ByteData? signature,
|
||||
String? instanceUrl,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysProof',
|
||||
'challenge': challenge.toJson(),
|
||||
'signature': signature.toJson(),
|
||||
'instanceUrl': instanceUrl,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysProof',
|
||||
'challenge': challenge.toJson(),
|
||||
'signature': signature.toJson(),
|
||||
'instanceUrl': instanceUrl,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _KeysProofImpl extends KeysProof {
|
||||
_KeysProofImpl({
|
||||
required _is.UuidValue challenge,
|
||||
required _idt.ByteData signature,
|
||||
required String instanceUrl,
|
||||
}) : super._(
|
||||
challenge: challenge,
|
||||
signature: signature,
|
||||
instanceUrl: instanceUrl,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [KeysProof]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
KeysProof copyWith({
|
||||
_is.UuidValue? challenge,
|
||||
_idt.ByteData? signature,
|
||||
String? instanceUrl,
|
||||
}) {
|
||||
return KeysProof(
|
||||
challenge: challenge ?? this.challenge,
|
||||
signature: signature ?? this.signature.clone(),
|
||||
instanceUrl: instanceUrl ?? this.instanceUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:halkyon_base_server/src/generated/protocol.dart' as _ibewtqgw;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import '../password/password_costs.dart' as _ioe6tior;
|
||||
import '../password/wrapped_keys.dart' as _i2rxumte;
|
||||
|
||||
/// What a device sends to give an account that signs in with SimpleLogin
|
||||
/// its keys (Halkyon Learn's ADR 0020, 6 and D19), all made on the device:
|
||||
/// the salt and costs it derived the password's keys with, the public half
|
||||
/// of the password's proof key, the account's public keys, and its private
|
||||
/// keys wrapped under the password's key and the recovery phrase's, with
|
||||
/// the phrase's proof key. As ADR 0017's `PasswordSetup`, without SRP's
|
||||
/// verifier: nothing of it signs in, and nothing decrypts without the
|
||||
/// password or the phrase.
|
||||
abstract class KeysSetup
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
KeysSetup._({
|
||||
required this.salt,
|
||||
required this.costs,
|
||||
required this.passwordProofKey,
|
||||
required this.encryptionPublicKey,
|
||||
required this.signingPublicKey,
|
||||
required this.passwordKeys,
|
||||
required this.recoveryKeys,
|
||||
required this.recoveryProofKey,
|
||||
});
|
||||
|
||||
factory KeysSetup({
|
||||
required _idt.ByteData salt,
|
||||
required _ioe6tior.PasswordCosts costs,
|
||||
required _idt.ByteData passwordProofKey,
|
||||
required _idt.ByteData encryptionPublicKey,
|
||||
required _idt.ByteData signingPublicKey,
|
||||
required _i2rxumte.WrappedKeys passwordKeys,
|
||||
required _i2rxumte.WrappedKeys recoveryKeys,
|
||||
required _idt.ByteData recoveryProofKey,
|
||||
}) = _KeysSetupImpl;
|
||||
|
||||
factory KeysSetup.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return KeysSetup(
|
||||
salt: _is.ByteDataJsonExtension.fromJson(jsonSerialization['salt']),
|
||||
costs: _ibewtqgw.Protocol().deserialize<_ioe6tior.PasswordCosts>(
|
||||
jsonSerialization['costs'],
|
||||
),
|
||||
passwordProofKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['passwordProofKey'],
|
||||
),
|
||||
encryptionPublicKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['encryptionPublicKey'],
|
||||
),
|
||||
signingPublicKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['signingPublicKey'],
|
||||
),
|
||||
passwordKeys: _ibewtqgw.Protocol().deserialize<_i2rxumte.WrappedKeys>(
|
||||
jsonSerialization['passwordKeys'],
|
||||
),
|
||||
recoveryKeys: _ibewtqgw.Protocol().deserialize<_i2rxumte.WrappedKeys>(
|
||||
jsonSerialization['recoveryKeys'],
|
||||
),
|
||||
recoveryProofKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['recoveryProofKey'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_idt.ByteData salt;
|
||||
|
||||
_ioe6tior.PasswordCosts costs;
|
||||
|
||||
/// The public half of the password's proof key (Ed25519).
|
||||
_idt.ByteData passwordProofKey;
|
||||
|
||||
_idt.ByteData encryptionPublicKey;
|
||||
|
||||
_idt.ByteData signingPublicKey;
|
||||
|
||||
_i2rxumte.WrappedKeys passwordKeys;
|
||||
|
||||
_i2rxumte.WrappedKeys recoveryKeys;
|
||||
|
||||
/// The public half of the recovery phrase's proof key (Ed25519, from the
|
||||
/// phrase alone).
|
||||
_idt.ByteData recoveryProofKey;
|
||||
|
||||
/// Returns a shallow copy of this [KeysSetup]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
KeysSetup copyWith({
|
||||
_idt.ByteData? salt,
|
||||
_ioe6tior.PasswordCosts? costs,
|
||||
_idt.ByteData? passwordProofKey,
|
||||
_idt.ByteData? encryptionPublicKey,
|
||||
_idt.ByteData? signingPublicKey,
|
||||
_i2rxumte.WrappedKeys? passwordKeys,
|
||||
_i2rxumte.WrappedKeys? recoveryKeys,
|
||||
_idt.ByteData? recoveryProofKey,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysSetup',
|
||||
'salt': salt.toJson(),
|
||||
'costs': costs.toJson(),
|
||||
'passwordProofKey': passwordProofKey.toJson(),
|
||||
'encryptionPublicKey': encryptionPublicKey.toJson(),
|
||||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeys': passwordKeys.toJson(),
|
||||
'recoveryKeys': recoveryKeys.toJson(),
|
||||
'recoveryProofKey': recoveryProofKey.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.KeysSetup',
|
||||
'salt': salt.toJson(),
|
||||
'costs': costs.toJsonForProtocol(),
|
||||
'passwordProofKey': passwordProofKey.toJson(),
|
||||
'encryptionPublicKey': encryptionPublicKey.toJson(),
|
||||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeys': passwordKeys.toJsonForProtocol(),
|
||||
'recoveryKeys': recoveryKeys.toJsonForProtocol(),
|
||||
'recoveryProofKey': recoveryProofKey.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _KeysSetupImpl extends KeysSetup {
|
||||
_KeysSetupImpl({
|
||||
required _idt.ByteData salt,
|
||||
required _ioe6tior.PasswordCosts costs,
|
||||
required _idt.ByteData passwordProofKey,
|
||||
required _idt.ByteData encryptionPublicKey,
|
||||
required _idt.ByteData signingPublicKey,
|
||||
required _i2rxumte.WrappedKeys passwordKeys,
|
||||
required _i2rxumte.WrappedKeys recoveryKeys,
|
||||
required _idt.ByteData recoveryProofKey,
|
||||
}) : super._(
|
||||
salt: salt,
|
||||
costs: costs,
|
||||
passwordProofKey: passwordProofKey,
|
||||
encryptionPublicKey: encryptionPublicKey,
|
||||
signingPublicKey: signingPublicKey,
|
||||
passwordKeys: passwordKeys,
|
||||
recoveryKeys: recoveryKeys,
|
||||
recoveryProofKey: recoveryProofKey,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [KeysSetup]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
KeysSetup copyWith({
|
||||
_idt.ByteData? salt,
|
||||
_ioe6tior.PasswordCosts? costs,
|
||||
_idt.ByteData? passwordProofKey,
|
||||
_idt.ByteData? encryptionPublicKey,
|
||||
_idt.ByteData? signingPublicKey,
|
||||
_i2rxumte.WrappedKeys? passwordKeys,
|
||||
_i2rxumte.WrappedKeys? recoveryKeys,
|
||||
_idt.ByteData? recoveryProofKey,
|
||||
}) {
|
||||
return KeysSetup(
|
||||
salt: salt ?? this.salt.clone(),
|
||||
costs: costs ?? this.costs.copyWith(),
|
||||
passwordProofKey: passwordProofKey ?? this.passwordProofKey.clone(),
|
||||
encryptionPublicKey:
|
||||
encryptionPublicKey ?? this.encryptionPublicKey.clone(),
|
||||
signingPublicKey: signingPublicKey ?? this.signingPublicKey.clone(),
|
||||
passwordKeys: passwordKeys ?? this.passwordKeys.copyWith(),
|
||||
recoveryKeys: recoveryKeys ?? this.recoveryKeys.copyWith(),
|
||||
recoveryProofKey: recoveryProofKey ?? this.recoveryProofKey.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,9 +16,11 @@ import 'package:serverpod/serverpod.dart' as _is;
|
|||
/// An account's password as the register keeps it (ADR 0017): nothing that
|
||||
/// signs in or decrypts in the password's place. The salt and Argon2id's
|
||||
/// costs the device derives the password's keys with, SRP's verifier
|
||||
/// (v = g^x mod N, PAD to N's length), the account's public keys, and its
|
||||
/// private keys wrapped under the password's key and again under the
|
||||
/// recovery phrase's, which only a device can open.
|
||||
/// (v = g^x mod N, PAD to N's length) or, for an account that signs in with
|
||||
/// SimpleLogin, the password's proof key (Halkyon Learn's ADR 0020, D19),
|
||||
/// the account's public keys, and its private keys wrapped under the
|
||||
/// password's key and again under the recovery phrase's, which only a
|
||||
/// device can open.
|
||||
abstract class AccountPassword
|
||||
implements _is.TableRow<int?>, _is.ProtocolSerialization {
|
||||
AccountPassword._({
|
||||
|
|
@ -28,7 +30,8 @@ abstract class AccountPassword
|
|||
required this.kdfAlgorithm,
|
||||
required this.kdfOpsLimit,
|
||||
required this.kdfMemLimit,
|
||||
required this.verifier,
|
||||
this.verifier,
|
||||
this.passwordProofKey,
|
||||
required this.encryptionPublicKey,
|
||||
required this.signingPublicKey,
|
||||
required this.passwordKeysNonce,
|
||||
|
|
@ -48,7 +51,8 @@ abstract class AccountPassword
|
|||
required String kdfAlgorithm,
|
||||
required int kdfOpsLimit,
|
||||
required int kdfMemLimit,
|
||||
required _idt.ByteData verifier,
|
||||
_idt.ByteData? verifier,
|
||||
_idt.ByteData? passwordProofKey,
|
||||
required _idt.ByteData encryptionPublicKey,
|
||||
required _idt.ByteData signingPublicKey,
|
||||
required _idt.ByteData passwordKeysNonce,
|
||||
|
|
@ -71,9 +75,14 @@ abstract class AccountPassword
|
|||
kdfAlgorithm: jsonSerialization['kdfAlgorithm'] as String,
|
||||
kdfOpsLimit: jsonSerialization['kdfOpsLimit'] as int,
|
||||
kdfMemLimit: jsonSerialization['kdfMemLimit'] as int,
|
||||
verifier: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['verifier'],
|
||||
),
|
||||
verifier: jsonSerialization['verifier'] == null
|
||||
? null
|
||||
: _is.ByteDataJsonExtension.fromJson(jsonSerialization['verifier']),
|
||||
passwordProofKey: jsonSerialization['passwordProofKey'] == null
|
||||
? null
|
||||
: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['passwordProofKey'],
|
||||
),
|
||||
encryptionPublicKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['encryptionPublicKey'],
|
||||
),
|
||||
|
|
@ -134,8 +143,16 @@ abstract class AccountPassword
|
|||
/// Argon2id's memory, in bytes.
|
||||
int kdfMemLimit;
|
||||
|
||||
/// SRP's verifier, big-endian, as long as N.
|
||||
_idt.ByteData verifier;
|
||||
/// SRP's verifier, big-endian, as long as N; null for an account that
|
||||
/// signs in with SimpleLogin, which proves the password with
|
||||
/// [passwordProofKey] instead.
|
||||
_idt.ByteData? verifier;
|
||||
|
||||
/// The public half of the password's own Ed25519 proof key, derived from
|
||||
/// the password alone (halkyon_keys' `PasswordKeys.proofPublicKey`): what
|
||||
/// proves the password of an account without SRP's verifier. Null for
|
||||
/// one with it.
|
||||
_idt.ByteData? passwordProofKey;
|
||||
|
||||
/// The account's X25519 public key, which data keys are sealed to.
|
||||
_idt.ByteData encryptionPublicKey;
|
||||
|
|
@ -187,6 +204,7 @@ abstract class AccountPassword
|
|||
int? kdfOpsLimit,
|
||||
int? kdfMemLimit,
|
||||
_idt.ByteData? verifier,
|
||||
_idt.ByteData? passwordProofKey,
|
||||
_idt.ByteData? encryptionPublicKey,
|
||||
_idt.ByteData? signingPublicKey,
|
||||
_idt.ByteData? passwordKeysNonce,
|
||||
|
|
@ -208,7 +226,9 @@ abstract class AccountPassword
|
|||
'kdfAlgorithm': kdfAlgorithm,
|
||||
'kdfOpsLimit': kdfOpsLimit,
|
||||
'kdfMemLimit': kdfMemLimit,
|
||||
'verifier': verifier.toJson(),
|
||||
if (verifier != null) 'verifier': verifier?.toJson(),
|
||||
if (passwordProofKey != null)
|
||||
'passwordProofKey': passwordProofKey?.toJson(),
|
||||
'encryptionPublicKey': encryptionPublicKey.toJson(),
|
||||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeysNonce': passwordKeysNonce.toJson(),
|
||||
|
|
@ -267,7 +287,8 @@ class _AccountPasswordImpl extends AccountPassword {
|
|||
required String kdfAlgorithm,
|
||||
required int kdfOpsLimit,
|
||||
required int kdfMemLimit,
|
||||
required _idt.ByteData verifier,
|
||||
_idt.ByteData? verifier,
|
||||
_idt.ByteData? passwordProofKey,
|
||||
required _idt.ByteData encryptionPublicKey,
|
||||
required _idt.ByteData signingPublicKey,
|
||||
required _idt.ByteData passwordKeysNonce,
|
||||
|
|
@ -286,6 +307,7 @@ class _AccountPasswordImpl extends AccountPassword {
|
|||
kdfOpsLimit: kdfOpsLimit,
|
||||
kdfMemLimit: kdfMemLimit,
|
||||
verifier: verifier,
|
||||
passwordProofKey: passwordProofKey,
|
||||
encryptionPublicKey: encryptionPublicKey,
|
||||
signingPublicKey: signingPublicKey,
|
||||
passwordKeysNonce: passwordKeysNonce,
|
||||
|
|
@ -309,7 +331,8 @@ class _AccountPasswordImpl extends AccountPassword {
|
|||
String? kdfAlgorithm,
|
||||
int? kdfOpsLimit,
|
||||
int? kdfMemLimit,
|
||||
_idt.ByteData? verifier,
|
||||
Object? verifier = _Undefined,
|
||||
Object? passwordProofKey = _Undefined,
|
||||
_idt.ByteData? encryptionPublicKey,
|
||||
_idt.ByteData? signingPublicKey,
|
||||
_idt.ByteData? passwordKeysNonce,
|
||||
|
|
@ -328,7 +351,10 @@ class _AccountPasswordImpl extends AccountPassword {
|
|||
kdfAlgorithm: kdfAlgorithm ?? this.kdfAlgorithm,
|
||||
kdfOpsLimit: kdfOpsLimit ?? this.kdfOpsLimit,
|
||||
kdfMemLimit: kdfMemLimit ?? this.kdfMemLimit,
|
||||
verifier: verifier ?? this.verifier.clone(),
|
||||
verifier: verifier is _idt.ByteData? ? verifier : this.verifier?.clone(),
|
||||
passwordProofKey: passwordProofKey is _idt.ByteData?
|
||||
? passwordProofKey
|
||||
: this.passwordProofKey?.clone(),
|
||||
encryptionPublicKey:
|
||||
encryptionPublicKey ?? this.encryptionPublicKey.clone(),
|
||||
signingPublicKey: signingPublicKey ?? this.signingPublicKey.clone(),
|
||||
|
|
@ -381,11 +407,19 @@ class AccountPasswordUpdateTable extends _is.UpdateTable<AccountPasswordTable> {
|
|||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> verifier(_idt.ByteData value) =>
|
||||
_is.ColumnValue(
|
||||
table.verifier,
|
||||
value,
|
||||
);
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> verifier(
|
||||
_idt.ByteData? value,
|
||||
) => _is.ColumnValue(
|
||||
table.verifier,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> passwordProofKey(
|
||||
_idt.ByteData? value,
|
||||
) => _is.ColumnValue(
|
||||
table.passwordProofKey,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> encryptionPublicKey(
|
||||
_idt.ByteData value,
|
||||
|
|
@ -484,6 +518,10 @@ class AccountPasswordTable extends _is.Table<int?> {
|
|||
'verifier',
|
||||
this,
|
||||
);
|
||||
passwordProofKey = _is.ColumnByteData(
|
||||
'passwordProofKey',
|
||||
this,
|
||||
);
|
||||
encryptionPublicKey = _is.ColumnByteData(
|
||||
'encryptionPublicKey',
|
||||
this,
|
||||
|
|
@ -543,9 +581,17 @@ class AccountPasswordTable extends _is.Table<int?> {
|
|||
/// Argon2id's memory, in bytes.
|
||||
late final _is.ColumnInt kdfMemLimit;
|
||||
|
||||
/// SRP's verifier, big-endian, as long as N.
|
||||
/// SRP's verifier, big-endian, as long as N; null for an account that
|
||||
/// signs in with SimpleLogin, which proves the password with
|
||||
/// [passwordProofKey] instead.
|
||||
late final _is.ColumnByteData verifier;
|
||||
|
||||
/// The public half of the password's own Ed25519 proof key, derived from
|
||||
/// the password alone (halkyon_keys' `PasswordKeys.proofPublicKey`): what
|
||||
/// proves the password of an account without SRP's verifier. Null for
|
||||
/// one with it.
|
||||
late final _is.ColumnByteData passwordProofKey;
|
||||
|
||||
/// The account's X25519 public key, which data keys are sealed to.
|
||||
late final _is.ColumnByteData encryptionPublicKey;
|
||||
|
||||
|
|
@ -591,6 +637,7 @@ class AccountPasswordTable extends _is.Table<int?> {
|
|||
kdfOpsLimit,
|
||||
kdfMemLimit,
|
||||
verifier,
|
||||
passwordProofKey,
|
||||
encryptionPublicKey,
|
||||
signingPublicKey,
|
||||
passwordKeysNonce,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue