Skip to content

Commit 3ef1e21

Browse files
committed
Handle FIDO transports_for_reset
1 parent f5bae81 commit 3ef1e21

File tree

15 files changed

+227
-77
lines changed

15 files changed

+227
-77
lines changed

lib/app/views/reset_dialog.dart

Lines changed: 113 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
7575
late Capability? _application;
7676
late bool _globalReset;
7777
late bool _longTouch;
78+
// If empty, FIDO reset is enabled for the current transport.
79+
// Otherwise, the reset is disabled for the current transport and requires
80+
// one of the listed transports to be used instead.
81+
List<Transport> _fidoDisabledRequiredTransports = [];
7882
StreamSubscription<InteractionEvent>? _subscription;
7983
InteractionEvent? _interaction;
8084
int _currentStep = -1;
@@ -147,6 +151,28 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
147151
final showResetProgress =
148152
_resetting && (!Platform.isAndroid || usbTransport);
149153

154+
if (widget.data.info.config.enabledCapabilities[widget
155+
.data
156+
.node
157+
.transport]! &
158+
Capability.fido2.value !=
159+
0) {
160+
final ctapInfo =
161+
ref.watch(fidoStateProvider(widget.data.node.path)).value?.info ?? {};
162+
_longTouch = ctapInfo['long_touch_for_reset'] == true;
163+
164+
final transportsForReset =
165+
ctapInfo['transports_for_reset'] as List? ?? [];
166+
if (transportsForReset.isNotEmpty &&
167+
!transportsForReset.contains(widget.data.node.transport.name)) {
168+
_fidoDisabledRequiredTransports = [
169+
for (var t in transportsForReset) Transport.values.byName(t),
170+
];
171+
} else {
172+
_fidoDisabledRequiredTransports = [];
173+
}
174+
}
175+
150176
return ResponsiveDialog(
151177
title: Text(l10n.s_factory_reset),
152178
key: factoryResetCancel,
@@ -175,83 +201,87 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
175201
onPressed:
176202
!_resetting
177203
? switch (_application) {
178-
Capability.fido2 => () async {
179-
final ctapInfo =
180-
ref
181-
.read(fidoStateProvider(widget.data.node.path))
182-
.value
183-
?.info ??
184-
{};
185-
_longTouch = ctapInfo['long_touch_for_reset'] == true;
186-
_subscription = ref
187-
.read(
188-
fidoStateProvider(widget.data.node.path).notifier,
189-
)
190-
.reset()
191-
.listen(
192-
(event) {
193-
setState(() {
194-
_resetting = true;
195-
_currentStep++;
196-
_interaction = event;
197-
});
198-
},
199-
onDone: () async {
200-
setState(() {
201-
_currentStep = _totalSteps;
202-
});
203-
_subscription = null;
204-
if (isAndroid && !usbTransport) {
205-
// close the dialog after reset over NFC on Android
206-
await ref.read(withContextProvider)((
207-
context,
208-
) async {
209-
Navigator.of(context).pop();
210-
showMessage(context, l10n.l_fido_app_reset);
211-
});
212-
}
213-
},
214-
onError: (e) {
215-
if (e is CancellationException) {
216-
setState(() {
217-
_resetting = false;
218-
_currentStep = -1;
219-
_application = null;
220-
});
221-
return;
222-
}
204+
Capability.fido2 =>
205+
_fidoDisabledRequiredTransports.isEmpty
206+
? () async {
207+
_subscription = ref
208+
.read(
209+
fidoStateProvider(
210+
widget.data.node.path,
211+
).notifier,
212+
)
213+
.reset()
214+
.listen(
215+
(event) {
216+
setState(() {
217+
_resetting = true;
218+
_currentStep++;
219+
_interaction = event;
220+
});
221+
},
222+
onDone: () async {
223+
setState(() {
224+
_currentStep = _totalSteps;
225+
});
226+
_subscription = null;
227+
if (isAndroid && !usbTransport) {
228+
// close the dialog after reset over NFC on Android
229+
await ref.read(withContextProvider)((
230+
context,
231+
) async {
232+
Navigator.of(context).pop();
233+
showMessage(
234+
context,
235+
l10n.l_fido_app_reset,
236+
);
237+
});
238+
}
239+
},
240+
onError: (e) {
241+
if (e is CancellationException) {
242+
setState(() {
243+
_resetting = false;
244+
_currentStep = -1;
245+
_application = null;
246+
});
247+
return;
248+
}
223249

224-
_log.error('Error performing FIDO reset', e);
250+
_log.error(
251+
'Error performing FIDO reset',
252+
e,
253+
);
225254

226-
if (!context.mounted) return;
227-
Navigator.of(context).pop();
228-
final String errorMessage;
229-
// TODO: Make this cleaner than importing desktop specific RpcError.
230-
if (e is RpcError) {
231-
if (e.status == 'connection-error') {
232-
errorMessage =
233-
l10n.l_failed_connecting_to_fido;
234-
} else if (e.status == 'key-mismatch') {
235-
errorMessage =
236-
l10n.l_wrong_inserted_yk_error;
237-
} else if (e.status ==
238-
'user-action-timeout') {
239-
errorMessage =
240-
l10n.l_user_action_timeout_error;
241-
} else {
242-
errorMessage = e.message;
243-
}
244-
} else {
245-
errorMessage = e.toString();
246-
}
247-
showMessage(
248-
context,
249-
l10n.l_reset_failed(errorMessage),
250-
duration: const Duration(seconds: 4),
251-
);
252-
},
253-
);
254-
},
255+
if (!context.mounted) return;
256+
Navigator.of(context).pop();
257+
final String errorMessage;
258+
// TODO: Make this cleaner than importing desktop specific RpcError.
259+
if (e is RpcError) {
260+
if (e.status == 'connection-error') {
261+
errorMessage =
262+
l10n.l_failed_connecting_to_fido;
263+
} else if (e.status == 'key-mismatch') {
264+
errorMessage =
265+
l10n.l_wrong_inserted_yk_error;
266+
} else if (e.status ==
267+
'user-action-timeout') {
268+
errorMessage =
269+
l10n.l_user_action_timeout_error;
270+
} else {
271+
errorMessage = e.message;
272+
}
273+
} else {
274+
errorMessage = e.toString();
275+
}
276+
showMessage(
277+
context,
278+
l10n.l_reset_failed(errorMessage),
279+
duration: const Duration(seconds: 4),
280+
);
281+
},
282+
);
283+
}
284+
: null,
255285
Capability.oath => () async {
256286
setState(() {
257287
_resetting = true;
@@ -423,7 +453,14 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
423453
Capability.oath =>
424454
l10n.p_warning_disable_credentials,
425455
Capability.piv => l10n.p_warning_piv_reset_desc,
426-
Capability.fido2 => l10n.p_warning_disable_accounts,
456+
Capability.fido2 =>
457+
_fidoDisabledRequiredTransports.isEmpty
458+
? l10n.p_warning_disable_accounts
459+
: l10n.p_transports_required_for_reset(
460+
_fidoDisabledRequiredTransports
461+
.map((t) => t.getDisplayName(l10n))
462+
.join(', '),
463+
),
427464
_ =>
428465
_globalReset
429466
? l10n.p_warning_global_reset_desc

lib/core/models.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,21 @@ import 'package:collection/collection.dart';
1818
import 'package:freezed_annotation/freezed_annotation.dart';
1919
import 'package:intl/intl.dart';
2020

21+
import '../generated/l10n/app_localizations.dart';
2122
import '../management/models.dart';
2223

2324
part 'models.freezed.dart';
2425
part 'models.g.dart';
2526

26-
enum Transport { usb, nfc }
27+
enum Transport {
28+
usb,
29+
nfc;
30+
31+
String getDisplayName(AppLocalizations l10n) => switch (this) {
32+
Transport.usb => l10n.s_usb,
33+
Transport.nfc => l10n.s_nfc,
34+
};
35+
}
2736

2837
enum UsbInterface {
2938
otp(0x01),

lib/l10n/app_cs.arb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,14 @@
975975
"p_warning_disable_credentials": "Vaše OATH přihlašovací údaje, stejně jako veškerá nastavení hesel, budou odstraněny z tohoto YubiKey. Ujistěte se, že jste je odstranili z příslušných webových stránek, abyste se vyhnuli zablokování vašich účtů.",
976976
"p_warning_deletes_accounts": "Varování! Neodvolatelně smažete z vašeho YubiKey všechny účty U2F a FIDO2 včetně přístupových klíčů.",
977977
"p_warning_disable_accounts": "Vaše přihlašovací údaje včetně kódu PIN, budou odstraněny z tohoto YubiKey. Ujistěte se, že jste je odstranili z příslušných webových stránek, abyste se vyhnuli zablokování vašich účtů.",
978+
"p_transports_required_for_reset": null,
979+
"@p_transports_required_for_reset": {
980+
"placeholders": {
981+
"transports": {
982+
"type": "String"
983+
}
984+
}
985+
},
978986
"p_warning_piv_reset": "Varování! Všechna data uložená pro PIV budou neodvolatelně smazána z vašeho YubiKey.",
979987
"p_warning_piv_reset_desc": "To zahrnuje soukromé klíče a certifikáty. Váš PIN, PUK, a klíč správy bude resetován na jejich výchozí tovární hodnoty.",
980988
"p_warning_global_reset": "Varování! Tímto se neodvolatelně smažou všechna uložená data, včetně přihlašovacích údajů, z vašeho YubiKey.",

lib/l10n/app_de.arb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,14 @@
975975
"p_warning_disable_credentials": "Deine OATH Anmeldedaten und sämtliche Passwörter werden von diesem YubiKey entfernt. Deaktiviere die Anmeldedaten unbedingt zuerst auf den zugehörigen Webseiten, bevor du fortfährst, um nicht aus deinen Konten ausgesperrt zu werden.",
976976
"p_warning_deletes_accounts": "Achtung! Dies löscht alle U2F und FIDO2 Konten dauerhaft von deinem YubiKey.",
977977
"p_warning_disable_accounts": "Deine Anmeldedaten und sämtliche PINs werden von diesem YubiKey entfernt. Deaktiviere die Anmeldedaten unbedingt zuerst auf den zugehörigen Webseiten, bevor du fortfährst, um nicht aus deinen Konten ausgesperrt zu werden.",
978+
"p_transports_required_for_reset": null,
979+
"@p_transports_required_for_reset": {
980+
"placeholders": {
981+
"transports": {
982+
"type": "String"
983+
}
984+
}
985+
},
978986
"p_warning_piv_reset": "Achtung! Alle gespeicherten PIV Daten werden dauerhaft von deinem YubiKey gelöscht.",
979987
"p_warning_piv_reset_desc": "Dies schließt private Schlüssel und Zertifikate mit ein. Deine PIN, PUK und der Management-Key werden auf Werkseinstellung zurückgesetzt.",
980988
"p_warning_global_reset": "Achtung! Dies löscht alle gespeicherten Daten inkl. Anmeldedaten dauerhaft von deinem YubiKey.",

lib/l10n/app_en.arb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,14 @@
975975
"p_warning_disable_credentials": "Your OATH credentials, as well as any password set, will be removed from this YubiKey. Make sure to disable these from their respective web sites to avoid being locked out of your accounts.",
976976
"p_warning_deletes_accounts": "Warning! This will irrevocably delete all U2F and FIDO2 accounts, including passkeys, from your YubiKey.",
977977
"p_warning_disable_accounts": "Your credentials, as well as any PIN set, will be removed from this YubiKey. Make sure to disable these from their respective web sites to avoid being locked out of your accounts.",
978+
"p_transports_required_for_reset": "FIDO reset is disabled for this transport, use one of the following instead: {transports}",
979+
"@p_transports_required_for_reset": {
980+
"placeholders": {
981+
"transports": {
982+
"type": "String"
983+
}
984+
}
985+
},
978986
"p_warning_piv_reset": "Warning! All data stored for PIV will be irrevocably deleted from your YubiKey.",
979987
"p_warning_piv_reset_desc": "This includes private keys and certificates. Your PIN, PUK, and management key will be reset to their factory default values.",
980988
"p_warning_global_reset": "Warning! This will irrevocably delete all saved data, including credentials, from your YubiKey.",

lib/l10n/app_es.arb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,14 @@
975975
"p_warning_disable_credentials": "Tus credenciales OATH, así como cualquier contraseña establecida, serán removidas de esta YubiKey. Asegúrate de desactivarlas desde sus respectivos sitios web para evitar ser bloqueado de tus cuentas.",
976976
"p_warning_deletes_accounts": "¡Advertencia! Esto borrará irrevocablemente todas las cuentas U2F y FIDO2, incluidas las passkeys, de tu YubiKey.",
977977
"p_warning_disable_accounts": "Tus credenciales, así como cualquier PIN establecido, se eliminarán de esta YubiKey. Asegúrate de desvincularlos de los respectivos sitios web para evitar quedar bloqueado/a de tus cuentas.",
978+
"p_transports_required_for_reset": null,
979+
"@p_transports_required_for_reset": {
980+
"placeholders": {
981+
"transports": {
982+
"type": "String"
983+
}
984+
}
985+
},
978986
"p_warning_piv_reset": "¡Advertencia! Todos los datos almacenados para PIV se eliminarán irrevocablemente de tu YubiKey.",
979987
"p_warning_piv_reset_desc": "Esto incluye claves privadas y certificados. Su PIN, PUK y clave de administración se restablecerán a sus valores predeterminados de fábrica.",
980988
"p_warning_global_reset": "¡Advertencia! Esto borrará irrevocablemente todos los datos guardados, incluidas las credenciales, de tu YubiKey.",

lib/l10n/app_fr.arb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,14 @@
975975
"p_warning_disable_credentials": "Vos informations d'identification OATH, ainsi que tout mot de passe défini, seront supprimés de cette clé YubiKey. Veillez à les désactiver à partir de leurs sites web respectifs afin d'éviter que vos comptes ne soient verrouillés.",
976976
"p_warning_deletes_accounts": "Attention\u00a0! Cela supprimera définitivement tous les comptes U2F et FIDO2, notamment les passkeys, de votre YubiKey.",
977977
"p_warning_disable_accounts": "Vos informations d'identification, ainsi que tout code PIN défini, seront supprimés de cette YubiKey. Veillez à les désactiver à partir de leurs sites web respectifs pour éviter que vos comptes ne soient verrouillés.",
978+
"p_transports_required_for_reset": null,
979+
"@p_transports_required_for_reset": {
980+
"placeholders": {
981+
"transports": {
982+
"type": "String"
983+
}
984+
}
985+
},
978986
"p_warning_piv_reset": "Attention\u00a0! Toutes les données PIV seront définitivement supprimées de votre YubiKey.",
979987
"p_warning_piv_reset_desc": "Cela inclut les clés privées et les certificats. Vos PIN, PUK et clé de gestion seront réinitialisés à leurs valeurs d'usine.",
980988
"p_warning_global_reset": "Attention\u00a0! Cela supprimera définitivement toutes les données enregistrées, notamment les identifiants, de votre YubiKey.",

lib/l10n/app_ja.arb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,14 @@
975975
"p_warning_disable_credentials": "あなたの OATH 認証情報、および設定されているパスワードは、この YubiKey から削除されます。アカウントにログインできなくならないように、それぞれのウェブサイトから該当する認証情報を無効にしてください。",
976976
"p_warning_deletes_accounts": "警告!パスキーを含めたすべての U2F および FIDO2 アカウントが YubiKey から削除されます。削除されたデータは回復できません。",
977977
"p_warning_disable_accounts": "あなたの認証情報および設定された PIN は、この YubiKey から削除されます。アカウントにログインできなくならないように、それぞれのウェブサイトから該当する認証情報を無効にしてください。",
978+
"p_transports_required_for_reset": null,
979+
"@p_transports_required_for_reset": {
980+
"placeholders": {
981+
"transports": {
982+
"type": "String"
983+
}
984+
}
985+
},
978986
"p_warning_piv_reset": "警告!PIV に保存されたすべてのデータが YubiKey から削除されます。削除されたデータは回復できません。",
979987
"p_warning_piv_reset_desc": "秘密鍵と証明書が含まれます。PIN、PUK、および管理キーが工場出荷時のデフォルト値にリセットされます。",
980988
"p_warning_global_reset": "警告!資格情報を含めた保存されているすべてのデータが YubiKey から削除されます。削除されたデータは回復できません。",

0 commit comments

Comments
 (0)