mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-29 23:20:03 +03:00
Merge branch 'main' into adamve/android_fido
This commit is contained in:
commit
5f77cfdd8b
2
.github/workflows/env
vendored
2
.github/workflows/env
vendored
@ -1,2 +1,2 @@
|
|||||||
FLUTTER=3.16.9
|
FLUTTER=3.16.9
|
||||||
PYVER=3.12.1
|
PYVER=3.12.2
|
||||||
|
1
.github/workflows/linux.yml
vendored
1
.github/workflows/linux.yml
vendored
@ -21,6 +21,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
echo "PYVER=3.12.1" >> $GITHUB_ENV # Remove once 3.12.2 is available from PPA
|
||||||
export PYVER_MINOR=${PYVER%.*}
|
export PYVER_MINOR=${PYVER%.*}
|
||||||
echo "PYVER_MINOR: $PYVER_MINOR"
|
echo "PYVER_MINOR: $PYVER_MINOR"
|
||||||
apt-get update
|
apt-get update
|
||||||
|
15
NEWS
15
NEWS
@ -1,3 +1,18 @@
|
|||||||
|
* Version 6.4.0 (released 2024-02-20)
|
||||||
|
** UI: Major UI overhaul, with improvements including:
|
||||||
|
*** Add new UI layouts for wider windows to better utilize screen space.
|
||||||
|
*** Add YubiKey personalization through custom naming and theme color.
|
||||||
|
*** Split FIDO/WebAuthn into multiple sections.
|
||||||
|
*** Move factory reset functionality into a single dialog, from the individual sections.
|
||||||
|
** Add support for Yubico OTP provisioning.
|
||||||
|
** PIV: Display more information about keys and certificates.
|
||||||
|
** PIV: Add output format for public key when generating keys.
|
||||||
|
** Desktop: Window hidden/shown state no longer saved when closing the app,
|
||||||
|
use --hidden to start the app in a hidden to systray state.
|
||||||
|
** Desktop: Fix FIDO reset over NFC.
|
||||||
|
** Windows: Add option to launch Windows Settings for FIDO management.
|
||||||
|
** Android: Increase read timeout for NFC, improving compatibility with older YubiKeys.
|
||||||
|
|
||||||
* Version 6.3.1 (released 2023-12-12)
|
* Version 6.3.1 (released 2023-12-12)
|
||||||
** Add command line options: --hidden/--shown, --log-file FILE.
|
** Add command line options: --hidden/--shown, --log-file FILE.
|
||||||
** Disable autocorrect in text fields.
|
** Disable autocorrect in text fields.
|
||||||
|
@ -124,10 +124,12 @@ class Ctap2Node(RpcNode):
|
|||||||
removed = False
|
removed = False
|
||||||
while not event.wait(0.5):
|
while not event.wait(0.5):
|
||||||
try:
|
try:
|
||||||
with dev.open_connection(FidoConnection):
|
conn = dev.open_connection(FidoConnection)
|
||||||
if removed:
|
if removed:
|
||||||
sleep(1.0) # Wait for the device to settle
|
conn.close()
|
||||||
return dev.open_connection(FidoConnection)
|
sleep(1.0) # Wait for the device to settle
|
||||||
|
return dev.open_connection(FidoConnection)
|
||||||
|
conn.close()
|
||||||
except CardConnectionException:
|
except CardConnectionException:
|
||||||
pass # Expected, ignore
|
pass # Expected, ignore
|
||||||
except NoCardException:
|
except NoCardException:
|
||||||
|
21
helper/poetry.lock
generated
21
helper/poetry.lock
generated
@ -235,21 +235,21 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jaraco-classes"
|
name = "jaraco-classes"
|
||||||
version = "3.3.0"
|
version = "3.3.1"
|
||||||
description = "Utility functions for Python class constructs"
|
description = "Utility functions for Python class constructs"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "jaraco.classes-3.3.0-py3-none-any.whl", hash = "sha256:10afa92b6743f25c0cf5f37c6bb6e18e2c5bb84a16527ccfc0040ea377e7aaeb"},
|
{file = "jaraco.classes-3.3.1-py3-none-any.whl", hash = "sha256:86b534de565381f6b3c1c830d13f931d7be1a75f0081c57dff615578676e2206"},
|
||||||
{file = "jaraco.classes-3.3.0.tar.gz", hash = "sha256:c063dd08e89217cee02c8d5e5ec560f2c8ce6cdc2fcdc2e68f7b2e5547ed3621"},
|
{file = "jaraco.classes-3.3.1.tar.gz", hash = "sha256:cb28a5ebda8bc47d8c8015307d93163464f9f2b91ab4006e09ff0ce07e8bfb30"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
more-itertools = "*"
|
more-itertools = "*"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||||
testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
|
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jeepney"
|
name = "jeepney"
|
||||||
@ -575,11 +575,16 @@ description = "Smartcard module for Python."
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
files = [
|
files = [
|
||||||
|
{file = "pyscard-2.0.7-cp310-cp310-win32.whl", hash = "sha256:06666a597e1293421fa90e0d4fc2418add447b10b7dc85f49b3cafc23480f046"},
|
||||||
{file = "pyscard-2.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a2266345bd387854298153264bff8b74f494581880a76e3e8679460c1b090fab"},
|
{file = "pyscard-2.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a2266345bd387854298153264bff8b74f494581880a76e3e8679460c1b090fab"},
|
||||||
|
{file = "pyscard-2.0.7-cp311-cp311-win32.whl", hash = "sha256:beacdcdc3d1516e195f7a38ec3966c5d4df7390c8f036cb41f6fef72bc5cc646"},
|
||||||
{file = "pyscard-2.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e37b697327e8dc4848c481428d1cbd10b7ae2ce037bc799e5b8bbd2fc3ab5ed"},
|
{file = "pyscard-2.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e37b697327e8dc4848c481428d1cbd10b7ae2ce037bc799e5b8bbd2fc3ab5ed"},
|
||||||
|
{file = "pyscard-2.0.7-cp37-cp37m-win32.whl", hash = "sha256:a0c5edbedafba62c68160884f878d9f53996d7219a3fc11b1cea6bab59c7f34a"},
|
||||||
{file = "pyscard-2.0.7-cp37-cp37m-win_amd64.whl", hash = "sha256:f704ad40dc40306e1c0981941789518ab16aa1f84443b1d52ec0264884092b3b"},
|
{file = "pyscard-2.0.7-cp37-cp37m-win_amd64.whl", hash = "sha256:f704ad40dc40306e1c0981941789518ab16aa1f84443b1d52ec0264884092b3b"},
|
||||||
|
{file = "pyscard-2.0.7-cp38-cp38-win32.whl", hash = "sha256:59a466ab7ae20188dd197664b9ca1ea9524d115a5aa5b16b575a6b772cdcb73c"},
|
||||||
{file = "pyscard-2.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:da70aa5b7be5868b88cdb6d4a419d2791b6165beeb90cd01d2748033302a0f43"},
|
{file = "pyscard-2.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:da70aa5b7be5868b88cdb6d4a419d2791b6165beeb90cd01d2748033302a0f43"},
|
||||||
{file = "pyscard-2.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2d4bdc1f4e0e6c46e417ac1bc9d5990f7cfb24a080e890d453781405f7bd29dc"},
|
{file = "pyscard-2.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2d4bdc1f4e0e6c46e417ac1bc9d5990f7cfb24a080e890d453781405f7bd29dc"},
|
||||||
|
{file = "pyscard-2.0.7-cp39-cp39-win32.whl", hash = "sha256:39e030c47878b37ae08038a917959357be6468da52e8b144e84ffc659f50e6e2"},
|
||||||
{file = "pyscard-2.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:5a5865675be294c8d91f22dc91e7d897c4138881e5295fb6b2cd821f7c0389d9"},
|
{file = "pyscard-2.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:5a5865675be294c8d91f22dc91e7d897c4138881e5295fb6b2cd821f7c0389d9"},
|
||||||
{file = "pyscard-2.0.7.tar.gz", hash = "sha256:278054525fa75fbe8b10460d87edcd03a70ad94d688b11345e4739987f85c1bf"},
|
{file = "pyscard-2.0.7.tar.gz", hash = "sha256:278054525fa75fbe8b10460d87edcd03a70ad94d688b11345e4739987f85c1bf"},
|
||||||
]
|
]
|
||||||
@ -688,13 +693,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-pillow"
|
name = "types-pillow"
|
||||||
version = "10.2.0.20240125"
|
version = "10.2.0.20240206"
|
||||||
description = "Typing stubs for Pillow"
|
description = "Typing stubs for Pillow"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "types-Pillow-10.2.0.20240125.tar.gz", hash = "sha256:c449b2c43b9fdbe0494a7b950e6b39a4e50516091213fec24ef3f33c1d017717"},
|
{file = "types-Pillow-10.2.0.20240206.tar.gz", hash = "sha256:f0de5107ff8362ffdbbd53ec896202ac905e6ab22ae784b46bcdad160ea143b9"},
|
||||||
{file = "types_Pillow-10.2.0.20240125-py3-none-any.whl", hash = "sha256:322dbae32b4b7918da5e8a47c50ac0f24b0aa72a804a23857620f2722b03c858"},
|
{file = "types_Pillow-10.2.0.20240206-py3-none-any.whl", hash = "sha256:abc339ae28af5916146a7729261480d68ac902cd4ff57e0bdd402eee7962644d"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -6,8 +6,8 @@ VSVersionInfo(
|
|||||||
ffi=FixedFileInfo(
|
ffi=FixedFileInfo(
|
||||||
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
|
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
|
||||||
# Set not needed items to zero 0.
|
# Set not needed items to zero 0.
|
||||||
filevers=(6, 4, 0, 0),
|
filevers=(6, 4, 1, 0),
|
||||||
prodvers=(6, 4, 0, 0),
|
prodvers=(6, 4, 1, 0),
|
||||||
# Contains a bitmask that specifies the valid bits 'flags'r
|
# Contains a bitmask that specifies the valid bits 'flags'r
|
||||||
mask=0x3f,
|
mask=0x3f,
|
||||||
# Contains a bitmask that specifies the Boolean attributes of the file.
|
# Contains a bitmask that specifies the Boolean attributes of the file.
|
||||||
@ -31,11 +31,11 @@ VSVersionInfo(
|
|||||||
'040904b0',
|
'040904b0',
|
||||||
[StringStruct('CompanyName', 'Yubico'),
|
[StringStruct('CompanyName', 'Yubico'),
|
||||||
StringStruct('FileDescription', 'Yubico Authenticator Helper'),
|
StringStruct('FileDescription', 'Yubico Authenticator Helper'),
|
||||||
StringStruct('FileVersion', '6.4.0-dev.0'),
|
StringStruct('FileVersion', '6.4.1-dev.0'),
|
||||||
StringStruct('LegalCopyright', 'Copyright (c) Yubico'),
|
StringStruct('LegalCopyright', 'Copyright (c) Yubico'),
|
||||||
StringStruct('OriginalFilename', 'authenticator-helper.exe'),
|
StringStruct('OriginalFilename', 'authenticator-helper.exe'),
|
||||||
StringStruct('ProductName', 'Yubico Authenticator'),
|
StringStruct('ProductName', 'Yubico Authenticator'),
|
||||||
StringStruct('ProductVersion', '6.4.0-dev.0')])
|
StringStruct('ProductVersion', '6.4.1-dev.0')])
|
||||||
]),
|
]),
|
||||||
VarFileInfo([VarStruct('Translation', [1033, 1200])])
|
VarFileInfo([VarStruct('Translation', [1033, 1200])])
|
||||||
]
|
]
|
||||||
|
@ -43,7 +43,7 @@ class ElevateFidoButtons extends ConsumerWidget {
|
|||||||
duration: const Duration(seconds: 30));
|
duration: const Duration(seconds: 30));
|
||||||
try {
|
try {
|
||||||
if (await ref.read(rpcProvider).requireValue.elevate()) {
|
if (await ref.read(rpcProvider).requireValue.elevate()) {
|
||||||
ref.invalidate(rpcProvider);
|
ref.invalidate(rpcStateProvider);
|
||||||
} else {
|
} else {
|
||||||
await ref.read(withContextProvider)((context) async =>
|
await ref.read(withContextProvider)((context) async =>
|
||||||
showMessage(context, l10n.s_permission_denied));
|
showMessage(context, l10n.s_permission_denied));
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
@ -25,6 +26,7 @@ import '../../app/logging.dart';
|
|||||||
import '../../core/models.dart';
|
import '../../core/models.dart';
|
||||||
import '../../core/state.dart';
|
import '../../core/state.dart';
|
||||||
import '../../desktop/models.dart';
|
import '../../desktop/models.dart';
|
||||||
|
import '../../desktop/state.dart';
|
||||||
import '../../fido/models.dart';
|
import '../../fido/models.dart';
|
||||||
import '../../fido/state.dart';
|
import '../../fido/state.dart';
|
||||||
import '../../management/models.dart';
|
import '../../management/models.dart';
|
||||||
@ -36,6 +38,7 @@ import '../features.dart' as features;
|
|||||||
import '../message.dart';
|
import '../message.dart';
|
||||||
import '../models.dart';
|
import '../models.dart';
|
||||||
import '../state.dart';
|
import '../state.dart';
|
||||||
|
import 'elevate_fido_buttons.dart';
|
||||||
import 'keys.dart';
|
import 'keys.dart';
|
||||||
|
|
||||||
final _log = Logger('fido.views.reset_dialog');
|
final _log = Logger('fido.views.reset_dialog');
|
||||||
@ -68,12 +71,19 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
|
|||||||
StreamSubscription<InteractionEvent>? _subscription;
|
StreamSubscription<InteractionEvent>? _subscription;
|
||||||
InteractionEvent? _interaction;
|
InteractionEvent? _interaction;
|
||||||
int _currentStep = -1;
|
int _currentStep = -1;
|
||||||
final _totalSteps = 3;
|
late final int _totalSteps;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
final nfc = widget.data.node.transport == Transport.nfc;
|
||||||
|
_totalSteps = nfc ? 2 : 3;
|
||||||
|
}
|
||||||
|
|
||||||
String _getMessage() {
|
String _getMessage() {
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
final nfc = widget.data.node.transport == Transport.nfc;
|
final nfc = widget.data.node.transport == Transport.nfc;
|
||||||
if (_currentStep == 3) {
|
if (_currentStep == _totalSteps) {
|
||||||
return l10n.l_fido_app_reset;
|
return l10n.l_fido_app_reset;
|
||||||
}
|
}
|
||||||
return switch (_interaction) {
|
return switch (_interaction) {
|
||||||
@ -99,20 +109,26 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
|
|||||||
.contains(widget.data.info.formFactor);
|
.contains(widget.data.info.formFactor);
|
||||||
final globalReset = isBio && (supported & Capability.piv.value) != 0;
|
final globalReset = isBio && (supported & Capability.piv.value) != 0;
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
double progress = _currentStep == -1 ? 0.0 : _currentStep / (_totalSteps);
|
double progress = _currentStep == -1 ? 0.0 : _currentStep / (_totalSteps);
|
||||||
|
final needsElevation = Platform.isWindows &&
|
||||||
|
_application == Capability.fido2 &&
|
||||||
|
!ref.watch(rpcStateProvider.select((state) => state.isAdmin));
|
||||||
|
|
||||||
return ResponsiveDialog(
|
return ResponsiveDialog(
|
||||||
title: Text(l10n.s_factory_reset),
|
title: Text(l10n.s_factory_reset),
|
||||||
key: factoryResetCancel,
|
key: factoryResetCancel,
|
||||||
onCancel: switch (_application) {
|
onCancel: switch (_application) {
|
||||||
Capability.fido2 => _currentStep < 3
|
Capability.fido2 => _currentStep < _totalSteps
|
||||||
? () {
|
? () {
|
||||||
|
_currentStep = -1;
|
||||||
_subscription?.cancel();
|
_subscription?.cancel();
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
_ => null,
|
_ => null,
|
||||||
},
|
},
|
||||||
actions: [
|
actions: [
|
||||||
if (_currentStep < 3)
|
if (_currentStep < _totalSteps)
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: switch (_application) {
|
onPressed: switch (_application) {
|
||||||
Capability.fido2 => _subscription == null
|
Capability.fido2 => _subscription == null
|
||||||
@ -222,7 +238,8 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
|
|||||||
: null,
|
: null,
|
||||||
tooltip:
|
tooltip:
|
||||||
!showLabels ? c.getDisplayName(l10n) : null,
|
!showLabels ? c.getDisplayName(l10n) : null,
|
||||||
enabled: enabled & c.value != 0,
|
enabled:
|
||||||
|
enabled & c.value != 0 && (_currentStep == -1),
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
selected: _application != null ? {_application!} : {},
|
selected: _application != null ? {_application!} : {},
|
||||||
@ -247,16 +264,21 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
|
|||||||
.bodyMedium
|
.bodyMedium
|
||||||
?.copyWith(fontWeight: FontWeight.w700),
|
?.copyWith(fontWeight: FontWeight.w700),
|
||||||
),
|
),
|
||||||
Text(
|
if (needsElevation) ...[
|
||||||
switch (_application) {
|
Text(l10n.p_elevated_permissions_required),
|
||||||
Capability.oath => l10n.p_warning_disable_credentials,
|
const ElevateFidoButtons(),
|
||||||
Capability.piv => l10n.p_warning_piv_reset_desc,
|
] else ...[
|
||||||
Capability.fido2 => l10n.p_warning_disable_accounts,
|
Text(
|
||||||
_ => globalReset
|
switch (_application) {
|
||||||
? l10n.p_warning_global_reset_desc
|
Capability.oath => l10n.p_warning_disable_credentials,
|
||||||
: l10n.p_factory_reset_desc,
|
Capability.piv => l10n.p_warning_piv_reset_desc,
|
||||||
},
|
Capability.fido2 => l10n.p_warning_disable_accounts,
|
||||||
),
|
_ => globalReset
|
||||||
|
? l10n.p_warning_global_reset_desc
|
||||||
|
: l10n.p_factory_reset_desc,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
if (_application == Capability.fido2 && _currentStep >= 0) ...[
|
if (_application == Capability.fido2 && _currentStep >= 0) ...[
|
||||||
Text('${l10n.s_status}: ${_getMessage()}'),
|
Text('${l10n.s_status}: ${_getMessage()}'),
|
||||||
LinearProgressIndicator(value: progress)
|
LinearProgressIndicator(value: progress)
|
||||||
|
@ -479,6 +479,7 @@
|
|||||||
"l_import_nothing": null,
|
"l_import_nothing": null,
|
||||||
"l_importing_file": null,
|
"l_importing_file": null,
|
||||||
"s_file_imported": null,
|
"s_file_imported": null,
|
||||||
|
"l_unsupported_key_type": null,
|
||||||
"l_delete_certificate": null,
|
"l_delete_certificate": null,
|
||||||
"l_delete_certificate_desc": null,
|
"l_delete_certificate_desc": null,
|
||||||
"s_issuer": null,
|
"s_issuer": null,
|
||||||
|
@ -479,6 +479,7 @@
|
|||||||
"l_import_nothing": "Nothing to import",
|
"l_import_nothing": "Nothing to import",
|
||||||
"l_importing_file": "Importing file\u2026",
|
"l_importing_file": "Importing file\u2026",
|
||||||
"s_file_imported": "File imported",
|
"s_file_imported": "File imported",
|
||||||
|
"l_unsupported_key_type": "Unsupported key type",
|
||||||
"l_delete_certificate": "Delete certificate",
|
"l_delete_certificate": "Delete certificate",
|
||||||
"l_delete_certificate_desc": "Remove the certificate from your YubiKey",
|
"l_delete_certificate_desc": "Remove the certificate from your YubiKey",
|
||||||
"s_issuer": "Issuer",
|
"s_issuer": "Issuer",
|
||||||
|
@ -479,6 +479,7 @@
|
|||||||
"l_import_nothing": null,
|
"l_import_nothing": null,
|
||||||
"l_importing_file": "Importation d'un fichier\u2026",
|
"l_importing_file": "Importation d'un fichier\u2026",
|
||||||
"s_file_imported": "Fichier importé",
|
"s_file_imported": "Fichier importé",
|
||||||
|
"l_unsupported_key_type": null,
|
||||||
"l_delete_certificate": "Supprimer un certificat",
|
"l_delete_certificate": "Supprimer un certificat",
|
||||||
"l_delete_certificate_desc": "Supprimer un certificat de votre YubiKey",
|
"l_delete_certificate_desc": "Supprimer un certificat de votre YubiKey",
|
||||||
"s_issuer": "Émetteur",
|
"s_issuer": "Émetteur",
|
||||||
|
@ -479,6 +479,7 @@
|
|||||||
"l_import_nothing": null,
|
"l_import_nothing": null,
|
||||||
"l_importing_file": "ファイルのインポート中\u2026",
|
"l_importing_file": "ファイルのインポート中\u2026",
|
||||||
"s_file_imported": "ファイル をインポートしました",
|
"s_file_imported": "ファイル をインポートしました",
|
||||||
|
"l_unsupported_key_type": null,
|
||||||
"l_delete_certificate": "証明書を削除",
|
"l_delete_certificate": "証明書を削除",
|
||||||
"l_delete_certificate_desc": "YubiKeyか証明書の削除",
|
"l_delete_certificate_desc": "YubiKeyか証明書の削除",
|
||||||
"s_issuer": "発行者",
|
"s_issuer": "発行者",
|
||||||
|
@ -479,6 +479,7 @@
|
|||||||
"l_import_nothing": null,
|
"l_import_nothing": null,
|
||||||
"l_importing_file": "Importowanie pliku\u2026",
|
"l_importing_file": "Importowanie pliku\u2026",
|
||||||
"s_file_imported": "Plik został zaimportowany",
|
"s_file_imported": "Plik został zaimportowany",
|
||||||
|
"l_unsupported_key_type": null,
|
||||||
"l_delete_certificate": "Usuń certyfikat",
|
"l_delete_certificate": "Usuń certyfikat",
|
||||||
"l_delete_certificate_desc": "Usuń certyfikat z klucza YubiKey",
|
"l_delete_certificate_desc": "Usuń certyfikat z klucza YubiKey",
|
||||||
"s_issuer": "Wydawca",
|
"s_issuer": "Wydawca",
|
||||||
|
@ -83,7 +83,7 @@ class _ConfigureStaticDialogState extends ConsumerState<ConfigureStaticDialog> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
final password = _passwordController.text.replaceAll(' ', '');
|
final password = _passwordController.text;
|
||||||
final passwordLengthValid =
|
final passwordLengthValid =
|
||||||
password.isNotEmpty && password.length <= passwordMaxLength;
|
password.isNotEmpty && password.length <= passwordMaxLength;
|
||||||
final passwordFormatValid =
|
final passwordFormatValid =
|
||||||
|
@ -77,7 +77,11 @@ enum PinPolicy {
|
|||||||
@JsonValue(0x02)
|
@JsonValue(0x02)
|
||||||
once,
|
once,
|
||||||
@JsonValue(0x03)
|
@JsonValue(0x03)
|
||||||
always;
|
always,
|
||||||
|
@JsonValue(0x04)
|
||||||
|
matchOnce,
|
||||||
|
@JsonValue(0x05)
|
||||||
|
matchAlways;
|
||||||
|
|
||||||
const PinPolicy();
|
const PinPolicy();
|
||||||
|
|
||||||
|
@ -84,6 +84,8 @@ const _$PinPolicyEnumMap = {
|
|||||||
PinPolicy.never: 1,
|
PinPolicy.never: 1,
|
||||||
PinPolicy.once: 2,
|
PinPolicy.once: 2,
|
||||||
PinPolicy.always: 3,
|
PinPolicy.always: 3,
|
||||||
|
PinPolicy.matchOnce: 4,
|
||||||
|
PinPolicy.matchAlways: 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
_$PivStateMetadataImpl _$$PivStateMetadataImplFromJson(
|
_$PivStateMetadataImpl _$$PivStateMetadataImplFromJson(
|
||||||
|
@ -30,6 +30,7 @@ import '../keys.dart' as keys;
|
|||||||
import '../models.dart';
|
import '../models.dart';
|
||||||
import '../state.dart';
|
import '../state.dart';
|
||||||
import 'overwrite_confirm_dialog.dart';
|
import 'overwrite_confirm_dialog.dart';
|
||||||
|
import 'utils.dart';
|
||||||
|
|
||||||
class GenerateKeyDialog extends ConsumerStatefulWidget {
|
class GenerateKeyDialog extends ConsumerStatefulWidget {
|
||||||
final DevicePath devicePath;
|
final DevicePath devicePath;
|
||||||
@ -65,19 +66,6 @@ class _GenerateKeyDialogState extends ConsumerState<GenerateKeyDialog> {
|
|||||||
_validToMax = DateTime.utc(now.year + 10, now.month, now.day);
|
_validToMax = DateTime.utc(now.year + 10, now.month, now.day);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<KeyType> _getSupportedKeyTypes(bool isFips) => [
|
|
||||||
if (!isFips) KeyType.rsa1024,
|
|
||||||
KeyType.rsa2048,
|
|
||||||
if (widget.pivState.version.isAtLeast(5, 7)) ...[
|
|
||||||
KeyType.rsa3072,
|
|
||||||
KeyType.rsa4096,
|
|
||||||
KeyType.ed25519,
|
|
||||||
if (!isFips) KeyType.x25519,
|
|
||||||
],
|
|
||||||
KeyType.eccp256,
|
|
||||||
KeyType.eccp384,
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
@ -202,7 +190,8 @@ class _GenerateKeyDialogState extends ConsumerState<GenerateKeyDialog> {
|
|||||||
runSpacing: 8.0,
|
runSpacing: 8.0,
|
||||||
children: [
|
children: [
|
||||||
ChoiceFilterChip<KeyType>(
|
ChoiceFilterChip<KeyType>(
|
||||||
items: _getSupportedKeyTypes(isFips),
|
items:
|
||||||
|
getSupportedKeyTypes(widget.pivState.version, isFips),
|
||||||
value: _keyType,
|
value: _keyType,
|
||||||
selected: _keyType != defaultKeyType,
|
selected: _keyType != defaultKeyType,
|
||||||
itemBuilder: (value) => Text(value.getDisplayName(l10n)),
|
itemBuilder: (value) => Text(value.getDisplayName(l10n)),
|
||||||
|
@ -31,6 +31,7 @@ import '../models.dart';
|
|||||||
import '../state.dart';
|
import '../state.dart';
|
||||||
import 'cert_info_view.dart';
|
import 'cert_info_view.dart';
|
||||||
import 'overwrite_confirm_dialog.dart';
|
import 'overwrite_confirm_dialog.dart';
|
||||||
|
import 'utils.dart';
|
||||||
|
|
||||||
class ImportFileDialog extends ConsumerStatefulWidget {
|
class ImportFileDialog extends ConsumerStatefulWidget {
|
||||||
final DevicePath devicePath;
|
final DevicePath devicePath;
|
||||||
@ -86,10 +87,13 @@ class _ImportFileDialogState extends ConsumerState<ImportFileDialog> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
final textTheme = Theme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
// This is what ListTile uses for subtitle
|
// This is what ListTile uses for subtitle
|
||||||
final subtitleStyle = textTheme.bodyMedium!.copyWith(
|
final subtitleStyle = textTheme.bodyMedium!.copyWith(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
color: colorScheme.onSurfaceVariant,
|
||||||
);
|
);
|
||||||
|
// This is what TextInput errors look like
|
||||||
|
final errorStyle = textTheme.labelLarge!.copyWith(color: colorScheme.error);
|
||||||
final state = _state;
|
final state = _state;
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
return ResponsiveDialog(
|
return ResponsiveDialog(
|
||||||
@ -166,116 +170,142 @@ class _ImportFileDialogState extends ConsumerState<ImportFileDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
result: (_, keyType, certInfo) => ResponsiveDialog(
|
result: (_, keyType, certInfo) {
|
||||||
title: Text(l10n.l_import_file),
|
final isFips =
|
||||||
actions: [
|
ref.watch(currentDeviceDataProvider).valueOrNull?.info.isFips ??
|
||||||
TextButton(
|
false;
|
||||||
key: keys.unlockButton,
|
final unsupportedKey = keyType != null &&
|
||||||
onPressed: (keyType == null && certInfo == null) || _importing
|
!getSupportedKeyTypes(widget.pivState.version, isFips)
|
||||||
? null
|
.contains(keyType);
|
||||||
: () async {
|
return ResponsiveDialog(
|
||||||
final withContext = ref.read(withContextProvider);
|
title: Text(l10n.l_import_file),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
key: keys.unlockButton,
|
||||||
|
onPressed: (keyType == null && certInfo == null) ||
|
||||||
|
_importing ||
|
||||||
|
unsupportedKey
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
final withContext = ref.read(withContextProvider);
|
||||||
|
|
||||||
if (!await confirmOverwrite(
|
if (!await confirmOverwrite(
|
||||||
context,
|
context,
|
||||||
widget.pivSlot,
|
widget.pivSlot,
|
||||||
writeKey: keyType != null,
|
writeKey: keyType != null,
|
||||||
writeCert: certInfo != null,
|
writeCert: certInfo != null,
|
||||||
)) {
|
)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_importing = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
void Function()? close;
|
|
||||||
try {
|
|
||||||
close = await withContext<void Function()>(
|
|
||||||
(context) async => showMessage(
|
|
||||||
context,
|
|
||||||
l10n.l_importing_file,
|
|
||||||
duration: const Duration(seconds: 30),
|
|
||||||
));
|
|
||||||
await ref
|
|
||||||
.read(pivSlotsProvider(widget.devicePath).notifier)
|
|
||||||
.import(widget.pivSlot.slot, _data,
|
|
||||||
password:
|
|
||||||
_password.isNotEmpty ? _password : null);
|
|
||||||
await withContext(
|
|
||||||
(context) async {
|
|
||||||
Navigator.of(context).pop(true);
|
|
||||||
showMessage(context, l10n.s_file_imported);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
// TODO: More error cases
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_passwordIsWrong = true;
|
_importing = true;
|
||||||
_importing = false;
|
|
||||||
});
|
});
|
||||||
} finally {
|
|
||||||
close?.call();
|
void Function()? close;
|
||||||
}
|
try {
|
||||||
},
|
close = await withContext<void Function()>(
|
||||||
child: Text(l10n.s_import),
|
(context) async => showMessage(
|
||||||
),
|
context,
|
||||||
],
|
l10n.l_importing_file,
|
||||||
child: Padding(
|
duration: const Duration(seconds: 30),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
));
|
||||||
child: Column(
|
await ref
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
.read(pivSlotsProvider(widget.devicePath).notifier)
|
||||||
children: [
|
.import(widget.pivSlot.slot, _data,
|
||||||
Text(l10n.p_import_items_desc(
|
password:
|
||||||
widget.pivSlot.slot.getDisplayName(l10n))),
|
_password.isNotEmpty ? _password : null);
|
||||||
if (keyType == null && certInfo == null) ...[
|
await withContext(
|
||||||
Text(
|
(context) async {
|
||||||
l10n.l_import_nothing,
|
Navigator.of(context).pop(true);
|
||||||
style: subtitleStyle,
|
showMessage(context, l10n.s_file_imported);
|
||||||
softWrap: true,
|
},
|
||||||
textAlign: TextAlign.center,
|
);
|
||||||
),
|
} catch (err) {
|
||||||
],
|
// TODO: More error cases
|
||||||
if (keyType != null) ...[
|
setState(() {
|
||||||
Text(
|
_passwordIsWrong = true;
|
||||||
l10n.s_private_key,
|
_importing = false;
|
||||||
style: textTheme.bodyLarge,
|
});
|
||||||
softWrap: true,
|
} finally {
|
||||||
textAlign: TextAlign.center,
|
close?.call();
|
||||||
),
|
}
|
||||||
Row(
|
},
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Text(l10n.s_import),
|
||||||
children: [
|
),
|
||||||
Text(l10n.s_algorithm),
|
],
|
||||||
const SizedBox(width: 8),
|
child: Padding(
|
||||||
Text(
|
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||||
keyType.name.toUpperCase(),
|
child: Column(
|
||||||
style: subtitleStyle,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(l10n.p_import_items_desc(
|
||||||
|
widget.pivSlot.slot.getDisplayName(l10n))),
|
||||||
|
if (keyType == null && certInfo == null) ...[
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.error, color: colorScheme.error),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
l10n.l_import_nothing,
|
||||||
|
style: errorStyle,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (keyType != null) ...[
|
||||||
|
Text(
|
||||||
|
l10n.s_private_key,
|
||||||
|
style: textTheme.bodyLarge,
|
||||||
|
softWrap: true,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(l10n.s_algorithm),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
keyType.name.toUpperCase(),
|
||||||
|
style: subtitleStyle,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (unsupportedKey)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.error, color: colorScheme.error),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
l10n.l_unsupported_key_type,
|
||||||
|
style: errorStyle,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
if (certInfo != null) ...[
|
||||||
],
|
Text(
|
||||||
if (certInfo != null) ...[
|
l10n.s_certificate,
|
||||||
Text(
|
style: textTheme.bodyLarge,
|
||||||
l10n.s_certificate,
|
softWrap: true,
|
||||||
style: textTheme.bodyLarge,
|
textAlign: TextAlign.center,
|
||||||
softWrap: true,
|
),
|
||||||
textAlign: TextAlign.center,
|
SizedBox(
|
||||||
),
|
height:
|
||||||
SizedBox(
|
140, // Needed for layout, adapt if text sizes changes
|
||||||
height: 140, // Needed for layout, adapt if text sizes changes
|
child: CertInfoTable(certInfo, null),
|
||||||
child: CertInfoTable(certInfo, null),
|
),
|
||||||
),
|
]
|
||||||
]
|
]
|
||||||
]
|
.map((e) => Padding(
|
||||||
.map((e) => Padding(
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
child: e,
|
||||||
child: e,
|
))
|
||||||
))
|
.toList(),
|
||||||
.toList(),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
31
lib/piv/views/utils.dart
Normal file
31
lib/piv/views/utils.dart
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Yubico.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import '../../core/models.dart';
|
||||||
|
import '../models.dart';
|
||||||
|
|
||||||
|
List<KeyType> getSupportedKeyTypes(Version version, bool isFips) => [
|
||||||
|
if (!isFips) KeyType.rsa1024,
|
||||||
|
KeyType.rsa2048,
|
||||||
|
if (version.isAtLeast(5, 7)) ...[
|
||||||
|
KeyType.rsa3072,
|
||||||
|
KeyType.rsa4096,
|
||||||
|
KeyType.ed25519,
|
||||||
|
if (!isFips) KeyType.x25519,
|
||||||
|
],
|
||||||
|
KeyType.eccp256,
|
||||||
|
KeyType.eccp384,
|
||||||
|
];
|
@ -1,5 +1,5 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// This file is generated by running ./set-version.py <version> <build>
|
// This file is generated by running ./set-version.py <version> <build>
|
||||||
|
|
||||||
const String version = '6.4.0-dev.0';
|
const String version = '6.4.1-dev.0';
|
||||||
const int build = 60400;
|
const int build = 60402;
|
||||||
|
@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
|
|
||||||
# This field is updated by running ./set-version.py <version>
|
# This field is updated by running ./set-version.py <version>
|
||||||
# DO NOT MANUALLY EDIT THIS!
|
# DO NOT MANUALLY EDIT THIS!
|
||||||
version: 6.4.0-dev.0+60400
|
version: 6.4.1-dev.0+60402
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
$version="6.4.0-dev.0"
|
$version="6.4.1-dev.0"
|
||||||
|
|
||||||
echo "Clean-up of old files"
|
echo "Clean-up of old files"
|
||||||
rm *.msi
|
rm *.msi
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
||||||
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||||
<?define ProductVersion="6.4.0" ?>
|
<?define ProductVersion="6.4.1" ?>
|
||||||
<?define ProductName="Yubico Authenticator" ?>
|
<?define ProductName="Yubico Authenticator" ?>
|
||||||
|
|
||||||
<Product Id="*" UpgradeCode="fcbafc57-aaaa-47b8-b861-20bda48cd4f6" Name="$(var.ProductName)" Version="$(var.ProductVersion)" Manufacturer="Yubico AB" Language="1033">
|
<Product Id="*" UpgradeCode="fcbafc57-aaaa-47b8-b861-20bda48cd4f6" Name="$(var.ProductName)" Version="$(var.ProductVersion)" Manufacturer="Yubico AB" Language="1033">
|
||||||
|
Loading…
Reference in New Issue
Block a user