[Bug 3938] New: FIDO2 verify-required keys fail to sign on non-biometric tokens ("option uv is unknown")

bugzilla-daemon at mindrot.org bugzilla-daemon at mindrot.org
Fri Mar 27 04:44:00 AEDT 2026


https://bugzilla.mindrot.org/show_bug.cgi?id=3938

            Bug ID: 3938
           Summary: FIDO2 verify-required keys fail to sign on
                    non-biometric tokens ("option uv is unknown")
           Product: Portable OpenSSH
           Version: 10.2p1
          Hardware: ARM64
                OS: Mac OS X
            Status: NEW
          Severity: minor
          Priority: P5
         Component: ssh
          Assignee: unassigned-bugs at mindrot.org
          Reporter: hello at niklaas.eu

## Summary

SSH signing with ED25519-SK keys created with `-O verify-required`
fails on
non-biometric FIDO2 tokens (e.g. YubiKey 5 series). The failure occurs
because
`sk_sign()` in `sk-usbhid.c` checks for the FIDO2 `"uv"` option to
determine
whether the device can perform user verification, but non-biometric
tokens
don't advertise `"uv"`. They use `clientPin` for user verification
instead.
The code returns `SSH_SK_ERR_PIN_REQUIRED`, which should trigger a PIN
prompt,
but a second issue in `sshconnect2.c` prevents the fallback from being
reached.

See disclaimer below regarding the analysis above.

## Environment

- OpenSSH 10.2p1 (also affects 9.1p1 through current HEAD)
- libfido2 1.16.0
- YubiKey 5 series, firmware 5.7.1
- macOS (arm64), but the bug is platform-independent

## Steps to reproduce

1. Set a FIDO2 PIN on a non-biometric YubiKey.
2. Generate a key with verify-required:
   ```
   ssh-keygen -t ed25519-sk -O verify-required -C "test"
   ```
3. Attempt to use the key:
   ```
   ssh -v -i ~/.ssh/id_ed25519_sk -o IdentitiesOnly=yes git at github.com
   ```

## Expected behavior

SSH prompts for the YubiKey PIN, then requests a touch, and the
signature
succeeds.

## Actual behavior

Signing fails immediately without a PIN prompt:

```
debug1: check_sk_options: option uv is unknown
debug1: ssh_sk_sign: check_sk_options uv
debug1: sshsk_sign: sk_sign failed with code -3
debug1: ssh-sk-helper: Signing failed: incorrect passphrase supplied to
decrypt private key
sign_and_send_pubkey: signing failed for ED25519-SK "test": incorrect
passphrase supplied to decrypt private key
```

When the key is loaded in ssh-agent, the error is:

```
sign_and_send_pubkey: signing failed for ED25519-SK "test" from agent:
agent refused operation
```

---

Disclaimer: Everything below this line was analyzed and written by my
clanker. I don't know C and I didn't verify what follows. I'm posting
this just in case there might be something truthful and worth following
up to in it. I got curious and wanted to take a glimpse at
understanding what might cause the bug. Again, please be aware that
parts (if not all) could be hallucinated.

---

## Root cause

There are two interacting issues:

### 1. `sk_sign()` treats absent `"uv"` option as "device cannot do
user verification"

In `sk-usbhid.c`, `sk_sign()` (around line 1216 in 10.2p1) does:

```c
if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) {
    if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 ||
        internal_uv != 1) {
        skdebug(__func__, "check_sk_options uv");
        ret = SSH_SK_ERR_PIN_REQUIRED;
        goto out;
    }
    ...
}
```

`check_sk_options()` queries the device's CBOR option list. The `"uv"`
option
indicates built-in biometric verification (e.g. a fingerprint reader).
A
YubiKey 5 reports these options:

```
options: rk, up, noplat, noalwaysUv, credMgmt, authnrCfg, clientPin,
         largeBlobs, pinUvAuthToken, setMinPINLength, makeCredUvNotRqd,
         credentialMgmtPreview
```

There is no `"uv"` because the device has no biometric sensor. It
supports
user verification through `clientPin` and `pinUvAuthToken` instead.
Since
`"uv"` is absent, `check_sk_options()` returns 0 with `internal_uv =
-1`
(unknown). The condition `internal_uv != 1` is true, and the function
returns
`SSH_SK_ERR_PIN_REQUIRED` without attempting to prompt for a PIN.

The intent of this code (introduced in f3c34df8, 2021-11-02) was to
detect
whether a biometric token can handle UV internally so the PIN prompt
can be
skipped. But the fallback for the `"uv"` absent case should be to
request a
PIN, not to fail.

### 2. `identity_sign()` in `sshconnect2.c` never retries with a PIN

The `SSH_SK_ERR_PIN_REQUIRED` error propagates as
`SSH_ERR_KEY_WRONG_PASSPHRASE`.
In `identity_sign()`, there is retry logic to prompt for a PIN on this
error:

```c
if (!retried && pin == NULL && !is_agent &&
    sshkey_is_sk(sign_key) &&
    r == SSH_ERR_KEY_WRONG_PASSPHRASE) {
    ...
    pin = read_passphrase(prompt, 0);
    retried = 1;
    goto retry_pin;
}
```

However, the `!is_agent` guard (added in f9648090, 2022-08-19) prevents
this
path from being taken. For SK keys loaded from files, `is_agent` is set
to 1
because `id->key->flags & SSHKEY_FLAG_EXT` is true:

```c
if (id->key != NULL &&
    (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) {
    sign_key = id->key;
    is_agent = 1;  // <-- set for all external/hardware keys
}
```

So the retry path is unreachable for SK keys, whether loaded from a
file or
through ssh-agent. The signing attempt fails on the first try and the
error
is reported to the user.

## Affected versions

- `sk_sign()` issue: introduced in f3c34df8 (2021-11-02), first release
V_8_9_P1
- `identity_sign()` issue: introduced in f9648090 (2022-08-19), first
release V_9_1_P1
- Both issues are present on current HEAD

## Suggested fix

In `sk_sign()` (`sk-usbhid.c`): when `"uv"` is not reported by the
device but
`clientPin` is available, the function should return
`SSH_SK_ERR_PIN_REQUIRED`
and let the caller prompt for a PIN and retry. Currently it does return
this
error code, but the caller doesn't act on it (issue 2).

In `identity_sign()` (`sshconnect2.c`): the `!is_agent` condition in
the PIN
retry block should not apply to SK keys loaded from files. The
`is_agent`
variable conflates "key is in ssh-agent" with "key is hardware-backed",
but
these are different things. Hardware-backed keys loaded from handle
files
should still be eligible for PIN retry.

A minimal fix for the `sshconnect2.c` side would be to change the
condition to
also allow retry when the key is an SK key loaded from a file (i.e.
`SSHKEY_FLAG_EXT` is set but `agent_fd == -1`).

-- 
You are receiving this mail because:
You are watching the assignee of the bug.


More information about the openssh-bugs mailing list