Skip to content

Support Identiv uTrust FIDO2's PIV applet by tolerating cards not fully implementing mutual GENERAL AUTHENTICATE - #104

Open
episource wants to merge 1 commit into
framefilter:mainfrom
episource:feature/identiv-utrust-fido2
Open

Support Identiv uTrust FIDO2's PIV applet by tolerating cards not fully implementing mutual GENERAL AUTHENTICATE#104
episource wants to merge 1 commit into
framefilter:mainfrom
episource:feature/identiv-utrust-fido2

Conversation

@episource

Copy link
Copy Markdown
Contributor

The Identiv/Hirsch uTrust FIDO2 token doesn't implement full mutual GENERAL AUTHENTICATE for the card's management key: instead of the expected challenge template, it returns an empty response with a success status word. Since none of the PIV operations implemented here require anything beyond client authentication — card authentication is optional — this PR changes management key authentication to tolerate this and proceed silently. This PR does not switch to plain client authentication, but adjusts as little code as possible to support this very specific token.

`PivSession::authenticate_management`'s step 2 (GENERAL AUTHENTICATE with
the decrypted witness plus our own challenge) unconditionally parsed the
response as a `7C` dynamic-auth template to pull out the card's encrypted
echo of our challenge (`0x82`), the half of mutual auth that lets the host
verify the card. On an Identiv uTrust FIDO2 Security Key, that step
answers `SW_OK` with an empty body instead — the card verifies our witness
(a wrong one is still rejected with a non-success status, unchanged) but
never implements sending the challenge back. `parse_general_auth` then
failed on the empty buffer with "PIV response is not a 0x7C dynamic-auth
template", which surfaced as a hard error before every management-key-gated
command (`generate-key`, `import-cert`, `set-management-key`, ...) ever got
to run.

Treat an empty, `SW_OK` step-2 response as a completed (one-directional)
authentication rather than a parse error: our witness was still checked,
so a wrong key is still caught here — this card just can't prove itself
back to us, and there's no `0x82` template to wait for. Same category of
device quirk as the existing Nitrokey stub-`GET METADATA` and empty-`53`
cert-object handling in this file.

This maps directly onto the two authentication modes NIST SP 800-73
describes for the management key over GENERAL AUTHENTICATE: "client
(application) authentication" — only the client proves possession of the
key to the card — versus "mutual authentication", which adds the card
proving itself back to the client. Client auth is the half that actually
gates PIV admin operations (it's what authorizes *us* to write to the
card), so it's sufficient on its own — which is exactly what this fallback
now accepts.

Deliberately not switching the step-2 *request* itself to the leaner,
client-auth-only shape (dropping our `0x81` challenge tag) to match: the
mutual-shaped request is a strict superset with no extra cost — cards that
only implement client auth answer exactly as observed here regardless, and
cards that implement the full round (this crate's hardware-verified
Token2/Yubico-compatible targets) give us the extra assurance, before
writing new key material, that the `SW_OK` we got back came from something
that actually holds the key rather than a card that blindly answers
success. Trimming the request would only pay off against a card that
hard-rejects the mutual shape outright (non-`9000` SW) rather than
short-answering it as Identiv does here — no such device has been
observed, and Token2/Yubico-compatible firmware may well expect the full
shape, so narrowing the request now would trade a working, hardware-
verified path for an unvalidated one. Revisit only against a real device
that fails the mutual-shaped request, per a targeted fallback in the same
style as `general_auth_sign_chained`.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant