| Internet-Draft | PTV Agent Identity | September 2026 |
| Damodaran | Expires 2 April 2027 | [Page] |
This document describes the Prove-Transform-Verify (PTV) protocol for hardware-anchored attestation of AI agent identity. PTV enables an agent to prove, at exercise time, that it is bound to an enrolled attestation key and an authorized configuration, without exposing model weights or inference inputs.¶
PTV is a thin request/response profile over the RATS architecture (RFC 9334) and the Entity Attestation Token (RFC 9711). It does not replace workload identifiers such as SPIFFE or WIMSE. It does not attest behavioral continuity; behavioral continuity is a separate requirement class that relying parties need to treat as such.¶
This revision is intended as Experimental. It defines a common CBOR/CDDL message set, four message types, a COSE-based message protection rule, an informative EAT claim mapping, and a threat model that separates identity binding integrity from behavioral continuity.¶
This note is to be removed before publishing as an RFC.¶
Status information for this document may be found at https://datatracker.ietf.org/doc/draft-anandakrishnan-rats-ptv-agent-identity/.¶
Discussion of this document takes place on the Remote ATtestation procedureS (RATS) Working Group mailing list (mailto:rats@ietf.org), which is archived at https://mailarchive.ietf.org/arch/browse/rats/. Subscribe at https://www.ietf.org/mailman/listinfo/rats/.¶
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 2 April 2027.¶
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.¶
Current identity frameworks (OAuth 2.0, SPIFFE, WIMSE) establish workload identity but do not, by themselves, verify which model an agent is running or whether the running configuration matches what was enrolled. A compromised agent can therefore present valid credentials while executing unauthorized code or policy.¶
PTV addresses the identity-binding gap:¶
bind the attestation to a hardware root of trust (TPM 2.0 Attestation Key [TPM2], or equivalent TEE attestation key);¶
require a verifier- or relying-party-issued nonce so that evidence is fresh at exercise time;¶
carry either raw evidence (ROUTINE band) or a zero-knowledge proof of an enrolled predicate (ZK_PROOF band);¶
leave behavioral continuity, delegation provenance, and execution-outcome verification to complementary mechanisms.¶
This document does not claim that a valid PTV attestation means the agent is still behaving inside its enrolled envelope after context compression, fine-tuning, or prompt injection. A relying party that needs that property MUST combine PTV with exercise-time re-attestation plus an execution-receipt mechanism (for example SCITT [RFC9943]).¶
Example relying-party questions PTV can answer:¶
"Is the signer of this evidence the enrolled agent instance?"¶
"Does the attested software/configuration hash match the authorized set?"¶
"Was this evidence produced in response to this nonce?"¶
Example questions PTV cannot answer alone:¶
"Did the agent follow the clinical protocol on this patient?"¶
"Who delegated this action?"¶
"What side effect did the agent actually cause?"¶
When a WIMSE or SPIFFE identifier is present, that identifier names the workload. PTV evidence is additional appraisal input about the same workload. PTV does not mint a new identifier namespace. See [I-D.ietf-wimse-aims].¶
Intended status changed from Standards Track to Experimental.¶
Behavioral-continuity non-claim moved into the Abstract.¶
Federation Hub removed from the protocol diagram; Transform is performed by the Attester or an Edge Proxy.¶
triage_band reduced to ROUTINE (0) and ZK_PROOF (1). BFT (2) and PTV_ERR_004 removed until a consensus mechanism is specified.¶
sovereign_bound is OPTIONAL unless the policy object requires it.¶
CDDL rewritten: one map per message type, no duplicate keys, and evidence and proof are mutually exclusive via a CDDL group choice. The generic envelope is removed.¶
Message protection added: types 2 and 3 are COSE_Sign1 signed by the attestation key.¶
Nonce issuance and Verifier-side nonce checking specified.¶
Nonce size constrained to 16-64 bytes for compatibility with the EAT nonce claim.¶
Informative EAT claim mapping added and corrected.¶
Edge Proxy key-custody threats added to Security Considerations.¶
Trusted-setup, privacy, and self-asserted-metadata considerations added.¶
Prototype performance table moved to an appendix and aligned with the prototype repository.¶
Predicate for the ZK_PROOF band stated as a public-input list, without mandating a proof system.¶
SCITT reference corrected to RFC 9943.¶
Implementation Status section added.¶
Appendix B added with CBOR diagnostic-notation examples.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
An autonomous software entity that performs tasks on behalf of a user or system.¶
The agent component that generates Evidence or a Proof about its identity-binding state. Corresponds to the Attester role in [RFC9334].¶
A system that appraises Evidence or a Proof without needing the Attester's inference inputs or model weights. Corresponds to the Verifier role in [RFC9334].¶
A system that consumes Attestation Results to make an authorization decision. Corresponds to the Relying Party role in [RFC9334].¶
An optional gateway that generates or forwards attestation on behalf of a constrained device that lacks a local hardware root of trust. The Edge Proxy is an Attester from the Verifier's point of view and MUST be treated as a distinct trust component.¶
The Relying Party or the Verifier, whichever generates the nonce for a session. See Section 4.2.¶
A cryptographically random value supplied by the Nonce Issuer to bind an attestation session and prevent replay. See [RFC4086].¶
The attestation mechanism selected for a session. This version defines 0 (ROUTINE: Evidence) and 1 (ZK_PROOF: Proof).¶
Optional constraints the Attester must satisfy, referenced by URI or carried inline.¶
A zero-knowledge proof that a stated predicate holds over Attester-private inputs. The proof system is not mandated.¶
Attestation Evidence in the RATS sense [RFC9334], typically an EAT [RFC9711] or a TPM quote.¶
Optional jurisdiction, residency, and compliance labels carried in the message. They are integrity-protected by the message signature (Section 5.4) but are self-asserted unless covered by appraised Evidence.¶
A TPM 2.0, Secure Enclave, or equivalent component that can produce Evidence bound to an attestation key that never leaves the component.¶
Identity binding integrity asks: "Is this the enrolled agent instance, and is the signer trusted to hold the corresponding attestation key?"¶
PTV addresses this class by:¶
binding Evidence or Proof to a hardware attestation key through the message signature (Section 5.4);¶
using a session nonce for freshness;¶
allowing the Verifier to appraise configuration hashes against an endorsed reference value.¶
A Relying Party MUST NOT treat a PTV result as evidence of anything beyond identity binding integrity unless additional mechanisms are in place.¶
Behavioral continuity asks: "Is the enrolled agent still behaving within its attested envelope after context changes?"¶
This version of PTV does not detect or mitigate:¶
behavioral drift after context compression or summarization;¶
model-weight replacement or fine-tuning after enrollment;¶
runtime drift from prompt injection or accumulated state corruption.¶
A valid PTV attestation on a behaviorally drifted agent is a false signal of behavioral identity continuity. For high-stakes use (clinical decision support, ICS/OT), Relying Parties SHOULD treat behavioral continuity as a separate requirement and compose PTV with delegation provenance and execution receipts. See Section 9.¶
For high-stakes deployments, Relying Parties SHOULD demand fresh attestation at exercise time (per authorization decision) via an EAT nonce challenge or a PTV_PROVE_REQ, rather than relying solely on a credential issued at enrollment.¶
PTV has three phases. They are logical, not a new transport.¶
PROVE. The Attester produces Evidence or a Proof over the current configuration, bound to the challenge nonce and the hardware attestation key.¶
TRANSFORM. Only Evidence or Proof is forwarded. Inference inputs, model weights, and plant telemetry stay local. The forwarder is the Attester itself or an Edge Proxy.¶
VERIFY. The Verifier appraises the Evidence or Proof and returns a result to the Relying Party.¶
Relying Party Attester / Edge Proxy Verifier
| | |
|-- nonce, policy (A) ---------------------------------->|
|-- PTV_PROVE_REQ ------->| |
| (nonce, policy) | |
| |-- generate Evidence/Proof |
| | |
| |-- PTV_PROVE_RESP --------->|
| | or PTV_TRANSFORM_ATT |
| | (COSE_Sign1 by AK) |
| | |
|<--------- PTV_VERIFY_RESULT -------------------------|
This flow is a profile of the Challenge/Response interaction model in [I-D.ietf-rats-reference-interaction-models].¶
The Verifier can only judge freshness if it knows which nonce was issued for the session. One of the following two modes MUST be used:¶
The Relying Party generates the nonce, sends it in PTV_PROVE_REQ, and also gives the Verifier the nonce (and the policy) for the session over an authenticated channel (step A in Figure 1). The Verifier MUST reject a response whose nonce does not match a registered, unconsumed session.¶
The Verifier generates the nonce and hands it to the Relying Party, which places it in PTV_PROVE_REQ. The Verifier MUST record the nonce as outstanding and mark it consumed on first successful or failed appraisal.¶
In both modes a nonce is single-use. A Verifier that cannot associate a received nonce with an issued session MUST return result failure with reason PTV_ERR_006.¶
Attester: produces PTV_PROVE_RESP.¶
Edge Proxy: optional; produces PTV_TRANSFORM_ATT on behalf of a constrained device and is itself an Attester.¶
Verifier: consumes RESP or TRANSFORM_ATT; produces PTV_VERIFY_RESULT.¶
Relying Party: issues PTV_PROVE_REQ and consumes PTV_VERIFY_RESULT.¶
The Verifier and Relying Party MAY be co-located, in which case step A in Figure 1 is internal.¶
The agent has a TPM 2.0 or TEE and produces Evidence or Proof locally.¶
A constrained device (legacy PLC, sensor) has no hardware root of trust. An Edge Proxy attests that it is mediating that device under an enrolled proxy policy. The Verifier appraises the proxy, not the device CPU. See Section 7.5.¶
All PTV messages are CBOR maps [RFC8949] described in CDDL [RFC8610]. The CDDL fragments in this section, concatenated in order, form one CDDL module. Implementers SHOULD check the concatenation with a CDDL tool.¶
triage-band = 0 / 1 ; 0=ROUTINE, 1=ZK_PROOF
; 16..64 bytes, compatible with the EAT nonce claim (RFC 9711)
nonce-type = bstr .size (16..64)
ptv-result = &(
success: 0,
failure: 1,
indeterminate: 2
)
ptv-policy = {
? audience: tstr,
? policy_uri: tstr,
? trust_domain: tstr,
? requirements: [* tstr],
? require_sovereign_bound: bool
}
sovereign-bound = {
jurisdiction: tstr,
data_residency: tstr,
compliance: [* tstr]
}
; Fields common to every message. msg_type and timestamp are
; NOT here; each message map declares them exactly once.
ptv-common = (
ptv_version: tstr, ; this document: "1.1"
nonce: nonce-type,
? extensions: { * tstr => any }
)
¶
behavior_fingerprint remains OPTIONAL and opaque. This version defines no processing rules for it.¶
In PTV_PROVE_RESP and PTV_TRANSFORM_ATT:¶
ROUTINE (triage_band = 0): evidence MUST be present and proof MUST NOT be present.¶
ZK_PROOF (triage_band = 1): proof MUST be present and evidence MUST NOT be present.¶
A message that contains both, or neither, MUST be rejected with PTV_ERR_008.¶
payload-routine = ( triage_band: 0, evidence: bstr ) payload-zk = ( triage_band: 1, proof: bstr )¶
sovereign_bound MUST be present if policy.require_sovereign_bound is true in the corresponding request. Otherwise it is OPTIONAL.¶
Sent by the Relying Party (or Verifier acting for it).¶
ptv-prove-req = {
ptv-common,
msg_type: 1,
? timestamp: time,
? policy: ptv-policy
}
¶
The nonce MUST be at least 128 bits of cryptographic randomness [RFC4086], MUST be 16 to 64 bytes long, and MUST be unique across attestation sessions of its Nonce Issuer.¶
Sent by a direct-mode Attester.¶
ptv-prove-resp = {
ptv-common,
msg_type: 2,
attester_id: tstr,
? sovereign_bound: sovereign-bound,
(payload-routine // payload-zk),
? behavior_fingerprint: bstr,
timestamp: time
}
¶
evidence, when present, SHOULD be an EAT [RFC9711] whose eat_nonce equals the request nonce. See Section 6.¶
proof, when present, is an opaque encoding of a ZK proof. The public inputs of the proof MUST include:¶
the request nonce;¶
the Attester identifier, or a hash of the public part of the attestation key that signs the message (Section 5.4);¶
a configuration digest H(model_id || policy_id || runtime_measurement) whose reference value is endorsed out of band;¶
optionally, a digest of sovereign_bound when that field is present.¶
This document does not mandate Groth16, PLONK, or any other system. Deployments MUST document the proof system, curve, and verifying key distribution.¶
Sent by an Edge Proxy. Same payload rules as Type 2. attester_id identifies the proxy. The mediated device identity, if any, MAY appear in extensions.¶
ptv-transform-att = {
ptv-common,
msg_type: 3,
attester_id: tstr,
? sovereign_bound: sovereign-bound,
(payload-routine // payload-zk),
timestamp: time
}
¶
ptv-verify-result = {
ptv-common,
msg_type: 4,
result: ptv-result,
? reason: tstr,
? audit_id: tstr,
timestamp: time
}
ptv-message = ptv-prove-req / ptv-prove-resp /
ptv-transform-att / ptv-verify-result
¶
reason SHOULD use a code from Section 5.5 when result is not success.¶
PTV_PROVE_RESP and PTV_TRANSFORM_ATT MUST be carried as a COSE_Sign1 [RFC9052] whose payload is the CBOR encoding of the message and whose signature is produced by the attestation key (AK) identified by attester_id. This signature is what binds a ZK_PROOF to the hardware root of trust: the proof alone carries no hardware binding, and a Verifier MUST NOT treat a ZK_PROOF message without a valid AK signature as attested. For the ROUTINE band, the embedded Evidence is separately signed per its own format; the outer signature additionally protects sovereign_bound and the other envelope fields.¶
PTV_VERIFY_RESULT SHOULD be a COSE_Sign1 signed by the Verifier, unless it is carried over a channel that authenticates the Verifier to the Relying Party and protects integrity.¶
ptv-signed-message = #6.18([
protected: bstr,
unprotected: { * any => any },
payload: bstr .cbor ptv-message,
signature: bstr
])
¶
The mapping of attester_id to a verification key is an enrollment matter and is outside this document; the Verifier MUST have that key (or a certificate chain to it) from enrollment.¶
These codes are for the reason field. They are not an IANA registry in this version.¶
| Code | Meaning | Typical recovery |
|---|---|---|
| PTV_ERR_001 | TPM/TEE attestation failed | Check local RoT and AK |
| PTV_ERR_002 | Proof verification failed | Regenerate with correct inputs |
| PTV_ERR_003 | Jurisdiction mismatch | Compare sovereign_bound |
| PTV_ERR_005 | Envelope expired | Request a new nonce |
| PTV_ERR_006 | Nonce replay or unknown | Discard; issue new nonce |
| PTV_ERR_007 | Config digest not approved | Update endorsed reference set |
| PTV_ERR_008 | Evidence/proof mutex error | Reject; do not retry same msg |
| PTV_ERR_009 | Proxy policy unsatisfied | Re-enroll proxy or device |
| PTV_ERR_010 | Signature invalid | Check AK enrollment |
PTV_ERR_004 (quorum/BFT) from -00 is reserved and MUST NOT be used until a consensus mechanism is specified.¶
When Evidence is an EAT, the following mapping is RECOMMENDED. It is not a new EAT profile registration. Claim names are those of [RFC9711].¶
| PTV field / concept | EAT claim | Notes |
|---|---|---|
| nonce | eat_nonce | MUST match PTV nonce (PTV: 16-64 bytes) |
| attester_id | ueid or oemid | Stable instance identity |
| configuration digest | measurements | Endorsed reference values |
| hardware model | hwmodel, hwversion | As available; not the RoT type itself |
| timestamp | iat | Also carried in the PTV message |
| policy.audience | aud | CWT/JWT claim |
| policy.trust_domain | none standard | Carry in PTV policy |
| EAT profile | eat_profile | Identifies the profile, not a trust domain |
| sovereign_bound | none standard | Carry in EAT submodule or PTV message |
| proof (ZK_PROOF band) | not an EAT | Proof is the payload; EAT optional sidecar |
PTV Evidence MAY itself be a CMW-wrapped EAT [RFC9999]. A future revision may define a concrete eat_profile URI.¶
Every PTV attestation MUST echo the request nonce. Verifiers MUST reject a nonce that was not issued for a registered session or that has already been consumed (Section 4.2). Timestamps SHOULD be checked against a deployment-specific window. A window of 300 seconds is RECOMMENDED as a starting point, not a protocol constant.¶
PTV is compatible with ZK proof systems and does not mandate one. The predicate is the public-input list in Section 5.3.2. Deployers MUST treat verifying-key distribution as an endorsement problem in the RATS sense [RFC9334].¶
A ZK proof that does not bind the nonce is not a PTV proof. A ZK proof that is not covered by a valid AK signature (Section 5.4) is not hardware-bound.¶
Some proof systems (Groth16, for example) require a per-circuit trusted setup. Whoever retains the setup randomness ("toxic waste") can forge proofs that verify. Deployments using such systems MUST document how the setup was performed, SHOULD use a multi-party ceremony, and MUST treat a single-party setup as suitable only for non-adversarial testing. Deployments SHOULD also state the security level of the chosen curve.¶
Attestations SHOULD be bound to the agent instance via a hardware attestation key. Software-only attestations are NOT RECOMMENDED for high-assurance deployments. Values read from a platform without a signature by a hardware-protected key (for example a public key or an event log read through an operating system interface) are not Evidence in the sense of [RFC9334] and MUST NOT be presented as such.¶
sovereign_bound labels are integrity-protected by the message signature but are claims made by the Attester. A Verifier MUST NOT treat them as verified facts unless they are also covered by appraised Evidence or an endorsement.¶
In Proxy-Assisted Mode the Verifier learns that a proxy attested, not that the constrained device ran a particular binary. Threats include:¶
a compromised proxy attesting for a device it does not mediate;¶
key reuse across proxied devices;¶
silent substitution of the downstream channel.¶
Mitigations:¶
Verifiers SHOULD rate-limit proof verification and MAY cache verifying keys and ROUTINE Evidence appraisals keyed by (attester_id, config_digest).¶
attester_id, ueid, and a stable attestation key are long-lived identifiers. Presenting them to many Relying Parties lets those parties correlate an agent's activity. Deployments SHOULD limit the Relying Parties that receive attester_id, and MAY use per-Relying-Party pseudonymous identifiers or per-audience attestation keys where the hardware supports it.¶
The ZK_PROOF band is intended to avoid disclosing model weights and inference inputs, but the configuration digest and any reference-value set are visible to the Verifier. Sovereign-bound labels may reveal deployment location. Neither the proof nor these labels amount to legal compliance certification.¶
PTV is Layer 1 (identity binding) in the three-layer agent accountability framing [ACCOUNT-LAYERS]:¶
PTV / EAT: who is running, and in what enrolled state;¶
Delegation provenance (for example HDP [I-D.helixar-hdp-agentic-delegation]): who authorized the act;¶
Execution receipts (SCITT [RFC9943] or similar): what external effect was produced.¶
Composition SHOULD be by hash reference (receipt cites hash of PTV result and hash of delegation record), not by embedding one protocol inside another.¶
A registered EAT profile URI for PTV ROUTINE Evidence.¶
A COSE header for behavior_fingerprint if that field is standardized.¶
A concrete proof-system profile if the WG wants ZK_PROOF to be interoperable rather than opaque.¶
Whether PTV should remain a message protocol or collapse to EAT Challenge/Response with these claims.¶
This Experimental revision exists so that implementers can answer item 4 with running code.¶
Note to RFC Editor: remove this section before publication.¶
A prototype is available at [PTV-PROTOTYPE]. As of this revision it implements the ZK_PROOF path using Poseidon hashing and Groth16 over BN254 (circom/snarkjs), and reads platform measurements from a TPM 2.0 on Windows. It does not yet produce a TPM quote signed by an attestation key, does not implement the COSE_Sign1 protection in Section 5.4, and uses a single-party trusted setup. It is therefore not yet an implementation of the hardware binding described in this document and SHOULD NOT be used in adversarial environments.¶
This document makes no requests of IANA. A future revision may request an EAT profile URI, a CBOR tag, or a media type once the message set is stable.¶
Comments from participants on the RATS list and from the three-layer accountability discussion improved the -00 separation of identity binding from behavioral continuity. Remaining errors are the author's.¶
The following figures are from the prototype in [PTV-PROTOTYPE] (10 runs each, Windows 11, Node.js v24, snarkjs v0.7, library mode, Poseidon plus equality circuit with 426 non-linear constraints). They are not requirements, they cover proof generation and verification only, and they do not include TPM quote generation or COSE signing.¶
| Metric | Observed | Conditions |
|---|---|---|
| Proof generation | about 49 ms | library mode, 10 runs |
| Proof verification | about 9 ms | single proof, 10 runs |
All values are illustrative. Byte strings are shortened where noted; a real message carries full EAT bytes, a full proof, and a real COSE_Sign1 signature. Timestamps are CBOR tag 1 (epoch seconds).¶
{
"ptv_version": "1.1",
"nonce": h'a1b2c3d4e5f60718293a4b5c6d7e8f90',
"msg_type": 1,
"timestamp": 1(1790640000),
"policy": {
"audience": "https://rp.example",
"requirements": ["approved-config"],
"require_sovereign_bound": false
}
}
¶
This is the payload of the COSE_Sign1 in Section 5.4. The evidence field is an EAT whose eat_nonce equals the request nonce (bytes elided here).¶
{
"ptv_version": "1.1",
"nonce": h'a1b2c3d4e5f60718293a4b5c6d7e8f90',
"msg_type": 2,
"attester_id": "ueid:0102030405060708",
"triage_band": 0,
"evidence": h'd28443a10126', / EAT, elided /
"timestamp": 1(1790640002)
}
¶
The proof bytes are elided. Its public inputs are the nonce, the hash of the AK public key, and the configuration digest, in the order fixed by the deployment's proof-system profile.¶
{
"ptv_version": "1.1",
"nonce": h'a1b2c3d4e5f60718293a4b5c6d7e8f90',
"msg_type": 2,
"attester_id": "ueid:0102030405060708",
"sovereign_bound": {
"jurisdiction": "IN",
"data_residency": "IN",
"compliance": ["example-policy-1"]
},
"triage_band": 1,
"proof": h'9f3c00a1', / proof, elided /
"timestamp": 1(1790640002)
}
¶
{
"ptv_version": "1.1",
"nonce": h'a1b2c3d4e5f60718293a4b5c6d7e8f90',
"msg_type": 4,
"result": 0,
"audit_id": "audit-0001",
"timestamp": 1(1790640003)
}
¶
A PTV_PROVE_RESP carrying both evidence and proof, or neither, is malformed and is answered with result 1 and reason PTV_ERR_008:¶
{
"ptv_version": "1.1",
"nonce": h'a1b2c3d4e5f60718293a4b5c6d7e8f90',
"msg_type": 4,
"result": 1,
"reason": "PTV_ERR_008",
"timestamp": 1(1790640003)
}
¶