<?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-02" 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-02"/>

    <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="June" day="9"/>

    <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 one use, in 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>
      </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 (Case-Sensitive Unicode Code-Point Sort)</name>
        <t>Object members <bcp14>MUST</bcp14> be ordered by case-sensitive ascending Unicode code-point
        comparison of their keys, byte by byte after UTF-8 encoding. 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
        Unicode code-point value of their differing character; 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>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: the JSON six-
        character escape forms for U+0022 (quotation mark), U+005C (reverse solidus), U+0008, U+000C,
        U+000A, U+000D, and U+0009 <bcp14>MUST</bcp14> be used; the "\uXXXX" form <bcp14>MUST</bcp14> use
        lowercase hexadecimal digits; surrogate pairs <bcp14>MUST</bcp14> be emitted as written by the
        producer (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>; they <bcp14>MUST</bcp14> emit canonical
          bytes via a manual sort using a case-sensitive comparator (for example, Swift's
          <tt>Dictionary.keys.sorted()</tt> with the default less-than operator on String, which is
          case-sensitive).</dd>

          <dt>JavaScript / TypeScript platforms</dt>
          <dd><tt>Array.prototype.sort()</tt> on string keys performs a case-sensitive Unicode code-point
          sort, 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> performs case-sensitive Unicode code-point comparison; 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 performs case-sensitive Unicode code-point sort.
          The <tt>json</tt> module's <tt>sort_keys=True</tt> matches this section's rule.</dd>

          <dt>Go, Rust</dt>
          <dd>Default string comparison on both languages is case-sensitive byte-wise on UTF-8, which
          matches this section's rule for keys composed entirely of ASCII characters.</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 7. 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.4) with the stable profile identifier
        <tt>urn:ietf:params:psea:eat-profile:1</tt> (or the fallback identifier of
        <xref target="iana-urn-namespace"/> if the URN registration is not granted in the publication
        stream). A Verifier <bcp14>MUST</bcp14> reject a
        proof whose <tt>eat_profile</tt> claim is absent or carries any other value.</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 7)</name>
          <t>This subsection states the profile in the form <xref target="RFC9711"/>, Section 7 calls for,
          so the <tt>eat_profile</tt> commitment is auditable in one place. A token claiming
          <tt>urn:ietf:params:psea:eat-profile:1</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.4)
            <bcp14>MUST</bcp14> equal the URI <tt>urn:ietf:params:psea:eat-profile:1</tt> — or, if the
            <tt>urn:ietf:params:psea</tt> registration is not granted in the publication stream, the single
            fallback URI fixed by the published document (<xref target="iana-urn-namespace"/>). A published
            document fixes exactly one value for this claim.</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): 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>); this is a deliberate, disclosed choice whose rationale and migration
            path are 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.</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: 0x01 || SHA-256(deviceId || iss),
  |                 ;   33 bytes; per-issuer (pairwise): the same
  |                 ;   device yields a distinct ueid per deployment
  |                 ;   (RFC 9711 Sec 4.2.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   ; "urn:ietf:params:psea:eat-profile:1"
  |                 ;   (RFC 9711 Sec 4.3.4); 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.
        The three OPTIONAL members <tt>psea_chain_pending</tt>, <tt>psea_last_confirmed_head</tt>, and
        <tt>psea_rp_context_hash</tt> are registered in the schema below as opaque, non-appraised
        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>

        <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 RAND-type UEID: base64url
                              (no padding) of the 33-byte string
                              0x01 || SHA-256(deviceId || iss). 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": ["urn:ietf:params:psea:eat-profile:1"],
              "description": "EAT profile identifier (RFC 9711 Sec 4.3.4).
                              REQUIRED. The enum value is the requested
                              primary identifier; if the urn:ietf:params:psea
                              registration is not granted in the publication
                              stream, the published document fixes the single
                              fallback URI instead (see the IANA URN
                              sub-namespace section). A published document
                              fixes exactly one 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_user_hash": { "type": "string",
              "pattern": "^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
              "description": "OPTIONAL. base64url (no padding) of
                              SHA-256(subject_id); audit-attribution only.
                              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_rp_context_hash": {
      "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
        binds a relying-party request context." }
  },
  "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>, <tt>signalReport</tt>, and <tt>proofId</tt>
        are correlation and operational hints only and confer no authority.</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.</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>MUST NOT</bcp14> make an authorization decision based on the per-proof
        <tt>psea_user_hash</tt> claim of a PSEA proof. Subject attribution is for ledger and audit purposes
        only. The cryptographic chain of authorization at the Verifier rests on the device-bound signature,
        the monotonic counter, and the presence Evidence (<xref target="presence-via-signature"/>); the
        Attester's commitment to a subject identifier is an additional audit-trail property, not a security
        gate.</t>

        <t>The per-proof <tt>psea_user_hash</tt> is distinct from any enrollment-bound subject binding a
        deployment records at enrollment time; that binding, and its use in decision-bearing checks, is
        deployment-specific and out of scope. The per-proof signed value on the PSEA
        proof is a commitment whose role is solely audit-trail attribution.</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 deployment that
          has not enrolled an expected caller identity <bcp14>MUST NOT</bcp14> 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>

    </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(deviceId || iss)</tt> (<xref target="proof-top-level"/>), so the same device
        yields a different <tt>ueid</tt> for each distinct <tt>iss</tt>. 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>). This is a deliberate, disclosed design choice, not an oversight, and it warrants an
        explicit justification because the RAND type tag nominally denotes a randomly generated UEID
        (<xref target="RFC9711"/>, Section 4.2.1), whereas the value this profile places under that tag is
        the deterministic derivation <tt>SHA-256(deviceId || iss)</tt>. The cross-issuer unlinkability this
        profile requires is supplied here by the per-issuer derivation (a distinct, non-correlatable
        value per <tt>iss</tt>), not by randomness; within a single issuer the value is intentionally
        stable, because this profile depends on binding a device's successive proofs to one
        enrollment. The semantically purer vehicle for a stable-but-pairwise identifier of this kind is the
        EAT semipermanent UEID (<tt>sueids</tt>) claim (<xref target="RFC9711"/>), which is defined
        precisely for non-random, semipermanent per-relationship identifiers.</t>

        <t>The trade-off this revision weighed is the following. The <tt>sueids</tt> claim is the more exact
        EAT semantics, but adopting it adds a second identifier claim to the wire with its own array
        structure and per-entry labelling semantics, which every conforming Attester and every conforming
        Verifier must then produce and appraise. The RAND-tagged <tt>ueid</tt> reuses a single,
        already-specified 33-octet UEID encoding that all parties already implement, and the per-issuer
        derivation gives the required cross-issuer confinement (<xref target="privacy-ueid"/>) without a
        second claim. This revision therefore chose one stable per-issuer <tt>ueid</tt> over introducing
        <tt>sueids</tt> handling, accepting the looser fit against the RAND tag's "randomly generated"
        connotation as the cost of that simplicity. The choice is recorded here so a consumer is not misled
        into treating the value as random: it is a per-issuer pseudonym, deterministic by construction and
        verifiable from the enrolled <tt>deviceId</tt> input. A future revision <bcp14>MAY</bcp14> migrate
        the per-issuer (and per-Relying-Party) identifier to the <tt>sueids</tt> form; that migration is the
        recommended direction for aligning the wire encoding with the EAT identifier semantics, and it
        composes with the per-Relying-Party unlinkability direction noted below.</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 <tt>SHA-256(deviceId || iss)</tt> with
        a known <tt>iss</tt> is recoverable by brute force, defeating the confinement the per-issuer
        derivation is meant to provide. 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 and register a URN sub-namespace:</t>
        <ul>
          <li>One media-type registration (<xref target="iana-media-types"/>).</li>
          <li>Registration of the <tt>urn:ietf:params:psea</tt> URN sub-namespace
          (<xref target="iana-urn-namespace"/>).</li>
        </ul>
        <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 anchor="iana-urn-namespace" numbered="true">
        <name>URN Sub-namespace Registration (urn:ietf:params:psea)</name>
        <t>This document requests that IANA register the <tt>psea</tt> sub-namespace within the
        <tt>urn:ietf:params</tt> hierarchy, per the procedures of <xref target="RFC3553"/> (BCP 73). The
        registration enables stable, IANA-managed identifiers under <tt>urn:ietf:params:psea</tt>, of
        which the EAT profile identifier <tt>urn:ietf:params:psea:eat-profile:1</tt>
        (<xref target="eat-profile"/>) is the first assignment.</t>
        <t>The registration template (per <xref target="RFC3553"/>, Section 4.3) is:</t>
        <dl>
          <dt>Registry name</dt>
          <dd>PSEA (Post-Session Execution Assurance)</dd>
          <dt>Specification</dt>
          <dd>This document.</dd>
          <dt>Repository</dt>
          <dd>Identifier strings assigned under <tt>urn:ietf:params:psea</tt> by this and future PSEA
          documents.</dd>
          <dt>Index value</dt>
          <dd>Sub-strings of the form <tt>urn:ietf:params:psea:&lt;class&gt;:&lt;id&gt;</tt> are assigned
          by Specification Required <xref target="RFC8126"/>. The initial assignment is
          <tt>urn:ietf:params:psea:eat-profile:1</tt>.</dd>
        </dl>
        <t>The <tt>urn:ietf:params</tt> hierarchy is managed conservatively and its sub-namespace
        registrations are normally associated with the IETF document stream. If this document is published
        on a stream for which a <tt>urn:ietf:params:psea</tt> sub-namespace registration is not granted, or
        if the registration is otherwise not completed, the profile identifier
        <bcp14>MUST</bcp14> fall back to the stable HTTPS URI
        <tt>https://yuthent.com/psea/eat-profile/1</tt>, under a namespace the author controls, carrying
        identical semantics. Conforming implementations <bcp14>MUST</bcp14> treat whichever single value
        this document's final published form fixes as the canonical <tt>eat_profile</tt> value; the two
        forms are not interchangeable on the wire within one deployment. The remainder of this document
        uses the <tt>urn:ietf:params:psea:eat-profile:1</tt> form as the requested primary identifier.</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="RFC3553" target="https://www.rfc-editor.org/info/rfc3553">
          <front><title>An IETF URN Sub-namespace for Registered Protocol Parameters</title>
            <author fullname="M. Mealling" initials="M." surname="Mealling"/>
            <author fullname="L. Masinter" initials="L." surname="Masinter"/>
            <author fullname="T. Hardie" initials="T." surname="Hardie"/>
            <author fullname="G. Klyne" initials="G." surname="Klyne"/>
            <date year="2003" month="June"/></front>
          <seriesInfo name="BCP" value="73"/>
          <seriesInfo name="RFC" value="3553"/>
        </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="RFC8126" target="https://www.rfc-editor.org/info/rfc8126">
          <front><title>Guidelines for Writing an IANA Considerations Section in RFCs</title>
            <author fullname="M. Cotton" initials="M." surname="Cotton"/>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <author fullname="T. Narten" initials="T." surname="Narten"/>
            <date year="2017" month="June"/></front>
          <seriesInfo name="BCP" value="26"/>
          <seriesInfo name="RFC" value="8126"/>
        </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="2024" month="December"/></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 input that PSEA hashes (the action payload bound by <tt>psea_payload_hash</tt>) 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; only
      the action-payload hash is taken over canonical JSON octets. 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 case-sensitive Unicode code-point sort: 'R' = U+0052 = 82
        sorts before 'e' = U+0065 = 101, so "endReason" precedes "endedAt"):</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 one normative use 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 case-sensitive Unicode code-point 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>urn:ietf:params:psea:eat-profile:1</tt> (<xref target="eat-profile"/>), 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"/>); a
            deployment that has not enrolled an expected caller identity MUST NOT treat the claim's
            absence as failure.</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 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>,
            <tt>proofId</tt>, <tt>signalReport</tt>) as an input to any security decision
            (<xref target="envelope-schemas"/>).</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>

  </back>
</rfc>
