From 4e053b5efc3734b142f34028bcd5932bbf858105 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 11 Feb 2023 22:04:14 +0000 Subject: [PATCH] TUI: Warn YubiKey 4 users of issue with PIN policy "Once" --- i18n/en-US/age_plugin_yubikey.ftl | 8 ++++ src/main.rs | 71 +++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/i18n/en-US/age_plugin_yubikey.ftl b/i18n/en-US/age_plugin_yubikey.ftl index 08d4f24..9a0bc66 100644 --- a/i18n/en-US/age_plugin_yubikey.ftl +++ b/i18n/en-US/age_plugin_yubikey.ftl @@ -76,6 +76,14 @@ cli-setup-name-identity = 📛 Name this identity cli-setup-select-pin-policy = 🔤 Select a PIN policy cli-setup-select-touch-policy = 👆 Select a touch policy +cli-setup-yk4-pin-policy = + ⚠️ Your {-yubikey} is a {-yubikey} 4 series. With ephemeral applications like + {-age-plugin-yubikey}, a PIN policy of "Once" behaves like a PIN policy of + "Always", and your PIN will be requested for every decryption. However, you + might still benefit from a PIN policy of "Once" in long-running applications + like agents. +cli-setup-yk4-pin-policy-confirm = Use PIN policy of "Once" with {-yubikey} 4? + cli-setup-generate-new = Generate new identity in slot {$slot_index}? cli-setup-use-existing = Use existing identity in slot {$slot_index}? diff --git a/src/main.rs b/src/main.rs index f6cd93d..757cab6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -487,29 +487,54 @@ fn main() -> Result<(), Error> { .report(true) .interact_text()?; - let pin_policy = match Select::new() - .with_prompt(fl!("cli-setup-select-pin-policy")) - .items(&[ - fl!("pin-policy-always"), - fl!("pin-policy-once"), - fl!("pin-policy-never"), - ]) - .default( - [PinPolicy::Always, PinPolicy::Once, PinPolicy::Never] - .iter() - .position(|p| { - p == &flags.pin_policy.unwrap_or(builder::DEFAULT_PIN_POLICY) - }) - .unwrap(), - ) - .report(true) - .interact_opt()? - { - Some(0) => PinPolicy::Always, - Some(1) => PinPolicy::Once, - Some(2) => PinPolicy::Never, - Some(_) => unreachable!(), - None => return Ok(()), + let mut displayed_yk4_warning = false; + let pin_policy = loop { + let pin_policy = match Select::new() + .with_prompt(fl!("cli-setup-select-pin-policy")) + .items(&[ + fl!("pin-policy-always"), + fl!("pin-policy-once"), + fl!("pin-policy-never"), + ]) + .default( + [PinPolicy::Always, PinPolicy::Once, PinPolicy::Never] + .iter() + .position(|p| { + p == &flags.pin_policy.unwrap_or(builder::DEFAULT_PIN_POLICY) + }) + .unwrap(), + ) + .report(true) + .interact_opt()? + { + Some(0) => PinPolicy::Always, + Some(1) => PinPolicy::Once, + Some(2) => PinPolicy::Never, + Some(_) => unreachable!(), + None => return Ok(()), + }; + + // We can't preserve the PIN cache for YubiKey 4 series, because to + // retrieve the serial we switch to the OTP applet. + match (pin_policy, yubikey.version().major) { + (PinPolicy::Once, 4) => { + if !displayed_yk4_warning { + eprintln!(); + eprintln!("{}", fl!("cli-setup-yk4-pin-policy")); + eprintln!(); + displayed_yk4_warning = true; + } + + if Confirm::new() + .with_prompt(fl!("cli-setup-yk4-pin-policy-confirm")) + .report(true) + .interact()? + { + break pin_policy; + } + } + _ => break pin_policy, + } }; let touch_policy = match Select::new()