Skip to content

Hint at a required YubiKey touch on 0x6982 errors#1076

Open
TicketWindowSeat wants to merge 1 commit into
smallstep:masterfrom
TicketWindowSeat:yubikey-touch-hint
Open

Hint at a required YubiKey touch on 0x6982 errors#1076
TicketWindowSeat wants to merge 1 commit into
smallstep:masterfrom
TicketWindowSeat:yubikey-touch-hint

Conversation

@TicketWindowSeat

Copy link
Copy Markdown

Name of feature:

Add a hint to touch the YubiKey when a sign or decrypt fails with card status
0x6982 ("security status not satisfied").

Pain or issue this feature alleviates:

When a YubiKey slot has a touch policy, a sign or decrypt blocks until the key is
physically touched. If the user doesn't touch it, the operation fails with:

smart card error 6982: security status not satisfied

Nothing in that message says a touch was expected — the key blinks silently and then
the command fails with an opaque status word. (Reported upstream for the plugin in
smallstep/step-kms-plugin#40, where a proactive "touch now" prompt was declined
because the touch policy can't be reliably known up front, but "a generic message"
was suggested. This is that generic message, at the library layer.)

Why is this important to the project (if not answered above):

The YubiKey KMS forces PINPolicyAlways, so the PIN is verified immediately before
every sign/decrypt. That means a 0x6982 from the operation itself is a missing touch rather than a PIN problem. Turning the bare status word into an actionable hint ("touch the key and retry") directly addresses the most confusing failure mode of touch-policy keys, at essentially zero cost and with no behaviour change on success.

Is there documentation on how to use this feature? If so, where?

No new API or documentation. Error output on a 0x6982 failure now ends with
; if the key's slot has a touch policy, touch the blinking YubiKey and retry.
Everything else is unchanged.

In what environments or workflows is this feature supported?

All platforms; the change is in syncSigner.Sign / syncDecrypter.Decrypt, which
every YubiKey KMS signer/decrypter goes through. Unit-tested without hardware
(TestWrapTouchHint), and the test runs in CI (make test builds the yubikey package
with cgo + libpcsclite-dev).

Also verified end-to-end on a real YubiKey 4 (slot 9c, pin=always, touch=always),
signing through this KMS without touching the key so the card returns 0x6982 after
its ~15 s timeout:

before:  command failed: smart card error 6982: security status not satisfied
after:   command failed: smart card error 6982: security status not satisfied; if the key's slot has a touch policy, touch the blinking YubiKey and retry

In what environments or workflows is this feature explicitly NOT supported (if any)?

The hint is deliberately hedged ("if the key's slot has a touch policy") because
0x6982 is not touch-specific: it can also indicate an unsatisfied PIN condition, or
— absent the existing mutex — concurrent card access. The code does not (and on older
YubiKeys cannot cheaply) query the slot's touch policy to confirm, so it never asserts
a touch was definitely required.

Supporting links/other PRs/issues:

  • When touich policy is enabled, user isn't prompted to touch the yubikey step-kms-plugin#40 — the original "prompt to touch" request; a generic
    message was the accepted path.
  • The 0x6982 status is detected through an anonymous interface{ Status() uint16 }
    because piv-go's apduErr is unexported. A companion piv-go change proposes an
    exported ErrSecurityStatusNotSatisfied sentinel [link at file time]; once it ships,
    this detector can become a plain errors.Is.
  • This touches syncSigner.Sign / syncDecrypter.Decrypt, the same methods as Say when a failed PIN verification tried the default PIN #1075
    (default-PIN error). The two are independent concerns (0x6982 vs AuthErr) and
    compose as func(error) error; whichever merges second, the wraps combine into one
    dispatch (AuthErr → PIN hint, 0x6982 → touch hint).

💔Thank you!

When a YubiKey slot has a touch policy, a sign or decrypt blocks until
the key is physically touched; if it is not, the card returns 0x6982
"security status not satisfied" with no indication that a touch was
expected. The KMS forces PINPolicyAlways, so the PIN has already been
verified by the time the operation runs -- a 0x6982 there is
overwhelmingly a missing touch.

Wrap the error from syncSigner.Sign and syncDecrypter.Decrypt with a
conditional hint to touch the key and retry. The status word is detected
through an anonymous interface{ Status() uint16 } because piv-go's
apduErr type is unexported. The message stays hedged ("if the slot has a
touch policy") because 0x6982 can also indicate an unsatisfied PIN or,
without the existing mutex, concurrent access.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants