Repository navigation
CTAP 2.1 platform-style passkeys: PIN, credential management, reset, PRF - #44
Open
pipobscure wants to merge 1 commit into
Open
pipobscure wants to merge 1 commit into
pipobscure wants to merge 1 commit into
Conversation
…eset, PRF Make linux-id a full CTAP 2.1 authenticator, so it can serve as the machine's passkey provider with fingerprint verification. Transport - A CTAPHID state machine per CTAP 2.1 §11.2: one request in flight, ERR_CHANNEL_BUSY for others, validated reassembly and the matching error codes, and PING and WINK. - KEEPALIVE every 100 ms with UPNEEDED while waiting for the user. - CANCEL and INIT-resync cancel the request; the fingerprint scan stops. CTAP 2.1 - Several resident credentials per RP (numberOfCredentials, getNextAssertion). Re-registering the same user replaces the credential. Name and displayName are returned only with UV. - up=false silent probes; authenticatorSelection. - pinUvAuthToken via built-in UV and via PIN (protocols 1 and 2). The fingerprint is primary; after repeated no-matches, or with fprintd unavailable, UV_BLOCKED sends platforms to the PIN. - Tokens are never issued from the verification cache, which is scoped to the RP. Chrome reads a token as "the user chose this authenticator". - authenticatorCredentialManagement (and the 2.1-PRE alias), plus a `linux-id creds list|delete` CLI. Deleted credentials stop signing even where an RP still holds their ID. - credProtect, hmac-secret (WebAuthn PRF) and hmac-secret-mc. CredRandom is a TPM2_HMAC under a non-exportable key, so it can't be computed without this TPM. - authenticatorReset, accepted within 10 s of start with a fresh UV, and `linux-id reset`. - GetInfo reports FIDO_2_1 and the real limits. Keys and secrets: nothing secret in local files - Credential keys are bound to a credential generation. Its number is a TPM monotonic NV counter (0x013C1D01) and its secret is derived in the TPM on each use. A reset increments the counter and cannot be undone. - Credential IDs carry their options (credProtect, hmac-secret) in a header the key is bound to, so a stripped or downgraded ID does not sign. - The PIN is a DA-protected TPM sealed object whose authValue is the PIN hash. It is created and checked over salted HMAC sessions, so the hash never crosses the TPM interface in clear. pin.json holds only the wrapped object and the retry counter. - Existing credentials (bare key handles) keep working after upgrading, until the first reset retires them (NV index 0x013C1D02, defined before the counter moves). Public keys of old discoverable credentials are derived from their key handles. Other - An optional desktop notification (--notify) names the site while fprintd waits. The service restarts if the uaccess ACLs arrive after it starts. --credstore selects the store. Unknown --auth values are rejected. Tests: unit tests (-race), TPM tests behind LINUXID_TPM_TEST=1 that use their own NV indices, and e2e/ — python-fido2 and OpenSSH against a throwaway instance over real uhid. Requires Go 1.27 (for github.com/telesma-app/ctap, whose PIN/UV protocol primitives, COSE and CBOR types are used).
pipobscure
marked this pull request as ready for review
November 6, 2026 21:46
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This makes linux-id a complete CTAP 2.1 authenticator, so it can act as the machine's passkey provider with fingerprint verification, closer to Touch ID or Windows Hello than to a security key. It's large (about 10k lines including tests), so it's a draft: I'm happy to split it or to drop parts you'd rather not take.
What changes for users
fido2-token, orlinux-id creds list|delete.linux-id reset.hmac-secret) for password managers and end-to-end encrypted apps, pluscredProtect.ssh-keygen -O resident -O verify-requiredandssh-keygen -Kboth work.--notifynames the site while fprintd waits. Browsers already show their own prompt, so it's off by default.Restart=on-failure. The service used to give up at boot when it started before logind granted the TPM/uhid ACLs.Compatibility
0x013C1D01, and the "legacy credentials retired" marker is at0x013C1D02. Both are in the owner range and created on first use; they assume an empty owner authValue, as everything else in linux-id already does.creds.jsonholds TPM-wrapped key handles and metadata;pin.jsonholds only a TPM-wrapped PIN object and the retry counter.github.com/telesma-app/ctapv0.52.0. It provides the PIN/UV protocol primitives and the COSE and CBOR types, rather than re-implementing them.Design notes
hmac-secret's CredRandom is a TPM2_HMAC under a non-exportable key.Relation to open PRs
--auth systemmode is a different verification approach and could be added alongside this as another verifier.Testing
go test -race ./....LINUXID_TPM_TEST=1 go test ./...runs against/dev/tpmrm0. These tests use their own NV indices and remove them, and the PIN test makes one deliberate wrong guess.e2e/runs a throwaway instance over the real uhid transport, driven by python-fido2 (resident credentials, tokens, PIN incl. fallback, credential management, extensions, reset) and by OpenSSH in a bubblewrap sandbox. Seee2e/README.md.--auth fprintdon Arch with Chrome. That covered webauthn.io, a self-hosted app with step-up re-authentication, Chrome's PIN setup and passkey management, and ssh. Also using this with firefox (though obviously not managing credentials there)Not included