| Internet-Draft | IDPoP | February 2026 |
| Ritz | Expires 5 August 2026 | [Page] |
This document describes IDPoP, an extension to DPoP [RFC9449] that uses a key derivation scheme to separate access control from identity. It mitigates credential exfiltration risks by requiring fresh hardware attestation to unseal identity keys via an interactive challenge.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 5 August 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Standard DPoP binds tokens to keys but recent events have increased risks to exfiltration attacks where autonomous systems (such as agentic AI) leak valid credentials via prompt injection. Likewise, when malware compromises a device filesystem, attackers steal credentials and impersonate users until explicit revocation.¶
This proposal introduces a derivation scheme separating access control (encapsulate/decapsulate) from identity (signing).¶
Factors:¶
PF (Provisioning Factor): Represents authorization intent (client_id + scope + audience).¶
EF (Evidence Factor): Confidential attestation proof (TPM quote, YubiKey HMAC-secret, biometric), never transmitted.¶
VF (Vault Factor): High-entropy secret, encapsulated by Evidence derived key.¶
Two-Stage Derivation:¶
Access gating: K_kem = HKDF(salt=PF, ikm=EF, info="seal")¶
Identity derivation: K_attest = HKDF(salt=PF, ikm=VF, info="dpop")¶
.-------------------.
| AS-A (Verifier) | 2. AS-A (RATS Verifier)
| RATS Notary as | Appraises Evidence
| OAuth AS | Derive K_kem, K_dpop
'----+----+---------' Seal id seed → VF_sealed
^ |
1. Evidence | | 3. Attestation Result (AR)
conveyed | | as JWT OAuth grant with:
(as commitment)| - requested_cnf {jkt, K_kem_pub}
| | - VF_sealed
| v
.----+--------. .-------------------.
| |------------->| AS-B (OAuth) |
| Client | 4. Present | Token Issuer |
| (Attester) | AR grant '---+------+--------'
| |<-| | ^
'------+------. +---------------+ |
| 5. Validate AR
| 6. Present token Issue access token
| + DPoP proof |
| (signed with K_dpop_priv) |
v |
.------+-------. 7. Validate token cnf
| RS-B +------------> DPoP signature
| (Resource | with K_dpop_pub
| Server) |
'--------------'
¶
Figure 1: OAuth Identity Chaining Flow with RATS Verifier as AS-A and "IDPoP"-aware OAuth AS-B as delegated notary¶
NOTE: K_kem controls access to VF (identity key seed material). K_attest is the identity. Compromise of the identity key alone is insufficient—attacker needs to demonstrate access to notorized Evidence (EF) to derive K_kem and unseal.¶
Input: PF, EF Output: (K_kem_priv, K_kem_pub) K_kem_priv = HKDF(salt=PF, ikm=EF, info="seal") K_kem_pub = X25519.Public(K_kem_priv)¶
Input: PF, K_kem_pub (from client attestation) Output: (VF_sealed, K_dpop_pub) VF = Random(32) VF_sealed = HPKE.Seal(recipient_pk=K_kem_pub, info=PF, plaintext=VF) K_dpop_priv = HKDF(salt=PF, ikm=VF, info="dpop") K_dpop_pub = Ed25519.Public(K_dpop_priv)¶
Input: PF, K_kem_priv, VF_sealed Output: K_dpop_priv VF = HPKE.Unseal(recipient_sk=K_kem_priv, info=PF, ciphertext=VF_sealed) K_dpop_priv = HKDF(salt=PF, ikm=VF, info="dpop")¶
Convergence: Client derives K_dpop_priv from (PF, VF) only after unsealing the encapsulated VF secret.¶
Setup (Client → AS-A):¶
Client generates attestation evidence (EF)¶
Client: K_kem_priv = DeriveKEM(PF, EF)¶
Client sends attestation to AS-A with K_kem_pub¶
Notarization (RATS Verifier as AS-A):¶
AS-A Appraises attestation Evidence¶
AS-A: (VF_sealed, K_dpop_pub) = EncapsulateSeed(PF, K_kem_pub)¶
AS-A creates JWT grant to AS-B:¶
{
"iss": "https://as-a.example",
"sub": "client_id",
"aud": "https://as-b.example",
"iat": 1706745600,
"exp": 1706745660,
"jti": "grant-7f3a9c",
"requested_cnf": {
"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I",
"K_kem_pub": {
"kty": "OKP",
"crv": "X25519",
"x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs"
}
}
}
¶
Token Issuance (AS-B):¶
AS-B validates grant, issues DPoP-bound access token:¶
{
"access_token": "eyJ...(JWT with cnf.jkt inside)...",
"token_type": "DPoP",
"expires_in": 3600
}
¶
The access token JWT contains:¶
{
"iss": "https://as-b.example",
"sub": "client_id",
"aud": "https://rs-b.example",
"iat": 1706745600,
"exp": 1706749200,
"cnf": {
"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I", // Validates the DPoP signature (Standard)
"K_kem_pub": { // Enables the Liveness Challenge (IDPoP Extension)
"kty": "OKP",
"crv": "X25519",
"x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs"
}
}
}
¶
Interactive Challenge (AS-B → Client):¶
Client sends request with DPoP proof (no nonce or cached nonce)¶
AS-B decides: "I need hardware liveness proof"¶
AS-B → 401 Unauthorized¶
WWW-Authenticate: DPoP error="use_dpop_nonce"
DPoP-Nonce: <Base64Url(HPKE.AuthSeal(nonce, K_kem_pub, AS_B_privkey))>
¶
Client Proof:¶
VF = HPKE.Unseal(K_kem_priv, VF_sealed) [proves fresh EF]¶
Client extracts sealed nonce from header¶
Client: nonce = HPKE.AuthUnseal(nonce_sealed, K_kem_priv, AS_B_pubkey)¶
Client: K_dpop_priv = HKDF(PF, VF, "dpop")¶
Client creates new DPoP proof with unsealed nonce and retries:¶
{
"typ": "idpop+jwt", // Proposed new type
"alg": "EdDSA",
"jwk": { // The Public Identity Key (K_dpop)
"kty": "OKP",
"crv": "Ed25519",
"x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"
}
}
{
"jti": "e1j3V_bKic8-LAEB",
"htm": "POST",
"htu": "https://rs-b.example/api",
"iat": 1706745600,
"ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo",
"nonce": "kH8Ws3xYnE2f7d9pQ1vR5jL4mT6oU0aC8bN3gZ7yXsA"
}
¶
Verification (AS-B or RS-B):¶
Verify DPoP signature with K_dpop_pub (from token's cnf.jkt)¶
Verify proof.nonce == SHA256(expected_nonce)¶
Accept request¶
Note: The interactive challenge (Steps 8-10) introduces a round-trip. To mitigate latency, this challenge MAY be performed once per session or upon detecting high-risk context. The resulting nonce acts as a session binding signal for subsequent DPoP proofs, reducing overhead for high-frequency API calls.¶
No durable secrets at AS-A. Post-issuance compromise of AS-A yields no client key material. RATS Verifier (AS-A) computes K_dpop_priv transiently, extracts K_dpop_pub, discards both K_dpop_priv and secret seed material (VF).¶
Sealed grant confidentiality. Interception without K_kem_priv yields nothing. VF_sealed is HPKE-encrypted to K_kem_pub.¶
Attestation-bound unsealing. Stolen VF_sealed without device access is unusable. K_kem_priv = HKDF(PF, EF). Unsealing VF requires fresh hardware attestation.¶
Key separation. K_kem controls access. K_dpop asserts identity. Compromise of signing key doesn't grant unsealing; compromise of unsealing requires live EF.¶
Convergent derivation. No key transport. Client derives K_dpop from (PF, VF) independently.¶
This specification utilizes a Key Transport pattern where the Identity Key material (VF) is generated by the server and transmitted to the client in an HPKE-sealed envelope (VF_sealed). While Key Transport is sometimes disfavored in comparison to Key Agreement, it is necessary here to bind the identity to the specific hardware attestation presented by the client.¶
The security of this transport relies on Hardware Binding: The VF is sealed to K_kem_pub, which is derived from the client's hardware-bound Evidence Factor (EF). Even if VF_sealed is intercepted, it remains "inert" (unusable) without the corresponding hardware-bound private key K_kem_priv.¶
A prominent concern with any encrypted transport of secrets is the "Harvest Now, Decrypt Later" (HNDL) strategy, where an adversary records encrypted traffic today to decrypt it in the future once quantum computing breaks current asymmetric primitives (e.g., X25519).¶
In the context of IDPoP, the impact of HNDL is significantly mitigated by the ephemeral nature of the Identity Key:¶
Short-Lived Credential Utility: The VF seed is used solely to derive the DPoP signing key (K_dpop). This key is only useful for signing DPoP proofs for the validity period of the associated access token (defined by exp).¶
Obsolescence upon Decryption: If an adversary successfully breaks the KEM algorithm years in the future and recovers a historical VF, the associated access token will have long since expired. Unlike confidentiality keys used to encrypt persistent data (where future decryption is catastrophic), recovering an authentication key after the session window has closed yields no advantage, provided the AS enforces standard expiration (exp) and replay protection (jti).¶
To further mitigate HNDL risks and ensure long-term resistance, implementations SHOULD support cryptographic agility in the HPKE configuration.¶
Hybrid KEMs: Implementations can adopt hybrid KEMs such as X-Wing (combining X25519 and ML-KEM-768) to provide post-quantum resistance while maintaining classical security guarantees.¶
Algorithm Negotiation: The alg parameter in the attestation request or metadata allows the client and AS to negotiate Quantum-Resistant KEMs (e.g., ML-KEM) as they become standardized, ensuring the VF_sealed remains secure against future quantum cryptanalysis.¶
To prevent an attacker with temporary access to the signing key (or the ability to predict nonces) from pre-computing proofs, this specification mandates the use of HPKE-sealed nonces.¶
Liveness Requirement: By sealing the nonce to the client's K_kem_pub, the AS ensures that only a client with active access to the hardware-bound K_kem_priv can recover the nonce and sign the proof.¶
Mitigation of Malware: Malware capable of stealing a cached K_dpop from memory cannot pre-generate valid proofs for future requests because it cannot decrypt the future nonces without invoking the hardware anchor.¶
TODO¶