PATTERN Cited by 1 source
Identity-to-key binding¶
When a token attests both a user's identity and a public key, the verifier MUST check that the public key in the token matches the public key actually in use for the session. Without this check, a stolen token can be replayed against an attacker's key, and the token's key-binding claim is worthless.
Why this check is load-bearing¶
A token like a PK Token says "IdP
attests: user is Alice, Alice's public key is 0x123." An
adversary who obtains the token (e.g. by grabbing it out of a
user's .ssh directory) could try to:
- Take the PK Token as-is but pair it with the adversary's keypair 0x456.
- Present
(PK Token, 0x456)to a server.
Without the binding check, the server sees a signed, unexpired
token that says Alice is at alice@example.com and assumes
the session is Alice's. The attacker authenticates.
The binding check — "does the public key field in the session equal the public key inside the PK Token?" — catches this. The adversary who has only the token (not Alice's private key) can't produce a session whose key matches the token's binding, and can't mint a new token without Alice's IdP credentials.
OPKSSH's phrasing¶
"The OpenPubkey verifier also checks that the public key in the public key field in the SSH public key matches the user's public key inside the PK Token. This works because the public key field in the SSH public key is the actual public key that secures the SSH session."
The session-securing key and the in-token key are the same public key. The holder of that key must also hold the private key to complete the SSH handshake. So demonstrating possession of the session key is demonstrating possession of the key named in the token.
Generalization beyond SSH¶
The pattern applies anywhere a token binds identity and key material:
- PK Token + SSH — OPKSSH (the canonical instance).
- DPoP (Demonstrating Proof-of-Possession) + HTTP — the OAuth 2.0 DPoP spec binds OAuth access tokens to a specific client key by embedding the key in the token; the client signs each request with that key. Same shape.
- Attestations + device keys — hardware attestations (TPM, TEE) that certify "this code is running on this key" must be verified with a proof-of-possession of that key, or the attestation is replayable.
- Signed commits + developer keys — a commit signature is only as strong as the binding between the developer identity and the signing key.
Required verifier invariants¶
For any protocol using this pattern:
- Token signature is valid against the attesting authority's keyset.
- Token is unexpired.
- Identity claim is what the relying party expects.
- Public key in the token matches the public key actually used in this session. ← this pattern.
- (Optional) Challenge/nonce freshness if replay of old sessions is a concern.
Missing (4) is the canonical failure mode. Missing (5) is a separate replay surface for protocols where the same session key can be reused across sessions.
Failure modes¶
- Silent acceptance of mismatched keys. If the binding check is skipped (perceived as redundant), token theft becomes full account takeover.
- Weak key-equality checks. Normalizing keys to a canonical form is important — comparing raw bytes without accounting for encoding variations can accept non-matching keys that happen to parse to the same mathematical value, or reject matching keys that are encoded differently.
Seen in¶
- sources/2025-03-25-cloudflare-opkssh-open-sourcing — OPKSSH's fourth verifier invariant: PK Token public key must equal SSH session public key. Explicitly called out as the binding check that makes the PK Token non-replayable against a different session keypair.
Related¶
- concepts/pk-token — the primitive this pattern verifies.
- systems/opkssh — the canonical application.
- systems/openpubkey — the protocol defining the PK Token.
- concepts/sso-authentication — the upstream trust anchor.