This commit is contained in:
Elias Bonnici 2024-03-27 16:52:09 +01:00
commit f674836397
No known key found for this signature in database
GPG Key ID: 5EAC28EA3F980CCF
29 changed files with 1145 additions and 185 deletions

View File

@ -49,8 +49,10 @@ data class Info(
val isNfc: Boolean, val isNfc: Boolean,
@SerialName("usb_pid") @SerialName("usb_pid")
val usbPid: Int?, val usbPid: Int?,
@SerialName("pin_complexity")
val pinComplexity: Boolean,
@SerialName("supported_capabilities") @SerialName("supported_capabilities")
val supportedCapabilities: Capabilities val supportedCapabilities: Capabilities,
) { ) {
constructor(name: String, isNfc: Boolean, usbPid: Int?, deviceInfo: DeviceInfo) : this( constructor(name: String, isNfc: Boolean, usbPid: Int?, deviceInfo: DeviceInfo) : this(
config = Config(deviceInfo.config), config = Config(deviceInfo.config),
@ -63,6 +65,7 @@ data class Info(
name = name, name = name,
isNfc = isNfc, isNfc = isNfc,
usbPid = usbPid, usbPid = usbPid,
pinComplexity = deviceInfo.pinComplexity,
supportedCapabilities = Capabilities( supportedCapabilities = Capabilities(
nfc = deviceInfo.capabilitiesFor(Transport.NFC), nfc = deviceInfo.capabilitiesFor(Transport.NFC),
usb = deviceInfo.capabilitiesFor(Transport.USB), usb = deviceInfo.capabilitiesFor(Transport.USB),

View File

@ -18,5 +18,6 @@ val UnknownDevice = Info(
name = "Unrecognized device", name = "Unrecognized device",
isNfc = false, isNfc = false,
usbPid = null, usbPid = null,
pinComplexity = false,
supportedCapabilities = Capabilities() supportedCapabilities = Capabilities()
) )

View File

@ -313,18 +313,28 @@ class FidoManager(
} catch (ctapException: CtapException) { } catch (ctapException: CtapException) {
if (ctapException.ctapError == CtapException.ERR_PIN_INVALID || if (ctapException.ctapError == CtapException.ERR_PIN_INVALID ||
ctapException.ctapError == CtapException.ERR_PIN_BLOCKED || ctapException.ctapError == CtapException.ERR_PIN_BLOCKED ||
ctapException.ctapError == CtapException.ERR_PIN_AUTH_BLOCKED ctapException.ctapError == CtapException.ERR_PIN_AUTH_BLOCKED ||
ctapException.ctapError == CtapException.ERR_PIN_POLICY_VIOLATION
) { ) {
pinStore.setPin(null) pinStore.setPin(null)
fidoViewModel.updateCredentials(emptyList()) fidoViewModel.updateCredentials(emptyList())
val pinRetriesResult = clientPin.pinRetries
JSONObject( if (ctapException.ctapError == CtapException.ERR_PIN_POLICY_VIOLATION) {
mapOf( JSONObject(
"success" to false, mapOf(
"pinRetries" to pinRetriesResult.count, "success" to false,
"authBlocked" to (ctapException.ctapError == CtapException.ERR_PIN_AUTH_BLOCKED) "pinViolation" to true
) )
).toString() ).toString()
} else {
JSONObject(
mapOf(
"success" to false,
"pinRetries" to clientPin.pinRetries.count,
"authBlocked" to (ctapException.ctapError == CtapException.ERR_PIN_AUTH_BLOCKED),
)
).toString()
}
} else { } else {
throw ctapException throw ctapException
} }

View File

@ -74,6 +74,7 @@ class SkyHelper(private val compatUtil: CompatUtil) {
name = (device.usbDevice.productName ?: "Yubico Security Key"), name = (device.usbDevice.productName ?: "Yubico Security Key"),
isNfc = false, isNfc = false,
usbPid = pid.value, usbPid = pid.value,
pinComplexity = false,
supportedCapabilities = Capabilities(usb = 0) supportedCapabilities = Capabilities(usb = 0)
) )
} }

View File

@ -75,6 +75,11 @@ class AuthRequiredException(RpcException):
super().__init__("auth-required", "Authentication is required") super().__init__("auth-required", "Authentication is required")
class PinComplexityException(RpcException):
def __init__(self):
super().__init__("pin-complexity", "PIN does not meet complexity requirements")
class ChildResetException(Exception): class ChildResetException(Exception):
def __init__(self, message): def __init__(self, message):
self.message = message self.message = message

View File

@ -19,6 +19,7 @@ from .base import (
RpcException, RpcException,
TimeoutException, TimeoutException,
AuthRequiredException, AuthRequiredException,
PinComplexityException,
) )
from fido2.ctap import CtapError from fido2.ctap import CtapError
from fido2.ctap2 import Ctap2, ClientPin from fido2.ctap2 import Ctap2, ClientPin
@ -76,6 +77,8 @@ def _handle_pin_error(e, client_pin):
raise PinValidationException( raise PinValidationException(
pin_retries, e.code == CtapError.ERR.PIN_AUTH_BLOCKED pin_retries, e.code == CtapError.ERR.PIN_AUTH_BLOCKED
) )
if e.code == CtapError.ERR.PIN_POLICY_VIOLATION:
raise PinComplexityException()
raise e raise e

View File

@ -20,6 +20,7 @@ from .base import (
ChildResetException, ChildResetException,
TimeoutException, TimeoutException,
AuthRequiredException, AuthRequiredException,
PinComplexityException,
) )
from yubikit.core import NotSupportedError, BadResponseError, InvalidPinError from yubikit.core import NotSupportedError, BadResponseError, InvalidPinError
from yubikit.core.smartcard import ApduError, SW from yubikit.core.smartcard import ApduError, SW
@ -80,6 +81,15 @@ class GENERATE_TYPE(str, Enum):
CERTIFICATE = "certificate" CERTIFICATE = "certificate"
def _handle_pin_puk_error(e):
if isinstance(e, ApduError):
if e.sw == SW.CONDITIONS_NOT_SATISFIED:
raise PinComplexityException()
if isinstance(e, InvalidPinError):
raise InvalidPinException(cause=e)
raise e
class PivNode(RpcNode): class PivNode(RpcNode):
def __init__(self, connection): def __init__(self, connection):
super().__init__() super().__init__()
@ -208,21 +218,30 @@ class PivNode(RpcNode):
def change_pin(self, params, event, signal): def change_pin(self, params, event, signal):
old_pin = params.pop("pin") old_pin = params.pop("pin")
new_pin = params.pop("new_pin") new_pin = params.pop("new_pin")
pivman_change_pin(self.session, old_pin, new_pin) try:
pivman_change_pin(self.session, old_pin, new_pin)
except Exception as e:
_handle_pin_puk_error(e)
return dict() return dict()
@action @action
def change_puk(self, params, event, signal): def change_puk(self, params, event, signal):
old_puk = params.pop("puk") old_puk = params.pop("puk")
new_puk = params.pop("new_puk") new_puk = params.pop("new_puk")
self.session.change_puk(old_puk, new_puk) try:
self.session.change_puk(old_puk, new_puk)
except Exception as e:
_handle_pin_puk_error(e)
return dict() return dict()
@action @action
def unblock_pin(self, params, event, signal): def unblock_pin(self, params, event, signal):
puk = params.pop("puk") puk = params.pop("puk")
new_pin = params.pop("new_pin") new_pin = params.pop("new_pin")
self.session.unblock_pin(puk, new_pin) try:
self.session.unblock_pin(puk, new_pin)
except Exception as e:
_handle_pin_puk_error(e)
return dict() return dict()
@action @action

12
helper/poetry.lock generated
View File

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
[[package]] [[package]]
name = "altgraph" name = "altgraph"
@ -717,13 +717,13 @@ files = [
[[package]] [[package]]
name = "yubikey-manager" name = "yubikey-manager"
version = "5.3.0" version = "5.4.0"
description = "Tool for managing your YubiKey configuration." description = "Tool for managing your YubiKey configuration."
optional = false optional = false
python-versions = ">=3.8,<4.0" python-versions = "<4.0,>=3.8"
files = [ files = [
{file = "yubikey_manager-5.3.0-py3-none-any.whl", hash = "sha256:9a809620f5c910c1047323570095e10b885002f6b0a2e4d8ced7f62d7c2ce628"}, {file = "yubikey_manager-5.4.0-py3-none-any.whl", hash = "sha256:d53acb06c4028a833be7a05ca4145833afef1affa67aaab4347bc50ecce37985"},
{file = "yubikey_manager-5.3.0.tar.gz", hash = "sha256:5492c36a10ce6a5995b8ea1d32cf5bd60db7587201b2aa3e63e0c1da2334b8b6"}, {file = "yubikey_manager-5.4.0.tar.gz", hash = "sha256:53726a186722cd2683b2f5fd781fc0a2861f47ce62ba9d3527960832c8fabec8"},
] ]
[package.dependencies] [package.dependencies]
@ -787,4 +787,4 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.8" python-versions = "^3.8"
content-hash = "6664f12e752d8b41c996d11e43a8572ed47f7fbfaaf84fa894be725fe2208d80" content-hash = "7543cc0ac90ea4eb701a7f52321d831bfe05c65e0ae896a6107eb7a9540d8543"

View File

@ -10,7 +10,7 @@ packages = [
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.8" python = "^3.8"
yubikey-manager = "^5.2" yubikey-manager = "^5.4"
mss = "^9.0.1" mss = "^9.0.1"
Pillow = "^10.2.0" Pillow = "^10.2.0"
zxing-cpp = "^2.2.0" zxing-cpp = "^2.2.0"

View File

@ -109,15 +109,18 @@ class _FidoStateNotifier extends FidoStateNotifier {
}, },
)); ));
if (response['success'] == true) { if (response['success'] == true) {
_log.debug('FIDO pin set/change successful'); _log.debug('FIDO PIN set/change successful');
return PinResult.success(); return PinResult.success();
} }
_log.debug('FIDO pin set/change failed'); if (response['pinViolation'] == true) {
return PinResult.failed( _log.debug('FIDO PIN violation');
response['pinRetries'], return PinResult.failed(const FidoPinFailureReason.weakPin());
response['authBlocked'], }
);
_log.debug('FIDO PIN set/change failed');
return PinResult.failed(FidoPinFailureReason.invalidPin(
response['pinRetries'], response['authBlocked']));
} on PlatformException catch (pe) { } on PlatformException catch (pe) {
var decodedException = pe.decode(); var decodedException = pe.decode();
if (decodedException is CancellationException) { if (decodedException is CancellationException) {
@ -141,10 +144,8 @@ class _FidoStateNotifier extends FidoStateNotifier {
} }
_log.debug('FIDO applet unlock failed'); _log.debug('FIDO applet unlock failed');
return PinResult.failed( return PinResult.failed(FidoPinFailureReason.invalidPin(
response['pinRetries'], response['pinRetries'], response['authBlocked']));
response['authBlocked'],
);
} on PlatformException catch (pe) { } on PlatformException catch (pe) {
var decodedException = pe.decode(); var decodedException = pe.decode();
if (decodedException is! CancellationException) { if (decodedException is! CancellationException) {

View File

@ -153,7 +153,11 @@ class _DesktopFidoStateNotifier extends FidoStateNotifier {
return unlock(newPin); return unlock(newPin);
} on RpcError catch (e) { } on RpcError catch (e) {
if (e.status == 'pin-validation') { if (e.status == 'pin-validation') {
return PinResult.failed(e.body['retries'], e.body['auth_blocked']); return PinResult.failed(FidoPinFailureReason.invalidPin(
e.body['retries'], e.body['auth_blocked']));
}
if (e.status == 'pin-complexity') {
return PinResult.failed(const FidoPinFailureReason.weakPin());
} }
rethrow; rethrow;
} }
@ -172,7 +176,8 @@ class _DesktopFidoStateNotifier extends FidoStateNotifier {
} on RpcError catch (e) { } on RpcError catch (e) {
if (e.status == 'pin-validation') { if (e.status == 'pin-validation') {
_pinController.state = null; _pinController.state = null;
return PinResult.failed(e.body['retries'], e.body['auth_blocked']); return PinResult.failed(FidoPinFailureReason.invalidPin(
e.body['retries'], e.body['auth_blocked']));
} }
rethrow; rethrow;
} }

View File

@ -233,7 +233,12 @@ class _DesktopPivStateNotifier extends PivStateNotifier {
return const PinVerificationStatus.success(); return const PinVerificationStatus.success();
} on RpcError catch (e) { } on RpcError catch (e) {
if (e.status == 'invalid-pin') { if (e.status == 'invalid-pin') {
return PinVerificationStatus.failure(e.body['attempts_remaining']); return PinVerificationStatus.failure(
PivPinFailureReason.invalidPin(e.body['attempts_remaining']));
}
if (e.status == 'pin-complexity') {
return PinVerificationStatus.failure(
const PivPinFailureReason.weakPin());
} }
rethrow; rethrow;
} finally { } finally {
@ -251,7 +256,12 @@ class _DesktopPivStateNotifier extends PivStateNotifier {
return const PinVerificationStatus.success(); return const PinVerificationStatus.success();
} on RpcError catch (e) { } on RpcError catch (e) {
if (e.status == 'invalid-pin') { if (e.status == 'invalid-pin') {
return PinVerificationStatus.failure(e.body['attempts_remaining']); return PinVerificationStatus.failure(
PivPinFailureReason.invalidPin(e.body['attempts_remaining']));
}
if (e.status == 'pin-complexity') {
return PinVerificationStatus.failure(
const PivPinFailureReason.weakPin());
} }
rethrow; rethrow;
} finally { } finally {
@ -286,7 +296,12 @@ class _DesktopPivStateNotifier extends PivStateNotifier {
return const PinVerificationStatus.success(); return const PinVerificationStatus.success();
} on RpcError catch (e) { } on RpcError catch (e) {
if (e.status == 'invalid-pin') { if (e.status == 'invalid-pin') {
return PinVerificationStatus.failure(e.body['attempts_remaining']); return PinVerificationStatus.failure(
PivPinFailureReason.invalidPin(e.body['attempts_remaining']));
}
if (e.status == 'pin-complexity') {
return PinVerificationStatus.failure(
const PivPinFailureReason.weakPin());
} }
rethrow; rethrow;
} finally { } finally {

View File

@ -52,7 +52,14 @@ class FidoState with _$FidoState {
@freezed @freezed
class PinResult with _$PinResult { class PinResult with _$PinResult {
factory PinResult.success() = _PinSuccess; factory PinResult.success() = _PinSuccess;
factory PinResult.failed(int retries, bool authBlocked) = _PinFailure; factory PinResult.failed(FidoPinFailureReason reason) = _PinFailure;
}
@freezed
class FidoPinFailureReason with _$FidoPinFailureReason {
factory FidoPinFailureReason.invalidPin(int retries, bool authBlocked) =
FidoInvalidPin;
const factory FidoPinFailureReason.weakPin() = FidoWeakPin;
} }
@freezed @freezed

View File

@ -184,19 +184,19 @@ mixin _$PinResult {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() success, required TResult Function() success,
required TResult Function(int retries, bool authBlocked) failed, required TResult Function(FidoPinFailureReason reason) failed,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? success, TResult? Function()? success,
TResult? Function(int retries, bool authBlocked)? failed, TResult? Function(FidoPinFailureReason reason)? failed,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? success, TResult Function()? success,
TResult Function(int retries, bool authBlocked)? failed, TResult Function(FidoPinFailureReason reason)? failed,
required TResult orElse(), required TResult orElse(),
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@ -277,7 +277,7 @@ class _$PinSuccessImpl implements _PinSuccess {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() success, required TResult Function() success,
required TResult Function(int retries, bool authBlocked) failed, required TResult Function(FidoPinFailureReason reason) failed,
}) { }) {
return success(); return success();
} }
@ -286,7 +286,7 @@ class _$PinSuccessImpl implements _PinSuccess {
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? success, TResult? Function()? success,
TResult? Function(int retries, bool authBlocked)? failed, TResult? Function(FidoPinFailureReason reason)? failed,
}) { }) {
return success?.call(); return success?.call();
} }
@ -295,7 +295,7 @@ class _$PinSuccessImpl implements _PinSuccess {
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? success, TResult Function()? success,
TResult Function(int retries, bool authBlocked)? failed, TResult Function(FidoPinFailureReason reason)? failed,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (success != null) { if (success != null) {
@ -346,7 +346,9 @@ abstract class _$$PinFailureImplCopyWith<$Res> {
_$PinFailureImpl value, $Res Function(_$PinFailureImpl) then) = _$PinFailureImpl value, $Res Function(_$PinFailureImpl) then) =
__$$PinFailureImplCopyWithImpl<$Res>; __$$PinFailureImplCopyWithImpl<$Res>;
@useResult @useResult
$Res call({int retries, bool authBlocked}); $Res call({FidoPinFailureReason reason});
$FidoPinFailureReasonCopyWith<$Res> get reason;
} }
/// @nodoc /// @nodoc
@ -360,35 +362,36 @@ class __$$PinFailureImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? retries = null, Object? reason = null,
Object? authBlocked = null,
}) { }) {
return _then(_$PinFailureImpl( return _then(_$PinFailureImpl(
null == retries null == reason
? _value.retries ? _value.reason
: retries // ignore: cast_nullable_to_non_nullable : reason // ignore: cast_nullable_to_non_nullable
as int, as FidoPinFailureReason,
null == authBlocked
? _value.authBlocked
: authBlocked // ignore: cast_nullable_to_non_nullable
as bool,
)); ));
} }
@override
@pragma('vm:prefer-inline')
$FidoPinFailureReasonCopyWith<$Res> get reason {
return $FidoPinFailureReasonCopyWith<$Res>(_value.reason, (value) {
return _then(_value.copyWith(reason: value));
});
}
} }
/// @nodoc /// @nodoc
class _$PinFailureImpl implements _PinFailure { class _$PinFailureImpl implements _PinFailure {
_$PinFailureImpl(this.retries, this.authBlocked); _$PinFailureImpl(this.reason);
@override @override
final int retries; final FidoPinFailureReason reason;
@override
final bool authBlocked;
@override @override
String toString() { String toString() {
return 'PinResult.failed(retries: $retries, authBlocked: $authBlocked)'; return 'PinResult.failed(reason: $reason)';
} }
@override @override
@ -396,13 +399,11 @@ class _$PinFailureImpl implements _PinFailure {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$PinFailureImpl && other is _$PinFailureImpl &&
(identical(other.retries, retries) || other.retries == retries) && (identical(other.reason, reason) || other.reason == reason));
(identical(other.authBlocked, authBlocked) ||
other.authBlocked == authBlocked));
} }
@override @override
int get hashCode => Object.hash(runtimeType, retries, authBlocked); int get hashCode => Object.hash(runtimeType, reason);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@ -414,29 +415,29 @@ class _$PinFailureImpl implements _PinFailure {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() success, required TResult Function() success,
required TResult Function(int retries, bool authBlocked) failed, required TResult Function(FidoPinFailureReason reason) failed,
}) { }) {
return failed(retries, authBlocked); return failed(reason);
} }
@override @override
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? success, TResult? Function()? success,
TResult? Function(int retries, bool authBlocked)? failed, TResult? Function(FidoPinFailureReason reason)? failed,
}) { }) {
return failed?.call(retries, authBlocked); return failed?.call(reason);
} }
@override @override
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? success, TResult Function()? success,
TResult Function(int retries, bool authBlocked)? failed, TResult Function(FidoPinFailureReason reason)? failed,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (failed != null) { if (failed != null) {
return failed(retries, authBlocked); return failed(reason);
} }
return orElse(); return orElse();
} }
@ -474,16 +475,322 @@ class _$PinFailureImpl implements _PinFailure {
} }
abstract class _PinFailure implements PinResult { abstract class _PinFailure implements PinResult {
factory _PinFailure(final int retries, final bool authBlocked) = factory _PinFailure(final FidoPinFailureReason reason) = _$PinFailureImpl;
_$PinFailureImpl;
FidoPinFailureReason get reason;
@JsonKey(ignore: true)
_$$PinFailureImplCopyWith<_$PinFailureImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$FidoPinFailureReason {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int retries, bool authBlocked) invalidPin,
required TResult Function() weakPin,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int retries, bool authBlocked)? invalidPin,
TResult? Function()? weakPin,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int retries, bool authBlocked)? invalidPin,
TResult Function()? weakPin,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(FidoInvalidPin value) invalidPin,
required TResult Function(FidoWeakPin value) weakPin,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(FidoInvalidPin value)? invalidPin,
TResult? Function(FidoWeakPin value)? weakPin,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(FidoInvalidPin value)? invalidPin,
TResult Function(FidoWeakPin value)? weakPin,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $FidoPinFailureReasonCopyWith<$Res> {
factory $FidoPinFailureReasonCopyWith(FidoPinFailureReason value,
$Res Function(FidoPinFailureReason) then) =
_$FidoPinFailureReasonCopyWithImpl<$Res, FidoPinFailureReason>;
}
/// @nodoc
class _$FidoPinFailureReasonCopyWithImpl<$Res,
$Val extends FidoPinFailureReason>
implements $FidoPinFailureReasonCopyWith<$Res> {
_$FidoPinFailureReasonCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
}
/// @nodoc
abstract class _$$FidoInvalidPinImplCopyWith<$Res> {
factory _$$FidoInvalidPinImplCopyWith(_$FidoInvalidPinImpl value,
$Res Function(_$FidoInvalidPinImpl) then) =
__$$FidoInvalidPinImplCopyWithImpl<$Res>;
@useResult
$Res call({int retries, bool authBlocked});
}
/// @nodoc
class __$$FidoInvalidPinImplCopyWithImpl<$Res>
extends _$FidoPinFailureReasonCopyWithImpl<$Res, _$FidoInvalidPinImpl>
implements _$$FidoInvalidPinImplCopyWith<$Res> {
__$$FidoInvalidPinImplCopyWithImpl(
_$FidoInvalidPinImpl _value, $Res Function(_$FidoInvalidPinImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? retries = null,
Object? authBlocked = null,
}) {
return _then(_$FidoInvalidPinImpl(
null == retries
? _value.retries
: retries // ignore: cast_nullable_to_non_nullable
as int,
null == authBlocked
? _value.authBlocked
: authBlocked // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
class _$FidoInvalidPinImpl implements FidoInvalidPin {
_$FidoInvalidPinImpl(this.retries, this.authBlocked);
@override
final int retries;
@override
final bool authBlocked;
@override
String toString() {
return 'FidoPinFailureReason.invalidPin(retries: $retries, authBlocked: $authBlocked)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$FidoInvalidPinImpl &&
(identical(other.retries, retries) || other.retries == retries) &&
(identical(other.authBlocked, authBlocked) ||
other.authBlocked == authBlocked));
}
@override
int get hashCode => Object.hash(runtimeType, retries, authBlocked);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$FidoInvalidPinImplCopyWith<_$FidoInvalidPinImpl> get copyWith =>
__$$FidoInvalidPinImplCopyWithImpl<_$FidoInvalidPinImpl>(
this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int retries, bool authBlocked) invalidPin,
required TResult Function() weakPin,
}) {
return invalidPin(retries, authBlocked);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int retries, bool authBlocked)? invalidPin,
TResult? Function()? weakPin,
}) {
return invalidPin?.call(retries, authBlocked);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int retries, bool authBlocked)? invalidPin,
TResult Function()? weakPin,
required TResult orElse(),
}) {
if (invalidPin != null) {
return invalidPin(retries, authBlocked);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(FidoInvalidPin value) invalidPin,
required TResult Function(FidoWeakPin value) weakPin,
}) {
return invalidPin(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(FidoInvalidPin value)? invalidPin,
TResult? Function(FidoWeakPin value)? weakPin,
}) {
return invalidPin?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(FidoInvalidPin value)? invalidPin,
TResult Function(FidoWeakPin value)? weakPin,
required TResult orElse(),
}) {
if (invalidPin != null) {
return invalidPin(this);
}
return orElse();
}
}
abstract class FidoInvalidPin implements FidoPinFailureReason {
factory FidoInvalidPin(final int retries, final bool authBlocked) =
_$FidoInvalidPinImpl;
int get retries; int get retries;
bool get authBlocked; bool get authBlocked;
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$PinFailureImplCopyWith<_$PinFailureImpl> get copyWith => _$$FidoInvalidPinImplCopyWith<_$FidoInvalidPinImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
/// @nodoc
abstract class _$$FidoWeakPinImplCopyWith<$Res> {
factory _$$FidoWeakPinImplCopyWith(
_$FidoWeakPinImpl value, $Res Function(_$FidoWeakPinImpl) then) =
__$$FidoWeakPinImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$FidoWeakPinImplCopyWithImpl<$Res>
extends _$FidoPinFailureReasonCopyWithImpl<$Res, _$FidoWeakPinImpl>
implements _$$FidoWeakPinImplCopyWith<$Res> {
__$$FidoWeakPinImplCopyWithImpl(
_$FidoWeakPinImpl _value, $Res Function(_$FidoWeakPinImpl) _then)
: super(_value, _then);
}
/// @nodoc
class _$FidoWeakPinImpl implements FidoWeakPin {
const _$FidoWeakPinImpl();
@override
String toString() {
return 'FidoPinFailureReason.weakPin()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$FidoWeakPinImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int retries, bool authBlocked) invalidPin,
required TResult Function() weakPin,
}) {
return weakPin();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int retries, bool authBlocked)? invalidPin,
TResult? Function()? weakPin,
}) {
return weakPin?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int retries, bool authBlocked)? invalidPin,
TResult Function()? weakPin,
required TResult orElse(),
}) {
if (weakPin != null) {
return weakPin();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(FidoInvalidPin value) invalidPin,
required TResult Function(FidoWeakPin value) weakPin,
}) {
return weakPin(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(FidoInvalidPin value)? invalidPin,
TResult? Function(FidoWeakPin value)? weakPin,
}) {
return weakPin?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(FidoInvalidPin value)? invalidPin,
TResult Function(FidoWeakPin value)? weakPin,
required TResult orElse(),
}) {
if (weakPin != null) {
return weakPin(this);
}
return orElse();
}
}
abstract class FidoWeakPin implements FidoPinFailureReason {
const factory FidoWeakPin() = _$FidoWeakPinImpl;
}
Fingerprint _$FingerprintFromJson(Map<String, dynamic> json) { Fingerprint _$FingerprintFromJson(Map<String, dynamic> json) {
return _Fingerprint.fromJson(json); return _Fingerprint.fromJson(json);
} }

View File

@ -48,7 +48,8 @@ class FidoPinDialog extends ConsumerStatefulWidget {
class _FidoPinDialogState extends ConsumerState<FidoPinDialog> { class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
final _currentPinController = TextEditingController(); final _currentPinController = TextEditingController();
final _currentPinFocus = FocusNode(); final _currentPinFocus = FocusNode();
String _newPin = ''; final _newPinController = TextEditingController();
final _newPinFocus = FocusNode();
String _confirmPin = ''; String _confirmPin = '';
String? _currentPinError; String? _currentPinError;
String? _newPinError; String? _newPinError;
@ -63,6 +64,8 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
void dispose() { void dispose() {
_currentPinController.dispose(); _currentPinController.dispose();
_currentPinFocus.dispose(); _currentPinFocus.dispose();
_newPinController.dispose();
_newPinFocus.dispose();
super.dispose(); super.dispose();
} }
@ -77,8 +80,13 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
: (widget.state.forcePinChange ? 4 : widget.state.minPinLength); : (widget.state.forcePinChange ? 4 : widget.state.minPinLength);
final currentPinLenOk = final currentPinLenOk =
_currentPinController.text.length >= currentMinPinLen; _currentPinController.text.length >= currentMinPinLen;
final newPinLenOk = _newPin.length >= minPinLength; final newPinLenOk = _newPinController.text.length >= minPinLength;
final isValid = currentPinLenOk && newPinLenOk && _newPin == _confirmPin; final isValid =
currentPinLenOk && newPinLenOk && _newPinController.text == _confirmPin;
final hasPinComplexity =
ref.read(currentDeviceDataProvider).valueOrNull?.info.pinComplexity ??
false;
return ResponsiveDialog( return ResponsiveDialog(
title: Text(hasPin ? l10n.s_change_pin : l10n.s_set_pin), title: Text(hasPin ? l10n.s_change_pin : l10n.s_set_pin),
@ -130,11 +138,15 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
}, },
).init(), ).init(),
], ],
Text(l10n.p_enter_new_fido2_pin(minPinLength)), Text(hasPinComplexity
? l10n.p_enter_new_fido2_pin_complexity_active(
minPinLength, 2, '123456')
: l10n.p_enter_new_fido2_pin(minPinLength)),
// TODO: Set max characters based on UTF-8 bytes // TODO: Set max characters based on UTF-8 bytes
AppTextFormField( AppTextFormField(
key: newPin, key: newPin,
initialValue: _newPin, controller: _newPinController,
focusNode: _newPinFocus,
autofocus: !hasPin, autofocus: !hasPin,
obscureText: _isObscureNew, obscureText: _isObscureNew,
autofillHints: const [AutofillHints.password], autofillHints: const [AutofillHints.password],
@ -160,7 +172,6 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_newIsWrong = false; _newIsWrong = false;
_newPin = value;
}); });
}, },
).init(), ).init(),
@ -186,10 +197,11 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
_isObscureConfirm ? l10n.s_show_pin : l10n.s_hide_pin, _isObscureConfirm ? l10n.s_show_pin : l10n.s_hide_pin,
), ),
enabled: !_isBlocked && currentPinLenOk && newPinLenOk, enabled: !_isBlocked && currentPinLenOk && newPinLenOk,
errorText: _newPin.length == _confirmPin.length && errorText:
_newPin != _confirmPin _newPinController.text.length == _confirmPin.length &&
? l10n.l_pin_mismatch _newPinController.text != _confirmPin
: null, ? l10n.l_pin_mismatch
: null,
helperText: '', // Prevents resizing when errorText shown helperText: '', // Prevents resizing when errorText shown
), ),
onChanged: (value) { onChanged: (value) {
@ -219,28 +231,47 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
final oldPin = _currentPinController.text.isNotEmpty final oldPin = _currentPinController.text.isNotEmpty
? _currentPinController.text ? _currentPinController.text
: null; : null;
final newPin = _newPinController.text;
try { try {
final result = await ref final result = await ref
.read(fidoStateProvider(widget.devicePath).notifier) .read(fidoStateProvider(widget.devicePath).notifier)
.setPin(_newPin, oldPin: oldPin); .setPin(newPin, oldPin: oldPin);
result.when(success: () { result.whenOrNull(
Navigator.of(context).pop(true); success: () {
showMessage(context, l10n.s_pin_set); Navigator.of(context).pop(true);
}, failed: (retries, authBlocked) { showMessage(context, l10n.s_pin_set);
setState(() { },
_currentPinController.selection = TextSelection( failed: (reason) {
baseOffset: 0, extentOffset: _currentPinController.text.length); reason.when(
_currentPinFocus.requestFocus(); invalidPin: (retries, authBlocked) {
if (authBlocked) { _currentPinController.selection = TextSelection(
_currentPinError = l10n.l_pin_soft_locked; baseOffset: 0,
_currentIsWrong = true; extentOffset: _currentPinController.text.length);
_isBlocked = true; _currentPinFocus.requestFocus();
} else { setState(() {
_currentPinError = l10n.l_wrong_pin_attempts_remaining(retries); if (authBlocked) {
_currentIsWrong = true; _currentPinError = l10n.l_pin_soft_locked;
} _currentIsWrong = true;
}); _isBlocked = true;
}); } else {
_currentPinError =
l10n.l_wrong_pin_attempts_remaining(retries);
_currentIsWrong = true;
}
});
},
weakPin: () {
_newPinController.selection = TextSelection(
baseOffset: 0, extentOffset: _newPinController.text.length);
_newPinFocus.requestFocus();
setState(() {
_newPinError = l10n.p_pin_puk_complexity_failure(l10n.s_pin);
_newIsWrong = true;
});
},
);
},
);
} on CancellationException catch (_) { } on CancellationException catch (_) {
// ignored // ignored
} catch (e) { } catch (e) {

View File

@ -60,15 +60,20 @@ class _PinEntryFormState extends ConsumerState<PinEntryForm> {
final result = await ref final result = await ref
.read(fidoStateProvider(widget._deviceNode.path).notifier) .read(fidoStateProvider(widget._deviceNode.path).notifier)
.unlock(_pinController.text); .unlock(_pinController.text);
result.whenOrNull(failed: (retries, authBlocked) { result.whenOrNull(failed: (reason) {
_pinController.selection = TextSelection( reason.maybeWhen(
baseOffset: 0, extentOffset: _pinController.text.length); invalidPin: (retries, authBlocked) {
_pinFocus.requestFocus(); _pinController.selection = TextSelection(
setState(() { baseOffset: 0, extentOffset: _pinController.text.length);
_pinIsWrong = true; _pinFocus.requestFocus();
_retries = retries; setState(() {
_blocked = authBlocked; _pinIsWrong = true;
}); _retries = retries;
_blocked = authBlocked;
});
},
orElse: () {},
);
}); });
} on CancellationException catch (_) { } on CancellationException catch (_) {
// ignored // ignored

View File

@ -252,6 +252,18 @@
"message": {} "message": {}
} }
}, },
"l_set_puk_failed": null,
"@l_set_puk_failed": {
"placeholders": {
"message": {}
}
},
"l_unblock_pin_failed": null,
"@l_unblock_pin_failed": {
"placeholders": {
"message": {}
}
},
"l_attempts_remaining": null, "l_attempts_remaining": null,
"@l_attempts_remaining": { "@l_attempts_remaining": {
"placeholders": { "placeholders": {
@ -288,6 +300,14 @@
"length": {} "length": {}
} }
}, },
"p_enter_new_fido2_pin_complexity_active": null,
"@p_enter_new_fido2_pin_complexity_active": {
"placeholders": {
"length": {},
"unique_characters": {},
"common_pin": {}
}
},
"s_pin_required": null, "s_pin_required": null,
"p_pin_required_desc": null, "p_pin_required_desc": null,
"l_piv_pin_blocked": null, "l_piv_pin_blocked": null,
@ -298,6 +318,19 @@
"name": {} "name": {}
} }
}, },
"p_enter_new_piv_pin_puk_complexity_active": null,
"@p_enter_new_piv_pin_puk_complexity_active": {
"placeholders": {
"name": {},
"common": {}
}
},
"p_pin_puk_complexity_failure": null,
"@p_pin_puk_complexity_failure": {
"placeholders": {
"name": {}
}
},
"l_warning_default_pin": null, "l_warning_default_pin": null,
"l_warning_default_puk": null, "l_warning_default_puk": null,
"l_default_pin_used": null, "l_default_pin_used": null,

View File

@ -252,6 +252,18 @@
"message": {} "message": {}
} }
}, },
"l_set_puk_failed": "Failed to set PUK: {message}",
"@l_set_puk_failed": {
"placeholders": {
"message": {}
}
},
"l_unblock_pin_failed": "Failed to unblock PIN: {message}",
"@l_unblock_pin_failed": {
"placeholders": {
"message": {}
}
},
"l_attempts_remaining": "{retries} attempt(s) remaining", "l_attempts_remaining": "{retries} attempt(s) remaining",
"@l_attempts_remaining": { "@l_attempts_remaining": {
"placeholders": { "placeholders": {
@ -288,6 +300,14 @@
"length": {} "length": {}
} }
}, },
"p_enter_new_fido2_pin_complexity_active": "Enter your new PIN. A PIN must be at least {length} characters long, contain at least {unique_characters} unique characters, and not be a commonly used PIN, like \"{common_pin}\". It may contain letters, numbers, and special characters.",
"@p_enter_new_fido2_pin_complexity_active": {
"placeholders": {
"length": {},
"unique_characters": {},
"common_pin": {}
}
},
"s_pin_required": "PIN required", "s_pin_required": "PIN required",
"p_pin_required_desc": "The action you are about to perform requires the PIV PIN to be entered.", "p_pin_required_desc": "The action you are about to perform requires the PIV PIN to be entered.",
"l_piv_pin_blocked": "Blocked, use PUK to reset", "l_piv_pin_blocked": "Blocked, use PUK to reset",
@ -298,6 +318,19 @@
"name": {} "name": {}
} }
}, },
"p_enter_new_piv_pin_puk_complexity_active": "Enter a new {name} to set. Must be 6-8 characters, contain at least 2 unique characters, and not be a commonly used {name}, like \"{common}\".",
"@p_enter_new_piv_pin_puk_complexity_active": {
"placeholders": {
"name": {},
"common": {}
}
},
"p_pin_puk_complexity_failure": "New {name} doesn't meet complexity requirements.",
"@p_pin_puk_complexity_failure": {
"placeholders": {
"name": {}
}
},
"l_warning_default_pin": "Warning: Default PIN used", "l_warning_default_pin": "Warning: Default PIN used",
"l_warning_default_puk": "Warning: Default PUK used", "l_warning_default_puk": "Warning: Default PUK used",
"l_default_pin_used": "Default PIN used", "l_default_pin_used": "Default PIN used",

View File

@ -252,6 +252,18 @@
"message": {} "message": {}
} }
}, },
"l_set_puk_failed": null,
"@l_set_puk_failed": {
"placeholders": {
"message": {}
}
},
"l_unblock_pin_failed": null,
"@l_unblock_pin_failed": {
"placeholders": {
"message": {}
}
},
"l_attempts_remaining": "Nombre de tentative(s) restante(s) : {retries}", "l_attempts_remaining": "Nombre de tentative(s) restante(s) : {retries}",
"@l_attempts_remaining": { "@l_attempts_remaining": {
"placeholders": { "placeholders": {
@ -288,6 +300,14 @@
"length": {} "length": {}
} }
}, },
"p_enter_new_fido2_pin_complexity_active": null,
"@p_enter_new_fido2_pin_complexity_active": {
"placeholders": {
"length": {},
"unique_characters": {},
"common_pin": {}
}
},
"s_pin_required": "PIN requis", "s_pin_required": "PIN requis",
"p_pin_required_desc": "L'action que vous allez faire demande d'entrer le code PIN du PIV.", "p_pin_required_desc": "L'action que vous allez faire demande d'entrer le code PIN du PIV.",
"l_piv_pin_blocked": "Vous êtes bloqué, utilisez le code PUK pour réinitialiser", "l_piv_pin_blocked": "Vous êtes bloqué, utilisez le code PUK pour réinitialiser",
@ -298,6 +318,19 @@
"name": {} "name": {}
} }
}, },
"p_enter_new_piv_pin_puk_complexity_active": null,
"@p_enter_new_piv_pin_puk_complexity_active": {
"placeholders": {
"name": {},
"common": {}
}
},
"p_pin_puk_complexity_failure": null,
"@p_pin_puk_complexity_failure": {
"placeholders": {
"name": {}
}
},
"l_warning_default_pin": null, "l_warning_default_pin": null,
"l_warning_default_puk": null, "l_warning_default_puk": null,
"l_default_pin_used": null, "l_default_pin_used": null,

View File

@ -252,6 +252,18 @@
"message": {} "message": {}
} }
}, },
"l_set_puk_failed": null,
"@l_set_puk_failed": {
"placeholders": {
"message": {}
}
},
"l_unblock_pin_failed": null,
"@l_unblock_pin_failed": {
"placeholders": {
"message": {}
}
},
"l_attempts_remaining": "あと{retries}回試行できます", "l_attempts_remaining": "あと{retries}回試行できます",
"@l_attempts_remaining": { "@l_attempts_remaining": {
"placeholders": { "placeholders": {
@ -288,6 +300,14 @@
"length": {} "length": {}
} }
}, },
"p_enter_new_fido2_pin_complexity_active": null,
"@p_enter_new_fido2_pin_complexity_active": {
"placeholders": {
"length": {},
"unique_characters": {},
"common_pin": {}
}
},
"s_pin_required": "PINが必要", "s_pin_required": "PINが必要",
"p_pin_required_desc": "実行しようとしている操作には、PIV PINの入力が必要です", "p_pin_required_desc": "実行しようとしている操作には、PIV PINの入力が必要です",
"l_piv_pin_blocked": "ブロックされています。PUK を使用してリセットしてください", "l_piv_pin_blocked": "ブロックされています。PUK を使用してリセットしてください",
@ -298,6 +318,19 @@
"name": {} "name": {}
} }
}, },
"p_enter_new_piv_pin_puk_complexity_active": null,
"@p_enter_new_piv_pin_puk_complexity_active": {
"placeholders": {
"name": {},
"common": {}
}
},
"p_pin_puk_complexity_failure": null,
"@p_pin_puk_complexity_failure": {
"placeholders": {
"name": {}
}
},
"l_warning_default_pin": null, "l_warning_default_pin": null,
"l_warning_default_puk": null, "l_warning_default_puk": null,
"l_default_pin_used": null, "l_default_pin_used": null,

View File

@ -252,6 +252,18 @@
"message": {} "message": {}
} }
}, },
"l_set_puk_failed": null,
"@l_set_puk_failed": {
"placeholders": {
"message": {}
}
},
"l_unblock_pin_failed": null,
"@l_unblock_pin_failed": {
"placeholders": {
"message": {}
}
},
"l_attempts_remaining": "Pozostało prób: {retries}", "l_attempts_remaining": "Pozostało prób: {retries}",
"@l_attempts_remaining": { "@l_attempts_remaining": {
"placeholders": { "placeholders": {
@ -288,6 +300,14 @@
"length": {} "length": {}
} }
}, },
"p_enter_new_fido2_pin_complexity_active": null,
"@p_enter_new_fido2_pin_complexity_active": {
"placeholders": {
"length": {},
"unique_characters": {},
"common_pin": {}
}
},
"s_pin_required": "Wymagany PIN", "s_pin_required": "Wymagany PIN",
"p_pin_required_desc": "Czynność, którą zamierzasz wykonać, wymaga wprowadzenia kodu PIN PIV.", "p_pin_required_desc": "Czynność, którą zamierzasz wykonać, wymaga wprowadzenia kodu PIN PIV.",
"l_piv_pin_blocked": "Zablokowano, użyj PUK, aby zresetować", "l_piv_pin_blocked": "Zablokowano, użyj PUK, aby zresetować",
@ -298,6 +318,19 @@
"name": {} "name": {}
} }
}, },
"p_enter_new_piv_pin_puk_complexity_active": null,
"@p_enter_new_piv_pin_puk_complexity_active": {
"placeholders": {
"name": {},
"common": {}
}
},
"p_pin_puk_complexity_failure": null,
"@p_pin_puk_complexity_failure": {
"placeholders": {
"name": {}
}
},
"l_warning_default_pin": null, "l_warning_default_pin": null,
"l_warning_default_puk": null, "l_warning_default_puk": null,
"l_default_pin_used": null, "l_default_pin_used": null,

View File

@ -86,7 +86,8 @@ class DeviceInfo with _$DeviceInfo {
Map<Transport, int> supportedCapabilities, Map<Transport, int> supportedCapabilities,
bool isLocked, bool isLocked,
bool isFips, bool isFips,
bool isSky) = _DeviceInfo; bool isSky,
bool pinComplexity) = _DeviceInfo;
factory DeviceInfo.fromJson(Map<String, dynamic> json) => factory DeviceInfo.fromJson(Map<String, dynamic> json) =>
_$DeviceInfoFromJson(json); _$DeviceInfoFromJson(json);

View File

@ -245,6 +245,7 @@ mixin _$DeviceInfo {
bool get isLocked => throw _privateConstructorUsedError; bool get isLocked => throw _privateConstructorUsedError;
bool get isFips => throw _privateConstructorUsedError; bool get isFips => throw _privateConstructorUsedError;
bool get isSky => throw _privateConstructorUsedError; bool get isSky => throw _privateConstructorUsedError;
bool get pinComplexity => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
@ -266,7 +267,8 @@ abstract class $DeviceInfoCopyWith<$Res> {
Map<Transport, int> supportedCapabilities, Map<Transport, int> supportedCapabilities,
bool isLocked, bool isLocked,
bool isFips, bool isFips,
bool isSky}); bool isSky,
bool pinComplexity});
$DeviceConfigCopyWith<$Res> get config; $DeviceConfigCopyWith<$Res> get config;
$VersionCopyWith<$Res> get version; $VersionCopyWith<$Res> get version;
@ -293,6 +295,7 @@ class _$DeviceInfoCopyWithImpl<$Res, $Val extends DeviceInfo>
Object? isLocked = null, Object? isLocked = null,
Object? isFips = null, Object? isFips = null,
Object? isSky = null, Object? isSky = null,
Object? pinComplexity = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
config: null == config config: null == config
@ -327,6 +330,10 @@ class _$DeviceInfoCopyWithImpl<$Res, $Val extends DeviceInfo>
? _value.isSky ? _value.isSky
: isSky // ignore: cast_nullable_to_non_nullable : isSky // ignore: cast_nullable_to_non_nullable
as bool, as bool,
pinComplexity: null == pinComplexity
? _value.pinComplexity
: pinComplexity // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val); ) as $Val);
} }
@ -363,7 +370,8 @@ abstract class _$$DeviceInfoImplCopyWith<$Res>
Map<Transport, int> supportedCapabilities, Map<Transport, int> supportedCapabilities,
bool isLocked, bool isLocked,
bool isFips, bool isFips,
bool isSky}); bool isSky,
bool pinComplexity});
@override @override
$DeviceConfigCopyWith<$Res> get config; $DeviceConfigCopyWith<$Res> get config;
@ -390,6 +398,7 @@ class __$$DeviceInfoImplCopyWithImpl<$Res>
Object? isLocked = null, Object? isLocked = null,
Object? isFips = null, Object? isFips = null,
Object? isSky = null, Object? isSky = null,
Object? pinComplexity = null,
}) { }) {
return _then(_$DeviceInfoImpl( return _then(_$DeviceInfoImpl(
null == config null == config
@ -424,6 +433,10 @@ class __$$DeviceInfoImplCopyWithImpl<$Res>
? _value.isSky ? _value.isSky
: isSky // ignore: cast_nullable_to_non_nullable : isSky // ignore: cast_nullable_to_non_nullable
as bool, as bool,
null == pinComplexity
? _value.pinComplexity
: pinComplexity // ignore: cast_nullable_to_non_nullable
as bool,
)); ));
} }
} }
@ -439,7 +452,8 @@ class _$DeviceInfoImpl implements _DeviceInfo {
final Map<Transport, int> supportedCapabilities, final Map<Transport, int> supportedCapabilities,
this.isLocked, this.isLocked,
this.isFips, this.isFips,
this.isSky) this.isSky,
this.pinComplexity)
: _supportedCapabilities = supportedCapabilities; : _supportedCapabilities = supportedCapabilities;
factory _$DeviceInfoImpl.fromJson(Map<String, dynamic> json) => factory _$DeviceInfoImpl.fromJson(Map<String, dynamic> json) =>
@ -468,10 +482,12 @@ class _$DeviceInfoImpl implements _DeviceInfo {
final bool isFips; final bool isFips;
@override @override
final bool isSky; final bool isSky;
@override
final bool pinComplexity;
@override @override
String toString() { String toString() {
return 'DeviceInfo(config: $config, serial: $serial, version: $version, formFactor: $formFactor, supportedCapabilities: $supportedCapabilities, isLocked: $isLocked, isFips: $isFips, isSky: $isSky)'; return 'DeviceInfo(config: $config, serial: $serial, version: $version, formFactor: $formFactor, supportedCapabilities: $supportedCapabilities, isLocked: $isLocked, isFips: $isFips, isSky: $isSky, pinComplexity: $pinComplexity)';
} }
@override @override
@ -489,7 +505,9 @@ class _$DeviceInfoImpl implements _DeviceInfo {
(identical(other.isLocked, isLocked) || (identical(other.isLocked, isLocked) ||
other.isLocked == isLocked) && other.isLocked == isLocked) &&
(identical(other.isFips, isFips) || other.isFips == isFips) && (identical(other.isFips, isFips) || other.isFips == isFips) &&
(identical(other.isSky, isSky) || other.isSky == isSky)); (identical(other.isSky, isSky) || other.isSky == isSky) &&
(identical(other.pinComplexity, pinComplexity) ||
other.pinComplexity == pinComplexity));
} }
@JsonKey(ignore: true) @JsonKey(ignore: true)
@ -503,7 +521,8 @@ class _$DeviceInfoImpl implements _DeviceInfo {
const DeepCollectionEquality().hash(_supportedCapabilities), const DeepCollectionEquality().hash(_supportedCapabilities),
isLocked, isLocked,
isFips, isFips,
isSky); isSky,
pinComplexity);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@ -528,7 +547,8 @@ abstract class _DeviceInfo implements DeviceInfo {
final Map<Transport, int> supportedCapabilities, final Map<Transport, int> supportedCapabilities,
final bool isLocked, final bool isLocked,
final bool isFips, final bool isFips,
final bool isSky) = _$DeviceInfoImpl; final bool isSky,
final bool pinComplexity) = _$DeviceInfoImpl;
factory _DeviceInfo.fromJson(Map<String, dynamic> json) = factory _DeviceInfo.fromJson(Map<String, dynamic> json) =
_$DeviceInfoImpl.fromJson; _$DeviceInfoImpl.fromJson;
@ -550,6 +570,8 @@ abstract class _DeviceInfo implements DeviceInfo {
@override @override
bool get isSky; bool get isSky;
@override @override
bool get pinComplexity;
@override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$DeviceInfoImplCopyWith<_$DeviceInfoImpl> get copyWith => _$$DeviceInfoImplCopyWith<_$DeviceInfoImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;

View File

@ -42,6 +42,7 @@ _$DeviceInfoImpl _$$DeviceInfoImplFromJson(Map<String, dynamic> json) =>
json['is_locked'] as bool, json['is_locked'] as bool,
json['is_fips'] as bool, json['is_fips'] as bool,
json['is_sky'] as bool, json['is_sky'] as bool,
json['pin_complexity'] as bool,
); );
Map<String, dynamic> _$$DeviceInfoImplToJson(_$DeviceInfoImpl instance) => Map<String, dynamic> _$$DeviceInfoImplToJson(_$DeviceInfoImpl instance) =>
@ -55,6 +56,7 @@ Map<String, dynamic> _$$DeviceInfoImplToJson(_$DeviceInfoImpl instance) =>
'is_locked': instance.isLocked, 'is_locked': instance.isLocked,
'is_fips': instance.isFips, 'is_fips': instance.isFips,
'is_sky': instance.isSky, 'is_sky': instance.isSky,
'pin_complexity': instance.pinComplexity,
}; };
const _$FormFactorEnumMap = { const _$FormFactorEnumMap = {

View File

@ -209,7 +209,14 @@ class PinMetadata with _$PinMetadata {
@freezed @freezed
class PinVerificationStatus with _$PinVerificationStatus { class PinVerificationStatus with _$PinVerificationStatus {
const factory PinVerificationStatus.success() = PinSuccess; const factory PinVerificationStatus.success() = PinSuccess;
factory PinVerificationStatus.failure(int attemptsRemaining) = PinFailure; factory PinVerificationStatus.failure(PivPinFailureReason reason) =
PinFailure;
}
@freezed
class PivPinFailureReason with _$PivPinFailureReason {
factory PivPinFailureReason.invalidPin(int attemptsRemaining) = PivInvalidPin;
const factory PivPinFailureReason.weakPin() = PivWeakPin;
} }
@freezed @freezed

View File

@ -193,19 +193,19 @@ mixin _$PinVerificationStatus {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() success, required TResult Function() success,
required TResult Function(int attemptsRemaining) failure, required TResult Function(PivPinFailureReason reason) failure,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? success, TResult? Function()? success,
TResult? Function(int attemptsRemaining)? failure, TResult? Function(PivPinFailureReason reason)? failure,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? success, TResult Function()? success,
TResult Function(int attemptsRemaining)? failure, TResult Function(PivPinFailureReason reason)? failure,
required TResult orElse(), required TResult orElse(),
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@ -288,7 +288,7 @@ class _$PinSuccessImpl implements PinSuccess {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() success, required TResult Function() success,
required TResult Function(int attemptsRemaining) failure, required TResult Function(PivPinFailureReason reason) failure,
}) { }) {
return success(); return success();
} }
@ -297,7 +297,7 @@ class _$PinSuccessImpl implements PinSuccess {
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? success, TResult? Function()? success,
TResult? Function(int attemptsRemaining)? failure, TResult? Function(PivPinFailureReason reason)? failure,
}) { }) {
return success?.call(); return success?.call();
} }
@ -306,7 +306,7 @@ class _$PinSuccessImpl implements PinSuccess {
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? success, TResult Function()? success,
TResult Function(int attemptsRemaining)? failure, TResult Function(PivPinFailureReason reason)? failure,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (success != null) { if (success != null) {
@ -357,7 +357,9 @@ abstract class _$$PinFailureImplCopyWith<$Res> {
_$PinFailureImpl value, $Res Function(_$PinFailureImpl) then) = _$PinFailureImpl value, $Res Function(_$PinFailureImpl) then) =
__$$PinFailureImplCopyWithImpl<$Res>; __$$PinFailureImplCopyWithImpl<$Res>;
@useResult @useResult
$Res call({int attemptsRemaining}); $Res call({PivPinFailureReason reason});
$PivPinFailureReasonCopyWith<$Res> get reason;
} }
/// @nodoc /// @nodoc
@ -371,28 +373,36 @@ class __$$PinFailureImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? attemptsRemaining = null, Object? reason = null,
}) { }) {
return _then(_$PinFailureImpl( return _then(_$PinFailureImpl(
null == attemptsRemaining null == reason
? _value.attemptsRemaining ? _value.reason
: attemptsRemaining // ignore: cast_nullable_to_non_nullable : reason // ignore: cast_nullable_to_non_nullable
as int, as PivPinFailureReason,
)); ));
} }
@override
@pragma('vm:prefer-inline')
$PivPinFailureReasonCopyWith<$Res> get reason {
return $PivPinFailureReasonCopyWith<$Res>(_value.reason, (value) {
return _then(_value.copyWith(reason: value));
});
}
} }
/// @nodoc /// @nodoc
class _$PinFailureImpl implements PinFailure { class _$PinFailureImpl implements PinFailure {
_$PinFailureImpl(this.attemptsRemaining); _$PinFailureImpl(this.reason);
@override @override
final int attemptsRemaining; final PivPinFailureReason reason;
@override @override
String toString() { String toString() {
return 'PinVerificationStatus.failure(attemptsRemaining: $attemptsRemaining)'; return 'PinVerificationStatus.failure(reason: $reason)';
} }
@override @override
@ -400,12 +410,11 @@ class _$PinFailureImpl implements PinFailure {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$PinFailureImpl && other is _$PinFailureImpl &&
(identical(other.attemptsRemaining, attemptsRemaining) || (identical(other.reason, reason) || other.reason == reason));
other.attemptsRemaining == attemptsRemaining));
} }
@override @override
int get hashCode => Object.hash(runtimeType, attemptsRemaining); int get hashCode => Object.hash(runtimeType, reason);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@ -417,29 +426,29 @@ class _$PinFailureImpl implements PinFailure {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() success, required TResult Function() success,
required TResult Function(int attemptsRemaining) failure, required TResult Function(PivPinFailureReason reason) failure,
}) { }) {
return failure(attemptsRemaining); return failure(reason);
} }
@override @override
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? success, TResult? Function()? success,
TResult? Function(int attemptsRemaining)? failure, TResult? Function(PivPinFailureReason reason)? failure,
}) { }) {
return failure?.call(attemptsRemaining); return failure?.call(reason);
} }
@override @override
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? success, TResult Function()? success,
TResult Function(int attemptsRemaining)? failure, TResult Function(PivPinFailureReason reason)? failure,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (failure != null) { if (failure != null) {
return failure(attemptsRemaining); return failure(reason);
} }
return orElse(); return orElse();
} }
@ -477,14 +486,310 @@ class _$PinFailureImpl implements PinFailure {
} }
abstract class PinFailure implements PinVerificationStatus { abstract class PinFailure implements PinVerificationStatus {
factory PinFailure(final int attemptsRemaining) = _$PinFailureImpl; factory PinFailure(final PivPinFailureReason reason) = _$PinFailureImpl;
int get attemptsRemaining; PivPinFailureReason get reason;
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$PinFailureImplCopyWith<_$PinFailureImpl> get copyWith => _$$PinFailureImplCopyWith<_$PinFailureImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
/// @nodoc
mixin _$PivPinFailureReason {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int attemptsRemaining) invalidPin,
required TResult Function() weakPin,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int attemptsRemaining)? invalidPin,
TResult? Function()? weakPin,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int attemptsRemaining)? invalidPin,
TResult Function()? weakPin,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(PivInvalidPin value) invalidPin,
required TResult Function(PivWeakPin value) weakPin,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(PivInvalidPin value)? invalidPin,
TResult? Function(PivWeakPin value)? weakPin,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(PivInvalidPin value)? invalidPin,
TResult Function(PivWeakPin value)? weakPin,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PivPinFailureReasonCopyWith<$Res> {
factory $PivPinFailureReasonCopyWith(
PivPinFailureReason value, $Res Function(PivPinFailureReason) then) =
_$PivPinFailureReasonCopyWithImpl<$Res, PivPinFailureReason>;
}
/// @nodoc
class _$PivPinFailureReasonCopyWithImpl<$Res, $Val extends PivPinFailureReason>
implements $PivPinFailureReasonCopyWith<$Res> {
_$PivPinFailureReasonCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
}
/// @nodoc
abstract class _$$PivInvalidPinImplCopyWith<$Res> {
factory _$$PivInvalidPinImplCopyWith(
_$PivInvalidPinImpl value, $Res Function(_$PivInvalidPinImpl) then) =
__$$PivInvalidPinImplCopyWithImpl<$Res>;
@useResult
$Res call({int attemptsRemaining});
}
/// @nodoc
class __$$PivInvalidPinImplCopyWithImpl<$Res>
extends _$PivPinFailureReasonCopyWithImpl<$Res, _$PivInvalidPinImpl>
implements _$$PivInvalidPinImplCopyWith<$Res> {
__$$PivInvalidPinImplCopyWithImpl(
_$PivInvalidPinImpl _value, $Res Function(_$PivInvalidPinImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? attemptsRemaining = null,
}) {
return _then(_$PivInvalidPinImpl(
null == attemptsRemaining
? _value.attemptsRemaining
: attemptsRemaining // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
class _$PivInvalidPinImpl implements PivInvalidPin {
_$PivInvalidPinImpl(this.attemptsRemaining);
@override
final int attemptsRemaining;
@override
String toString() {
return 'PivPinFailureReason.invalidPin(attemptsRemaining: $attemptsRemaining)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PivInvalidPinImpl &&
(identical(other.attemptsRemaining, attemptsRemaining) ||
other.attemptsRemaining == attemptsRemaining));
}
@override
int get hashCode => Object.hash(runtimeType, attemptsRemaining);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$PivInvalidPinImplCopyWith<_$PivInvalidPinImpl> get copyWith =>
__$$PivInvalidPinImplCopyWithImpl<_$PivInvalidPinImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int attemptsRemaining) invalidPin,
required TResult Function() weakPin,
}) {
return invalidPin(attemptsRemaining);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int attemptsRemaining)? invalidPin,
TResult? Function()? weakPin,
}) {
return invalidPin?.call(attemptsRemaining);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int attemptsRemaining)? invalidPin,
TResult Function()? weakPin,
required TResult orElse(),
}) {
if (invalidPin != null) {
return invalidPin(attemptsRemaining);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(PivInvalidPin value) invalidPin,
required TResult Function(PivWeakPin value) weakPin,
}) {
return invalidPin(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(PivInvalidPin value)? invalidPin,
TResult? Function(PivWeakPin value)? weakPin,
}) {
return invalidPin?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(PivInvalidPin value)? invalidPin,
TResult Function(PivWeakPin value)? weakPin,
required TResult orElse(),
}) {
if (invalidPin != null) {
return invalidPin(this);
}
return orElse();
}
}
abstract class PivInvalidPin implements PivPinFailureReason {
factory PivInvalidPin(final int attemptsRemaining) = _$PivInvalidPinImpl;
int get attemptsRemaining;
@JsonKey(ignore: true)
_$$PivInvalidPinImplCopyWith<_$PivInvalidPinImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$PivWeakPinImplCopyWith<$Res> {
factory _$$PivWeakPinImplCopyWith(
_$PivWeakPinImpl value, $Res Function(_$PivWeakPinImpl) then) =
__$$PivWeakPinImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$PivWeakPinImplCopyWithImpl<$Res>
extends _$PivPinFailureReasonCopyWithImpl<$Res, _$PivWeakPinImpl>
implements _$$PivWeakPinImplCopyWith<$Res> {
__$$PivWeakPinImplCopyWithImpl(
_$PivWeakPinImpl _value, $Res Function(_$PivWeakPinImpl) _then)
: super(_value, _then);
}
/// @nodoc
class _$PivWeakPinImpl implements PivWeakPin {
const _$PivWeakPinImpl();
@override
String toString() {
return 'PivPinFailureReason.weakPin()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$PivWeakPinImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(int attemptsRemaining) invalidPin,
required TResult Function() weakPin,
}) {
return weakPin();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(int attemptsRemaining)? invalidPin,
TResult? Function()? weakPin,
}) {
return weakPin?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(int attemptsRemaining)? invalidPin,
TResult Function()? weakPin,
required TResult orElse(),
}) {
if (weakPin != null) {
return weakPin();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(PivInvalidPin value) invalidPin,
required TResult Function(PivWeakPin value) weakPin,
}) {
return weakPin(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(PivInvalidPin value)? invalidPin,
TResult? Function(PivWeakPin value)? weakPin,
}) {
return weakPin?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(PivInvalidPin value)? invalidPin,
TResult Function(PivWeakPin value)? weakPin,
required TResult orElse(),
}) {
if (weakPin != null) {
return weakPin(this);
}
return orElse();
}
}
abstract class PivWeakPin implements PivPinFailureReason {
const factory PivWeakPin() = _$PivWeakPinImpl;
}
ManagementKeyMetadata _$ManagementKeyMetadataFromJson( ManagementKeyMetadata _$ManagementKeyMetadataFromJson(
Map<String, dynamic> json) { Map<String, dynamic> json) {
return _ManagementKeyMetadata.fromJson(json); return _ManagementKeyMetadata.fromJson(json);

View File

@ -106,14 +106,19 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
if (_usesStoredKey) { if (_usesStoredKey) {
final status = (await notifier.verifyPin(_currentController.text)).when( final status = (await notifier.verifyPin(_currentController.text)).when(
success: () => true, success: () => true,
failure: (attemptsRemaining) { failure: (reason) {
_currentController.selection = TextSelection( reason.maybeWhen(
baseOffset: 0, extentOffset: _currentController.text.length); invalidPin: (attemptsRemaining) {
_currentFocus.requestFocus(); _currentController.selection = TextSelection(
setState(() { baseOffset: 0, extentOffset: _currentController.text.length);
_attemptsRemaining = attemptsRemaining; _currentFocus.requestFocus();
_currentIsWrong = true; setState(() {
}); _attemptsRemaining = attemptsRemaining;
_currentIsWrong = true;
});
},
orElse: () {},
);
return false; return false;
}, },
); );

View File

@ -21,6 +21,7 @@ import 'package:material_symbols_icons/symbols.dart';
import '../../app/message.dart'; import '../../app/message.dart';
import '../../app/models.dart'; import '../../app/models.dart';
import '../../app/state.dart';
import '../../widgets/app_input_decoration.dart'; import '../../widgets/app_input_decoration.dart';
import '../../widgets/app_text_field.dart'; import '../../widgets/app_text_field.dart';
import '../../widgets/responsive_dialog.dart'; import '../../widgets/responsive_dialog.dart';
@ -46,10 +47,13 @@ class ManagePinPukDialog extends ConsumerStatefulWidget {
class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> { class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
final _currentPinController = TextEditingController(); final _currentPinController = TextEditingController();
final _currentPinFocus = FocusNode(); final _currentPinFocus = FocusNode();
String _newPin = ''; final _newPinController = TextEditingController();
final _newPinFocus = FocusNode();
String _confirmPin = ''; String _confirmPin = '';
bool _pinIsBlocked = false; bool _pinIsBlocked = false;
bool _currentIsWrong = false; bool _currentIsWrong = false;
bool _newIsWrong = false;
String? _newPinError;
int _attemptsRemaining = -1; int _attemptsRemaining = -1;
bool _isObscureCurrent = true; bool _isObscureCurrent = true;
bool _isObscureNew = true; bool _isObscureNew = true;
@ -80,23 +84,26 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
void dispose() { void dispose() {
_currentPinController.dispose(); _currentPinController.dispose();
_currentPinFocus.dispose(); _currentPinFocus.dispose();
_newPinController.dispose();
_newPinFocus.dispose();
super.dispose(); super.dispose();
} }
_submit() async { _submit() async {
final notifier = ref.read(pivStateProvider(widget.path).notifier); final notifier = ref.read(pivStateProvider(widget.path).notifier);
final l10n = AppLocalizations.of(context)!;
final result = await switch (widget.target) { final result = await switch (widget.target) {
ManageTarget.pin => ManageTarget.pin =>
notifier.changePin(_currentPinController.text, _newPin), notifier.changePin(_currentPinController.text, _newPinController.text),
ManageTarget.puk => ManageTarget.puk =>
notifier.changePuk(_currentPinController.text, _newPin), notifier.changePuk(_currentPinController.text, _newPinController.text),
ManageTarget.unblock => ManageTarget.unblock =>
notifier.unblockPin(_currentPinController.text, _newPin), notifier.unblockPin(_currentPinController.text, _newPinController.text),
}; };
result.when(success: () { result.when(success: () {
if (!mounted) return; if (!mounted) return;
final l10n = AppLocalizations.of(context)!;
Navigator.of(context).pop(); Navigator.of(context).pop();
showMessage( showMessage(
context, context,
@ -104,17 +111,31 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
ManageTarget.puk => l10n.s_puk_set, ManageTarget.puk => l10n.s_puk_set,
_ => l10n.s_pin_set, _ => l10n.s_pin_set,
}); });
}, failure: (attemptsRemaining) { }, failure: (reason) {
_currentPinController.selection = TextSelection( reason.when(
baseOffset: 0, extentOffset: _currentPinController.text.length); invalidPin: (attemptsRemaining) {
_currentPinFocus.requestFocus(); _currentPinController.selection = TextSelection(
setState(() { baseOffset: 0, extentOffset: _currentPinController.text.length);
_attemptsRemaining = attemptsRemaining; _currentPinFocus.requestFocus();
_currentIsWrong = true; setState(() {
if (_attemptsRemaining == 0) { _attemptsRemaining = attemptsRemaining;
_pinIsBlocked = true; _currentIsWrong = true;
} if (_attemptsRemaining == 0) {
}); _pinIsBlocked = true;
}
});
},
weakPin: () {
_newPinController.selection = TextSelection(
baseOffset: 0, extentOffset: _newPinController.text.length);
_newPinFocus.requestFocus();
setState(() {
_newPinError = l10n.p_pin_puk_complexity_failure(
widget.target == ManageTarget.puk ? l10n.s_puk : l10n.s_pin);
_newIsWrong = true;
});
},
);
}); });
} }
@ -123,10 +144,11 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
final currentPin = _currentPinController.text; final currentPin = _currentPinController.text;
final currentPinLen = byteLength(currentPin); final currentPinLen = byteLength(currentPin);
final newPinLen = byteLength(_newPin); final newPin = _newPinController.text;
final newPinLen = byteLength(newPin);
final isValid = !_currentIsWrong && final isValid = !_currentIsWrong &&
_newPin.isNotEmpty && newPin.isNotEmpty &&
_newPin == _confirmPin && newPin == _confirmPin &&
currentPin.isNotEmpty; currentPin.isNotEmpty;
final titleText = switch (widget.target) { final titleText = switch (widget.target) {
@ -140,6 +162,10 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
final showDefaultPukUsed = final showDefaultPukUsed =
widget.target != ManageTarget.pin && _defaultPukUsed; widget.target != ManageTarget.pin && _defaultPukUsed;
final hasPinComplexity =
ref.read(currentDeviceDataProvider).valueOrNull?.info.pinComplexity ??
false;
return ResponsiveDialog( return ResponsiveDialog(
title: Text(titleText), title: Text(titleText),
actions: [ actions: [
@ -213,21 +239,29 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
}); });
}, },
).init(), ).init(),
Text(l10n.p_enter_new_piv_pin_puk( Text(hasPinComplexity
widget.target == ManageTarget.puk ? l10n.s_puk : l10n.s_pin)), ? l10n.p_enter_new_piv_pin_puk_complexity_active(
widget.target == ManageTarget.puk ? l10n.s_puk : l10n.s_pin,
'123456')
: l10n.p_enter_new_piv_pin_puk(widget.target == ManageTarget.puk
? l10n.s_puk
: l10n.s_pin)),
AppTextField( AppTextField(
key: keys.newPinPukField, key: keys.newPinPukField,
autofocus: showDefaultPinUsed || showDefaultPukUsed, autofocus: showDefaultPinUsed || showDefaultPukUsed,
obscureText: _isObscureNew, obscureText: _isObscureNew,
controller: _newPinController,
focusNode: _newPinFocus,
maxLength: 8, maxLength: 8,
inputFormatters: [limitBytesLength(8)], inputFormatters: [limitBytesLength(8)],
buildCounter: buildByteCounterFor(_newPin), buildCounter: buildByteCounterFor(newPin),
autofillHints: const [AutofillHints.newPassword], autofillHints: const [AutofillHints.newPassword],
decoration: AppInputDecoration( decoration: AppInputDecoration(
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
labelText: widget.target == ManageTarget.puk labelText: widget.target == ManageTarget.puk
? l10n.s_new_puk ? l10n.s_new_puk
: l10n.s_new_pin, : l10n.s_new_pin,
errorText: _newIsWrong ? _newPinError : null,
prefixIcon: const Icon(Symbols.password), prefixIcon: const Icon(Symbols.password),
suffixIcon: IconButton( suffixIcon: IconButton(
icon: Icon(_isObscureNew icon: Icon(_isObscureNew
@ -247,7 +281,7 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_newPin = value; _newIsWrong = false;
}); });
}, },
onSubmitted: (_) { onSubmitted: (_) {
@ -284,8 +318,9 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
), ),
enabled: currentPinLen >= _minPinLen && newPinLen >= 6, enabled: currentPinLen >= _minPinLen && newPinLen >= 6,
errorText: errorText:
newPinLen == _confirmPin.length && _newPin != _confirmPin newPinLen == _confirmPin.length && newPin != _confirmPin
? (widget.target == ManageTarget.pin ? (widget.target == ManageTarget.pin ||
widget.target == ManageTarget.unblock
? l10n.l_pin_mismatch ? l10n.l_pin_mismatch
: l10n.l_puk_mismatch) : l10n.l_puk_mismatch)
: null, : null,

View File

@ -60,14 +60,19 @@ class _PinDialogState extends ConsumerState<PinDialog> {
success: () { success: () {
navigator.pop(true); navigator.pop(true);
}, },
failure: (attemptsRemaining) { failure: (reason) {
_pinController.selection = TextSelection( reason.maybeWhen(
baseOffset: 0, extentOffset: _pinController.text.length); invalidPin: (attemptsRemaining) {
_pinFocus.requestFocus(); _pinController.selection = TextSelection(
setState(() { baseOffset: 0, extentOffset: _pinController.text.length);
_attemptsRemaining = attemptsRemaining; _pinFocus.requestFocus();
_pinIsWrong = true; setState(() {
}); _attemptsRemaining = attemptsRemaining;
_pinIsWrong = true;
});
},
orElse: () {},
);
}, },
); );
} on CancellationException catch (_) { } on CancellationException catch (_) {