Merge pull request #128 from str4d/115-yubikey-4-no-pin-cache

Document and warn that PIN policy "Once" doesn't work for YubiKey 4
This commit is contained in:
str4d
2023-02-16 18:34:11 +00:00
committed by GitHub
4 changed files with 63 additions and 27 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ to 0.3.0 are beta releases.
- MSRV is now 1.60.0. - MSRV is now 1.60.0.
- The YubiKey PIV PIN and touch caches are now preserved across processes in - The YubiKey PIV PIN and touch caches are now preserved across processes in
most cases. See [README.md](README.md#agent-support) for exceptions. This has most cases. See [README.md](README.md#agent-support) for exceptions. This has
several usability effects: several usability effects (not applicable to YubiKey 4 series):
- If a YubiKey's PIN is cached by an agent like `yubikey-agent`, and then - If a YubiKey's PIN is cached by an agent like `yubikey-agent`, and then
`age-plugin-yubikey` is run (either directly or as a plugin), the agent `age-plugin-yubikey` is run (either directly or as a plugin), the agent
won't request a PIN entry on its next use. won't request a PIN entry on its next use.
+6 -3
View File
@@ -123,9 +123,10 @@ age client as normal (e.g. `rage -d -i yubikey-identity.txt`).
### Agent support ### Agent support
`age-plugin-yubikey` does not provide or interact with an agent for decryption. `age-plugin-yubikey` does not provide or interact with an agent for decryption.
It does however preserve the PIN cache by not soft-resetting the YubiKey after a It does however attempt to preserve the PIN cache by not soft-resetting the
decryption or read-only operation, which enables YubiKey identities configured YubiKey after a decryption or read-only operation, which enables YubiKey
with a PIN policy of `once` to not prompt for the PIN on every decryption. identities configured with a PIN policy of `once` to not prompt for the PIN on
every decryption. **This does not work for YubiKey 4 series.**
The session that corresponds to the `once` policy can be ended in several ways, The session that corresponds to the `once` policy can be ended in several ways,
not all of which are necessarily intuitive: not all of which are necessarily intuitive:
@@ -133,6 +134,8 @@ not all of which are necessarily intuitive:
- Unplugging the YubiKey (the obvious way). - Unplugging the YubiKey (the obvious way).
- Using a different applet (e.g. FIDO2). This causes the PIV applet to be closed - Using a different applet (e.g. FIDO2). This causes the PIV applet to be closed
which clears its state. which clears its state.
- This is why the YubiKey 4 series does not support PIN cache preservation:
their serial can only be obtained by switching to the OTP applet.
- Generating a new age identity via `age-plugin-yubikey --generate` or the CLI - Generating a new age identity via `age-plugin-yubikey --generate` or the CLI
interface. This is to avoid leaving the YubiKey authenticated with the interface. This is to avoid leaving the YubiKey authenticated with the
management key. management key.
+8
View File
@@ -76,6 +76,14 @@ cli-setup-name-identity = 📛 Name this identity
cli-setup-select-pin-policy = 🔤 Select a PIN policy cli-setup-select-pin-policy = 🔤 Select a PIN policy
cli-setup-select-touch-policy = 👆 Select a touch 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-generate-new = Generate new identity in slot {$slot_index}?
cli-setup-use-existing = Use existing identity in slot {$slot_index}? cli-setup-use-existing = Use existing identity in slot {$slot_index}?
+48 -23
View File
@@ -487,29 +487,54 @@ fn main() -> Result<(), Error> {
.report(true) .report(true)
.interact_text()?; .interact_text()?;
let pin_policy = match Select::new() let mut displayed_yk4_warning = false;
.with_prompt(fl!("cli-setup-select-pin-policy")) let pin_policy = loop {
.items(&[ let pin_policy = match Select::new()
fl!("pin-policy-always"), .with_prompt(fl!("cli-setup-select-pin-policy"))
fl!("pin-policy-once"), .items(&[
fl!("pin-policy-never"), fl!("pin-policy-always"),
]) fl!("pin-policy-once"),
.default( fl!("pin-policy-never"),
[PinPolicy::Always, PinPolicy::Once, PinPolicy::Never] ])
.iter() .default(
.position(|p| { [PinPolicy::Always, PinPolicy::Once, PinPolicy::Never]
p == &flags.pin_policy.unwrap_or(builder::DEFAULT_PIN_POLICY) .iter()
}) .position(|p| {
.unwrap(), p == &flags.pin_policy.unwrap_or(builder::DEFAULT_PIN_POLICY)
) })
.report(true) .unwrap(),
.interact_opt()? )
{ .report(true)
Some(0) => PinPolicy::Always, .interact_opt()?
Some(1) => PinPolicy::Once, {
Some(2) => PinPolicy::Never, Some(0) => PinPolicy::Always,
Some(_) => unreachable!(), Some(1) => PinPolicy::Once,
None => return Ok(()), 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() let touch_policy = match Select::new()