<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<rfc version="3" ipr="trust200902" submissionType="IETF" category="std"
     docName="draft-yossif-psea-03" consensus="true"
     xmlns:xi="http://www.w3.org/2001/XInclude">

  <front>
    <title abbrev="PSEA Token Profile">PSEA Token Profile: An EAT Profile for Action-Bound, User-Verification-Gated Transaction-Confirmation Evidence</title>

    <seriesInfo name="Internet-Draft" value="draft-yossif-psea-03"/>

    <author fullname="Mohamad Khalil Yossif" initials="M. K." surname="Yossif">
      <organization>Yuthent</organization>
      <address>
        <email>mohamad@yuthent.com</email>
        <uri>https://yuthent.com/psea</uri>
      </address>
    </author>

    <date year="2026" month="October" day="8"/>

    <area>Security</area>

    <keyword>PSEA</keyword>
    <keyword>EAT profile</keyword>
    <keyword>transaction confirmation</keyword>
    <keyword>user verification</keyword>
    <keyword>action binding</keyword>
    <keyword>WYSIWYS</keyword>

    <abstract>
      <t>This document defines the PSEA Token Profile, an Entity Attestation Token
      (EAT) profile for action-bound, user-verification-gated transaction-confirmation
      Evidence.  The profile specifies the canonical encoding, the signed proof-token
      claim set, the action-payload and cross-replay bindings, an optional hash-chain
      integrity layer, and the security properties that together constitute a
      What-You-Sign-Is-What-You-Execute proof that a human, present and verified at an
      authenticator, approved a specific named action at the moment of execution.  The
      profile binds what is signed to what the Verifier executes; it does not, by
      itself, bind what a human saw on a potentially compromised display to what was
      signed (the What-You-See-Is-What-You-Sign problem), which remains out of scope.
      The strength of the human-presence assurance depends on the authenticator's
      user-verification enforcement: where the platform attestation conveys that
      enforcement it is hardware-attested, and otherwise it rests on the authenticator's
      signed assertion that user verification occurred.  The profile does not, by
      itself, prove a specific human identity.  It fills the transaction-confirmation
      gap left unaddressed by deployed authentication standards and complements OAuth
      2.0 Step-Up Authentication by supplying the per-action, cryptographically
      action-bound Evidence that step-up flows can require.</t>
    </abstract>

    <note removeInRFC="true">
      <name>Note to Readers</name>
      <t>This is an individual submission to the IETF. "Standards Track" above is the
      <em>intended</em> status; this document has not been adopted by any IETF working
      group and does not represent IETF consensus. The author intends to request
      dispatch of this work (for example through the SECDISPATCH working group) toward
      adoption by an appropriate working group. Review and comments are welcome at the
      repository referenced in the Introduction.</t>
    </note>
  </front>

  <middle>

    <section anchor="introduction" numbered="true" toc="default">
      <name>Introduction</name>

      <t>Session-based authentication answers "did this entity present valid credentials at some
      point?"; execution-time authority answers "is an authorized human, present and verified,
      approving this specific action right now?".  The gap between these two questions is where
      session-hijacking, autonomous malware, and unattended-device risk materialize.  This document
      defines a token profile that fills that gap at the token layer: a signed EAT-profile token
      whose claim set constitutes cryptographic Evidence of execution authority at the moment of
      action, independent of prior session state.</t>

      <t>The signed token is a JSON Web Signature (JWS) Compact Serialization object
      (<xref target="RFC7515"/>) whose payload is an EAT-JSON claims-set.  The profile reuses the
      registered EAT claims <tt>ueid</tt>, <tt>eat_nonce</tt>, <tt>submods</tt>, and
      <tt>eat_profile</tt> (<xref target="RFC9711"/>) alongside JSON Web Token (JWT) registered claims
      (<xref target="RFC7519"/>) and the PSEA-private <tt>psea_*</tt> extension claims defined in
      this document.</t>

      <t>The profile applies to any authenticator that can generate a hardware-backed,
      user-verification-gated ES256 signature over a <em>raw digest</em> the host supplies.  Conforming
      authenticator classes include Trusted Execution Environment (TEE)- or Secure-Element-backed mobile
      keystores (for example Android Keystore keys that require user authentication, or iOS Secure Enclave
      keys gated by a local-authentication context), PIN-gated Personal Identity Verification (PIV)-class
      smartcards that expose a user-verification-gated raw ECDSA-over-digest operation, and programmable
      secure elements.  A standard FIDO2/WebAuthn security key does not natively conform: a WebAuthn
      authenticator signs only its fixed assertion structure (authenticator data concatenated with a
      hash of the client data) and exposes no primitive to sign an arbitrary digest, so it cannot emit a
      JWS whose payload is an arbitrary EAT-JSON claims-set.  Binding a FIDO assertion into this profile
      would require a separate FIDO-assertion binding, which is out of scope of this document
      (<xref target="relationship-webauthn"/>).</t>

      <t>Where the authenticator exposes only a raw user-verification-gated signing primitive (the
      typical case for keystore- and smartcard-backed keys), the host application -- the integrating
      SDK -- assembles the JWS, computes the action-payload hash, maintains the replay counter, and
      populates the user-verification claim; the hardware authenticator contributes only the
      hardware-protected, user-verification-gated signature.  In this profile the term "Attester" denotes
      the composite of that host software and the hardware authenticator.  The security properties this
      profile claims rest on the hardware authenticator's protected key and its user-verification gating;
      the surrounding claim assembly is performed by the host and is itself untrusted until the
      Verifier validates the resulting signature and cross-checks the claims against the platform
      attestation.</t>

      <t>The deployment architecture -- including any operational enforcement
      model that maps applications to capability or assurance levels, the HTTP verifier endpoints, the
      attestation-evidence wrapper, the Verifier acknowledgement, and the trust-state model -- is
      deployment-specific and is out of scope of this profile.</t>

      <t>The transaction-confirmation gap this profile addresses was previously targeted by the
      WebAuthn txAuthSimple and txAuthGeneric extensions, both of which were removed in Web
      Authentication Level 2 without widely deployed successors (<xref target="relationship-webauthn"/>).
      This profile complements OAuth 2.0 Step-Up Authentication (<xref target="RFC9470"/>) by
      providing the per-action, cryptographically action-bound Evidence token that a step-up
      challenge can require and a resource server can verify
      (<xref target="relationship-rfc9470"/>).</t>

      <t>This document is the protocol-specification revision of PSEA. The earlier
      revisions draft-yossif-psea-00 and draft-yossif-psea-01 were Informational and
      described PSEA as a security model and a set of requirements for verifying
      authority at the moment of action. This revision specifies the concrete wire
      protocol that realizes that work as an EAT profile (<xref target="RFC9711"/>),
      with a defined claim set, a canonical encoding, and normative verification rules;
      the intended status is Standards Track accordingly.</t>

      <section anchor="terminology" numbered="true" toc="default">
        <name>Requirements Language</name>

        <t>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&nbsp;14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and
        only when, they appear in all capitals, as shown here.</t>
      </section>

      <section anchor="glossary" numbered="true" toc="default">
        <name>Terminology</name>

        <t>This document uses the following terms.  RATS terminology follows
        <xref target="RFC9334"/>; PSEA-specific terms are defined here.</t>

        <dl>
          <dt>Attester</dt>
          <dd>The entity that generates Evidence by producing a proof token signed with a
          hardware-protected, user-verification-gated key.  Concretely the Attester is the composite of
          (a) a hardware authenticator that holds the signing key and enforces the user-verification gate
          -- for example a TEE- or Secure-Element-backed mobile keystore, a PIN-gated PIV-class
          smartcard, or a programmable secure element -- and (b) the host software that assembles the
          token claim set around that signature.  Where the hardware authenticator exposes only a raw
          user-verification-gated signing primitive, the host computes <tt>psea_payload_hash</tt>,
          maintains <tt>psea_counter</tt>, and populates <tt>psea_uv</tt>; the authenticator contributes
          the signature.  A standard FIDO2/WebAuthn security key is not a conforming Attester under this
          profile (<xref target="introduction"/>).  A natural person is not an Attester.</dd>

          <dt>Verifier</dt>
          <dd>The server-side component that appraises Evidence and produces Attestation Results.</dd>

          <dt>Relying Party</dt>
          <dd>The application server that gates the action on the Verifier's Attestation Result.</dd>

          <dt>Subject (of Evidence)</dt>
          <dd>The natural person whose presence and intent the Attester's Evidence makes claims about.</dd>

          <dt>Evidence</dt>
          <dd>The signed proof token an Attester produces per this profile: a user-verification-gated,
          action-bound execution-authority proof.  See <xref target="proof-token-format"/>.</dd>

          <dt>Attestation Result</dt>
          <dd>The integrity-protected artifact the Verifier produces and returns to the Relying Party.</dd>

          <dt>Endorsement</dt>
          <dd>A trust anchor the Verifier uses to appraise Evidence (for example, an Android Key
          Attestation root, an Apple App Attest root, a FIDO authenticator vendor root, or a
          smartcard issuer root).</dd>

          <dt>action / action payload</dt>
          <dd>The specific operation the Subject approves.  The canonical encoding of the action
          payload is hashed and included in the signed Evidence body (see
          <xref target="action-binding"/>).</dd>

          <dt>capability / assurance level</dt>
          <dd>An abstract indicator of the authority or assurance context a proof is produced under,
          carried in the signed <tt>psea_tier</tt> claim and bound into the signature
          (<xref target="cross-replay-binding"/>).  The value space is opaque to this profile and is
          agreed out of band between producer and Verifier; this profile does not define a fixed set of
          levels, and the operational mapping of applications to levels is deployment-specific and out of
          scope of this profile.</dd>
        </dl>
      </section>
    </section>

    <section anchor="canonical-encoding" numbered="true" toc="default">
      <name>Canonical Encoding of Proof Payloads</name>

      <t>The canonical encoding rules in this section apply wherever a PSEA hash is taken over canonical
      JSON octets. The PSEA proof is carried as JWS Compact Serialization
      (<xref target="proof-token-format"/>), as is any Verifier acknowledgement a deployment defines (that
      artifact's format is out of scope of this profile); a JWS signature covers the received wire octets
      directly, so canonical encoding is therefore not load-bearing for verifying those JWS objects.
      It is normative (a <bcp14>MUST</bcp14>) for exactly two uses, in each of which the producer and the
      Verifier independently hash canonical JSON bytes with SHA-256 and compare the result:</t>
      <ul>
        <li>the action-payload binding -- the SHA-256 over the canonical <tt>actionPayload</tt> that
        produces the signed <tt>psea_payload_hash</tt> claim, which the Verifier independently re-computes
        and compares (<xref target="action-binding"/>);</li>
        <li>the <tt>ueid</tt> derivation -- the SHA-256 over the canonical two-member JSON object that
        encodes the <tt>deviceId</tt> and <tt>iss</tt> inputs, which the Verifier independently re-computes
        for its own <tt>iss</tt> (<xref target="privacy-ueid"/>).</li>
      </ul>
      <t>For this use, implementations that deviate from the rules below will produce byte sequences that
      fail the payload-binding comparison on peer platforms.</t>

      <t>The underlying data format is JSON (<xref target="RFC8259"/>). Canonical encoding
      <bcp14>MUST</bcp14> follow the JSON Canonicalization Scheme (JCS)
      <xref target="RFC8785"/> in full. This section restates the <xref target="RFC8785"/> rules that are
      most consequential for cross-platform PSEA implementers and notes the one schema restriction PSEA
      imposes: PSEA payload schemas use JSON integer numbers only at this revision, so the floating-point
      number-serialization rules of <xref target="RFC8785"/>, Section 3.2.2.3 do not arise (see
      <xref target="canonical-encoding-numbers"/>); in all other respects an implementation
      <bcp14>MUST</bcp14> conform to <xref target="RFC8785"/>. Implementers <bcp14>MUST</bcp14> read this
      section in full before relying on a JSON library's default serialization or a third-party JCS
      implementation; platform defaults frequently diverge from these requirements in ways that are
      difficult to detect without cross-platform test vectors.</t>

      <t>This document uses the verb "will" in lowercase to describe deterministic consequences of
      non-conformance, not as a BCP 14 keyword.</t>

      <section anchor="canonical-encoding-utf8" numbered="true">
        <name>UTF-8 Serialization</name>
        <t>The canonical byte sequence <bcp14>MUST</bcp14> be encoded as UTF-8
        <xref target="RFC3629"/>. The encoding <bcp14>MUST NOT</bcp14> include a byte-order mark.</t>
      </section>

      <section anchor="canonical-encoding-keys" numbered="true">
        <name>Object Key Ordering (UTF-16 Code-Unit Sort)</name>
        <t>Object members <bcp14>MUST</bcp14> be ordered as <xref target="RFC8785"/>, Section 3.2.3
        specifies: property names, in their raw (unescaped) form, are compared as arrays of UTF-16 code
        units, each code unit treated as an unsigned integer, independent of locale. The comparison
        <bcp14>MUST NOT</bcp14> normalize case, fold Unicode equivalence classes, or apply collation
        rules. Two keys that differ only in case (for example, "endedAt" and "endReason")
        <bcp14>MUST</bcp14> order according to the value of their first differing code unit; in this
        example, "endReason" (uppercase "R" = U+0052 = 82) sorts before "endedAt" (lowercase "e" at the
        comparison position = U+0065 = 101) because 82 &lt; 101.</t>

        <t>For names that contain no character outside the Basic Multilingual Plane, UTF-16 code-unit
        order, Unicode code-point order, and UTF-8 byte order coincide. They diverge when one name
        carries a character outside the Basic Multilingual Plane (encoded in UTF-16 as a surrogate pair
        in the range U+D800 to U+DFFF) and the other carries a character in the range U+E000 to U+FFFF at
        the same position; the UTF-16 code-unit order governs in that case.</t>

        <t>This rule is the canonical encoding's most consequential implementer-warning point. See
        <xref target="canonical-encoding-conformance"/> for cross-platform notes.</t>
      </section>

      <section anchor="canonical-encoding-arrays" numbered="true">
        <name>Array Ordering</name>
        <t>JSON arrays preserve their natural element order; the canonical encoding <bcp14>MUST NOT</bcp14>
        re-order array elements. The semantics of array order are payload-specific and are defined where
        applicable in <xref target="proof-token-format"/>.</t>
      </section>

      <section anchor="canonical-encoding-strings" numbered="true">
        <name>String Serialization</name>
        <t>String values <bcp14>MUST</bcp14> be serialized following the encoding rules in
        <xref target="RFC8785"/>, Section 3.2.2.2 (Serialization of Strings). In particular: U+0022
        (quotation mark) and U+005C (reverse solidus) <bcp14>MUST</bcp14> be serialized as the
        two-character escapes \" and \\; U+0008, U+0009, U+000A, U+000C, and U+000D
        <bcp14>MUST</bcp14> be serialized as the two-character escapes \b, \t, \n, \f, and \r; every other
        code point in the range U+0000 to U+001F <bcp14>MUST</bcp14> be serialized in the six-character
        form \uhhhh with lowercase hexadecimal digits; and every other code point is emitted as is.
        Input containing a lone surrogate (an unpaired code point in the range U+D800 to U+DFFF)
        <bcp14>MUST</bcp14> cause canonicalization to fail with an error. Canonical encoding does not
        normalize Unicode form.</t>
      </section>

      <section anchor="canonical-encoding-numbers" numbered="true">
        <name>Number Serialization (Integers Only)</name>
        <t>PSEA payload schemas define numeric fields only as integers. The canonical encoding of an
        integer N <bcp14>MUST</bcp14> follow <xref target="RFC8785"/>, Section 3.2.2.3, which for an
        integer is its shortest decimal representation (no leading zeros, no "+" sign, no exponent, no
        decimal point). Because every numeric field defined in this revision is an integer, the
        floating-point cases of <xref target="RFC8785"/> do not arise; a full RFC 8785 serializer and an
        implementation of only this integer rule produce identical output for every payload schema defined
        in this revision.</t>
        <t>Floating-point numbers, decimal fractions, and exponent notation <bcp14>MUST NOT</bcp14> appear
        in any PSEA payload field at this revision. A future revision <bcp14>MAY</bcp14> extend the subset;
        until such an extension is registered, any non-integer numeric value in a signed payload field
        is non-conformant.</t>
        <t>This integers-only rule also governs the <tt>actionPayload</tt> that the action binding hashes
        (<xref target="action-binding"/>). The <tt>actionPayload</tt> schema is deployment-defined, but it
        is hashed under the same canonical encoding, so a JSON floating-point value placed in it would
        re-introduce exactly the <xref target="RFC8785"/> floating-point serialization divergence this
        section avoids for the profile's own claims. Accordingly, monetary and other fractional quantities
        carried in <tt>actionPayload</tt> <bcp14>MUST</bcp14> be represented as integers in a fixed minor
        unit (for example, integer cents) or as strings, and <bcp14>MUST NOT</bcp14> be represented as JSON
        floating-point numbers. The worked example of <xref target="tv-payload-hash"/> follows this rule
        (an amount of 2500 minor units, not 25.00). A deployment that places a JSON float in
        <tt>actionPayload</tt> falls outside the cross-platform canonicalization guarantee of this section
        and will observe payload-binding failures between peers whose float serializers differ.</t>
      </section>

      <section anchor="canonical-encoding-literals" numbered="true">
        <name>Boolean and Null Serialization</name>
        <t>The literals <tt>true</tt>, <tt>false</tt>, and <tt>null</tt> <bcp14>MUST</bcp14> be serialized
        with their lowercase JSON spelling. <tt>null</tt> as a value <bcp14>SHOULD NOT</bcp14> appear in
        PSEA payload schemas; absent fields are conveyed by omitting the key rather than by including
        the key with a <tt>null</tt> value.</t>
      </section>

      <section anchor="canonical-encoding-whitespace" numbered="true">
        <name>Whitespace</name>
        <t>The canonical encoding <bcp14>MUST NOT</bcp14> include whitespace between tokens (no spaces,
        tabs, newlines, or carriage returns). Whitespace inside string values is preserved verbatim per
        <xref target="canonical-encoding-strings"/>.</t>
      </section>

      <section anchor="canonical-encoding-conformance" numbered="true">
        <name>Conformance and Cross-Platform Implementer Warning</name>
        <t>Implementations <bcp14>MUST</bcp14> verify their canonical encoder against the test vectors in
        <xref target="appendix-canonical-test-vectors"/>. A canonical encoder that produces output matching
        the test vectors for every input in that appendix is conformant; an encoder that diverges on any
        single test vector is non-conformant and will produce signed bytes that fail peer
        verification.</t>

        <t>Specific implementer-warning notes on common platform pitfalls:</t>

        <dl>
          <dt>Apple platforms</dt>
          <dd><tt>JSONSerialization</tt> with the <tt>sortedKeys</tt> option performs a
          <em>case-insensitive</em> sort, which diverges from this section's <em>case-sensitive</em> rule (see
          <xref target="canonical-encoding-keys"/>). Implementations on Apple platforms
          <bcp14>MUST NOT</bcp14> rely on <tt>sortedKeys</tt>. Swift's default comparison of
          <tt>String</tt> values does not compare UTF-16 code units either, so an implementation
          <bcp14>MUST</bcp14> emit canonical bytes via a manual sort that compares the UTF-16 code units
          of each name.</dd>

          <dt>JavaScript / TypeScript platforms</dt>
          <dd><tt>Array.prototype.sort()</tt> on string keys compares UTF-16 code units, matching this
          section's requirement. <tt>JSON.stringify</tt> with a comparator option is not standard; use a
          manual canonicalization pass.</dd>

          <dt>Java / Kotlin</dt>
          <dd><tt>String.compareTo()</tt> compares UTF-16 code units, matching this section's
          requirement; the standard JCS implementations in major libraries (Jackson, Gson) produce
          conformant output when configured for canonical mode.</dd>

          <dt>Python</dt>
          <dd>The standard <tt>sorted()</tt> on strings, and the <tt>json</tt> module's
          <tt>sort_keys=True</tt>, order by Unicode code point, which diverges from UTF-16 code-unit order
          in the case described in <xref target="canonical-encoding-keys"/>; an implementation
          <bcp14>MUST</bcp14> sort by the UTF-16 encoding of each name.</dd>

          <dt>Go, Rust</dt>
          <dd>Default string comparison on both languages is byte-wise on UTF-8, which matches this
          section's rule only for names containing no character outside the Basic Multilingual Plane; an
          implementation <bcp14>MUST</bcp14> sort by the UTF-16 encoding of each name.</dd>
        </dl>

        <t>The most consequential pitfall is an Apple <tt>sortedKeys</tt> case-insensitive sort silently
        producing canonical bytes that diverge from a peer using a case-sensitive sort, causing
        cross-platform signature-verification failures. This is the failure mode the implementer-warning
        here is specifically designed to prevent.</t>
      </section>
    </section>

    <section anchor="proof-token-format" numbered="true" toc="default">
      <name>Proof Token Format</name>

      <t>A PSEA proof is a JWS Compact Serialization object (<xref target="RFC7515"/>):
      <tt>BASE64URL(protected header) || "." || BASE64URL(payload) || "." || BASE64URL(signature)</tt>.
      The Attester signs the <tt>header.payload</tt> octets with its hardware-protected signing key. The payload is
      an Entity Attestation Token claims-set in EAT-JSON form (<xref target="RFC9711"/>); PSEA defines it
      as an EAT profile (<xref target="eat-profile"/>). The claim set
      reuses JWT registered claims (<xref target="RFC7519"/>), the registered EAT claims
      (<xref target="RFC9711"/>) <tt>ueid</tt>, <tt>eat_nonce</tt>, <tt>submods</tt>, and
      <tt>eat_profile</tt> for device-state conveyance and profile identification, and the profile's
      PSEA-private <tt>psea_*</tt> extension
      claims. The proof travels in a transport body (<xref target="envelope-schemas"/>) alongside the
      unsigned cleartext <tt>actionPayload</tt> that the Verifier independently hashes and compares
      against the signed payload-hash claim.</t>

      <t>The integrity flows are: the proof's authenticity and integrity are established by the JWS
      signature over its received octets; the action binding is established by the Verifier re-hashing
      the cleartext <tt>actionPayload</tt> and comparing to the signed <tt>psea_payload_hash</tt> claim
      (<xref target="action-binding"/>); the attestation evidence carried in <tt>integrityEvidence</tt>
      (contents out of scope) is appraised independently against the platform Endorsement; and, where the
      deployment-optional chain
      layer is enabled (<xref target="chain-entry"/>), <tt>chainEntry</tt> is computed deterministically
      by the Verifier from signed inputs, so its integrity flows from the signature on those inputs.</t>

      <t>This profile defines a single proof token type: the user-verification-gated, action-bound
      execution-authority proof. A deployment <bcp14>MAY</bcp14> define additional, lower-assurance
      signed-token types that reuse the same JWS Compact Serialization representation and the same
      canonical encoding; such tokens are out of scope of this profile.</t>

      <section anchor="eat-profile" numbered="true">
        <name>PSEA as an EAT Profile</name>

        <t>The PSEA proof is a profile of the Entity Attestation Token
        (<xref target="RFC9711"/>), as that term is used in <xref target="RFC9711"/>, Section 6. The
        token is a compact JWS (<xref target="RFC7515"/>) signed with ES256 (ECDSA P-256 with SHA-256)
        whose payload is an EAT-JSON claims-set. A conforming PSEA proof
        <bcp14>MUST</bcp14> carry the <tt>eat_profile</tt> claim (<xref target="RFC9711"/>,
        Section 4.3.2) with the stable profile identifier
        <tt>https://datatracker.ietf.org/doc/draft-yossif-psea/</tt>. The claim carries a URI or an OID
        and <xref target="RFC9711"/> defines no registry for profile identifiers, so this identifier
        requires no registration and carries no dependency on the publication stream this document
        appears in. A Verifier <bcp14>MUST</bcp14> reject a
        proof whose <tt>eat_profile</tt> claim is absent or carries any other value, except as
        permitted in <xref target="proof-schema"/>.</t>

        <t>The profile is composed of three claim families:</t>

        <ul>
          <li><strong>Registered EAT claims (<xref target="RFC9711"/>).</strong> <tt>ueid</tt> (the
          per-device identifier, <xref target="RFC9711"/>, Section 4.2.1; see the encoding requirement
          in <xref target="proof-top-level"/> and the privacy discussion in
          <xref target="privacy-ueid"/>), <tt>eat_nonce</tt> (OPTIONAL freshness nonce),
          <tt>submods</tt> (the device-state submodules map), and <tt>eat_profile</tt> (this profile's
          identifier).</li>
          <li><strong>Registered JWT claims (<xref target="RFC7519"/>).</strong> <tt>jti</tt>,
          <tt>aud</tt>, <tt>iss</tt>, <tt>iat</tt>, and <tt>exp</tt>.</li>
          <li><strong>Profile extension claims (<tt>psea_*</tt>).</strong> The properties for which EAT
          defines no registered claim: <tt>psea_payload_hash</tt> (action-binding payload hash;
          <xref target="action-binding"/>), <tt>psea_tier</tt> (capability/assurance-level binding),
          <tt>psea_counter</tt> (monotonic per-counter-scope replay counter;
          <xref target="counter-model"/>), <tt>psea_uv</tt> (user-verification claim;
          <xref target="presence-uv-claim"/>), <tt>psea_op</tt> (operation binding;
          <xref target="cross-replay-binding"/>), and <tt>psea_proof_version</tt>. The OPTIONAL extension
          claims <tt>psea_chain_prev</tt> (deployment-optional hash-chain anchor;
          <xref target="chain-entry"/>), <tt>psea_user_hash</tt>, <tt>psea_caller_package</tt>, and
          <tt>psea_sdk_version</tt> MAY also be present.</li>
        </ul>

        <t>This document does not claim that a generic EAT consumer can fully appraise a PSEA proof
        without understanding the <tt>psea_*</tt> extension claims; the extension claims are
        profile-specific and are defined normatively in <xref target="proof-token-format"/>. The
        <tt>eat_profile</tt> claim signals to a consumer which extension semantics apply.</t>

        <section anchor="eat-profile-definition" numbered="true">
          <name>EAT Profile Definition (per RFC 9711, Section 6)</name>
          <t>This subsection states the profile in the form <xref target="RFC9711"/>, Section 6 calls for,
          so the <tt>eat_profile</tt> commitment is auditable in one place. A token claiming
          <tt>https://datatracker.ietf.org/doc/draft-yossif-psea/</tt> <bcp14>MUST</bcp14> satisfy all of
          the following.</t>
          <dl>
            <dt>Profile identifier</dt>
            <dd>The <tt>eat_profile</tt> claim (<xref target="RFC9711"/>, Section 4.3.2)
            <bcp14>MUST</bcp14> equal the URI
            <tt>https://datatracker.ietf.org/doc/draft-yossif-psea/</tt>. This document fixes exactly one
            value for this claim, and that value requires no registration
            (<xref target="iana-registries-overview"/>).</dd>
            <dt>Serialization</dt>
            <dd>The token <bcp14>MUST</bcp14> be a JWS Compact Serialization (<xref target="RFC7515"/>)
            whose payload is an EAT-JSON claims-set. A CBOR/COSE representation is out of scope of this
            profile; a Verifier <bcp14>MUST NOT</bcp14> accept a CBOR/COSE-encoded token as conforming to
            this profile.</dd>
            <dt>Mandatory-to-implement algorithm</dt>
            <dd>ES256 (ECDSA on P-256 with SHA-256) is the only mandatory-to-implement signature algorithm
            (<xref target="crypto-mti"/>). A Verifier <bcp14>MUST</bcp14> reject a token whose JOSE
            <tt>alg</tt> is not <tt>"ES256"</tt> and <bcp14>MUST</bcp14> apply the header hardening of
            <xref target="jose-hardening"/>.</dd>
            <dt>Freshness</dt>
            <dd>Replay freshness is provided by the monotonic <tt>psea_counter</tt>
            (<xref target="counter-model"/>), by the <tt>iat</tt>/<tt>exp</tt> validity window
            (<xref target="freshness"/>), and, when the Verifier issues a challenge, by the OPTIONAL
            <tt>eat_nonce</tt> claim (<xref target="RFC9711"/>) carrying that challenge value. A Verifier
            that issued a challenge <bcp14>MUST</bcp14> reject a token whose <tt>eat_nonce</tt> is absent
            or does not equal the issued value.</dd>
            <dt>Key confirmation</dt>
            <dd>The verification key is resolved out of band from the enrolled record selected by the JOSE
            <tt>kid</tt>; a Verifier <bcp14>MUST NOT</bcp14> use any key conveyed in or referenced by the
            token header (<xref target="jose-hardening"/>).</dd>
            <dt>Claims</dt>
            <dd>The full claim set, and which claims are REQUIRED versus OPTIONAL, is defined in
            <xref target="proof-schema"/>.</dd>
            <dt>submods content</dt>
            <dd>The OPTIONAL <tt>submods</tt> map (<xref target="RFC9711"/>) carries a single
            <tt>psea-device-state</tt> submodule whose contents are out of scope; an authenticator that
            cannot produce device-state omits <tt>submods</tt> entirely.</dd>
            <dt>Detached EAT Bundles and nested tokens</dt>
            <dd>This profile does not use Detached EAT Bundles (<xref target="RFC9711"/>, Section 5) and
            does not use nested tokens (<xref target="RFC9711"/>, Section 4.2.18.3): a conforming PSEA proof
            is a single, self-contained JWS whose payload is one EAT-JSON claims-set. A Verifier
            <bcp14>MUST NOT</bcp14> accept a Detached EAT Bundle or a nested token as conforming to this
            profile.</dd>
            <dt>UEID type</dt>
            <dd>The <tt>ueid</tt> claim carries a deterministic per-issuer value under the RFC 9711 RAND
            type tag (<tt>0x01</tt>). The value is not preserved across factory reset, so it does not meet
            the permanence <xref target="RFC9711"/> requires of a UEID; it is retained for wire continuity,
            and the migration to <tt>sueids</tt> is stated in <xref target="privacy-ueid"/>.</dd>
            <dt>Extensibility model</dt>
            <dd>This profile is a deliberately strict, lock-step profile rather than an
            ignore-unknown-claims profile. A conforming Verifier rejects a proof carrying any claim outside
            this profile's defined set (the claim-set schema sets <tt>additionalProperties: false</tt>,
            <xref target="proof-schema"/>), any unknown <tt>psea_proof_version</tt>
            (<xref target="proof-versioning"/>), and any unrecognized <tt>crit</tt> header parameter
            (<xref target="jose-hardening"/>). A consequence intended by this design is that a future
            <tt>psea_*</tt> claim is rejected by current-revision Verifiers until they are upgraded to a
            revision that defines it; the wire format is versioned as a whole rather than extended in
            place. This departs from the ignore-unknown extensibility customary for EAT and JWT consumers,
            and is chosen so that the set of claims a Verifier appraises is exactly the set the profile
            revision specifies. Whether a profile may narrow EAT this far — <xref target="RFC9711"/>,
            Section 1.2 states that no claims are mandatory and that claims not recognized should be
            ignored — is under discussion on the IETF RATS mailing list at the time of writing, and a
            future revision may narrow this rule.</dd>
          </dl>
        </section>
      </section>

      <section anchor="proof-top-level" numbered="true">
        <name>Top-Level Structure</name>

        <figure anchor="fig-proof-structure">
          <name>PSEA proof as JWS Compact Serialization.</name>
          <artwork align="left"><![CDATA[
JWS Compact Serialization (value of the transport-body "proof" field)

   BASE64URL(UTF8(protected header))
   || "." || BASE64URL(JCS-canonical-JSON(payload))
   || "." || BASE64URL(r || s)        ; ES256, see Signature below

Protected header (a JSON object; base64url-encoded, no padding)
+-- alg   ; "ES256"  (ECDSA P-256 with SHA-256)
+-- kid   ; opaque, deployment-internal enrollment-lookup selector,
|         ;   untrusted until verified (a forged kid fails: the wrong
|         ;   key will not validate the signature). The SHA-256 of the
|         ;   signing-key SubjectPublicKeyInfo (SPKI) in standard base64
|         ;   is one ILLUSTRATIVE derivation; the value space is not
|         ;   constrained by this profile.
+-- typ   ; "psea-proof+jwt"

Payload (the claim set; base64url-encoded, no padding)
  JWT registered claims (RFC 7519)
  +-- jti   ; action id; also the global-uniqueness key
  +-- aud   ; intended Verifier/audience identifier (RFC 7519)
  +-- iss   ; tenant / deployment id
  +-- iat   ; NumericDate, epoch SECONDS
  +-- exp   ; NumericDate, epoch SECONDS
  EAT claims (RFC 9711)
  +-- ueid          ; base64url no-pad of the RAND-type UEID byte
  |                 ;   string, 33 bytes:
  |                 ;     0x01 || SHA-256(JCS({"deviceId": <deviceId>,
  |                 ;                          "iss": <iss>}))
  |                 ;   The two inputs are encoded as a canonical JSON
  |                 ;   object (RFC 8785), not concatenated, so the
  |                 ;   pair cannot be read two ways.
  |                 ;   Per-issuer (pairwise): the same device yields
  |                 ;   a distinct ueid per deployment
  |                 ;   (RFC 9711 Sec 4.2.1.1; 0x01 = RAND type tag)
  [+-- eat_nonce]   ; OPTIONAL server nonce for push-initiated
  |                 ;   freshness; omitted entirely when absent
  [+-- submods]     ; OPTIONAL; { "psea-device-state": {...} } --
  |                 ;   device-state appraisal input; contents
  |                 ;   out of scope of this document
  +-- eat_profile   ; "https://datatracker.ietf.org/doc/
  |                 ;    draft-yossif-psea/"  -- one string; wrapped
  |                 ;    here only to fit the diagram
  |                 ;   (RFC 9711 Sec 4.3.2); REQUIRED
  PSEA-private extension claims
  +-- psea_tier            ; capability/assurance indicator (opaque;
  |                        ;   deployment-defined value space)
  +-- psea_counter         ; JSON integer [0, 2^53-1], monotonic per
  |                        ;   (attester, counter scope)
  +-- psea_payload_hash    ; standard base64 of SHA-256(canonical
  |                        ;   actionPayload) -- the action binding
  +-- psea_op              ; operation/authority-context discriminator
  |                        ;   (REQUIRED; see cross-replay binding)
  +-- psea_uv              ; { "verified": bool, "method": string }
  |                        ;   user-verification (human-presence) claim;
  |                        ;   REQUIRED; see the psea_uv section
  +-- psea_proof_version   ; "1"
  [+-- psea_chain_prev]    ; OPTIONAL deployment-optional hash-chain
  |                        ;   anchor; 64-char lowercase hex
  |                        ;   (sentinel: 64 ASCII '0' for first proof);
  |                        ;   present only when the deployment enables
  |                        ;   the chain layer (see ChainEntry section)
  [+-- psea_caller_package] ; OPTIONAL; reverse-DNS app identifier
  [+-- psea_sdk_version]    ; OPTIONAL; Attester implementation version
  [+-- psea_user_hash]     ; OPTIONAL; base64url no-pad; omitted
                           ;   entirely when empty

Signature
   ECDSA P-256 (ES256). The signature is the fixed-width 64-byte
   concatenation of the (r, s) integers, base64url no-pad, computed
   over ASCII(headerB64 || "." || payloadB64). This is the JWS ES256
   signature encoding (RFC 7518 Sec 3.4), NOT a DER/ASN.1 encoding.

Encoding split: ueid uses base64url (no padding) of the 33-byte
RAND-type UEID; psea_payload_hash uses standard base64 (with
padding); psea_user_hash uses base64url (no padding).

NOTE on chainEntry (deployment-optional layer): when a deployment
enables the hash-chain tamper-evidence layer, the Attester sends
psea_chain_prev IN the signed payload (the prior proof's chainEntry,
or the sentinel for the first proof). chainEntry itself is not carried
inbound: the Verifier recomputes this proof's chainEntry from the
signed inputs (see the ChainEntry section). The mechanism by which the
recomputed chainEntry is conveyed back to the Attester for use as the
next proof's psea_chain_prev is deployment-specific and out of scope.
Deployments that do not enable the chain layer omit psea_chain_prev
entirely.
]]></artwork>
        </figure>

        <t><strong>Base64 encoding split (normative):</strong> <tt>psea_payload_hash</tt> <bcp14>MUST</bcp14> be encoded as
        standard base64 (<xref target="RFC4648"/>, Section 4) with padding (the pattern
        <tt>^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$</tt>; the restricted final-sextet class rejects
        non-canonical encodings of the 32-octet digest, whose trailing two bits are always zero).
        <tt>ueid</tt> and <tt>psea_user_hash</tt> <bcp14>MUST</bcp14> be encoded as base64url (<xref target="RFC4648"/>, Section 5) without
        padding.  This is a deliberate per-claim encoding split that implementers <bcp14>MUST</bcp14> observe
        exactly; using base64url for <tt>psea_payload_hash</tt> or standard base64 for <tt>ueid</tt> or
        <tt>psea_user_hash</tt> produces a non-conformant token.</t>

        <t>The two encodings are not interchangeable; they carry the same underlying octets and differ
        only in alphabet and padding. A Verifier validates each claim against the exact pattern in
        <xref target="proof-schema"/>.</t>

        <t>The operation a proof is presented for determines which <tt>psea_tier</tt> and
        <tt>psea_op</tt> values the Verifier expects in the payload; the expected values are
        deployment-defined and agreed out of band. The payload's <tt>psea_tier</tt>,
        <tt>psea_op</tt>, <tt>aud</tt>, and <tt>iss</tt> claims together close the cross-replay attack
        surface (<xref target="cross-replay-binding"/>).</t>

        <t>The transport body POSTed to the endpoint wraps the JWS in the <tt>proof</tt> field and carries
        the cleartext action and evidence alongside it:</t>

        <sourcecode type="json"><![CDATA[
{
  "proof":             "<compact JWS>",
  "actionPayload":     { ... },  // unsigned cleartext; the
                                 //   Verifier re-canonicalizes,
                                 //   SHA-256s, and compares against
                                 //   psea_payload_hash
  "integrityEvidence": { ... },  // OPTIONAL attestation evidence
  "requestId":         "...",    // OPTIONAL opaque request id
  "signalReport":      { ... },  // OPTIONAL device-signal report
  "proofId":           "..."     // OPTIONAL opaque proof
                                 //   identifier
}
]]></sourcecode>

        <t>The <tt>actionPayload</tt> is unsigned cleartext; the Verifier re-canonicalizes it
        (<xref target="canonical-encoding"/>), takes its SHA-256, and compares the result against the
        signed <tt>psea_payload_hash</tt> claim. This comparison is the fail-closed action binding
        (<xref target="action-binding"/>). Because a compact JWS has a single authoritative payload, the
        proof carries no envelope-level duplicates of the signed claims.</t>

      </section>

      <section anchor="envelope-body-consistency" numbered="true">
        <name>Single Authoritative Payload (No Envelope Duplicates)</name>

        <t>A compact JWS has a single, cryptographically attested payload. The PSEA proof
        format therefore carries no envelope-level duplicates of its signed claims, and there is no
        envelope-versus-signed-body consistency check to perform: the only field a Verifier reads ahead
        of signature verification is the protected-header <tt>kid</tt>, which is the
        untrusted-until-verified enrollment-lookup selector. A forged <tt>kid</tt> fails verification
        because the key it selects will not validate the signature, so no separate envelope-body
        comparison is needed to defend against a substituted routing value.</t>

        <t>The Verifier looks up the enrollment by <tt>kid</tt>, verifies the JWS signature with the
        enrolled key, and only then reads the authentic claim set (including <tt>jti</tt>,
        <tt>psea_counter</tt>, <tt>psea_payload_hash</tt>, and <tt>ueid</tt>) directly from the verified
        payload. There are no pre-signature lookup copies of these values to reconcile.</t>

        <t>A compact JWS carries a single authoritative payload, so there is no envelope/body pair to
        reconcile and this profile defines no envelope-body mismatch error codes. A Verifier reads the
        signed claim set (<tt>jti</tt>, <tt>psea_counter</tt>, <tt>psea_payload_hash</tt>, <tt>ueid</tt>)
        directly from the verified payload.</t>
      </section>

      <section anchor="jose-hardening" numbered="true">
        <name>JOSE Header Hardening (Normative)</name>

        <t>Because a JWS protected header is attacker-influenceable until the signature is verified, a
        Verifier <bcp14>MUST</bcp14> apply the following constraints to every PSEA proof JWS before
        trusting any claim. A deployment that represents its Verifier acknowledgement / Attestation Result
        as a JWS — that artifact's format is out of scope of this profile
        (<xref target="action-binding-rp"/>) — <bcp14>SHOULD</bcp14> apply equivalent header hardening when
        the Attester consumes it:</t>

        <ul>
          <li>The Verifier <bcp14>MUST</bcp14> reject any JWS whose protected-header <tt>alg</tt> is not
          <tt>"ES256"</tt>. ES256 is the only mandatory-to-implement algorithm at this revision
          (<xref target="crypto-mti"/>); a Verifier that does not implement algorithm negotiation
          (<xref target="crypto-negotiation"/>) <bcp14>MUST NOT</bcp14> accept any other
          <tt>alg</tt> value.</li>
          <li>The Verifier <bcp14>MUST</bcp14> reject any JWS whose protected-header <tt>alg</tt> is
          <tt>"none"</tt>. An unsigned token <bcp14>MUST NOT</bcp14> be accepted under any
          circumstance.</li>
          <li>The Verifier <bcp14>MUST</bcp14> ignore any key material embedded in the JWS header,
          including the <tt>jwk</tt>, <tt>jku</tt>, and <tt>x5u</tt> header parameters. The verification
          key is resolved <strong>only</strong> from the enrolled record selected by the
          untrusted-until-verified <tt>kid</tt>; the Verifier <bcp14>MUST NOT</bcp14> verify a PSEA JWS
          against any key conveyed in or referenced by the token itself.</li>
          <li>The Verifier <bcp14>MUST</bcp14> verify the protected-header <tt>typ</tt> value. A PSEA
          proof carries <tt>typ</tt> = <tt>"psea-proof+jwt"</tt>, and a consumer expecting a proof
          <bcp14>MUST</bcp14> reject a JWS whose <tt>typ</tt> is not <tt>"psea-proof+jwt"</tt>. A deployment
          that defines a Verifier acknowledgement / Attestation Result as a JWS (out of scope of this
          profile) <bcp14>SHOULD</bcp14> give it a distinct <tt>typ</tt> — for example
          <tt>"psea-ack+jwt"</tt> — applying the explicit-typing guidance of <xref target="RFC8725"/>,
          Section 3.11, so that a proof cannot be processed as an acknowledgement or the reverse.</li>
          <li>The Verifier <bcp14>MUST</bcp14> reject any JWS that carries a <tt>crit</tt> header
          parameter naming an extension this profile does not define, and <bcp14>MUST</bcp14> reject any
          JWS that uses the unencoded-payload option (the <tt>b64</tt> header parameter set to
          <tt>false</tt>, <xref target="RFC7797"/>). A conforming PSEA JWS uses only the base64url-encoded
          payload form and defines no critical header extensions at this revision.</li>
        </ul>

        <t>These constraints close the classic JOSE downgrade and key-confusion attacks (algorithm
        substitution, <tt>alg</tt>:"none", attacker-supplied verification keys, cross-type confusion
        between proof and acknowledgement, and unencoded-payload / unknown-critical-header tricks) at the
        protocol's wire surface, and align with the JSON Web Token Best Current Practices
        (<xref target="RFC8725"/>); see also <xref target="threats-spoofing"/>.</t>
      </section>

      <section anchor="proof-schema" numbered="true">
        <name>JWS Payload Claim Set</name>

        <t>The following JSON Schema describes the claim set carried in the
        <tt>BASE64URL(payload)</tt> segment of the PSEA proof JWS. The claims combine JWT
        registered claims (<xref target="RFC7519"/>), a subset of EAT claims (<xref target="RFC9711"/>),
        and PSEA-private <tt>psea_*</tt> claims. <tt>eat_nonce</tt> and <tt>psea_user_hash</tt> are
        optional and, when not applicable, are omitted entirely rather than serialized as empty or
        <tt>null</tt>.</t>

        <t>The schema sets <tt>additionalProperties: false</tt>. This is deliberate: as stated in the
        "Extensibility model" entry of <xref target="eat-profile-definition"/>, this profile is a strict
        lock-step profile, so a Verifier rejects a proof carrying any claim outside the set below, and a
        future <tt>psea_*</tt> claim is by design rejected by current-revision Verifiers until they are
        upgraded. This departs from the ignore-unknown-claims extensibility customary for EAT and JWT.</t>

        <t>Two OPTIONAL members, <tt>psea_chain_pending</tt> and <tt>psea_last_confirmed_head</tt>, are
        registered in the schema below as opaque diagnostic extension members: they are permitted so that
        proofs carrying deployment-defined diagnostic context are not rejected by
        <tt>additionalProperties: false</tt>, but they carry no Verifier appraisal obligation, and a
        Verifier that does not recognize them ignores them.</t>

        <t>The OPTIONAL member <tt>psea_rp_context_hash</tt> is not a diagnostic member and is not
        ignorable in the same way. A deployment <bcp14>MAY</bcp14> bind a relying-party request context to
        a proof; where a deployment does so, this claim carries that binding and a Verifier in that
        deployment appraises it. A binding that is carried but never appraised confers nothing. Whether a
        deployment establishes such a context at all, and the content model of the context, are
        deployment-defined and out of scope of this profile, which requires no deployment to establish
        one.</t>

        <t>The OPTIONAL member <tt>psea_signals_hash</tt> commits the proof to an auxiliary document
        carried beside it in the transport body (<xref target="envelope-schemas"/>) rather than inside the
        signed payload. It relates to that document as <tt>psea_payload_hash</tt> relates to
        <tt>actionPayload</tt>: the digest is signed, the document is not, and the document is bound only
        to the extent that a Verifier recomputes the digest over it. The document's content model is
        deployment-defined and out of scope. A deployment <bcp14>MAY</bcp14> require
        <tt>psea_signals_hash</tt> on every proof, and a Verifier in such a deployment
        <bcp14>MUST</bcp14> refuse a proof that omits it: without the claim the auxiliary document is
        unsigned and can be stripped, substituted, or replayed independently of the proof.</t>

        <t>The OPTIONAL member <tt>psea_chain_prev</tt> is carried only where the deployment enables the
        chain layer (<xref target="chain-entry"/>). A deployment that enables it <bcp14>MAY</bcp14>
        require the claim on every proof, and a Verifier in such a deployment <bcp14>MUST</bcp14> refuse
        a proof that omits it: a proof without the claim bypasses the linkage check, so it could be
        inserted into the stored proof sequence without the gap being detected.</t>

        <t>The OPTIONAL member <tt>psea_stepup_selection</tt> is carried only where the deployment,
        before the signature, asked the user a step-up question about the action and holds a commitment
        to the expected answer. The claim places a commitment to the user's selection inside the signed
        payload, so the selection is covered by the proof signature. How the question is formed, how the
        commitment is constructed, and how the Verifier obtains the value it compares against are
        deployment-defined and out of scope of this profile. A deployment that issued such a step-up for
        an action <bcp14>MAY</bcp14> require the claim on the proof for that action, and a Verifier in
        such a deployment <bcp14>MUST</bcp14> refuse a proof that omits it or whose value does not
        match.</t>

        <t>The <tt>eat_profile</tt> claim carries the profile identifier of
        <xref target="eat-profile"/>, and a conforming Attester emits only that value. Notwithstanding
        the enumeration in the schema below, a Verifier <bcp14>MAY</bcp14> additionally accept the value
        <tt>urn:ietf:params:psea:eat-profile:2</tt>, which implementations built before this revision
        emit, provided every other requirement of this profile is met. That value is not a registered
        URN: this document requests no registration in the <tt>urn:ietf:params</tt> namespace, and the
        value is accepted only as an identifier emitted by earlier implementations, never as one a new
        Attester emits.</t>

        <sourcecode type="json"><![CDATA[
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "PseaProofClaims",
  "description": "The JWS payload claim set for the PSEA proof.",
  "type": "object",
  "required": [
    "jti", "aud", "iss", "iat", "exp", "ueid", "eat_profile",
    "psea_tier", "psea_op", "psea_counter", "psea_payload_hash",
    "psea_uv", "psea_proof_version"
  ],
  "properties": {
    "jti":  { "type": "string", "minLength": 1, "maxLength": 128,
              "pattern": "^[A-Za-z0-9._-]+$",
              "description": "Action id; also the global-uniqueness
                key. Alphanumeric/dot/underscore/hyphen only. UUID
                RECOMMENDED." },
    "aud":  { "type": "string", "minLength": 1, "maxLength": 256,
              "description": "JWT audience (RFC 7519): identifier of
                the intended Verifier/audience. See cross-replay
                binding." },
    "iss":  { "type": "string", "minLength": 1, "maxLength": 128,
              "description": "Tenant / deployment identifier;
                verified against the deployment/tenant the Verifier
                resolves for the request. See cross-replay
                binding." },
    "iat":  { "type": "integer", "minimum": 0,
              "description": "NumericDate; epoch SECONDS." },
    "exp":  { "type": "integer", "minimum": 0,
              "description": "NumericDate; epoch SECONDS." },
    "ueid": { "type": "string", "pattern": "^[A-Za-z0-9_-]{44}$",
              "description": "RFC 9711 Sec 4.2.1.1 RAND-type UEID:
                base64url (no padding) of the 33-byte string 0x01 ||
                SHA-256(JCS({\"deviceId\": <deviceId>, \"iss\":
                <iss>})). JCS is the RFC 8785 canonical form of that
                two-member object; the inputs are encoded, not
                concatenated, so the pair cannot be read two ways.
                Per-issuer (pairwise): the same device yields a
                distinct ueid per iss, preventing cross-deployment
                correlation. The leading 0x01 is the RAND type tag.
                See Privacy Considerations." },
    "eat_nonce": { "type": "string",
              "description": "OPTIONAL. Server nonce for
                push-initiated freshness. Omitted entirely when
                absent." },
    "submods": { "type": "object",
              "properties": {
                "psea-device-state": { "type": "object",
                  "description": "Reserved placeholder extension
                    point; contributes nothing wire-normative in this
                    revision (contents out of scope)." }
              },
              "description": "OPTIONAL EAT submodules map. The
                psea-device-state submodule is a reserved,
                deployment-specific placeholder; an authenticator
                that cannot produce device-state omits submods
                entirely." },
    "eat_profile": { "type": "string",
      "enum": [
        "https://datatracker.ietf.org/doc/draft-yossif-psea/"
      ],
              "description": "EAT profile identifier (RFC 9711 Sec
                4.3.2). REQUIRED. This document fixes exactly one
                value for this claim. The claim carries a URI or an
                OID and RFC 9711 defines no registry for profile
                identifiers, so no registration is requested for this
                value." },
    "psea_tier": { "type": "string", "minLength": 1,
              "maxLength": 128,
              "description": "Capability/assurance-level binding.
                Opaque to this profile; deployment-defined value
                space agreed out of band. The Verifier rejects the
                proof if it does not match the level expected for the
                operation. See cross-replay binding." },
    "psea_op": { "type": "string", "minLength": 1, "maxLength": 128,
              "description": "Operation / authority-context
                discriminator the proof is bound to. Opaque to this
                profile; the Verifier rejects the proof if it does
                not match the operation being performed. See
                cross-replay binding." },
    "psea_counter": { "type": "integer", "minimum": 0,
                      "maximum": 9007199254740991,
              "description": "Monotonic per (attester, counter
                scope). JSON integer in [0, 2^53-1]; see counter
                model." },
    "psea_payload_hash": { "type": "string",
              "pattern": "^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$",
              "description": "Standard base64 (with padding) of
                SHA-256(canonical actionPayload); the action binding.
                The restricted final-sextet class rejects
                non-canonical encodings (the digest's trailing two
                bits are always zero). See action binding." },
    "psea_chain_prev": { "type": "string",
              "pattern": "^[0-9a-f]{64}$",
              "description": "OPTIONAL deployment-optional hash-chain
                anchor. 64-char lowercase hex; sentinel 64 ASCII '0'
                for the first proof. Present only when the deployment
                enables the chain tamper-evidence layer. See
                ChainEntry." },
    "psea_uv": { "type": "object",
              "required": ["verified", "method"],
              "properties": {
                "verified": { "type": "boolean",
                  "description": "Whether a contemporaneous user-
                    verification event gated this signature." },
                "method": { "type": "string",
                  "description": "User-verification method.
                    Extensible string; suggested baseline values
                    include \"biometric\", \"pin\", \"fido_uv\" (no
                    IANA registry is defined). Unknown values MUST be
                    tolerated." }
              },
              "description": "User-verification claim. REQUIRED on
                the PSEA proof. The Verifier MUST require
                verified==true and MUST cross-check it against the
                authenticator's attested UV-enforcement where the
                attestation conveys it. See the user-verification
                claim section." },
    "psea_proof_version": { "type": "string", "enum": ["1"] },
    "psea_caller_package": { "type": "string", "minLength": 1,
              "maxLength": 256,
              "description": "OPTIONAL profile-extension claim.
                Reverse-DNS identifier of the calling
                application." },
    "psea_sdk_version": { "type": "string", "maxLength": 64,
      "description": "OPTIONAL profile-extension claim. Attester
        implementation version." },
    "psea_signals_hash": { "type": "string",
      "pattern": "^[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$",
      "description": "OPTIONAL profile-extension claim. Standard
        base64 (with padding) of SHA-256 over the canonical form of
        an auxiliary document carried beside the proof in the
        transport body rather than inside the signed payload. Its
        presence commits the proof to that document, so the document
        cannot be substituted, stripped, or replayed independently of
        the signature. Same canonicalization and encoding as
        psea_payload_hash; the restricted final-sextet class rejects
        non-canonical encodings. The document's content model is
        deployment-defined and out of scope." },
    "psea_user_hash": { "type": "string",
              "pattern": "^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
      "description": "OPTIONAL. base64url (no padding) of
        SHA-256(subject_id): a pairwise, deployment-local commitment
        to the enrolled subject that establishes no identity, which a
        Verifier MAY require to match the enrollment record (see
        Subject Hash). The restricted final-sextet class rejects
        non-canonical encodings. Omitted entirely when empty. MUST be
        derived pairwise per issuer (see Privacy Considerations)." },
    "psea_chain_pending": {
      "description": "OPTIONAL registered opaque profile-extension
        member; empty schema (any JSON value). Semantics are
        deployment-defined and out of scope; the member carries no
        Verifier appraisal obligation, and a Verifier that does not
        recognize it ignores it. Present only when the deployment's
        chain gap-tolerance layer emits it." },
    "psea_last_confirmed_head": {
      "description": "OPTIONAL registered opaque profile-extension
        member; empty schema (any JSON value). Semantics are
        deployment-defined and out of scope; the member carries no
        Verifier appraisal obligation, and a Verifier that does not
        recognize it ignores it. Present only when the deployment's
        chain gap-tolerance layer emits it." },
    "psea_stepup_selection": { "type": "string",
      "pattern": "^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
      "description": "OPTIONAL profile-extension claim. base64url
        (no padding) of a 32-octet commitment to a selection the
        user made in a deployment-defined step-up interaction about
        the action. Present only when the deployment issued such a
        step-up for this action. The commitment's construction and
        the value a Verifier compares it against are
        deployment-defined and out of scope." },
    "psea_rp_context_hash": {
      "description": "OPTIONAL profile-extension member; empty schema
        (any JSON value). Present only when the deployment binds a
        relying-party request context. Where a deployment establishes
        such a context, this member carries that binding and a
        Verifier in that deployment appraises it rather than ignoring
        it. The content model of the context is deployment-defined
        and out of scope." }
  },
  "additionalProperties": false
}
]]></sourcecode>
      </section>

      <section anchor="envelope-schemas" numbered="true">
        <name>Wire Transport Bodies</name>

        <t>A PSEA proof is submitted to the Verifier in a transport
        body of the form
        <tt>{ proof, actionPayload, integrityEvidence?, requestId?, signalReport?, proofId? }</tt>, where
        <tt>proof</tt> is the compact JWS (<xref target="proof-top-level"/>), <tt>actionPayload</tt> is the
        unsigned cleartext action bound by <tt>psea_payload_hash</tt> (<xref target="action-binding"/>), and
        <tt>integrityEvidence</tt> is an OPTIONAL attestation evidence block whose contents are out of scope
        of this profile. The concrete transport (HTTP method and path) is deployment-specific and out of
        scope.</t>

        <t>Of the transport-body fields, only <tt>proof</tt> is signed, and <tt>actionPayload</tt> is
        cryptographically bound to it through <tt>psea_payload_hash</tt> (<xref target="action-binding"/>).
        The remaining fields — <tt>integrityEvidence</tt>, <tt>requestId</tt>, <tt>signalReport</tt>, and
        <tt>proofId</tt> — are unsigned and attacker-mutable in transit. A Verifier <bcp14>MUST NOT</bcp14>
        use any unsigned transport-body field as an input to a security decision. <tt>integrityEvidence</tt>
        is appraised on its own cryptographic merits against the platform Endorsement (its appraisal does
        not trust the envelope framing); <tt>requestId</tt> and <tt>proofId</tt> are correlation and
        operational hints only and confer no authority.</t>

        <t><tt>signalReport</tt> is not unconditionally in that last category. Where the proof carries
        <tt>psea_signals_hash</tt> (<xref target="proof-schema"/>), that claim commits to the report, and a
        Verifier in such a deployment appraises the received report against the claim by recomputing the
        digest over it — the same relation <tt>actionPayload</tt> bears to <tt>psea_payload_hash</tt>. A
        report appraised in that way is a committed field rather than an unsigned one, so the rule above is
        not engaged by it. Where the claim is absent, or where no such comparison has been made,
        <tt>signalReport</tt> is an unsigned hint conferring no authority and the rule above applies to it
        without qualification.</t>
      </section>

      <section anchor="presence-via-signature" numbered="true">
        <name>Human Presence via User-Verification-Gated Signature</name>

        <t>This profile establishes the human-presence requirement through the
        signing operation itself rather than through a separate wire-level presence block. The Attester's
        hardware-protected signing key <bcp14>MUST</bcp14> be gated by a platform user-verification event such
        that the signing operation cannot complete without a successful, contemporaneous user-verification
        ceremony. The signed proof body is therefore implicit Evidence that a user-verification event
        occurred at the moment of signing; the signature itself is the presence
        commitment.</t>

        <t><strong>Per-operation gating (normative).</strong> The user-verification gate
        <bcp14>MUST</bcp14> be enforced <em>per signing operation</em>: each proof signature
        <bcp14>MUST</bcp14> be preceded by its own fresh user-verification event. A configuration in which
        a single user-verification event authorizes signing for a time window or for multiple subsequent
        signatures (duration- or window-based authentication) is <bcp14>NOT RECOMMENDED</bcp14> for any
        signing and is non-conformant for proof signing: it would let a compromised host pre-compute a
        batch of proofs from one ceremony (see <xref target="threats-pre-mint"/>). A conforming Attester
        binds exactly one user-verification event to exactly one proof signature.</t>

        <t>This rules out "authenticate-once-then-reuse" key configurations. By way of example, the
        following common platform configurations are non-conformant for proof signing, and only their
        per-operation counterparts conform: a PIV-class smartcard in PIN-once mode (only a PIN-always /
        per-signature PIN policy conforms); an Android Keystore key with a non-zero user-authentication
        validity timeout (only a timeout of 0 — authentication required for every use — conforms); and an
        iOS authentication context reused across multiple signing operations (a fresh, non-reused
        authentication context per signature is required). The specific platform mechanism is out of
        scope; the normative requirement is the one-verification-per-signature binding above.</t>

        <t>The user-verification mechanisms that gate the signing key — for example, a platform biometric
        subsystem (fingerprint or face match), a Secure Enclave PIN entry, a hardware-token PIN, a
        smartcard with PIN, a magnetic-stripe credential combined with a PIN, or any equivalent platform
        user-verification primitive — are out of scope of this specification. The specific per-authenticator
        mechanism is mechanism-agnostic; what is normative is that the signing operation cannot complete
        without a contemporaneous user-verification event and that the proof carries an explicit
        <tt>psea_uv</tt> claim asserting that fact (<xref target="presence-uv-claim"/>).</t>

        <t>The Verifier <bcp14>MUST NOT</bcp14> infer presence silently from the bare existence of a
        signature from the hardware-protected signing key. For the PSEA proof it <bcp14>MUST</bcp14> require the explicit
        <tt>psea_uv</tt> claim and, where the authenticator's attestation conveys a UV-enforcement
        property, cross-check the claim against that attested property (appraised through the
        authenticator attestation and the deployment's Reference Values).
        The detailed normative rule, and the statement of where this property is attested versus where it
        rests on a documented trust assumption, are given in <xref target="presence-uv-claim"/>.</t>

        <t>This design uses a single, compact user-verification claim rather than a parallel wire-level
        presence block whose other fields would duplicate information already carried by the attestation
        evidence (in <tt>integrityEvidence</tt>) for platforms that bundle user-verification into the
        attestation chain. The
        cryptographic anchor for "a human was present at the moment of signing" is the <tt>psea_uv</tt>
        claim covered by the proof signature plus the hardware-protected-key attestation against which it is
        cross-checked.</t>

        <section anchor="presence-uv-claim" numbered="true">
          <name>User-Verification Claim (psea_uv)</name>

          <t>Every PSEA proof <bcp14>MUST</bcp14> carry a signed <tt>psea_uv</tt> claim:</t>
          <ul>
            <li><tt>psea_uv.verified</tt> — a boolean asserting that a contemporaneous user-verification
            event gated the signing operation that produced this proof.</li>
            <li><tt>psea_uv.method</tt> — a string identifying the user-verification method. The value
            space is extensible; suggested baseline values are <tt>"biometric"</tt>,
            <tt>"pin"</tt>, and <tt>"fido_uv"</tt> (this document defines no IANA registry for them). A
            Verifier <bcp14>MUST</bcp14> tolerate an unknown method value (treat it as an opaque label)
            rather than rejecting solely on the method string.</li>
          </ul>
          <t>A token that makes no human-presence claim (such tokens are out of scope of this profile)
          does not carry <tt>psea_uv</tt>. The claim is part of the JWS payload and is
          therefore covered by the proof's ES256 signature, so it is tamper-evident: an intermediary
          cannot flip <tt>verified</tt> from false to true without invalidating the signature.</t>

          <t>Normative Verifier rule: the Verifier <bcp14>MUST</bcp14> require
          <tt>psea_uv.verified == true</tt> and <bcp14>MUST</bcp14> reject the proof otherwise. Beyond
          that, anchoring of the claim is conditional on what the authenticator's attestation conveys:</t>
          <ul>
            <li>Where the platform attestation conveys a UV-enforcement property, the Verifier
            <bcp14>MUST</bcp14> cross-check <tt>psea_uv</tt> against it and <bcp14>MUST</bcp14> reject the
            proof if the attested key properties contradict the claim (for example, the claim asserts
            <tt>verified == true</tt> but the attested signing key is not user-authentication-gated). The
            Verifier <bcp14>MUST NOT</bcp14> accept such a <tt>psea_uv</tt> as merely self-asserted when it
            could have been cross-checked against the attestation.</li>
            <li>Where the attestation does not convey a UV-enforcement property, the Verifier
            <bcp14>MUST NOT</bcp14> treat the human-presence assurance as attested — the claim is signed
            but rests on a documented trust assumption (see below) — and for high-assurance operations
            <bcp14>SHOULD</bcp14> reject the proof or require an additional independent factor.</li>
          </ul>
          <t>This rule is device-agnostic: any conforming Attester emits <tt>psea_uv</tt>, and the
          Verifier anchors it wherever the authenticator's attestation permits.</t>

          <t>Examples of the per-authenticator cross-check (informative, NOT normative — the specific
          mechanism is out of scope): a phone with an attested UV-gated hardware key whose key
          attestation conveys an auth-enforced / user-authentication-required property; a PIN-gated
          smartcard whose attested key properties indicate a PIN-verified signing operation. In each case
          the conforming Attester emits <tt>psea_uv</tt> and the Verifier cross-checks it against that
          authenticator's own attested UV-enforcement. The FIDO/WebAuthn user-verified (UV) flag carried
          in signed authenticator data is the same idea in a different envelope, but a FIDO authenticator
          does not itself emit a PSEA proof (<xref target="introduction"/>); consuming a FIDO assertion's
          UV signal would require the separate, out-of-scope FIDO-assertion binding.</t>

          <t>Verification depth (known limitation): where the
          authenticator's platform attestation conveys UV-enforcement (for example, a hardware-key
          attestation carrying an auth-enforced field), the Verifier cross-checks <tt>psea_uv</tt>
          against it and the user-verification property is <strong>attested</strong>. Where the platform
          attestation cannot convey UV-enforcement, conformance rests on a <strong>documented
          trust assumption</strong> in the authenticator's user-verification enforcement; in that case
          the <tt>psea_uv</tt> claim is required and signed, but it is not independently attested.
          Authenticator-signed user-verification evidence — for example, a FIDO-style UV flag carried in
          signed authenticator data — is the path to full verifiability and is the RECOMMENDED direction
          for authenticators that can produce it. This document does not claim the property is fully
          solved on every platform; it normatively requires the claim and the cross-check, and documents
          the residual trust assumption where the attestation surface does not yet permit the
          cross-check.</t>

          <t>The rationale for the unattested case above is that such a <tt>psea_uv</tt> is advisory
          only — a signed boolean the host populated, with no independent cryptographic backing for the
          user-verification event — so it cannot be relied upon as an attested human-presence assurance
          for high-assurance operations.</t>
        </section>
      </section>

      <section anchor="user-id-hash" numbered="true">
        <name>Subject Hash (psea_user_hash)</name>

        <t>The OPTIONAL <tt>psea_user_hash</tt> claim (<xref target="proof-schema"/>) is an Attester-signed
        subject-binding commitment: the SHA-256 of an opaque, deployment-issued subject identifier,
        encoded as base64url without padding. The claim cryptographically attributes the action to a
        subject identifier for the audit trail. It is a pairwise, deployment-local commitment to the
        enrolled subject: it does not establish who the subject is, and a Verifier <bcp14>MUST NOT</bcp14>
        treat it as proof of the identity of any person.</t>

        <t>When present, <tt>psea_user_hash</tt> <bcp14>MUST</bcp14> be derived so that it does not
        function as a cross-deployment correlator, mirroring the pairwise <tt>ueid</tt> derivation
        (<xref target="privacy-ueid"/>): the subject identifier <bcp14>MUST</bcp14> be bound to the
        deployment before hashing — for example by salting the hash input with a per-issuer
        (<tt>iss</tt>) secret not published with the proofs, or by deriving a per-issuer subject
        identifier. A deployment <bcp14>MUST NOT</bcp14> emit a bare SHA-256 of a globally stable subject
        identifier (see <xref target="privacy-hashing"/>).</t>

        <t>A Verifier <bcp14>MAY</bcp14> require that the per-proof <tt>psea_user_hash</tt> match the
        value recorded for the subject at enrollment. A Verifier that does so <bcp14>MUST</bcp14> refuse a
        proof whose <tt>psea_user_hash</tt> is absent or does not byte-exactly equal the recorded value.
        Such a check establishes only that the proof was produced for the same deployment-issued subject
        identifier the enrollment recorded; it does not establish which natural person that identifier
        denotes. The cryptographic chain of authorization at the Verifier continues to rest on the
        device-bound signature, the monotonic counter, and the presence Evidence
        (<xref target="presence-via-signature"/>); the subject-hash comparison is a consistency check
        against the enrollment record, not a substitute for any of them.</t>

        <t>The per-proof <tt>psea_user_hash</tt> is distinct from the enrollment-bound subject binding a
        deployment records at enrollment time. The binding between the recorded value and a natural
        person is established at enrollment, by the deployment's enrollment ceremony and identity
        proofing, and is out of scope of this profile (<xref target="enrollment-root-of-trust"/>).</t>
      </section>

      <section anchor="device-state" numbered="true">
        <name>Device-State Submodule (submods.psea-device-state)</name>

        <t>The OPTIONAL <tt>psea-device-state</tt> submodule of the EAT <tt>submods</tt> map
        (<xref target="proof-schema"/>) carries an opaque device-posture commitment from the Attester.
        Its contents and the Verifier's appraisal logic are out of scope of this document. An
        authenticator that cannot produce device-state omits <tt>submods</tt> entirely.</t>

        <t>On the wire, the <tt>psea-device-state</tt> submodule is a JSON object whose internal
        structure is not constrained by this specification. The Verifier appraises the commitment as part
        of the deployment's policy evaluation; the wire surface visible to the Attester is binary -- the
        Verifier either accepts the commitment (the proof proceeds through subsequent appraisal steps)
        or rejects the proof through the deployment's policy verdict. The
        appraisal mechanism the Verifier applies, the categorical properties the commitment may carry,
        and any operator-side controls over appraisal sensitivity are deployment-specific and outside
        the scope of this specification.</t>

        <t>Because the submodule travels inside the signed JWS payload, its bytes are covered by the proof
        signature as written; a future revision of this document <bcp14>MAY</bcp14> define a normative
        sealed commitment form for the submodule, but until such a revision is published, conforming
        implementations treat <tt>submods.psea-device-state</tt> as an opaque deployment-specific object
        whose contents are out of scope.</t>
      </section>

      <section anchor="counter-model" numbered="true">
        <name>Counter Model (Monotonic Replay Ordering)</name>

        <t>The signed <tt>psea_counter</tt> claim is a monotonic replay-ordering anchor. The Verifier
        compares the submitted value against the last-accepted counter value it holds for the same
        Attester and the same counter scope (see below); a value that is not strictly greater than the
        stored value for that scope <bcp14>MUST</bcp14> cause the Verifier to reject the proof.</t>

        <t>An Attester <bcp14>MAY</bcp14> maintain a single counter or several independent counter scopes,
        and emits in <tt>psea_counter</tt> the value of the scope the proof is produced under. The number
        of scopes, and the mapping of an application's actions to scopes, is deployment-specific and out of
        scope of this profile. Independent scopes serve only to preserve independent offline progress: if
        proofs produced offline under different scopes drew every value from one shared sequence, an
        offline-signed proof would collide on submission with a proof signed under another scope after the
        first was composed but before it was synced. Independent scopes let each progress without
        colliding.</t>

        <t><strong>Scope selection (normative).</strong> When a deployment uses more than one counter
        scope, the value that identifies the scope a proof was produced under <bcp14>MUST</bcp14> be
        cryptographically bound to that proof — either carried as a signed claim (for example
        <tt>psea_tier</tt>) or carried in the action payload and therefore covered by the signed
        <tt>psea_payload_hash</tt> (<xref target="action-binding"/>). A Verifier <bcp14>MUST</bcp14> select
        the stored counter value to compare against using only such a bound scope identifier, and
        <bcp14>MUST NOT</bcp14> derive the scope from any field that is not covered by the signature or by
        the action-binding hash. This closes the otherwise-exploitable case in which an attacker steers
        the comparison toward a stale bucket by altering an unbound scope selector. Because the
        action-binding check (<xref target="action-binding-verifier"/>) <bcp14>MUST</bcp14> run and
        fail-closed before the counter comparison is reached, a scope identifier conveyed in the action
        payload is fully bound at the point the Verifier selects the bucket. A deployment that maintains a
        single counter has no scope to select and emits one monotonic sequence.</t>

        <t>The cross-scope replay surface that a single counter <em>would</em> defend against (a captured
        proof submitted under a different scope) is closed instead by a global action-identifier
        uniqueness check the Verifier <bcp14>MUST</bcp14> maintain (each <tt>jti</tt> action identifier
        finalizes exactly once) plus the cross-replay binding of <xref target="cross-replay-binding"/>
        (the signed <tt>psea_tier</tt> + <tt>psea_op</tt> bindings force operation-correctness independent
        of counter scope). The Verifier <bcp14>MUST</bcp14> retain each finalized <tt>jti</tt> for at least
        as long as a proof bearing it could still be within its <tt>exp</tt> validity window (that is, at
        least until the maximum <tt>exp</tt> the Verifier will accept has passed); a <tt>jti</tt>
        <bcp14>MAY</bcp14> be evicted once no proof carrying it could still pass the <tt>exp</tt> freshness
        check. A Verifier that retains finalized <tt>jti</tt> values for less than the <tt>exp</tt> window
        reopens the replay surface this check exists to close.</t>

        <t><strong>Atomicity (normative).</strong> A Verifier <bcp14>MUST</bcp14> perform the
        counter comparison-and-advance and the <tt>jti</tt> finalization atomically — serialized per
        (Attester, counter scope) — within a single transaction: read the stored counter, compare, and
        conditionally advance in one atomic step that also records the <tt>jti</tt>. The counter value
        compared <bcp14>MUST</bcp14> be the value read inside that transaction (not a value read earlier),
        and the stored value <bcp14>MUST</bcp14> be advanced only when the submitted counter is strictly
        greater than it. A blind last-write-wins advance is non-conformant. This guarantees that two
        concurrent or out-of-order submissions cannot both commit and cannot lower (regress) the stored
        high-water mark: of any set of submissions racing at a given scope, at most one advances, and a
        later-arriving lower counter is rejected rather than overwriting a higher stored value.</t>

        <t><strong>Interoperability bound (normative):</strong> Producers <bcp14>MUST</bcp14> emit
        <tt>psea_counter</tt> as a JSON integer in the range [0, 2<sup>53</sup> &#8722; 1] (inclusive) so
        that the value round-trips losslessly through IEEE 754 double-precision JSON parsers.  Verifiers
        <bcp14>MUST</bcp14> treat the claim as a 64-bit unsigned integer for comparison purposes.  This
        profile does not string-encode the counter; a string-valued <tt>psea_counter</tt> is
        non-conformant.  At any reasonable operational rate a producer never approaches the
        2<sup>53</sup> bound, but producers <bcp14>MUST</bcp14> enforce this bound explicitly.</t>
      </section>

      <section anchor="freshness" numbered="true">
        <name>Freshness and Expiry</name>

        <t>The signed <tt>iat</tt> and <tt>exp</tt> claims (<xref target="RFC7519"/>) bound the time
        window in which a proof is acceptable. Both are NumericDate values in epoch seconds and are
        REQUIRED (<xref target="proof-schema"/>). A producer <bcp14>MUST</bcp14> set <tt>iat</tt> to the
        time of signing and <tt>exp</tt> to a value no later than <tt>iat</tt> plus the deployment's
        maximum proof lifetime.</t>

        <t>A Verifier <bcp14>MUST</bcp14> reject a proof whose <tt>exp</tt> is at or before the current
        time, and <bcp14>MUST</bcp14> reject a proof whose <tt>iat</tt> is implausibly in the future. The
        Verifier <bcp14>MAY</bcp14> allow a small clock-skew tolerance when applying these checks;
        the tolerance <bcp14>SHOULD NOT</bcp14> exceed 60 seconds. The Verifier <bcp14>SHOULD</bcp14>
        additionally enforce a bounded maximum acceptable proof lifetime (the interval between
        <tt>iat</tt> and <tt>exp</tt>); a RECOMMENDED bound is given in <xref target="operational-defaults"/>.
        These checks <bcp14>MUST</bcp14> be applied after signature verification and before any state
        mutation.</t>

        <t>The <tt>iat</tt>/<tt>exp</tt> window complements, but does not replace, the replay defenses of
        <xref target="counter-model"/>: the monotonic <tt>psea_counter</tt> provides ordering, the global
        <tt>jti</tt> uniqueness check ensures each action finalizes exactly once, and the OPTIONAL
        <tt>eat_nonce</tt> (when the Verifier issued a challenge) binds the proof to that challenge. A
        deployment that produces proofs offline for deferred submission <bcp14>MUST</bcp14> choose an
        <tt>exp</tt> margin wide enough to cover its expected offline-to-sync interval; the freshness
        anchor for such proofs is then the counter and the <tt>jti</tt> uniqueness check rather than a
        narrow <tt>exp</tt> window.</t>

        <t>When a Verifier issued a challenge, it <bcp14>MUST</bcp14> correlate the returned proof to the
        issued challenge using only the signed <tt>eat_nonce</tt> claim value read from the verified
        payload — for example, by looking up its outstanding-challenge state keyed on the nonce value
        itself. A Verifier <bcp14>MUST NOT</bcp14> rely on any unsigned transport-body field (for example
        <tt>requestId</tt>, <xref target="envelope-schemas"/>) to decide which issued challenge a proof
        answers, because an unsigned field is attacker-mutable in transit and a mismatched correlation
        could let a proof carrying one (signed) nonce be accepted against a different outstanding
        challenge. The <tt>eat_nonce</tt> value is covered by the proof signature, so keying the
        correlation on it keeps the challenge-to-proof binding within the signed surface.</t>
      </section>

      <section anchor="chain-entry" numbered="true">
        <name>ChainEntry (Deployment-Optional Tamper-Evidence Layer)</name>

        <t>The <tt>chainEntry</tt> is a deterministic hash-chain anchor that links each
        proof to the prior proof from the same Attester. It provides a per-Attester tamper-evident ledger
        of accepted proofs <strong>in addition to</strong> the monotonic ordering already provided by
        <tt>psea_counter</tt> (<xref target="counter-model"/>); its value is detecting after-the-fact
        alteration or omission within a stored proof sequence, not freshness, which the counter and the
        action-binding already supply.</t>

        <t>The chain is a <strong>deployment-optional</strong> layer. A deployment <bcp14>MAY</bcp14>
        enable it; when enabled, the Attester carries the prior proof's chainEntry inbound as the
        <tt>psea_chain_prev</tt> claim and the Verifier behaves as specified in
        <xref target="chain-verifier-behavior"/>. A deployment that does not enable the chain
        <bcp14>MUST</bcp14> omit <tt>psea_chain_prev</tt> entirely (the claim is OPTIONAL in
        <xref target="proof-schema"/>), and a Verifier that does not implement the chain ignores it.</t>

        <t>Completing the chain loop requires the Verifier to convey each recomputed <tt>chainEntry</tt>
        back to the Attester so it can serve as the next proof's <tt>psea_chain_prev</tt>. That conveyance
        rides on the Verifier acknowledgement / Attestation Result, whose format is deployment-specific
        and out of scope of this profile. Two independent implementations therefore interoperate on the
        chain only after they additionally agree on that out-of-scope acknowledgement format; the chain is
        consequently specified here as an optional deployment feature rather than a mandatory wire
        element, and a self-contained conforming Verifier (<xref target="conformance-verifier"/>) is not
        required to implement it.</t>

        <section anchor="chain-formula" numbered="true">
          <name>Formula (Length-Prefixed Concatenation)</name>
          <t>For each PSEA proof, the chainEntry is computed over a length-prefixed
          concatenation of the three input fields, where <tt>actionId</tt> is the <tt>jti</tt> claim,
          <tt>counter</tt> is <tt>psea_counter</tt>, and <tt>chainPrev</tt> is <tt>psea_chain_prev</tt>.
          Length-prefixing guarantees domain separation:
          for any two distinct tuples <tt>(actionId, counter, chainPrev)</tt> the encoded inputs are
          byte-distinct regardless of whether any field contains a <tt>:</tt> character, NUL
          bytes, or any other character. This is the standard cryptographic pattern for
          unambiguous hash-input encoding.</t>

          <artwork><![CDATA[
encode(s)  = u32_be(len(utf8(s))) || utf8(s)

input      = encode(actionId)
          || encode(decimal(counter))
          || encode(chainPrev)

chainEntry = lowercase_hex( SHA-256( input ) )
]]></artwork>

          <t>where <tt>u32_be(n)</tt> is the 4-byte big-endian unsigned-32-bit encoding of the integer
          <tt>n</tt>, <tt>utf8(s)</tt> is the UTF-8 byte sequence of the string <tt>s</tt>,
          <tt>decimal(counter)</tt> is the digit-only decimal representation of <tt>counter</tt> per
          <xref target="canonical-encoding-numbers"/> (the byte length of any field encoded by
          <tt>encode</tt> in this revision fits comfortably in u32), and the output is the lowercase-hex
          representation of the SHA-256 digest <xref target="FIPS180-4"/>.</t>

          <t>Byte layout (worked example) for
          <tt>actionId="550e8400-e29b-41d4-a716-446655440000"</tt>, <tt>counter=42</tt>,
          <tt>chainPrev = 64 '0' characters</tt>:</t>

          <artwork><![CDATA[
Offset  Bytes                                Description
─────────────────────────────────────────────────────────────────────
[00–03] 00 00 00 24                          u32_be(36) = len(actionId)
[04–39] 35 35 30 65 38 34 30 30 ...          actionId UTF-8 (36 bytes)
[40–43] 00 00 00 02                          u32_be(2)  = len("42")
[44–45] 34 32                                "42" UTF-8 (2 bytes)
[46–49] 00 00 00 40                          u32_be(64) = len(chainPrev)
[50–113] 30 30 30 30 ...                     chainPrev UTF-8 (64 bytes)
─────────────────────────────────────────────────────────────────────
Total: 114 bytes → SHA-256 → 64 hex chars
]]></artwork>
        </section>

        <section anchor="chain-sentinel" numbered="true">
          <name>Sentinel Value</name>
          <t>For the first proof from an Attester since enrollment (i.e., when the Attester has no prior
          chain entry stored locally), the Attester <bcp14>MUST</bcp14> use the sentinel value 64 ASCII
          '0' characters (<tt>"0000000000000000000000000000000000000000000000000000000000000000"</tt>) as
          <tt>psea_chain_prev</tt>. The Verifier <bcp14>MUST</bcp14> accept this sentinel value when its
          stored prior chain entry for the Attester is empty.</t>
        </section>

        <section anchor="chain-verifier-behavior" numbered="true">
          <name>Verifier Behavior on Mismatch</name>
          <t>A Verifier that has enabled the chain layer performs a strict-equality linkage check on every
          proof that carries <tt>psea_chain_prev</tt>: if the inbound signed
          <tt>psea_chain_prev</tt> does not equal the chainEntry of the last proof the Verifier accepted
          for the Attester — or the sentinel of <xref target="chain-sentinel"/> when the Verifier holds no
          prior chain entry for the Attester — the Verifier <bcp14>MUST</bcp14> reject the proof. A
          Verifier <bcp14>MAY</bcp14>, as a local operational policy out of scope of this profile,
          additionally accept a bounded class of out-of-order or backfilled proofs; any such tolerance
          <bcp14>MUST</bcp14> preserve the tamper-evidence of the chain by accepting only chainEntry values
          the Verifier has itself previously computed and accepted for that Attester. A Verifier that has
          not enabled the chain layer ignores <tt>psea_chain_prev</tt> if present and performs no linkage
          check; the proof's freshness and replay resistance then rest on <tt>psea_counter</tt>
          (<xref target="counter-model"/>), the <tt>jti</tt> uniqueness check, and the action binding.</t>
        </section>
      </section>

      <section anchor="action-binding" numbered="true">
        <name>Action Binding (Fail-Closed by Default)</name>

        <t>PSEA's execution-authority guarantee requires that an approved action be cryptographically bound
        to a specific action payload. Without this binding, a captured proof from one execution can be
        replayed against a different payload at the Verifier.</t>

        <section anchor="action-binding-producer" numbered="true">
          <name>Producer Obligations</name>
          <t>A PSEA proof producer (a conforming Attester) <bcp14>MUST</bcp14> compute the action payload
          hash over the canonical encoding defined in <xref target="canonical-encoding"/> and include the
          resulting digest as the <tt>psea_payload_hash</tt> claim of the signed execution-authority
          proof. The proof <bcp14>MUST</bcp14> be signed by the hardware-protected signing key that holds the active
          user-verification binding for the originating action.</t>

          <t>A producer <bcp14>MUST NOT</bcp14> emit a proof whose signed <tt>psea_payload_hash</tt> claim
          is absent, empty, or computed over input other than the canonical encoding of the actual action
          payload being authorized by the human.</t>
        </section>

        <section anchor="action-binding-verifier" numbered="true">
          <name>Verifier Obligations: Fail-Closed Binding (Normative Default)</name>
          <t>A conforming Verifier <bcp14>MUST</bcp14>, on every per-action proof submission, perform the
          following steps in order:</t>

          <ol>
            <li>Decode the signed token and validate its cryptographic signature against the device-
            anchored public key bound to the active enrollment.</li>
            <li>Compute the canonical encoding of the request's action payload using
            <xref target="canonical-encoding"/> and hash the result with SHA-256.</li>
            <li>Compare the computed hash, byte-for-byte, against the <tt>psea_payload_hash</tt> claim
            decoded from the signed proof in step 1.</li>
            <li>If step 3 produces a mismatch, OR if the request omits the action payload entirely while
            the signed proof carries a <tt>psea_payload_hash</tt> claim, the Verifier
            <bcp14>MUST</bcp14> reject the proof and <bcp14>MUST NOT</bcp14> return any Attestation Result
            or other wire response that a Relying Party could interpret as approval.</li>
          </ol>

          <t>The Verifier <bcp14>MUST</bcp14> emit this rejection before producing any wire response that
          could be interpreted by the Relying Party as an approval. A rejection <bcp14>MUST NOT</bcp14>
          carry a success-bearing Attestation Result.</t>

          <t>The action-binding obligation in this subsection applies identically to every PSEA proof,
          regardless of the capability or assurance level under which it was produced; a Verifier that
          enforces the binding for some levels but not others is non-conformant.</t>
        </section>

        <section anchor="action-binding-rp" numbered="true">
          <name>Relying-Party Obligations</name>
          <t>On a successful appraisal, a Verifier that is not co-located with the Relying Party
          <bcp14>MUST</bcp14> convey the outcome as an integrity-protected Attestation Result. A Relying
          Party that receives a success-bearing, integrity-protected Attestation Result from a conforming
          Verifier <bcp14>MAY</bcp14> treat the action binding as enforced and is not required to
          re-perform the payload-hash computation. This is the central simplification that PSEA's
          normative fail-closed binding enables: the Relying Party can trust the Verifier's
          integrity-protected result without performing client-side binding enforcement.</t>

          <t>The concrete encoding of the Attestation Result, the Verifier-to-Relying-Party transport,
          and the enrollment ceremony that binds a <tt>kid</tt> to an enrolled key are deployment-specific
          and out of scope of this profile. The extent of what a second party can build from this document
          alone should be stated precisely. The proof-token validation core is fully specified here: from
          this document alone a second party can build a conforming Attester that emits byte-conformant
          proofs, and the Verifier-side validation core — JWS signature verification, the JOSE header
          hardening (<xref target="jose-hardening"/>), the fail-closed payload-hash action binding
          (<xref target="action-binding-verifier"/>), the cross-replay binding
          (<xref target="cross-replay-binding"/>), the monotonic counter (<xref target="counter-model"/>),
          the <tt>jti</tt> uniqueness check, and freshness (<xref target="freshness"/>). A
          <em>deployable</em> Verifier additionally requires two inputs this profile deliberately leaves
          out of scope: the enrollment binding that resolves <tt>kid</tt> to an enrolled public key
          (without which no signature can be verified), and — for human presence to be <em>attested</em>
          rather than merely signed — the appraisal of the out-of-scope attestation evidence against which
          <tt>psea_uv</tt> is cross-checked (<xref target="presence-uv-claim"/>). Interoperating on those
          two interfaces, and on the Verifier-to-Relying-Party Attestation Result, additionally requires a
          companion deployment profile that fixes them.</t>
        </section>

        <section anchor="cross-replay-binding" numbered="true">
          <name>Cross-Replay Binding (psea_tier + psea_op + aud + iss)</name>

          <t>In addition to the payload binding of <xref target="action-binding-verifier"/>, every
          PSEA proof <bcp14>MUST</bcp14> carry the following cross-replay
          binding claims in its signed JWS payload. These close attack surfaces where a captured signed
          proof could be replayed across operations, across authority levels, or across deployments without
          the signature alone catching the mismatch.</t>

          <ul>
            <li><tt>psea_tier</tt> — capability / assurance-level binding: an abstract indicator of the
            authority or assurance context the proof is produced under. The value space is opaque to this
            profile and agreed out of band between producer and Verifier. The Verifier
            <bcp14>MUST</bcp14> reject any proof whose signed <tt>psea_tier</tt> does not match the level
            expected for the operation being performed.</li>
            <li><tt>psea_op</tt> — operation / authority-context binding: a string identifying the
            operation the proof is presented for. The producer and Verifier agree on the value space out
            of band; the values are opaque to this profile. The Verifier <bcp14>MUST</bcp14> reject any
            proof whose signed <tt>psea_op</tt> does not equal the operation identifier for the operation
            being performed. This is the discriminator that prevents a proof minted for one operation from
            being accepted for another.</li>
            <li><tt>aud</tt> — the JWT audience claim (<xref target="RFC7519"/>): an identifier of the
            intended Verifier / audience. This profile restricts <tt>aud</tt> to a single case-sensitive
            string value; the array form that <xref target="RFC7519"/> otherwise permits is
            non-conformant in a PSEA proof, and a Verifier <bcp14>MUST</bcp14> reject a proof whose
            <tt>aud</tt> is an array or is absent. The Verifier <bcp14>MUST</bcp14> reject any proof whose
            signed <tt>aud</tt> does not identify it.</li>
            <li><tt>iss</tt> — the JWT issuer claim (<xref target="RFC7519"/>): the deployment / tenant
            identifier the Attester has locally available at sign time. The Verifier
            <bcp14>MUST</bcp14> reject any proof whose signed <tt>iss</tt> does not match the deployment /
            tenant the Verifier resolves for the request.</li>
          </ul>

          <t>All four checks <bcp14>MUST</bcp14> fire after the signed body's signature has been
          verified (so the bytes are confirmed authentic) and before any state mutation (counter
          consumption, ledger write, acknowledgement signing). The checks are byte-equality string
          comparisons; comparisons <bcp14>MUST NOT</bcp14> normalize case or whitespace.</t>

          <t>The expected <tt>psea_tier</tt> and <tt>psea_op</tt> values for a given operation are
          deployment-defined and agreed out of band; the proof also carries the <tt>aud</tt> and
          <tt>iss</tt> bindings above. Any additional, lower-assurance signed-token types a deployment
          defines (out of scope of this profile) carry the same binding claims under the same claim names
          with their own values.</t>

          <t>These bindings together close: (a) cross-operation replay (a captured proof for one
          operation presented for another), (b) cross-level downgrade (a higher-assurance proof processed
          as a lower-assurance one), and (c) cross-deployment replay (a captured proof presented to a
          different deployment's Verifier). The bindings are locally derivable at sign time without server
          round-trips, preserving the offline-capable property of proofs produced offline.</t>

          <t>The scope of this protection should not be over-read. The cross-replay binding defends against
          <em>capture-replay</em> of an existing proof — a proof minted for one operation, level,
          audience, or deployment being presented for a different one. It does <em>not</em> defend
          against a compromised host minting a <em>fresh</em> proof for the wrong operation: because the
          host chooses the claim values it asks the user-verification-gated key to sign, a compromised host
          that obtains a user-verification ceremony can sign a proof carrying whatever <tt>psea_op</tt> /
          <tt>psea_tier</tt> / <tt>actionPayload</tt> it wishes. The user-verification gate constrains
          <em>that signing can occur</em>, not <em>what content is signed</em>. That compromised-host case
          is the What-You-See-Is-What-You-Sign problem and is treated in
          <xref target="open-wysiwys"/>.</t>
        </section>

        <section anchor="caller-binding" numbered="true">
          <name>Caller-Identity Binding (psea_caller_package)</name>

          <t>When a deployment has enrolled an expected caller identity for the operation, a conforming
          Verifier <bcp14>MUST</bcp14> reject a proof whose signed <tt>psea_caller_package</tt> is absent
          or does not byte-exactly equal the expected value, before any state mutation. A Verifier
          <bcp14>MUST</bcp14> refuse a proof that omits <tt>psea_caller_package</tt> where the deployment
          has registered caller identities, and <bcp14>SHOULD</bcp14> refuse it otherwise. This is
          stricter than -02, which required a deployment without an enrolled caller identity not to treat
          the claim's absence as failure. The binding is covered by the JWS signature; the comparison is a byte-equality
          string comparison that <bcp14>MUST NOT</bcp14> normalize case or whitespace.</t>
        </section>
      </section>

      <section anchor="enrollment-lifecycle" numbered="true">
        <name>Enrollment Lifecycle and Trust Gate (Verifier)</name>
        <t>The enrollment ceremony that binds a <tt>kid</tt> to an enrolled signing key and to a subject is
        deployment-specific and out of scope of this profile (<xref target="action-binding-rp"/>). The
        enrollment <em>lifecycle states</em> that the Verifier enforces on every proof are, by contrast, in
        scope and normative, because they are the trust gate that makes the rest of the profile meaningful:
        a valid signature from an enrolled key is necessary but not sufficient for acceptance.</t>
        <t>A conforming Verifier <bcp14>MUST</bcp14> maintain an authoritative, server-side enrollment
        lifecycle for each enrolled Attester with at least the states <tt>active</tt>, <tt>suspended</tt>,
        and <tt>revoked</tt>, and <bcp14>MUST</bcp14> reject any proof whose enrollment is not in the
        <tt>active</tt> state. This enrollment-status check is authoritative: this profile carries no
        self-asserted wire trust-state field, and a Verifier <bcp14>MUST NOT</bcp14> derive the trust state
        from any value the Attester supplies. The check <bcp14>MUST</bcp14> be applied after signature
        verification and before any state mutation. The transitions between these states, and the events
        that trigger them, are deployment-specific; the requirement here is only that the three states
        exist and gate acceptance. The Elevation-of-Privilege analysis in
        <xref target="threats-elevation"/> relies on this requirement.</t>
      </section>

      <section anchor="proof-versioning" numbered="true">
        <name>Versioning</name>
        <t>The <tt>psea_proof_version</tt> claim identifies the wire-format
        revision the producer targets. This revision uses <tt>"1"</tt>. Future revisions
        <bcp14>MAY</bcp14> extend the value space; a Verifier
        <bcp14>MUST</bcp14> reject a proof carrying an unknown version value. Conforming implementations
        at this revision emit only <tt>"1"</tt>; the
        version field is included in the signed payload so that the wire-version commitment is
        cryptographically anchored.</t>
      </section>
    </section>

    <section anchor="relationship-webauthn" numbered="true">
      <name>Relationship to WebAuthn and FIDO Transaction Confirmation</name>

      <t>WebAuthn and FIDO2 provide phishing-resistant, hardware-backed authentication for the login
      boundary. Their transaction-confirmation features do not provide the per-action execution-time
      action-binding this profile defines, for the following specific reason.</t>

      <t>Web Authentication Level 1 (<xref target="WebAuthn-L1"/>) defined two transaction-confirmation
      extensions: <tt>txAuthSimple</tt> (display a text prompt and bind the user's approval of that exact
      text into the signed assertion) and <tt>txAuthGeneric</tt> (the same for a hashed content blob).
      These were the mechanism by which a WebAuthn assertion could attest that the user approved a
      specific transaction -- a What-You-See-Is-What-You-Sign (WYSIWYS) binding. No browser implemented
      these extensions, and they were removed in Web Authentication Level 2
      (<xref target="WebAuthn-L2"/>). A current WebAuthn assertion
      therefore attests that an authenticator holding a given credential was exercised with user presence
      (and optionally user verification), but it does not cryptographically bind the assertion to a
      specific application-level action or to human-readable transaction content.</t>

      <t>Consequently, the transaction-binding / WYSIWYS property -- proof that a present, verified human
      approved a specific named action, with specific content, at the moment of execution -- is not
      provided by any deployed authentication standard today. PSEA addresses precisely this gap: a PSEA
      proof binds the action identity and a SHA-256 hash of the action payload (the fail-closed
      action-binding defined later in this document) into a hardware-signed Attestation produced at
      execution time. PSEA is complementary to WebAuthn and FIDO rather than a replacement: WebAuthn and
      FIDO2 remain appropriate for establishing the session and verifying the human's credential, while
      PSEA supplies the per-action execution-time Evidence that the transaction-confirmation extensions
      were intended to provide but, in deployed form, do not.</t>
    </section>
    <section anchor="relationship-rfc9470" numbered="true">
      <name>Relationship to OAuth 2.0 Step-Up Authentication</name>

      <t>OAuth 2.0 Step-Up Authentication Challenge <xref target="RFC9470"/> defines a mechanism by which
      an OAuth-protected resource server signals to a client that the bearer access token presented does
      not satisfy the resource's authentication requirements. The resource server responds with HTTP 401
      and a WWW-Authenticate challenge carrying a required Authentication Context Class Reference
      (acr_values) and/or a maximum authentication age (max_age). The client then drives the user through
      re-authentication and obtains a new access token that satisfies the requirement.</t>

      <t>RFC 9470 and PSEA address adjacent but distinct points in the access-control chain. Both react to
      the same operational gap: an access token or session that was sufficient at issuance time may be
      insufficient at the moment a higher-risk action is requested. They differ in the unit of escalation
      and in the cryptographic properties of the artifact produced.</t>

      <section anchor="rfc9470-comparison" numbered="true">
        <name>Comparison</name>

        <table>
          <thead>
            <tr><th>Property</th><th>RFC 9470 step-up</th><th>PSEA</th></tr>
          </thead>
          <tbody>
            <tr><td>Unit of escalation</td><td>The access token (session-scoped).</td>
              <td>The individual action (action-scoped).</td></tr>
            <tr><td>Artifact produced</td>
              <td>A new bearer access token, possibly with refreshed acr / auth_time claims.</td>
              <td>Evidence (signed proof token; see <xref target="proof-token-format"/>) cryptographically
              bound to the specific action payload.</td></tr>
            <tr><td>Scope of authority granted</td>
              <td>Subsequent requests within the resource server's session policy, until the new token
              expires or fails its own freshness check.</td>
              <td>The single action whose payload is bound by the Evidence. No subsequent action inherits
              authority.</td></tr>
            <tr><td>Connectivity requirement</td>
              <td>Re-authentication requires reachability of the authorization server.</td>
              <td>Deployment-dependent: a proof MAY be produced offline with deferred Verifier
              interaction, or require synchronous Verifier interaction, per deployment policy (out of
              scope).</td></tr>
            <tr><td>Action-payload binding</td>
              <td>Not defined. The new token does not bind to any specific resource or operation.</td>
              <td>Required for every PSEA proof. The Evidence body contains the action's payload hash;
              see <xref target="action-binding"/>.</td></tr>
            <tr><td>Authentication context</td>
              <td>Conveyed via acr_values; identifiers are registry-managed and opaque to OAuth
              itself.</td>
              <td>Conveyed via the abstract capability/assurance indicator and other fields in the
              Evidence.</td></tr>
          </tbody>
        </table>
      </section>

      <section anchor="rfc9470-complementarity" numbered="true">
        <name>Complementarity</name>

        <t>RFC 9470 step-up and PSEA <bcp14>MAY</bcp14> be composed: the first to signal that a per-action
        authority check is required; the second to produce the cryptographically action-bound Evidence
        that satisfies that check.</t>

        <ol>
          <li>An OAuth-protected resource server, acting as a Relying Party, receives a request whose access
          token does not satisfy the resource's per-action policy.</li>
          <li>The resource server returns HTTP 401 with a WWW-Authenticate challenge per
          <xref target="RFC9470"/>, indicating the required step-up.</li>
          <li>The client drives the step-up flow on a device hosting a PSEA Attester. The Attester
          produces fresh Evidence at the capability level the resource's policy demands,
          cryptographically bound to the specific action that triggered the 401.</li>
          <li>The client re-submits the request, conveying the Evidence to the Verifier. The Verifier
          appraises the Evidence and produces an Attestation Result. The resource server gates the
          action on both the OAuth access token (session validity) and the Attestation Result (per-action
          authority).</li>
        </ol>

        <t>In this composition, OAuth supplies the session and identity context; PSEA supplies the per-
        action cryptographic authority proof. Neither mechanism replaces the other.</t>
      </section>

      <section anchor="rfc9470-out-of-scope" numbered="true">
        <name>Out of Scope for This Document</name>
        <t>This section identifies architectural complementarity. It does not define a normative
        integration profile between PSEA and <xref target="RFC9470"/>. A normative integration profile
        (specifying the precise WWW-Authenticate challenge syntax, the acr_values registration for PSEA
        capability/assurance levels, the error-mapping table between RFC 9470 challenges and PSEA error
        codes, and the conveyance of the Evidence in the re-submitted request) is deferred to a future
        document.</t>
      </section>
    </section>
    <section anchor="security-considerations" numbered="true" toc="default">
      <name>Security Considerations</name>

      <section anchor="threat-scope" numbered="true">
        <name>Scope and Method</name>
        <t>This section enumerates the threats this document addresses, the assumptions on
        which the protocol's security properties rest, and the residual risks. The method is a STRIDE
        decomposition (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service,
        Elevation of privilege) applied to the protocol's wire surface; threats specific to a particular
        deployment's policy or non-protocol implementation choices are out of scope.</t>
      </section>

      <section anchor="attester-assumptions" numbered="true">
        <name>Attester Assumptions</name>
        <t>The protocol's security properties depend on the following assumptions about a conforming
        Attester:</t>

        <dl>
          <dt>A1. Hardware-protected signing key</dt>
          <dd>The Attester's signing key is generated in and never leaves a hardware-protected key store
          (for example a TEE, a Secure Enclave, a StrongBox, a smartcard secure chip, or a programmable
          secure element). The authenticator's attestation chain, verified by the Verifier against a trust
          anchor (<xref target="attester-assumptions"/>), provides cryptographic evidence of this
          property.</dd>

          <dt>A2. Contemporaneous user-verification gating</dt>
          <dd>The Attester's signing key is gated by a platform user-verification ceremony such that
          signing cannot complete without a successful, contemporaneous user-verification event (see
          <xref target="presence-via-signature"/>). The mechanism by which the platform implements this
          gating is out of scope; the Attester's assertion of the property is appraised through the
          platform attestation.</dd>

          <dt>A3. Platform user-verification subsystem integrity</dt>
          <dd>The Attester relies on the integrity of the platform user-verification subsystem (for
          example, a platform biometric subsystem, PIN entry into a Secure Enclave, hardware-token
          PIN handling, smartcard or magnetic-stripe-with-PIN reader logic). This is a platform
          guarantee; PSEA does not attempt to verify platform user-verification subsystem
          internals.</dd>

          <dt>A4. Authenticator attestation as a trust anchor</dt>
          <dd>The Verifier holds the authenticator's attestation root (for example an Android Key
          Attestation root, an Apple App Attest root, a hardware-token vendor root, or a smartcard issuer
          root) as a trust anchor.
          The Verifier's appraisal of attestation chains is bounded by the authenticator's published
          chain-validation rules.</dd>
        </dl>
      </section>

      <section anchor="enrollment-root-of-trust" numbered="true">
        <name>Enrollment Is the Root of Trust</name>
        <t>Every security property in this document is conditional on one out-of-scope step: the
        enrollment ceremony that binds an enrolled signing key (selected at verification time by
        <tt>kid</tt>) to the correct device and the correct subject. This is stated as an out-of-scope
        assumption in O3 (<xref target="threats-out-of-scope"/>), but its consequence is foundational and
        is called out here explicitly so it is not understated: if enrollment binds the wrong key, the
        entire chain of guarantees is anchored to the wrong hardware, and every downstream check in this
        profile still passes.</t>
        <t>Concretely, an adversary who can complete the deployment's enrollment ceremony with hardware
        the adversary controls — enrolling the adversary's own hardware-protected key as if it were the
        victim's device — thereafter produces proofs that are valid in every respect this profile can
        check: the signature verifies against the enrolled key, the attestation chain is genuine (it
        attests the adversary's real hardware), the user-verification gate fires (on the adversary), the
        action binding holds, and the counter and <tt>jti</tt> checks pass. The Verifier has no in-protocol
        signal that the enrolled key belongs to the wrong person, and the substitution persists for the
        lifetime of the enrollment. The forged-binding cannot be detected by the wire protocol; it can be
        detected only by the strength of the enrollment ceremony and by out-of-band identity proofing.</t>
        <t>Deployments therefore <bcp14>MUST</bcp14> treat enrollment as the security-critical root of the
        whole system and apply identity-proofing, attestation freshness, and binding controls commensurate
        with the authority the resulting proofs will carry. No property claimed elsewhere in this document
        is stronger than the enrollment binding it rests on.</t>
      </section>

      <section anchor="threats-addressed" numbered="true">
        <name>STRIDE Threats Addressed</name>

        <section anchor="threats-spoofing" numbered="true">
          <name>Spoofing</name>
          <t>S1. <em>Attacker forging Evidence on behalf of an Attester.</em> Mitigated by hardware-protected
          signing keys (A1) and the Verifier's appraisal of the attestation chain. An attacker without access to the hardware key
          store cannot produce a valid signature, so the Verifier rejects the proof. The
          JOSE header hardening of <xref target="jose-hardening"/> additionally closes algorithm-
          substitution, <tt>alg</tt>:"none", and attacker-supplied-key (<tt>jwk</tt> / <tt>jku</tt> /
          <tt>x5u</tt>) forgery vectors: the Verifier accepts only ES256 and resolves the verification key
          solely from the enrolled record selected by <tt>kid</tt>.</t>
          <t>S2. <em>Attacker presenting a recorded user-verification artifact.</em> Mitigated by
          contemporaneous gating (A2): the platform user-verification ceremony is required at the moment
          of signing. The Attester delegates user-verification to platform mechanisms; the attestation
          chain proves the gating is in place. Residual risk: bounded by the platform's user-verification
          anti-spoofing capability (e.g., presentation-attack detection in biometric subsystems, tamper
          resistance of hardware-token / smartcard readers, PIN-shoulder-surfing mitigations).</t>
          <t anchor="threats-pre-mint">S3. <em>Compromised host pre-minting a batch of proofs from a
          single user-verification event.</em> If the signing key were gated by a duration- or
          window-based user-verification policy (one ceremony unlocking the key for an interval), a
          compromised host could sign a stack of future proofs — each with a distinct <tt>jti</tt>, an
          increasing <tt>psea_counter</tt>, and a distant <tt>exp</tt> — from one ceremony, and every such
          proof would pass the wire checks. Mitigated by the per-operation gating requirement of
          <xref target="presence-via-signature"/>: each proof signature <bcp14>MUST</bcp14> be preceded by
          its own fresh user-verification event, so the number of valid proofs is bounded by the number of
          ceremonies and pre-minting is not reachable. This mitigation is subject to the same
          attestation-conditionality as the <tt>psea_uv</tt> claim (<xref target="presence-uv-claim"/>):
          the Verifier can confirm that the signing key is gated per operation (rather than under a
          duration- or window-based policy that would permit pre-minting) only where the platform
          attestation conveys the key's user-verification-gating configuration. Where the attestation
          conveys it, the Verifier <bcp14>MUST</bcp14> reject a proof whose attested key properties show a
          duration- or window-based gating policy, and pre-minting is attested-closed. Where the
          attestation does not convey the gating configuration, per-operation gating rests on a documented
          trust assumption in the Attester rather than an attested property, and a compromised host that
          configures window-based authentication could pre-mint without the Verifier detecting it from the
          attestation; for high-assurance operations a Verifier <bcp14>SHOULD</bcp14> reject or require an
          additional independent factor in that case, as for <tt>psea_uv</tt>. Residual risk: a host that
          subverts the platform user-verification subsystem itself (O1) is out of scope.</t>
        </section>

        <section anchor="threats-tampering" numbered="true">
          <name>Tampering</name>
          <t>T1. <em>Attacker modifying Evidence in transit.</em> Mitigated by the signed body's ECDSA
          P-256 signature. Any tampering of the signed octets causes signature verification to fail and the Verifier to reject the proof.</t>
          <t>T2. <em>Attacker swapping action payloads between proof and request.</em> Mitigated by the
          action-binding fail-closed check (<xref target="action-binding-verifier"/>): the Verifier
          recomputes the payload hash and rejects the proof on mismatch.</t>
          <t>T3. <em>Attacker re-using a captured proof against a different request.</em> Mitigated by
          the monotonic counter (<xref target="counter-model"/>) and by the action-binding (T2). The counter
          prevents re-issuance with the same counter value; the action-binding prevents reuse of a proof
          against a different payload.</t>
        </section>

        <section anchor="threats-repudiation" numbered="true">
          <name>Repudiation</name>
          <t>R1. <em>Subject claims they did not approve an action.</em> Mitigated by the device-bound
          signature (binding to a specific physical device) plus the contemporaneous user-verification
          ceremony (binding to a human at that moment). The per-proof <tt>psea_user_hash</tt> claim
          (<xref target="user-id-hash"/>) provides an additional Attester commitment to subject
          identifier for audit-trail purposes; it does not affect the cryptographic non-repudiability,
          which rests on the device-bound key and the presence Evidence binding.</t>
        </section>

        <section anchor="threats-info-disclosure" numbered="true">
          <name>Information Disclosure</name>
          <t>I1. <em>User-verification template leakage.</em> Out of scope (the user-verification template
          is platform-internal; PSEA does not transit or store it).</t>
          <t>I2. <em>Subject identifier leakage via wire fields.</em> Reduced — not eliminated — by
          hashing: the wire carries SHA-256 hashes of subject identifiers, not the raw identifiers, so
          the raw value and the biometric template never transit. Hashing is not anonymization; an
          unsalted hash of a low-entropy identifier is reversible and linkable. See
          <xref target="privacy-hashing"/> for the treatment and the salt / pairwise-derivation
          mitigations.</t>
          <t>I3. <em>Wire-protocol leak via verbose error responses.</em> Mitigated by constraining rejection responses to a fixed set of abstract outcomes without leaking
          deployment-internal diagnostic detail to the Attester.</t>
        </section>

        <section anchor="threats-dos" numbered="true">
          <name>Denial of Service</name>
          <t>D1. <em>Rate-flooding of Verifier endpoints.</em> Mitigated by per-deployment rate limiting.</t>
          <t>D2. <em>Quota exhaustion of Verifier processing.</em> Mitigated by per-deployment
          rate limiting and by implementation-specific quota controls (an exhausted-quota
          rejection is out of scope for this protocol and is not assigned a PSEA wire response code).</t>
        </section>

        <section anchor="threats-elevation" numbered="true">
          <name>Elevation of Privilege</name>
          <t>E1. <em>Attester signing a proof while it should be locally untrusted.</em> Mitigated by
          the authoritative server-side enrollment lifecycle the Verifier maintains, specified normatively
          in <xref target="enrollment-lifecycle"/>: the Verifier rejects any proof whose enrollment is not
          in the <tt>active</tt> state. This server-side enrollment-status / revocation check is the trust
          gate, so a misbehaving or compromised Attester that signs a proof it should have withheld cannot
          bypass a suspended or revoked state. This profile carries no self-asserted wire trust-state
          field; the Verifier's enrollment record, not any Attester self-assertion, is authoritative.</t>
          <t>E2. <em>Attester emitting proofs after local-state revocation.</em> Mitigated by the
          Verifier's enrollment lifecycle: even if a misbehaving Attester continues to emit
          proofs after local revocation, the Verifier rejects them.</t>
        </section>
      </section>

      <section anchor="verifier-state-integrity" numbered="true">
        <name>Verifier State Integrity: Sharding and Rollback</name>
        <t>The replay defenses of <xref target="counter-model"/> — the strictly-increasing
        <tt>psea_counter</tt> compared-and-advanced atomically per (Attester, counter scope), and the
        global <tt>jti</tt> uniqueness check that finalizes each action identifier exactly once — are
        stated as logical, single-domain guarantees. A horizontally-scaled (sharded) Verifier deployment
        and the durability of the Verifier's state both bear on whether those guarantees actually hold, and
        neither is closed by the wire format alone.</t>
        <t><em>Sharding / distributed serialization.</em> The atomic compare-and-advance of
        <xref target="counter-model"/> assumes a single transactional domain per (Attester, counter scope),
        and the <tt>jti</tt> uniqueness check assumes a globally consistent index. A deployment that spreads
        verification across multiple nodes <bcp14>MUST</bcp14> preserve these assumptions: all submissions
        for a given (Attester, counter scope) <bcp14>MUST</bcp14> be serialized to a single authority (for
        example by routing or partitioning on the Attester identity, or by a shared transactional store
        that serializes per scope), and the <tt>jti</tt> finalization index <bcp14>MUST</bcp14> be globally
        consistent across all nodes that can accept proofs for the deployment. A design in which two nodes
        can independently advance the same scope's counter, or independently finalize the same
        <tt>jti</tt>, without a serializing authority is non-conformant: it reopens the
        concurrent-double-accept and counter-regression surfaces that the atomicity requirement of
        <xref target="counter-model"/> exists to close, this time at the storage tier rather than at the
        single-node transaction.</t>
        <t><em>Rollback / durability.</em> <xref target="counter-model"/> prevents an Attester from
        regressing its counter; the symmetric risk is the Verifier regressing its own stored high-water
        mark and <tt>jti</tt> set. If the Verifier's counter / <tt>jti</tt> state is restored from an older
        backup or otherwise rolled back, counter values and <tt>jti</tt> values consumed since that backup
        become acceptable again, reopening the replay surface for proofs still within their <tt>exp</tt>
        window. A conforming Verifier <bcp14>MUST</bcp14> protect its replay state (the per-scope
        high-water-mark counters and the finalized-<tt>jti</tt> set) against rollback, such that a restore
        or failover does not lower a stored high-water mark or forget a finalized <tt>jti</tt> that could
        still be replayed within the <tt>exp</tt> window. This is the server-side mirror of the client-side
        regression the counter model forbids; durability of the replay state is part of the replay defense,
        not an operational afterthought.</t>
      </section>

      <section anchor="rp-countersignature-future" numbered="true">
        <name>Future Work: Relying Party Counter-Signature</name>

        <t>The current protocol provides cryptographic action-binding between the Attester and the
        Verifier. A future extension MAY introduce a Relying-Party counter-signature primitive in which
        the Relying Party countersigns the Attestation Result, producing a three-party signed artifact
        that binds the action approval to (a) the Subject (via the Attester's hardware key + user-
        verification), (b) the Verifier's appraisal, and (c) the Relying Party's policy acceptance.</t>

        <t>This three-party binding would close the residual risk that a compromised Relying Party
        accepts an Attestation Result it should have rejected on policy grounds. The wire-level
        primitive is informative-only at this revision; a normative specification is deferred to a
        future document.</t>
      </section>

      <section anchor="threats-out-of-scope" numbered="true">
        <name>Threats Out of Scope</name>
        <t>The following threats are acknowledged but are out of scope of the wire protocol:</t>

        <dl>
          <dt>O1. Platform user-verification subsystem compromise</dt>
          <dd>Compromise of the platform user-verification subsystem (biometric subsystem, Secure
          Enclave, hardware-token reader, smartcard reader, or equivalent). Bounded by platform vendor
          guarantees; PSEA appraises the resulting attestation but does not verify platform
          internals.</dd>

          <dt>O2. Insider threat at the Verifier or Relying Party</dt>
          <dd>An insider with administrative access to the Verifier or Relying Party can subvert appraisal
          regardless of the wire protocol. Deployments SHOULD apply standard operational controls
          (separation of duties, audit logging, key custodianship).</dd>

          <dt>O3. Pre-enrollment trust establishment</dt>
          <dd>The protocol assumes that an enrollment ceremony validly binds an Attester to a subject
          identity. Deployment-specific enrollment policies are out of scope. This is the root-of-trust
          assumption for the entire profile and its consequence (a forged enrollment binding yields
          fully-valid proofs anchored to the wrong hardware) is treated explicitly in
          <xref target="enrollment-root-of-trust"/>.</dd>
        </dl>
      </section>

      <section anchor="known-open-problems" numbered="true">
        <name>Known Open Problems</name>

        <t>The following are attack surfaces that PSEA does not solve and that remain unsolved at the
        authenticator/platform layer at the time of writing. They are documented so that deployers and
        reviewers can assess residual risk in their own deployment context, and each is a candidate for
        future research and protocol evolution.</t>

        <section anchor="open-coercion" numbered="true">
          <name>Coercion and Duress</name>
          <t>An attacker physically present with the legitimate subject can compel the subject to
          perform the user-verification ceremony. The resulting proof is cryptographically
          indistinguishable from a freely-given approval: the signed body shows the correct hardware
          key, the contemporaneous user-verification event happened, and the action payload binds
          correctly.</t>
          <t>Why this is hard for any authenticator: the platform user-verification APIs do not expose which
          finger was used, which face geometry matched, which PIN was entered, or whether the subject
          appeared distressed. The Attester sees only a binary "verification succeeded." A duress-code
          scheme (e.g., a specific finger reserved for duress, a specific PIN suffix) would require
          either (a) platform-level API changes to surface which credential was used so the Attester
          can interpret it, or (b) the application binding distinct credentials to distinct
          server-side meanings — but on face-based verification this is effectively impossible because
          there is no way to enroll a "duress face." This remains an open problem; deployments that
          require duress detection today rely on out-of-band mechanisms (transaction-monitoring rules,
          behavioral analytics, secondary channels) that lie outside this protocol.</t>
        </section>

        <section anchor="open-wysiwys" numbered="true">
          <name>What-You-See-Is-What-You-Sign (WYSIWYS)</name>
          <t>The property PSEA guarantees, for every PSEA proof, is <strong>action-binding</strong>:
          what is signed is what the Verifier executes. The fail-closed action-binding check
          (<xref target="action-binding-verifier"/>) ties the signed proof to the exact action payload
          via <tt>psea_payload_hash</tt>, so the signature-to-execution binding is cryptographically
          enforced and a captured proof cannot be replayed against a different payload.</t>
          <t>What PSEA does not guarantee on a compromised device is the eye-to-signature binding —
          that what the human actually saw on screen at the moment of the user-verification ceremony is
          identical to what was signed. A compromised UI layer or a malicious overlay can display
          "Approve $10 transfer to Alice" while the underlying action payload submitted to the
          Verifier is "$10,000 transfer to Mallory." The hardware key signs the payload it was given,
          not the pixels the human saw. PSEA binds signature to execution; it does not, by itself, bind
          the human's perception to either.</t>
          <t>It is important to state the degraded-mode value plainly rather than leave it implied. On a
          fully compromised display — or, equivalently, where a compromised Relying Party or in-path
          modifier substitutes a plausible-but-tampered payload that the human did not intend — PSEA does
          <em>not</em> prevent execution of the unintended action: the tampered payload is signed by
          the genuine hardware key under a genuine user-verification event, so the action-binding check
          passes on it. What PSEA retains in that case is forensic / non-repudiation value: the signed
          proof is durable, hardware-anchored evidence of exactly what was signed (and that a
          user-verification event gated the signing), establishing after the fact what the device actually
          committed to. PSEA's contribution on a compromised display/RP is therefore non-repudiation, not
          prevention; prevention of the eye-to-signature mismatch requires the trusted-display path below,
          which is out of scope.</t>
          <t>Why this is hard for any authenticator: full WYSIWYS requires a trusted display path the
          application cannot influence — pixels rendered by a trusted execution path (for example a
          Trusted Execution Environment), with the
          user-verification ceremony tied to a render-of-record that the platform attests to. Some
          platforms have partial support (e.g., trusted-UI on certain TEEs), but the API surface for
          application developers to express "render THIS exact content in trusted UI before
          signing" is not generally available across authenticator platforms in a way that survives platform
          UI redesigns. <strong>Trusted-display / secure-UI is out of scope of this specification</strong>
          (it is platform-dependent and not universally available) and remains the open problem.</t>
          <t>Mitigation for high-consequence actions: for high-consequence operations, deployments
          <bcp14>SHOULD</bcp14> use a server-signed confirmation round-trip. In this RECOMMENDED pattern
          the Verifier signs the canonical action it is about to execute and returns it to the
          authenticator for display and a user-verification step before the action commits, giving the
          signed content an independent, server-anchored origin rather than one chosen entirely by a
          potentially-compromised client UI. This is a SHOULD / RECOMMENDED measure for high-consequence
          operations, not a universal MUST, and it reduces but does not eliminate the residual risk on a
          fully compromised display.</t>
          <t>Out-of-band channels vulnerable to SIM-swap or one-time-passcode interception (for example,
          SMS OTP) are <bcp14>NOT RECOMMENDED</bcp14> as a WYSIWYS mitigation: they reintroduce exactly
          the channel-interception and credential-replay weaknesses PSEA exists to eliminate, and a
          deployment that layers them on does not obtain a trusted-display guarantee. Trusted display
          remains an open problem.</t>
        </section>

        <section anchor="open-no-identity" numbered="true">
          <name>Subject-Identity Limits</name>
          <t>PSEA proves <em>a</em> human was present and approved this action. It does not identify
          <em>which</em> human, beyond the deployment-issued subject identifier the Attester commits
          to (<tt>psea_user_hash</tt> — an opaque commitment, not a cryptographic identity binding).
          Subject identity attribution — KYC, anti-money-laundering, multi-user-on-one-device
          scenarios — is the responsibility of the deployment's enrollment ceremony and out-of-band
          identity proofing. A device legitimately enrolled to user A whose user-verification
          credential is then re-enrolled to user B will produce proofs that the platform attests
          contemporaneously gated by user B's verification; PSEA cannot detect this. Deployments
          requiring strong subject identity binding rely on enrollment-time mechanisms (in-person
          ceremony, document verification, etc.) outside the wire protocol.</t>
        </section>

        <section anchor="open-ordering" numbered="true">
          <name>Authorization-to-Effect Ordering</name>
          <t>Nothing in a PSEA proof establishes that the signature preceded the effect it authorizes. A
          proof minted in ratification of an action that has already completed is structurally identical to
          one minted before that action began: the same claim set, the same action binding, the same
          contemporaneous user-verification event, the same hardware key. A Verifier appraising the
          artifact alone cannot separate the two cases, and neither can a party auditing the artifact
          afterwards.</t>
          <t>The criterion this would require is not a property of the artifact. Ordering evidence has to
          come from a boundary that observed both the authorization and the effect, or from two records
          whose producing boundaries can be related to one another. Authenticating the artifact is not that
          criterion: a timestamp minted beside the signature observes only the signature, and establishes
          when the signature existed rather than what had already happened when it was produced. This is
          why the gap is not closed by strengthening the proof, and why it is recorded here rather than
          treated as a deficiency in the claim set.</t>
          <t>No ordering input is defined at this revision. A composition or appraisal boundary that
          requires authorization to be shown to precede execution therefore treats the disposition for a
          PSEA proof as UNSATISFIED rather than INDETERMINATE: the profile does not define an input that
          happens to be unavailable in a given deployment, it defines none. Deployments that need the
          property today obtain it from the surrounding system — an execution boundary that records both
          events, or an independently anchored log that orders them — rather than from this profile.</t>
        </section>
      </section>

    </section>

    <section anchor="privacy-considerations" numbered="true" toc="default">
      <name>Privacy Considerations</name>

      <t>This section follows the guidance of <xref target="RFC6973"/>. PSEA Evidence is, by design, a
      device-bound and (via the user-verification gate) human-presence-bearing artifact; that same
      binding creates privacy exposure that deployers <bcp14>MUST</bcp14> understand. The principal
      concerns are the linkability of the stable per-device identifier (<tt>ueid</tt>) and the common
      but incorrect assumption that hashing an identifier anonymizes it.</t>

      <section anchor="privacy-ueid" numbered="true">
        <name>UEID Linkability</name>

        <t>The <tt>ueid</tt> claim (<xref target="proof-top-level"/>) is, within one deployment, a stable
        per-device identifier: it is the same value across every action a given device attests to a given
        issuer (<tt>iss</tt>). A Verifier therefore can, and by construction does, link all of a given
        device's proofs to one another within its deployment. This intra-deployment linkability is an
        inherent property of a device-bound attestation identifier, not a defect; this profile
        depends on the Verifier being able to bind successive proofs to one enrollment.</t>

        <t>To prevent the same physical device from being correlated <em>across</em> deployments, the
        <tt>ueid</tt> <bcp14>MUST</bcp14> be derived pairwise per issuer: the UEID byte string is
        <tt>0x01 || SHA-256(JCS({"deviceId": &lt;deviceId&gt;, "iss": &lt;iss&gt;}))</tt>
        (<xref target="proof-top-level"/>), where <tt>JCS()</tt> denotes the JSON Canonicalization Scheme
        (<xref target="RFC8785"/>) form of that two-member JSON object, taken under the canonical-encoding
        rules of <xref target="canonical-encoding"/>, so the same device
        yields a different <tt>ueid</tt> for each distinct <tt>iss</tt>. The two inputs are encoded as a
        canonical JSON object rather than concatenated because a bare concatenation of two
        variable-length strings is ambiguous: the pairs <tt>("acme", "corp.example")</tt> and
        <tt>("acmecorp", ".example")</tt> concatenate to identical octets and would derive one
        <tt>ueid</tt> for two distinct deployments, defeating the very separation the pairwise derivation
        exists to provide. Because <tt>iss</tt> is itself a
        signed claim bound into the proof, the derivation is verifiable: a Verifier recomputes the
        expected <tt>ueid</tt> for its own <tt>iss</tt> from the enrolled device-id input. The privacy
        rationale is that a single, globally-shared device identifier would let independent Relying
        Parties and deployments correlate one person's device activity across unrelated services; binding
        the identifier to <tt>iss</tt> confines linkability to the single deployment that legitimately
        requires it.</t>

        <t>This profile carries a deterministic per-issuer value under the RFC 9711 RAND type tag
        (<tt>0x01</tt>). <xref target="RFC9711"/>, Section 4.2.1.1 requires that a UEID be permanent and
        not change for a given entity. The value this profile carries does not meet that requirement: it
        is derived from a <tt>deviceId</tt> input that is wiped on factory reset and re-derived
        afterwards, as happens both to a platform-provided device identifier and to an identifier
        generated and stored on the device, so the same entity presents a new value after a factory
        reset. <xref target="RFC9711"/>, Section 4.2.2 defines the semipermanent UEID (<tt>sueids</tt>)
        claim for this case: a value with the format of a UEID that may change on entity life-cycle
        events, of which factory reset is one. <tt>sueids</tt> is the correct claim for the value this
        profile carries. This revision retains <tt>ueid</tt> for wire continuity only.</t>

        <t>The <tt>sueids</tt> claim is a map, not an array: each entry pairs a text-string label with a
        value in the UEID format, and a label is required even when only one entry is present
        (<xref target="RFC9711"/>, Section 4.2.2). This profile defines no means for a device owner to
        add an identifier: the Attester emits exactly one device identifier, derived from its
        <tt>deviceId</tt> input and the signed <tt>iss</tt>.</t>

        <t>Migrating the identifier to <tt>sueids</tt> is a wire-incompatible change. It is the stated
        intent for the next wire-incompatible revision of this profile, which will also define the label.
        Until then the <tt>ueid</tt> value is a per-issuer pseudonym for the current enrollment:
        deterministic by construction, not random, and not preserved across factory reset.</t>

        <t>Per-issuer derivation does not provide per-Relying-Party unlinkability <em>within</em> a single
        deployment, nor does it defeat correlation by other shared fields. A future revision
        <bcp14>MAY</bcp14> define a pairwise / <tt>sueids</tt>-style per-Relying-Party identifier
        derivation (in the spirit of the EAT semipermanent-UEID concept, <xref target="RFC9711"/>) so that
        one device presents a distinct, unlinkable identifier to each Relying Party within a deployment;
        this is the path to per-RP unlinkability and is RECOMMENDED as the direction for deployments with
        strong unlinkability requirements.</t>

        <t>The same "hashing is not anonymization" caution that applies to <tt>psea_user_hash</tt>
        (<xref target="privacy-hashing"/>) applies to the <tt>deviceId</tt> input of the <tt>ueid</tt>
        derivation. If <tt>deviceId</tt> is a low-entropy or enumerable value (for example a hardware
        serial number or other guessable platform identifier), then the derived <tt>ueid</tt> is
        recoverable by brute force from a known <tt>iss</tt>, defeating the confinement the per-issuer
        derivation is meant to provide; encoding the inputs unambiguously prevents one derived
        <tt>ueid</tt> from standing for two input pairs, and does not raise the entropy of either input.
        A deployment <bcp14>SHOULD</bcp14> use a high-entropy,
        per-enrollment random value as <tt>deviceId</tt> (a value generated at enrollment and not derived
        from a stable hardware identifier), so that the derived <tt>ueid</tt> is not recoverable from
        guessable inputs and is not a globally stable value computable by any other party.</t>
      </section>

      <section anchor="privacy-hashing" numbered="true">
        <name>Hashing Is Not Anonymization</name>

        <t>PSEA wire fields carry SHA-256 hashes of identifiers (the device-id hash inside <tt>ueid</tt>,
        and the OPTIONAL <tt>psea_user_hash</tt> subject hash) rather than the identifiers themselves.
        Hashing reduces casual exposure of the raw value, but it <bcp14>MUST NOT</bcp14> be relied upon
        as a privacy or anonymization guarantee. An <strong>unsalted</strong> hash of a
        <strong>low-entropy</strong> identifier — a phone number, an account number, an email address, a
        national ID, or any identifier drawn from a small or enumerable space — is reversible by
        brute-force or dictionary attack: an adversary who can compute the same hash function over
        candidate inputs recovers the input, and two systems that hash the same identifier produce the
        same digest, making the hash a stable, globally linkable identifier in its own right.</t>

        <t>Where linkability or re-identification of the hashed identifier matters, a deployment
        <bcp14>SHOULD</bcp14> either (a) salt or pepper the hash input with a per-deployment secret that
        is not published with the proofs, so the digest is not a globally stable value computable by any
        other party, or (b) derive a pairwise identifier per Relying Party (see
        <xref target="privacy-ueid"/>), so the hashed value is not a single stable identifier shared
        across contexts. A deployment <bcp14>MUST NOT</bcp14> treat a bare SHA-256 of a user or device
        identifier as sufficient to claim the wire field is non-identifying. The minimization PSEA does
        provide is that the <em>raw</em> identifier and the biometric template never transit the wire
        (<xref target="threats-info-disclosure"/>); that is a reduction in exposure, not anonymization.</t>
      </section>

      <section anchor="privacy-minimization" numbered="true">
        <name>Data Minimization at the Verifier</name>

        <t>The Verifier and Relying Party necessarily retain proof records to serve PSEA's audit-trail
        purpose, and those records are linkable per <xref target="privacy-ueid"/>. Deployments
        <bcp14>SHOULD</bcp14> apply retention limits, access controls, and purpose limitation to stored
        proofs and Attestation Results consistent with their regulatory environment, and
        <bcp14>SHOULD NOT</bcp14> retain the OPTIONAL <tt>psea_user_hash</tt> beyond the audit-trail need
        it serves. Correlation of proofs with out-of-band identity (which natural person a
        <tt>ueid</tt> or <tt>psea_user_hash</tt> corresponds to) is established only at enrollment and is
        a deployment responsibility outside the wire protocol.</t>
      </section>
    </section>

    <section anchor="iana" numbered="true" toc="default">
      <name>IANA Considerations</name>

      <section anchor="iana-registries-overview" numbered="true">
        <name>Summary of Requested Registrations</name>
        <t>This document requests IANA to register one media type:</t>
        <ul>
          <li>One media-type registration (<xref target="iana-media-types"/>).</li>
        </ul>
        <t>This document requests no URN sub-namespace and no registration of any kind for its
        <tt>eat_profile</tt> identifier. The <tt>eat_profile</tt> claim carries a URI or an OID and
        <xref target="RFC9711"/> defines no registry for profile identifiers, so the identifier this
        profile uses — the HTTPS URI <tt>https://datatracker.ietf.org/doc/draft-yossif-psea/</tt>
        (<xref target="eat-profile"/>) — requires no registration action and does not depend on the stream
        this document is published on.</t>
        <t>This document does not request registration of the <tt>psea_*</tt> claims in the JSON Web Token
        Claims registry (<xref target="RFC7519"/>, Section 10.1) or in the CBOR Web Token (CWT) Claims
        registry. The <tt>psea_*</tt> claims are profile-private extension claims whose semantics are
        meaningful only to a consumer that understands this <tt>eat_profile</tt>
        (<xref target="eat-profile"/>); their names are scoped by the <tt>eat_profile</tt> claim rather
        than by a global registry, consistent with the strict, profile-scoped extensibility model of this
        document (<xref target="eat-profile-definition"/>). A future revision <bcp14>MAY</bcp14> register
        one or more <tt>psea_*</tt> claims in the JWT Claims registry should cross-profile reuse warrant a
        globally-managed name; this revision intentionally does not, to avoid reserving global names for
        semantics that are defined only relative to this profile.</t>
      </section>

      <section anchor="iana-media-types" numbered="true">
        <name>Media Type Registration</name>
        <t>This document requests registration of the media type <tt>application/psea+jwt</tt> in the
        "Media Types" registry, following the procedures of <xref target="RFC6838"/> and using the
        registered <tt>+jwt</tt> structured syntax suffix (<xref target="RFC7519"/>). The subtype carries
        the PSEA proof defined by this profile (protected-header <tt>typ = "psea-proof+jwt"</tt>) per
        <xref target="proof-token-format"/>. The media type <tt>application/psea+jwt</tt> labels the object
        at the transport layer, and a consumer expecting a proof <bcp14>MUST</bcp14> verify the
        protected-header <tt>typ</tt> value (<xref target="jose-hardening"/>). The Verifier acknowledgement
        / Attestation Result is out of scope of this profile (<xref target="action-binding-rp"/>); a
        deployment that represents that artifact as a JWS <bcp14>MAY</bcp14> reuse this media type with a
        distinct protected-header <tt>typ</tt> value (for example <tt>"psea-ack+jwt"</tt>), applying the
        explicit-typing guidance of <xref target="RFC8725"/>, Section 3.11 so the two object types are not
        confused, but this document does not define that artifact.</t>
        <dl>
          <dt>Type name</dt><dd>application</dd>
          <dt>Subtype name</dt><dd>psea+jwt</dd>
          <dt>Required parameters</dt><dd>N/A</dd>
          <dt>Optional parameters</dt><dd>N/A</dd>
          <dt>Encoding considerations</dt>
          <dd>8bit; a PSEA token is a JWS Compact Serialization (<xref target="RFC7515"/>): a series of
          base64url-encoded values separated by period (".") characters.</dd>
          <dt>Security considerations</dt>
          <dd>See <xref target="security-considerations"/> of this document and the Security
          Considerations of <xref target="RFC7515"/> and <xref target="RFC7519"/>. In particular, a
          consumer MUST apply the JOSE header hardening of <xref target="jose-hardening"/> (ES256 only;
          reject <tt>alg</tt>:"none"; ignore <tt>jwk</tt>/<tt>jku</tt>/<tt>x5u</tt>; verify <tt>typ</tt>;
          reject unknown <tt>crit</tt> parameters and the unencoded-payload option).</dd>
          <dt>Interoperability considerations</dt>
          <dd>See <xref target="proof-token-format"/> and <xref target="canonical-encoding"/>.</dd>
          <dt>Published specification</dt><dd>This document.</dd>
          <dt>Applications that use this media type</dt>
          <dd>PSEA Attesters, Verifiers, and Relying Parties exchanging action-bound,
          user-verification-gated execution-authority Evidence and the corresponding Attestation
          Results.</dd>
          <dt>Fragment identifier considerations</dt><dd>N/A</dd>
          <dt>Additional information</dt>
          <dd>
            Deprecated alias names for this type: N/A;
            Magic number(s): N/A;
            File extension(s): N/A;
            Macintosh file type code(s): N/A.
          </dd>
          <dt>Person &amp; email address to contact for further information</dt>
          <dd>Mohamad Khalil Yossif &lt;mohamad@yuthent.com&gt;</dd>
          <dt>Intended usage</dt><dd>COMMON</dd>
          <dt>Restrictions on usage</dt><dd>None</dd>
          <dt>Author</dt><dd>Mohamad Khalil Yossif</dd>
          <dt>Change controller</dt><dd>IETF</dd>
        </dl>
        <t>A CWT/COSE representation (<xref target="RFC9052"/>) carrying the same EAT
        (<xref target="RFC9711"/>) and <tt>psea_*</tt> claim set is future work; this revision does not
        register a <tt>+cose</tt> media type.</t>
      </section>

    </section>
    <section anchor="algorithm-agility" numbered="true" toc="default">
      <name>Algorithm Agility and Operational Defaults</name>

      <section anchor="crypto-algorithms" numbered="true">
        <name>Cryptographic Algorithms</name>

        <section anchor="crypto-mti" numbered="true">
          <name>Mandatory-to-Implement (MTI)</name>
          <t>Conforming Attesters and Verifiers <bcp14>MUST</bcp14> implement:</t>
          <ul>
            <li>Signature: ECDSA on the P-256 curve with SHA-256 (<xref target="FIPS180-4"/>,
            <xref target="RFC7518"/>).</li>
            <li>Hash: SHA-256 (<xref target="FIPS180-4"/>).</li>
            <li>Encoding: canonical JSON per <xref target="canonical-encoding"/>.</li>
          </ul>
        </section>

        <section anchor="crypto-pq-migration" numbered="true">
          <name>Post-Quantum Migration</name>
          <t>This revision does not specify a post-quantum signature suite. A future revision
          <bcp14>MAY</bcp14> add a ML-DSA-based suite (<xref target="FIPS204"/>) as an alternate or
          successor signature algorithm. The PSEA proof and the Verifier acknowledgement are JWS objects
          that carry the signature algorithm in the JWS protected <tt>alg</tt> header (ES256 is the
          mandatory-to-implement algorithm at this revision) and a protected <tt>kid</tt> header that
          provides the key-selection and key-rotation story; no PSEA JWS uses a fixed
          out-of-header algorithm. A dedicated rejection outcome for an unsupported algorithm is reserved for a future revision that
          admits more than one signature algorithm.</t>
        </section>

        <section anchor="crypto-negotiation" numbered="true">
          <name>Algorithm Negotiation</name>
          <t>Algorithm negotiation between Attester and Verifier is out of scope at this revision; the
          MTI algorithm is the only conforming choice. A future revision MAY introduce negotiation via
          a Verifier-published algorithm preference list at a deployment-specific well-known URL.</t>
        </section>
      </section>

      <section anchor="operational-defaults" numbered="true">
        <name>Operational Defaults</name>
        <t>The following operational defaults are RECOMMENDED for conforming deployments:</t>
        <dl>
          <dt>Nonce TTL</dt>
          <dd>Deployment-defined; RECOMMENDED 60-300 seconds.</dd>
          <dt>Maximum proof lifetime (<tt>exp</tt> - <tt>iat</tt>)</dt>
          <dd>Deployment-defined (<xref target="freshness"/>); RECOMMENDED 300 seconds for
          synchronously-submitted proofs, extended only as far as a deployment's offline-to-sync interval
          requires for proofs produced offline.</dd>
          <dt>Clock-skew tolerance</dt>
          <dd>Deployment-defined; SHOULD NOT exceed 60 seconds (<xref target="freshness"/>).</dd>
          <dt>Operational grace window after enrollment</dt>
          <dd>Deployment-defined; RECOMMENDED 24 hours during which the Verifier accepts proofs from a
          freshly-enrolled Attester before requiring strict policy enforcement.</dd>
          <dt>Counter range</dt>
          <dd>The counter is a JSON integer in [0, 2<sup>53</sup> &#8722; 1]
          (<xref target="counter-model"/>); exhaustion is not a practical concern at any reasonable
          operational rate.</dd>
        </dl>
      </section>
    </section>

  </middle>

  <back>

    <references>
      <name>References</name>

      <references>
        <name>Normative References</name>

        <reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119">
          <front><title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date year="1997" month="March"/></front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
        </reference>

        <reference anchor="RFC3629" target="https://www.rfc-editor.org/info/rfc3629">
          <front><title>UTF-8, a transformation format of ISO 10646</title>
            <author fullname="F. Yergeau" initials="F." surname="Yergeau"/>
            <date year="2003" month="November"/></front>
          <seriesInfo name="RFC" value="3629"/>
        </reference>

        <reference anchor="RFC4648" target="https://www.rfc-editor.org/info/rfc4648">
          <front><title>The Base16, Base32, and Base64 Data Encodings</title>
            <author fullname="S. Josefsson" initials="S." surname="Josefsson"/>
            <date year="2006" month="October"/></front>
          <seriesInfo name="RFC" value="4648"/>
        </reference>

        <reference anchor="RFC6838" target="https://www.rfc-editor.org/info/rfc6838">
          <front><title>Media Type Specifications and Registration Procedures</title>
            <author fullname="N. Freed" initials="N." surname="Freed"/>
            <author fullname="J. Klensin" initials="J." surname="Klensin"/>
            <author fullname="T. Hansen" initials="T." surname="Hansen"/>
            <date year="2013" month="January"/></front>
          <seriesInfo name="BCP" value="13"/>
          <seriesInfo name="RFC" value="6838"/>
        </reference>

        <reference anchor="RFC7515" target="https://www.rfc-editor.org/info/rfc7515">
          <front><title>JSON Web Signature (JWS)</title>
            <author fullname="M. Jones" initials="M." surname="Jones"/>
            <author fullname="J. Bradley" initials="J." surname="Bradley"/>
            <author fullname="N. Sakimura" initials="N." surname="Sakimura"/>
            <date year="2015" month="May"/></front>
          <seriesInfo name="RFC" value="7515"/>
        </reference>

        <reference anchor="RFC7518" target="https://www.rfc-editor.org/info/rfc7518">
          <front><title>JSON Web Algorithms (JWA)</title>
            <author fullname="M. Jones" initials="M." surname="Jones"/>
            <date year="2015" month="May"/></front>
          <seriesInfo name="RFC" value="7518"/>
        </reference>

        <reference anchor="RFC7519" target="https://www.rfc-editor.org/info/rfc7519">
          <front><title>JSON Web Token (JWT)</title>
            <author fullname="M. Jones" initials="M." surname="Jones"/>
            <author fullname="J. Bradley" initials="J." surname="Bradley"/>
            <author fullname="N. Sakimura" initials="N." surname="Sakimura"/>
            <date year="2015" month="May"/></front>
          <seriesInfo name="RFC" value="7519"/>
        </reference>

        <reference anchor="RFC7797" target="https://www.rfc-editor.org/info/rfc7797">
          <front><title>JSON Web Signature (JWS) Unencoded Payload Option</title>
            <author fullname="M. Jones" initials="M." surname="Jones"/>
            <date year="2016" month="February"/></front>
          <seriesInfo name="RFC" value="7797"/>
        </reference>

        <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174">
          <front><title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date year="2017" month="May"/></front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
        </reference>

        <reference anchor="RFC8259" target="https://www.rfc-editor.org/info/rfc8259">
          <front><title>The JavaScript Object Notation (JSON) Data Interchange Format</title>
            <author fullname="T. Bray" initials="T." surname="Bray" role="editor"/>
            <date year="2017" month="December"/></front>
          <seriesInfo name="STD" value="90"/>
          <seriesInfo name="RFC" value="8259"/>
        </reference>

        <reference anchor="RFC8725" target="https://www.rfc-editor.org/info/rfc8725">
          <front><title>JSON Web Token Best Current Practices</title>
            <author fullname="Y. Sheffer" initials="Y." surname="Sheffer"/>
            <author fullname="D. Hardt" initials="D." surname="Hardt"/>
            <author fullname="M. Jones" initials="M." surname="Jones"/>
            <date year="2020" month="February"/></front>
          <seriesInfo name="BCP" value="225"/>
          <seriesInfo name="RFC" value="8725"/>
        </reference>

        <reference anchor="RFC8785" target="https://www.rfc-editor.org/info/rfc8785">
          <front><title>JSON Canonicalization Scheme (JCS)</title>
            <author fullname="A. Rundgren" initials="A." surname="Rundgren"/>
            <author fullname="B. Jordan" initials="B." surname="Jordan"/>
            <author fullname="S. Erdtman" initials="S." surname="Erdtman"/>
            <date year="2020" month="June"/></front>
          <seriesInfo name="RFC" value="8785"/>
        </reference>

        <reference anchor="RFC9334" target="https://www.rfc-editor.org/info/rfc9334">
          <front><title>Remote ATtestation procedureS (RATS) Architecture</title>
            <author fullname="H. Birkholz" initials="H." surname="Birkholz"/>
            <author fullname="D. Thaler" initials="D." surname="Thaler"/>
            <author fullname="M. Richardson" initials="M." surname="Richardson"/>
            <author fullname="N. Smith" initials="N." surname="Smith"/>
            <author fullname="W. Pan" initials="W." surname="Pan"/>
            <date year="2023" month="January"/></front>
          <seriesInfo name="RFC" value="9334"/>
        </reference>

        <reference anchor="RFC9711" target="https://www.rfc-editor.org/info/rfc9711">
          <front><title>The Entity Attestation Token (EAT)</title>
            <author fullname="L. Lundblade" initials="L." surname="Lundblade"/>
            <author fullname="G. Mandyam" initials="G." surname="Mandyam"/>
            <author fullname="J. O'Donoghue" initials="J." surname="O'Donoghue"/>
            <author fullname="C. Wallace" initials="C." surname="Wallace"/>
            <date year="2025" month="April"/></front>
          <seriesInfo name="RFC" value="9711"/>
        </reference>

        <reference anchor="FIPS180-4" target="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf">
          <front><title>Secure Hash Standard (SHS)</title>
            <author><organization>National Institute of Standards and Technology</organization></author>
            <date year="2015" month="August"/></front>
          <seriesInfo name="FIPS" value="180-4"/>
        </reference>
      </references>

      <references>
        <name>Informative References</name>

        <reference anchor="RFC6973" target="https://www.rfc-editor.org/info/rfc6973">
          <front><title>Privacy Considerations for Internet Protocols</title>
            <author fullname="A. Cooper" initials="A." surname="Cooper"/>
            <author fullname="H. Tschofenig" initials="H." surname="Tschofenig"/>
            <author fullname="B. Aboba" initials="B." surname="Aboba"/>
            <author fullname="J. Peterson" initials="J." surname="Peterson"/>
            <author fullname="J. Morris" initials="J." surname="Morris"/>
            <author fullname="M. Hansen" initials="M." surname="Hansen"/>
            <author fullname="R. Smith" initials="R." surname="Smith"/>
            <date year="2013" month="July"/></front>
          <seriesInfo name="RFC" value="6973"/>
        </reference>

        <reference anchor="RFC9052" target="https://www.rfc-editor.org/info/rfc9052">
          <front><title>CBOR Object Signing and Encryption (COSE): Structures and Process</title>
            <author fullname="J. Schaad" initials="J." surname="Schaad"/>
            <date year="2022" month="August"/></front>
          <seriesInfo name="RFC" value="9052"/>
        </reference>

        <reference anchor="RFC9470" target="https://www.rfc-editor.org/info/rfc9470">
          <front><title>OAuth 2.0 Step Up Authentication Challenge Protocol</title>
            <author fullname="V. Bertocci" initials="V." surname="Bertocci"/>
            <author fullname="B. Campbell" initials="B." surname="Campbell"/>
            <date year="2023" month="September"/></front>
          <seriesInfo name="RFC" value="9470"/>
        </reference>

        <reference anchor="FIPS204" target="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf">
          <front><title>Module-Lattice-Based Digital Signature Standard (ML-DSA)</title>
            <author><organization>National Institute of Standards and Technology</organization></author>
            <date year="2024" month="August"/></front>
          <seriesInfo name="FIPS" value="204"/>
        </reference>

        <reference anchor="WebAuthn-L1" target="https://www.w3.org/TR/webauthn-1/">
          <front><title>Web Authentication: An API for accessing Public Key Credentials Level 1</title>
            <author><organization>W3C</organization></author>
            <date year="2019" month="March"/></front>
          <refcontent>W3C Recommendation</refcontent>
        </reference>

        <reference anchor="WebAuthn-L2" target="https://www.w3.org/TR/webauthn-2/">
          <front><title>Web Authentication: An API for accessing Public Key Credentials Level 2</title>
            <author><organization>W3C</organization></author>
            <date year="2021" month="April"/></front>
          <refcontent>W3C Recommendation</refcontent>
        </reference>

      </references>
    </references>

    <section anchor="appendix-canonical-test-vectors" numbered="true" toc="default">
      <name>Canonical Encoding Test Vectors</name>

      <t>This appendix provides test vectors for verifying canonical-encoding implementations. An
      implementation that produces output matching every vector in this appendix is conformant with
      <xref target="canonical-encoding"/>.</t>

      <t>Implementations SHOULD construct their own canonical-encoding test suite covering the
      canonical-JSON inputs that PSEA hashes (the action payload bound by <tt>psea_payload_hash</tt> and
      the two-member object of the <tt>ueid</tt> derivation) plus
      boundary cases for case-sensitive sort, mixed-case keys, integer serialization, escape forms, and
      UTF-8 multi-byte sequences. The PSEA proof and the acknowledgement are JWS objects verified over
      their received octets and so do not depend on canonical encoding for signature verification; the
      two hashes taken over canonical JSON octets are the action-payload hash and the <tt>ueid</tt>
      derivation (<xref target="canonical-encoding"/>). Implementations
      <bcp14>SHOULD</bcp14> maintain machine-readable golden vectors covering these dimensions;
      consulting or publishing them is informative and not a normative requirement of this
      specification.</t>

      <t>A representative subset, demonstrating the case-sensitive sort rule of
      <xref target="canonical-encoding-keys"/>, is reproduced here:</t>

      <section anchor="tv-case-sensitive-sort" numbered="true">
        <name>Case-Sensitive Key Sort</name>
        <t>Input object (logical):</t>
        <sourcecode type="json"><![CDATA[
{ "endReason": "TtlExpired",
  "endedAt":   1700000060,
  "sessionId": "abc-123",
  "startedAt": 1700000000 }
]]></sourcecode>
        <t>Canonical bytes (the keys ordered by UTF-16 code-unit sort: 'R' = U+0052 = 82
        sorts before 'e' = U+0065 = 101, so "endReason" precedes "endedAt"), shown with one line break
        added for display only; the canonical form contains no line break:</t>
        <sourcecode type="text"><![CDATA[
{"endReason":"TtlExpired","endedAt":1700000060,
"sessionId":"abc-123","startedAt":1700000000}
]]></sourcecode>
      </section>

      <section anchor="tv-integer-serialization" numbered="true">
        <name>Integer Serialization</name>
        <t>The integer 0 is serialized as the single character <tt>0</tt>; the integer 42 as <tt>42</tt>;
        the integer 1700000000000 as <tt>1700000000000</tt>; no leading zeros, no decimal point, no
        exponent.</t>
      </section>

      <section anchor="tv-payload-hash" numbered="true">
        <name>Action-Payload Hash (End-to-End Worked Example)</name>
        <t>This vector demonstrates the first of the two normative uses of canonical encoding
        (<xref target="canonical-encoding"/>): the action-payload binding. Given the logical
        <tt>actionPayload</tt>:</t>
        <sourcecode type="json"><![CDATA[
{ "amount": 2500, "actionType": "transfer",
  "to": "alice", "currency": "EUR" }
]]></sourcecode>
        <t>the canonical encoding orders the keys by UTF-16 code-unit sort
        (<tt>actionType</tt> &lt; <tt>amount</tt> &lt; <tt>currency</tt> &lt; <tt>to</tt>), removes all
        inter-token whitespace, and serializes the integer with no decimal point or exponent, yielding the
        69 canonical octets:</t>
        <sourcecode type="text"><![CDATA[
{"actionType":"transfer","amount":2500,"currency":"EUR","to":"alice"}
]]></sourcecode>
        <t>SHA-256 of those octets is, in lowercase hex:</t>
        <sourcecode type="text"><![CDATA[
f0f8eb390ecdb3b312765cfe3a888c39ad4571bb94ddfc553230a4b85171e942
]]></sourcecode>
        <t>The <tt>psea_payload_hash</tt> claim is that digest in standard base64 with padding
        (<xref target="proof-top-level"/>):</t>
        <sourcecode type="text"><![CDATA[
8PjrOQ7Ns7MSdlz+OoiMOa1FcbuU3fxVMjCkuFFx6UI=
]]></sourcecode>
        <t>A Verifier re-canonicalizes the received <tt>actionPayload</tt>, recomputes this digest, and
        compares it byte-for-byte against the signed <tt>psea_payload_hash</tt>
        (<xref target="action-binding-verifier"/>). The same 32-byte digest encoded as base64url without
        padding — the encoding used by <tt>ueid</tt> and <tt>psea_user_hash</tt>, not by
        <tt>psea_payload_hash</tt> — is <tt>8PjrOQ7Ns7MSdlz-OoiMOa1FcbuU3fxVMjCkuFFx6UI</tt>; the
        difference (<tt>+</tt> versus <tt>-</tt>, trailing <tt>=</tt> versus none) illustrates why the
        per-claim encoding split of <xref target="proof-top-level"/> must be observed exactly.</t>
      </section>
    </section>

    <section anchor="appendix-conformance" numbered="true" toc="default">
      <name>Conformance</name>

      <t>This appendix specifies conformance requirements for Attester, Verifier, and Relying Party
      implementations.</t>

      <section anchor="conformance-roles" numbered="true">
        <name>Conformance Roles</name>
        <t>This document defines conformance for three roles: Attester, Verifier, Relying Party.</t>
      </section>

      <section anchor="conformance-attester" numbered="true">
        <name>Attester Conformance</name>

        <section anchor="conformance-attester-common" numbered="true">
          <name>Requirements</name>
          <t>A conforming Attester:</t>
          <ul>
            <li>MUST implement the canonical encoding (<xref target="canonical-encoding"/>) and produce
            byte-identical canonical output for every test vector in
            <xref target="appendix-canonical-test-vectors"/>.</li>
            <li>MUST generate its signing key in a hardware-backed key store and MUST NOT permit key
            extraction.</li>
            <li>MUST gate signing on a fresh, per-operation platform user-verification event for any proof
            that carries a human-presence (<tt>psea_uv</tt>) claim — binding exactly one user-verification
            event to exactly one proof signature, never a duration- or window-based authorization
            (<xref target="presence-via-signature"/>) — and MUST compute <tt>psea_payload_hash</tt> over
            the canonical action payload per <xref target="action-binding-producer"/>.</li>
            <li>MUST emit a signed proof body only when its internal trust state permits
            proof emission for the requested operation; MUST fail fast locally and emit no proof
            body otherwise.</li>
            <li>MUST treat a revoking-class rejection from the Verifier as an instruction to transition
            local state to revoked.</li>
          </ul>
        </section>

      </section>

      <section anchor="conformance-verifier" numbered="true">
        <name>Verifier Conformance</name>

        <section anchor="conformance-verifier-common" numbered="true">
          <name>Requirements</name>
          <t>A conforming Verifier MUST perform every check below. The list is the authoritative
          enumeration of the Verifier obligations stated normatively in the body; an implementation that
          omits any item is non-conforming. Where ordering matters it is stated; in all cases the
          signature MUST be verified before any claim is trusted, and all binding/freshness checks MUST
          complete before any state mutation (counter advance, ledger write, acknowledgement signing).</t>
          <ul>
            <li>MUST apply the JOSE header hardening of <xref target="jose-hardening"/> before trusting any
            claim: accept only <tt>alg</tt> = <tt>"ES256"</tt>, reject <tt>alg</tt> = <tt>"none"</tt>,
            ignore any <tt>jwk</tt> / <tt>jku</tt> / <tt>x5u</tt> header key material, resolve the
            verification key only from the enrolled record selected by <tt>kid</tt>, verify the
            protected-header <tt>typ</tt> equals <tt>"psea-proof+jwt"</tt>, and reject any unrecognized
            <tt>crit</tt> header parameter or an unencoded-payload (<xref target="RFC7797"/>
            <tt>b64</tt>:false) header.</li>
            <li>MUST verify the ECDSA P-256 / SHA-256 signature against the enrolled public key, and
            thereafter read claims only from the verified payload.</li>
            <li>MUST reject a proof whose <tt>eat_profile</tt> claim is absent or is not
            <tt>https://datatracker.ietf.org/doc/draft-yossif-psea/</tt> (<xref target="eat-profile"/>),
            except as permitted in <xref target="proof-schema"/>, and a proof whose
            <tt>psea_proof_version</tt> is an unknown value (<xref target="proof-versioning"/>).</li>
            <li>MUST validate freshness per <xref target="freshness"/>: reject a proof whose <tt>exp</tt>
            is in the past or whose <tt>iat</tt> is implausibly in the future, applying only a small
            bounded clock-skew tolerance (SHOULD NOT exceed 60 seconds) and a bounded maximum proof
            lifetime (<xref target="operational-defaults"/>).</li>
            <li>MUST, when it issued a challenge, reject a proof whose <tt>eat_nonce</tt> is absent or does
            not equal the issued value (<xref target="eat-profile-definition"/>), and MUST correlate the
            proof to the issued challenge using only the signed <tt>eat_nonce</tt> value, never an unsigned
            transport-body field (<xref target="freshness"/>).</li>
            <li>MUST require <tt>psea_uv.verified == true</tt> and reject otherwise, and MUST anchor the
            <tt>psea_uv</tt> claim to the authenticator's attested UV-enforcement where the attestation
            conveys it; where it cannot, MUST NOT treat human presence as attested and SHOULD reject for
            high-assurance operations (<xref target="presence-uv-claim"/>).</li>
            <li>MUST implement the fail-closed action-binding check of
            <xref target="action-binding-verifier"/>: re-canonicalize the <tt>actionPayload</tt>, hash it
            with SHA-256, byte-compare against <tt>psea_payload_hash</tt>, and reject on mismatch or on a
            missing payload, before producing any approval-bearing response. This check MUST precede the
            counter comparison.</li>
            <li>MUST verify the cross-replay binding of <xref target="cross-replay-binding"/>
            (<tt>psea_tier</tt>, <tt>psea_op</tt>, <tt>aud</tt>, <tt>iss</tt>) by case- and
            whitespace-exact comparison, after signature verification and before any state mutation.</li>
            <li>MUST, when the deployment has enrolled an expected caller identity for the operation,
            reject a proof whose signed <tt>psea_caller_package</tt> is absent or does not byte-exactly
            equal the expected value, before any state mutation (<xref target="caller-binding"/>); MUST
            refuse a proof that omits <tt>psea_caller_package</tt> where the deployment has registered
            caller identities, and SHOULD refuse it otherwise.</li>
            <li>MAY require that <tt>psea_user_hash</tt> match the value recorded for the subject at
            enrollment; a Verifier that does so MUST refuse a proof whose <tt>psea_user_hash</tt> is absent
            or does not byte-exactly equal the recorded value (<xref target="user-id-hash"/>).</li>
            <li>MUST enforce the monotonic <tt>psea_counter</tt> (<xref target="counter-model"/>): select
            the comparison bucket using only a cryptographically bound scope identifier, reject a value
            not strictly greater than the stored value for that scope, and treat the claim as a 64-bit
            unsigned integer.</li>
            <li>MUST maintain the global <tt>jti</tt> uniqueness check (<xref target="counter-model"/>),
            finalizing each <tt>jti</tt> exactly once and retaining finalized <tt>jti</tt> values for at
            least the <tt>exp</tt> validity window.</li>
            <li>MUST, in a horizontally-scaled deployment, serialize all submissions for a given
            (Attester, counter scope) to a single authority and keep the <tt>jti</tt> finalization index
            globally consistent across all nodes, so that the counter atomicity and <tt>jti</tt>
            uniqueness guarantees hold under sharding (<xref target="verifier-state-integrity"/>).</li>
            <li>MUST protect its replay state (per-scope high-water-mark counters and the finalized-<tt>jti</tt>
            set) against rollback, so a backup restore or failover does not lower a stored high-water mark
            or forget a finalized <tt>jti</tt> still replayable within the <tt>exp</tt> window
            (<xref target="verifier-state-integrity"/>).</li>
            <li>MUST, when the deployment-optional chain layer (<xref target="chain-entry"/>) is enabled,
            perform the strict-equality <tt>psea_chain_prev</tt> linkage check of
            <xref target="chain-verifier-behavior"/>; a Verifier that does not enable the chain layer
            is not required to implement it.</li>
            <li>MUST, in a deployment that requires <tt>psea_chain_prev</tt> or <tt>psea_signals_hash</tt>
            on every proof, refuse a proof that omits the required claim (<xref target="proof-schema"/>).</li>
            <li>MUST maintain an authoritative enrollment lifecycle (at minimum: active, suspended,
            revoked) as the trust gate, and MUST reject proofs whose enrollment is not active
            (<xref target="enrollment-lifecycle"/>).</li>
            <li>MUST NOT use any unsigned transport-body field (for example <tt>requestId</tt>
            or <tt>proofId</tt>) as an input to any security decision
            (<xref target="envelope-schemas"/>); <tt>signalReport</tt> is such a field unless the proof
            carries <tt>psea_signals_hash</tt> and the Verifier has appraised the report against that
            claim, in which case it is a committed field rather than an unsigned one.</li>
            <li>MUST reject non-conforming proofs; the concrete rejection vocabulary is
            deployment-specific.</li>
            <li>MUST produce integrity-protected Attestation Results for non-co-located Relying
            Parties (<xref target="action-binding-rp"/>).</li>
          </ul>
        </section>

      </section>

      <section anchor="conformance-relying-party" numbered="true">
        <name>Relying Party Conformance</name>
        <t>A conforming Relying Party:</t>
        <ul>
          <li>MUST gate the action on the Verifier's Attestation Result.</li>
          <li>MUST verify the integrity of the Attestation Result for non-co-located Verifiers.</li>
        </ul>
      </section>
    </section>

    <section anchor="changes-from-02" numbered="true">
      <name>Changes from draft-yossif-psea-02</name>

      <t>This revision states one known limitation, corrects four claim-set descriptions against the
      reference implementation, corrects the <tt>ueid</tt> derivation, withdraws the URN sub-namespace
      registration request, and corrects the restatement of RFC 8785. It also changes normative Verifier
      requirements in three places: a Verifier may act on <tt>psea_user_hash</tt> against the enrollment
      record (<xref target="user-id-hash"/>), a deployment may require <tt>psea_chain_prev</tt> and
      <tt>psea_signals_hash</tt> (<xref target="proof-schema"/>), and an absent
      <tt>psea_caller_package</tt> is refused (<xref target="caller-binding"/>). Each change is listed
      below. The <xref target="envelope-schemas"/> prohibition on using unsigned transport-body fields as
      security-decision inputs retains its force; the only edits to it are to the illustrative lists
      that named a field incorrectly.</t>

      <t><strong>The <tt>ueid</tt> change is a wire-format correction, not an editorial one.</strong> A
      conforming Attester built against -02 and one built against this revision derive different
      <tt>ueid</tt> values from the same <tt>deviceId</tt> and <tt>iss</tt>, so an implementation and any
      enrolled record that stores or recomputes a <tt>ueid</tt> must be updated together. The defect was
      found in review by Thomas Fossati on the IETF RATS mailing list.</t>

      <ul>
        <li>Added <xref target="open-ordering"/>, stating that the profile does not establish that a
        signature preceded the effect it authorizes, that authenticating the artifact is not the criterion
        for showing that it did, and that no ordering input is defined at this revision.</li>
        <li>Declared the OPTIONAL <tt>psea_signals_hash</tt> claim in the claim-set schema
        (<xref target="proof-schema"/>). The claim was already produced by Attesters and appraised by
        Verifiers in the reference implementation; under <tt>additionalProperties: false</tt> its absence
        from the schema meant a deployment following the reference implementation emitted proofs the
        published schema rejected.</li>
        <li>Corrected the description of <tt>psea_rp_context_hash</tt> (<xref target="proof-schema"/>),
        which was grouped with the diagnostic extension members as carrying no Verifier appraisal
        obligation and being ignorable. Where a deployment binds a relying-party request context, the
        claim carries that binding and is appraised.</li>
        <li>Corrected the transport-body enumeration in <xref target="envelope-schemas"/> and the matching
        Verifier obligation in <xref target="conformance-verifier"/>, both of which listed
        <tt>signalReport</tt> unconditionally among the fields that confer no authority. Where
        <tt>psea_signals_hash</tt> commits to the report and a Verifier has appraised it against that
        claim, the report is a committed field, as <tt>actionPayload</tt> is under
        <tt>psea_payload_hash</tt>. The prohibition itself is unchanged; only the enumeration of which
        fields it reaches is corrected.</li>
        <li>Corrected the <tt>ueid</tt> derivation (<xref target="proof-top-level"/>,
        <xref target="proof-schema"/>, <xref target="privacy-ueid"/>). Through -02 the derivation was
        <tt>0x01 || SHA-256(deviceId || iss)</tt>, a bare concatenation of two variable-length strings and
        therefore ambiguous: <tt>("acme", "corp.example")</tt> and <tt>("acmecorp", ".example")</tt> hash
        identically, so two distinct deployments could share one <tt>ueid</tt> — the opposite of what the
        pairwise derivation exists to achieve. The two inputs are now encoded as a canonical JSON object
        (<xref target="RFC8785"/>) before hashing, reusing the canonicalization this profile already
        requires (<xref target="canonical-encoding"/>) rather than introducing a second one. The pairwise
        approach itself is unchanged. It achieves per-deployment separation; it is not the strategy of
        <xref target="RFC9711"/>, Section 8.1, whose precondition, that the entity authenticate the relying
        party before deriving an identifier for it, this profile does not meet, since <tt>iss</tt> is the
        identifier the Attester has locally available rather than one the relying party has proven.</li>
        <li>Withdrew the request to register the <tt>urn:ietf:params:psea</tt> URN sub-namespace, and
        removed with it the fallback profile identifier written for the case where that registration was
        not granted (<xref target="iana-registries-overview"/>). The <tt>urn:ietf:params</tt> policy would
        have made the profile identifier depend on an IETF-stream publication decision, whereas EAT
        deliberately requires no registration for profile identifiers. The <tt>eat_profile</tt> value is
        now the HTTPS URI <tt>https://datatracker.ietf.org/doc/draft-yossif-psea/</tt>
        (<xref target="eat-profile"/>). With no registration requested there is no non-grant case and
        nothing to fall back from, so the fallback clause is removed rather than restated. This changes the
        <tt>eat_profile</tt> value on the wire; Attester and Verifier must be updated together. Withdrawing
        the request was recommended in review by Thomas Fossati on the IETF RATS mailing list.</li>
        <li>Recorded in the "Extensibility model" entry of <xref target="eat-profile-definition"/> that
        whether this profile's strict rejection of undeclared claims conforms to
        <xref target="RFC9711"/>, Section 1.2 is under discussion on the IETF RATS mailing list. The rule
        itself is unchanged in this revision.</li>
        <li>Corrected seven <xref target="RFC9711"/> section citations that pointed at the wrong sections
        of the published RFC. The <tt>eat_profile</tt> claim is Section 4.3.2, not Section 4.3.4 (four
        occurrences: <xref target="eat-profile"/>, the <em>Profile identifier</em> entry and the
        claim-tree annotation, and the claim-set schema). EAT profiles are Section 6, not Section 7
        (three occurrences, including the heading of <xref target="eat-profile-definition"/>); Section 7
        of <xref target="RFC9711"/> is "Encoding and Collected CDDL" and is unrelated. The publication
        date in the <xref target="RFC9711"/> reference entry is also corrected to April 2025. No
        requirement is affected: every corrected citation is a pointer, and the normative text it
        accompanies is unchanged.</li>
        <li>Narrowed three citations that named <xref target="RFC9711"/>, Section 4.2.1 for the RAND type
        tag to Section 4.2.1.1, where Table 1 defines the type byte (<xref target="proof-top-level"/>,
        <xref target="proof-schema"/>, <xref target="privacy-ueid"/>). Section 4.2.1 defines the
        <tt>ueid</tt> claim and does not mention the RAND type at all, so a reader checking the
        RAND-tag rationale of <xref target="privacy-ueid"/> against Section 4.2.1 would not find it
        there. The citation of Section 4.2.1 in <xref target="eat-profile"/> is for the <tt>ueid</tt>
        claim itself and is unchanged.</li>
        <li>Changed <xref target="user-id-hash"/> and <xref target="conformance-verifier"/>: a Verifier
        <bcp14>MAY</bcp14> require <tt>psea_user_hash</tt> to match the value recorded at enrollment and,
        where it does, <bcp14>MUST</bcp14> refuse an absent or differing value; -02 prohibited any
        authorization decision based on the claim. The claim is stated as a pairwise, deployment-local
        commitment that establishes no identity, because a deployment that records a subject at
        enrollment can then detect a proof produced for a different subject identifier.</li>
        <li>Added to <xref target="proof-schema"/> and <xref target="conformance-verifier"/> that a
        deployment <bcp14>MAY</bcp14> require <tt>psea_chain_prev</tt> or <tt>psea_signals_hash</tt> and
        that its Verifier <bcp14>MUST</bcp14> refuse a proof omitting the required claim; both remain
        OPTIONAL in the schema, because omission would bypass the chain linkage check or leave the
        auxiliary document unsigned.</li>
        <li>Changed <xref target="caller-binding"/> and <xref target="conformance-verifier"/>: a Verifier
        <bcp14>MUST</bcp14> refuse an absent <tt>psea_caller_package</tt> where the deployment has
        registered caller identities and <bcp14>SHOULD</bcp14> refuse it otherwise, which is stricter
        than -02, because an absent claim would otherwise bypass the caller binding.</li>
        <li>Corrected <xref target="canonical-encoding-keys"/>, <xref target="canonical-encoding-strings"/>,
        <xref target="canonical-encoding-conformance"/>, and <xref target="appendix-canonical-test-vectors"/>:
        object keys sort by UTF-16 code units (<xref target="RFC8785"/>, Section 3.2.3), not by UTF-8
        bytes; the string escapes for quotation mark, reverse solidus, and the five named controls are
        two-character, and a lone surrogate is an error (<xref target="RFC8785"/>, Section 3.2.2.2); the
        platform notes, including the Swift advice, are corrected to match. The earlier text contradicted
        the RFC that <xref target="canonical-encoding"/> already required in full.</li>
        <li>Rewrote the identifier discussion in <xref target="privacy-ueid"/> and the "UEID type" entry of
        <xref target="eat-profile-definition"/>: the carried value is not preserved across factory reset,
        so <xref target="RFC9711"/>, Section 4.2.2 <tt>sueids</tt> is the correct claim for it;
        <tt>ueid</tt> is retained for wire continuity only, the description of <tt>sueids</tt> as an array
        is corrected to a labelled map, and migration to <tt>sueids</tt> is the stated intent for the next
        wire-incompatible revision. No wire change.</li>
        <li>Recorded that <xref target="canonical-encoding"/> now makes canonical encoding normative for
        two uses, the action-payload binding and the <tt>ueid</tt> derivation, rather than one, and
        corrected the two sentences of <xref target="appendix-canonical-test-vectors"/> that still named
        one use. Removed the RFC 3553 and RFC 8126 references, which were cited only by the withdrawn URN
        sub-namespace registration.</li>
        <li>Narrowed the nested-tokens citation in <xref target="eat-profile-definition"/> from
        <xref target="RFC9711"/>, Section 4.2.18 to Section 4.2.18.3, which defines nested tokens, and
        rewrapped over-long lines in the <xref target="proof-schema"/> schema and in
        <xref target="appendix-canonical-test-vectors"/>; no content of either changes.</li>
        <li>Added to <xref target="proof-schema"/> that a Verifier <bcp14>MAY</bcp14> additionally accept
        the <tt>eat_profile</tt> value <tt>urn:ietf:params:psea:eat-profile:2</tt> when every other
        requirement of this profile is met, and qualified the matching rejection rules in
        <xref target="eat-profile"/> and <xref target="conformance-verifier"/>. In deployed
        implementations the <tt>:2</tt> suffix marked the corrected <tt>ueid</tt> derivation;
        <tt>psea_proof_version</tt> remains the wire-format revision marker
        (<xref target="proof-versioning"/>). No claim is added.</li>
        <li>Declared the OPTIONAL <tt>psea_stepup_selection</tt> claim in the claim-set schema
        (<xref target="proof-schema"/>). As with <tt>psea_signals_hash</tt>, the claim was already
        produced by Attesters in the reference implementation when a deployment issues a step-up, and
        under <tt>additionalProperties: false</tt> its absence from the schema meant such proofs were
        rejected by the published schema. Its construction is deployment-defined and out of scope. No
        wire change.</li>
        <li>Stated in <xref target="proof-schema"/> that <tt>urn:ietf:params:psea:eat-profile:2</tt> is
        not a registered URN and that this document requests no registration for it.</li>
      </ul>
    </section>

    <section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>Thomas Fossati reviewed this profile on the IETF RATS mailing list and found the
      <tt>ueid</tt> derivation ambiguity and the unnecessary URN registration request, both corrected in
      this revision. Laurence Lundblade and Giridhar Mandyam answered which UEID claim fits a value that
      does not survive factory reset. Mikhail Sergeev supplied the criterion for ordering evidence stated
      in <xref target="open-ordering"/>. Iman Schrock found schema-validation defects in the reference
      Verifier. Songbo Bu wrote an independent Verifier and cross-ran the conformance suite against it,
      and Lars Kroehl independently recomputed the canonical encoding. Any remaining errors are the
      author's.</t>
    </section>

  </back>
</rfc>
