[Secdispatch] Re: [saag] Re: Re: draft-yossif-psea-0 2 posted — re-scoped as an EAT token profile (diff)
Mohamad Khalil-Yossif <[email protected]> Thu, 11 Jun 2026 11:46:35 +0300
| Newsgroups | gmane.ietf.secdispatch,gmane.ietf.saag |
|---|---|
| Message-ID | <[email protected]> |
--===============0126786695628512581== Content-Type: multipart/alternative; boundary="----=_NextPart_50449147.418924146652" ------=_NextPart_50449147.418924146652 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable One correction to my own matrix, before it gets folded into the survey =E2=80=94 two rows where I described deployment behavior rather than what -02 normatively specifies: - enforcement class: -02 does not define a fixed set of tiers. The =C2=A0 profile carries psea_tier as a signed, opaque claim bound into the =C2=A0 cross-replay check (Sections 1.2, 3.13.4); the tier value space and =C2=A0 the operational mapping of applications to levels are deliberately =C2=A0 deployment policy, outside the token profile. - relying-party result: the normative requirement is an =C2=A0 integrity-protected Attestation Result gating the action (3.13.3, =C2=A0 B.4). Whether a deployment requires synchronous verifier =C2=A0 interaction before execution, or accepts offline-produced proofs =C2=A0 with deferred submission, is per-deployment policy (Table 1) =E2=80= =94 the =C2=A0 profile fixes the evidence and the verification rules, not the =C2=A0 enforcement topology. The boundary is the point: -02 is deliberately a pure token profile =E2=80= =94 wire format, claim set, verifier obligations =E2=80=94 with enforcement semantics left to deployment profiles. For the survey's "profile-specific" axis that's probably the more useful characterization of PSEA anyway. Mohamad Khalil-Yossif On 11/06/2026 11:43:21, Mohamad Khalil-Yossif <[email protected]> wrote: Songbo, Iman, all, Songbo =E2=80=94 following up with substance on both of your notes. On the PSEA suggestions: the processing-model paragraph and the negative-test table are going into the next revision largely as you framed them. The invariant you want testable =E2=80=94 a rejected proof leaves the counter high-water mark, finalized-jti set, chain entry, ledger, and any approval-bearing result unchanged =E2=80=94 is how the existing reference verifier already behaves (rejection happens before any state mutation; counter advance and jti finalization are one atomic transaction), so it will be stated as a MUST backed by negative vectors in your failure grouping. Your offline / server-state boundary holds for PSEA exactly as you described: signature, action-binding, audience, and user-verification evidence are offline-checkable; replay (jti / counter high-water mark), enrollment-active, and revocation are necessarily verifier-state decisions. "Reject before any approval-bearing state mutation" as the invariant common to the profiles is the right conformance boundary. And so the comparison matrix has both columns =E2=80=94 here is PSEA on the nine axes you proposed, all of it normative in -02: - action canonicalization/hash: JCS (RFC 8785) =E2=86=92 SHA-256; =C2=A0 non-canonical input rejected. - approver/subject binding: a dedicated hardware-attested device key; =C2=A0 the profile requires hardware-resident key material with enforced =C2=A0 user verification, evidenced by a platform attestation chain. It is =C2=A0 deliberately device- and vendor-agnostic =E2=80=94 any platform that= can =C2=A0 produce that evidence can conform. - operation/audience binding: psea_tier, psea_op, aud, iss byte-exact =C2=A0 in the signed claim set; optional signed caller-identity binding. - replay key: jti plus a per-scope strictly-increasing counter. - one-time state: counter compare-advance and jti finalization in one =C2=A0 atomic transaction; replay terminally refused. Same property as =C2=A0 EP's nonce-consumption, enforced differently. - trust root: the platform key-attestation chain =E2=80=94 never the operat= or. - relying-party result: signed proof token; the highest enforcement =C2=A0 tier additionally requires a server ACK before execution, =C2=A0 fail-closed. - enforcement class: four tiers (P/S/E/A), from passive evidence to =C2=A0 per-action online fail-closed authorization. - residual risk: WYSIWYS / display integrity and natural-person =C2=A0 identity =E2=80=94 both explicitly out of scope, stated in the draft. Implementation status, in the same terms as the other efforts: a complete reference implementation across two major mobile platforms plus a deployed backend verifier, with cross-platform golden vectors locking the canonicalization byte-for-byte. Canonical vectors are in Appendix A of -02; a negative-case set in your failure grouping exists on this side as well =E2=80=94 happy to point you at it for the vector pass. Mohamad Khalil-Yossif On 11/06/2026 11:07:31, Iman Schrock <[email protected]> wrote: Songbo =E2=80=94 Thank you =E2=80=94 running the vectors yourself is the validation that mat= ters; reproduction beats assertion, and I'm glad Node and Web agreed on you= r machine too. Agreed completely: the offline / server-state split is a conformance bounda= ry, not an implementation detail, so it gets a first-class row in the matri= x rather than a footnote. Your phrasing of the invariant =E2=80=94=C2=A0rej= ect before any approval-bearing state mutation=C2=A0=E2=80=94 is the right = one to state once in the core section and inherit per profile. And it maps = to PSEA exactly as you put it: the signed proof can carry or bind the jti /= counter / freshness material, but the high-water-mark decision is verifier= state on both sides. That convergence =E2=80=94 one invariant, two attestation tiers, enforced i= n the same place =E2=80=94 is what makes the dispatch question concrete rat= her than asserted. Send the off-list nits whenever; I'll fold them in with = the skeleton. Iman, On Thu, Jun 11, 2026 at 12:46 AM Songbo Bu <[email protected] [mailto:ki= [email protected]]> wrote: Hi Iman, Thanks, this split is useful. I took a quick look at the new signoff vectors and ran the signoff conformance runner. The current shape matches the verifier boundary you describe: 9 vectors, 8 reject cases, all at the offline assertion layer, with the Node and Web Crypto verifiers agreeing. One thing I would make explicit in the survey matrix is that the offline/server-state split is not just an implementation detail; it is a conformance boundary: - verifier-state boundary: an offline assertion verifier can reject structural, cryptographic, action-binding, audience, and UV failures; replay / one-time consumption, enrollment-active, and revocation freshness require server-state or ledger-side evaluation. That maps cleanly to PSEA too. The signed proof can carry or bind the jti/counter/freshness material, but the high-water mark decision is still verifier state. So I would treat "reject before approval-bearing state mutation" as the common invariant across both profiles. I'll take a closer pass over the actual vectors and send concrete nits off-list once the survey skeleton lands. Best, Songbo Bu On Thu, 11 Jun 2026 00:38:02 -0700, Iman Schrock <[email protected] [m= ailto:[email protected]]> wrote: > Songbo =E2=80=94 > > The processing-model + negative-test-table framing is the > right call, and the decision-matrix axes you list are exactly the compari= son > I'd want too =E2=80=94 formats will differ, the verifier decision shouldn= 't. > > Two concrete things from my side: > > - I just > published EP's Class-A signoff negative vectors as a tagged battery, > grouped on your failure classes =E2=80=94 structural, cryptographic, > action-binding, operation/audience, lifecycle/UV =E2=80=94 and run throug= h two > independent verifiers. It's a starting table for the pass you offered; > please shred it. One honest scope line I drew, which I think matters for > the survey: I split OFFLINE assertion checks (crypto, action- binding, > audience, UV) from SERVER-STATE checks (replay / one-time consumption =E2= =80=94 > the nonce is our global consumption key =E2=80=94 and enrollment-active).= A > relying party verifying an EP receipt offline gets the former; the latter > live at the consume/ledger step. PSEA's counter/jti high-water-mark > invariant is the same idea on your side. > > - Here's > EP filled into your matrix, as a strawman for the joint note: > > - action > canonicalization/hash: JCS (RFC 8785) of the action object =E2=86=92 SHA-= 256 > context hash; non-canonical input MUST be rejected. > > - approver/subject > binding: device-bound approver key (Class A, WebAuthn P-256); approver_id > resolved via a signed Approver Directory. > > - operation/audience > binding: action_hash + policy ref + initiator in the signed context; > WebAuthn rp_id for the relying party. > > - replay > key: 128-bit CSPRNG nonce, globally unique =E2=80=94 it IS the consumptio= n key. > > - one-time > state: consumed at most once, globally; replay across > sessions/operators/time terminally refused. > > - trust > root: the signed Approver Directory; MUST NOT default to the EP operator. > > - relying-party > result: the offline-verifiable Trust Receipt (signed authorization + > consumption record + Merkle anchor). > > - enforcement > class: Class A device-bound (recommended) vs Class C operator-custodied > (legacy, weaker); execution-side enforcement is the strongest deployment > class. > > - residual > risk: presentation attack / WYSIWYS, and key-revocation timing =E2=80=94 = both > stated, neither claimed solved. > > The two profiles land on different rows only at assurance > tier (your dedicated-hardware attestation vs. our commodity authenticator= ) and > scope (we add enrollment + separation of duties). Same decision matrix. > > I'll fold this into the survey skeleton I'm roughing out and > send the whole thing off-list for you and Mohamad to edit. > > On Thu, Jun 11, 2026 at 12:24 AM Songbo Bu <[email protected] [mailto:= [email protected]]> wrote: > > Hi Mohamad, Iman, all, > > Thanks, Mohamad. And Iman, that is a useful data point. > > For PSEA, my first pass would be to make the verifier-side text more > > like a processing model, plus a small set of negative verifier cases. > > That seems more useful to implementers than adding another long > > requirements list. > > For the processing model, something along these lines: > > > On receipt of a proof submission, the Verifier treats the signed proof,= the submitted action payload, enrollment state, and replay state as one at= omic authorization decision. The Verifier first validates the signed token = and then uses only claims from the verified payload, together with server-s= ide enrollment and replay state, to evaluate the proof. Before any counter = advance, ledger write, acknowledgement signing, or success-bearing Attestat= ion Result, the Verifier canonicalizes the submitted actionPayload, recompu= tes the payload hash, and compares it byte-for-byte with the signed psea_pa= yload_hash. Any mismatch, missing required signed claim, stale freshness va= lue, replayed jti/counter, inactive enrollment state, or failed cross-repla= y binding is a non-approval outcome. > > I would then group failures into a small negative-test table rather > > than leave them spread across the document: > > - structural / cryptographic: wrong profile, unsupported alg, bad > > signature, unrecognized critical header; > > - action-binding: missing actionPayload, payload-hash mismatch, > > non-canonical payload, or psea_payload_hash encoding mismatch; > > - cross-replay: psea_tier, psea_op, aud, iss, or psea_caller_package > > not matching the expected operation context; > > - freshness / replay: expired proof, implausible iat, wrong eat_nonce, > > reused jti, or non-increasing counter; > > - lifecycle / trust gate: enrollment not active, revoked/suspended > > key, or user-verification evidence insufficient for the required > > assurance level; > > - optional chain layer: psea_chain_prev mismatch when that layer is enabl= ed. > > The key point I would make testable is that all of those failures have > > the same security effect: the verifier rejects before any state > > mutation that could make the proof look consumed or approved. In > > test-vector terms, a rejected proof should leave the counter > > high-water mark, finalized-jti set, chain entry, approval ledger, and > > approval-bearing Attestation Result unchanged or absent. > > For Iman's draft, I think the useful comparison is not whether the > > token formats are identical, but whether the verifier decision > > matrices line up: action canonicalization/hash, approver or subject > > binding, operation/audience binding, replay key, one-time state, trust > > root, relying-party result, enforcement class, and residual risk. I > > can take a pass over that next, especially around Class A signoff > > negative vectors and deployment-class claims. > > Best, > > Songbo Bu > > -- > > Iman Schrock > Founder & CEO > > EMILIA Protocol | Trust Before High-Risk Action > Eye warns. EP verifies. Signoff owns. > > [emiliaprotocol.ai [http://emiliaprotocol.ai]](http://emiliaprotocol.ai [= http://emiliaprotocol.ai]) | GitHub > [email protected] [mailto:[email protected]] -- Iman Schrock Founder & CEO EMILIA Protocol | Trust Before High-Risk Action Eye warns. EP verifies. Signoff owns. emiliaprotocol.ai [http://emiliaprotocol.ai] | GitHub [email protected] [mailto:[email protected]] ------=_NextPart_50449147.418924146652 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: quoted-printable <div id=3D"__MailbirdStyleContent" style=3D"font-size: 10pt;font-family: Ca= scadia Code;color: #1A1A1A;text-align: left" dir=3D"ltr"><div><span style= =3D"font-size: 13.3333px">One correction to my own matrix, before it gets f= olded into the</span></div><div><span style=3D"font-size: 13.3333px">survey= =E2=80=94 two rows where I described deployment behavior rather than</span= ></div><div><span style=3D"font-size: 13.3333px">what -02 normatively speci= fies:</span></div><div><span style=3D"font-size: 13.3333px"><br></span></di= v><div><span style=3D"font-size: 13.3333px">- enforcement class: -02 does n= ot define a fixed set of tiers. The</span></div><div><span style=3D"font-si= ze: 13.3333px"> profile carries psea_tier as a signed, opaque claim b= ound into the</span></div><div><span style=3D"font-size: 13.3333px"> = cross-replay check (Sections 1.2, 3.13.4); the tier value space and</span><= /div><div><span style=3D"font-size: 13.3333px"> the operational mappi= ng of applications to levels are deliberately</span></div><div><span style= =3D"font-size: 13.3333px"> deployment policy, outside the token profi= le.</span></div><div><span style=3D"font-size: 13.3333px"><br></span></div>= <div><span style=3D"font-size: 13.3333px">- relying-party result: the norma= tive requirement is an</span></div><div><span style=3D"font-size: 13.3333px= "> integrity-protected Attestation Result gating the action (3.13.3,<= /span></div><div><span style=3D"font-size: 13.3333px"> B.4). Whether = a deployment requires synchronous verifier</span></div><div><span style=3D"= font-size: 13.3333px"> interaction before execution, or accepts offli= ne-produced proofs</span></div><div><span style=3D"font-size: 13.3333px">&n= bsp; with deferred submission, is per-deployment policy (Table 1) =E2=80=94= the</span></div><div><span style=3D"font-size: 13.3333px"> profile f= ixes the evidence and the verification rules, not the</span></div><div><spa= n style=3D"font-size: 13.3333px"> enforcement topology.</span></div><= div><span style=3D"font-size: 13.3333px"><br></span></div><div><span style= =3D"font-size: 13.3333px">The boundary is the point: -02 is deliberately a = pure token profile =E2=80=94</span></div><div><span style=3D"font-size: 13.= 3333px">wire format, claim set, verifier obligations =E2=80=94 with enforce= ment</span></div><div><span style=3D"font-size: 13.3333px">semantics left t= o deployment profiles. For the survey's</span></div><div><span style=3D"fon= t-size: 13.3333px">"profile-specific" axis that's probably the more useful<= /span></div><div><span style=3D"font-size: 13.3333px">characterization of P= SEA anyway.</span></div><div><span style=3D"font-size: 13.3333px"><br></spa= n></div><div><span style=3D"font-size: 13.3333px">Mohamad Khalil-Yossif</sp= an></div><div><br></div><div class=3D"mb_sig"></div> <blockquote class=3D"history_contai= ner" type=3D"cite" style=3D"border-left-style: solid;border-width: 1px;marg= in-top: 20px;margin-left: 0px;padding-left: 10px;min-width: 500px"> <p style=3D"color: #AAAAAA; margin-top: 10px;">On 1= 1/06/2026 11:43:21, Mohamad Khalil-Yossif <[email protected]> wrote= :</p><div style=3D"font-family:Arial,Helvetica,sans-serif"><div id=3D"__Mai= lbirdStyleContent" style=3D"font-size: 10pt;font-family: Cascadia Code;colo= r: #1A1A1A;text-align: left" dir=3D"ltr"><div><span style=3D"font-size: 13.= 3333px">Songbo, Iman, all,</span></div><div><span style=3D"font-size: 13.33= 33px"><br></span></div><div><span style=3D"font-size: 13.3333px">Songbo =E2= =80=94 following up with substance on both of your notes.</span></div><div>= <span style=3D"font-size: 13.3333px"><br></span></div><div><span style=3D"f= ont-size: 13.3333px">On the PSEA suggestions: the processing-model paragrap= h and the</span></div><div><span style=3D"font-size: 13.3333px">negative-te= st table are going into the next revision largely as you</span></div><div><= span style=3D"font-size: 13.3333px">framed them. The invariant you want tes= table =E2=80=94 a rejected proof</span></div><div><span style=3D"font-size:= 13.3333px">leaves the counter high-water mark, finalized-jti set, chain en= try,</span></div><div><span style=3D"font-size: 13.3333px">ledger, and any = approval-bearing result unchanged =E2=80=94 is how the</span></div><div><sp= an style=3D"font-size: 13.3333px">existing reference verifier already behav= es (rejection happens before</span></div><div><span style=3D"font-size: 13.= 3333px">any state mutation; counter advance and jti finalization are one</s= pan></div><div><span style=3D"font-size: 13.3333px">atomic transaction), so= it will be stated as a MUST backed by</span></div><div><span style=3D"font= -size: 13.3333px">negative vectors in your failure grouping.</span></div><d= iv><span style=3D"font-size: 13.3333px"><br></span></div><div><span style= =3D"font-size: 13.3333px">Your offline / server-state boundary holds for PS= EA exactly as you</span></div><div><span style=3D"font-size: 13.3333px">des= cribed: signature, action-binding, audience, and user-verification</span></= div><div><span style=3D"font-size: 13.3333px">evidence are offline-checkabl= e; replay (jti / counter high-water</span></div><div><span style=3D"font-si= ze: 13.3333px">mark), enrollment-active, and revocation are necessarily</sp= an></div><div><span style=3D"font-size: 13.3333px">verifier-state decisions= . "Reject before any approval-bearing state</span></div><div><span style=3D= "font-size: 13.3333px">mutation" as the invariant common to the profiles is= the right</span></div><div><span style=3D"font-size: 13.3333px">conformanc= e boundary.</span></div><div><span style=3D"font-size: 13.3333px"><br></spa= n></div><div><span style=3D"font-size: 13.3333px">And so the comparison mat= rix has both columns =E2=80=94 here is PSEA on the</span></div><div><span s= tyle=3D"font-size: 13.3333px">nine axes you proposed, all of it normative i= n -02:</span></div><div><span style=3D"font-size: 13.3333px"><br></span></d= iv><div><span style=3D"font-size: 13.3333px">- action canonicalization/hash= : JCS (RFC 8785) =E2=86=92 SHA-256;</span></div><div><span style=3D"font-si= ze: 13.3333px"> non-canonical input rejected.</span></div><div><span = style=3D"font-size: 13.3333px">- approver/subject binding: a dedicated hard= ware-attested device key;</span></div><div><span style=3D"font-size: 13.333= 3px"> the profile requires hardware-resident key material with enforc= ed</span></div><div><span style=3D"font-size: 13.3333px"> user verifi= cation, evidenced by a platform attestation chain. It is</span></div><div><= span style=3D"font-size: 13.3333px"> deliberately device- and vendor-= agnostic =E2=80=94 any platform that can</span></div><div><span style=3D"fo= nt-size: 13.3333px"> produce that evidence can conform.</span></div><= div><span style=3D"font-size: 13.3333px">- operation/audience binding: psea= _tier, psea_op, aud, iss byte-exact</span></div><div><span style=3D"font-si= ze: 13.3333px"> in the signed claim set; optional signed caller-ident= ity binding.</span></div><div><span style=3D"font-size: 13.3333px">- replay= key: jti plus a per-scope strictly-increasing counter.</span></div><div><s= pan style=3D"font-size: 13.3333px">- one-time state: counter compare-advanc= e and jti finalization in one</span></div><div><span style=3D"font-size: 13= .3333px"> atomic transaction; replay terminally refused. Same propert= y as</span></div><div><span style=3D"font-size: 13.3333px"> EP's nonc= e-consumption, enforced differently.</span></div><div><span style=3D"font-s= ize: 13.3333px">- trust root: the platform key-attestation chain =E2=80=94 = never the operator.</span></div><div><span style=3D"font-size: 13.3333px">-= relying-party result: signed proof token; the highest enforcement</span></= div><div><span style=3D"font-size: 13.3333px"> tier additionally requ= ires a server ACK before execution,</span></div><div><span style=3D"font-si= ze: 13.3333px"> fail-closed.</span></div><div><span style=3D"font-siz= e: 13.3333px">- enforcement class: four tiers (P/S/E/A), from passive evide= nce to</span></div><div><span style=3D"font-size: 13.3333px"> per-act= ion online fail-closed authorization.</span></div><div><span style=3D"font-= size: 13.3333px">- residual risk: WYSIWYS / display integrity and natural-p= erson</span></div><div><span style=3D"font-size: 13.3333px"> identity= =E2=80=94 both explicitly out of scope, stated in the draft.</span></div><= div><span style=3D"font-size: 13.3333px"><br></span></div><div><span style= =3D"font-size: 13.3333px">Implementation status, in the same terms as the o= ther efforts: a</span></div><div><span style=3D"font-size: 13.3333px">compl= ete reference implementation across two major mobile platforms</span></div>= <div><span style=3D"font-size: 13.3333px">plus a deployed backend verifier,= with cross-platform golden vectors</span></div><div><span style=3D"font-si= ze: 13.3333px">locking the canonicalization byte-for-byte. Canonical vector= s are in</span></div><div><span style=3D"font-size: 13.3333px">Appendix A o= f -02; a negative-case set in your failure grouping</span></div><div><span = style=3D"font-size: 13.3333px">exists on this side as well =E2=80=94 happy = to point you at it for the</span></div><div><span style=3D"font-size: 13.33= 33px">vector pass.</span></div><div><span style=3D"font-size: 13.3333px"><b= r></span></div><div><span style=3D"font-size: 13.3333px">Mohamad Khalil-Yos= sif</span></div><div><br></div><div class=3D"mb_sig"></div> <blockquote class=3D"history_contai= ner" type=3D"cite" style=3D"border-left-style: solid;border-width: 1px;marg= in-top: 20px;margin-left: 0px;padding-left: 10px;min-width: 500px"> <p style=3D"color: #AAAAAA; margin-top: 10px;">On 1= 1/06/2026 11:07:31, Iman Schrock <[email protected]> wrote:</p><= div style=3D"font-family:Arial,Helvetica,sans-serif"><div dir=3D"ltr"><div = style=3D"font-size: 12pt;text-decoration-style: solid;direction: ltr;margin= : 0px;font-family: Aptos,Arial,Helvetica,sans-serif;color: rgb(0,0,0)">Song= bo =E2=80=94</div><div style=3D"font-size: 12pt;text-decoration-style: soli= d;margin: 0px;font-family: Aptos,Arial,Helvetica,sans-serif;color: rgb(0,0,= 0)">Thank you =E2=80=94 running the vectors yourself is the validation that= matters; reproduction beats assertion, and I'm glad Node and Web agreed on= your machine too.</div><div style=3D"font-size: 12pt;text-decoration-style= : solid;margin: 0px;font-family: Aptos,Arial,Helvetica,sans-serif;color: rg= b(0,0,0)">Agreed completely: the offline / server-state split is a conforma= nce boundary, not an implementation detail, so it gets a first-class row in= the matrix rather than a footnote. Your phrasing of the invariant =E2=80= =94<span class=3D"gmail-Apple-converted-space"> </span><i>reject befor= e any approval-bearing state mutation</i> =E2=80=94 is the right one t= o state once in the core section and inherit per profile. And it maps to PS= EA exactly as you put it: the signed proof can carry or bind the jti / coun= ter / freshness material, but the high-water-mark decision is verifier stat= e on both sides.</div><div style=3D"font-size: 12pt;text-decoration-style: = solid;margin: 0px;font-family: Aptos,Arial,Helvetica,sans-serif;color: rgb(= 0,0,0)">That convergence =E2=80=94 one invariant, two attestation tiers, en= forced in the same place =E2=80=94 is what makes the dispatch question conc= rete rather than asserted. Send the off-list nits whenever; I'll fold them = in with the skeleton.</div><div style=3D"font-size: 12pt;text-decoration-st= yle: solid;margin: 0px;font-family: Aptos,Arial,Helvetica,sans-serif;color:= rgb(0,0,0)">Iman,</div></div><br><div class=3D"gmail_quote gmail_quote_con= tainer"><div dir=3D"ltr" class=3D"gmail_attr">On Thu, Jun 11, 2026 at 12:46= =E2=80=AFAM Songbo Bu <<a href=3D"mailto:[email protected]">king34760= [email protected]</a>> wrote:<br></div><blockquote class=3D"gmail_quote" style= =3D"margin: 0px 0px 0px 0.8ex;border-left: 1px solid rgb(204,204,204);paddi= ng-left: 1ex;min-width: 500px">Hi Iman,<br> <br> Thanks, this split is useful.<br> <br> I took a quick look at the new signoff vectors and ran the signoff<br> conformance runner. The current shape matches the verifier boundary<br> you describe: 9 vectors, 8 reject cases, all at the offline assertion<br> layer, with the Node and Web Crypto verifiers agreeing.<br> <br> One thing I would make explicit in the survey matrix is that the<br> offline/server-state split is not just an implementation detail; it is<br> a conformance boundary:<br> <br> - verifier-state boundary: an offline assertion verifier can reject<br> structural, cryptographic, action-binding, audience, and UV failures;<br> replay / one-time consumption, enrollment-active, and revocation<br> freshness require server-state or ledger-side evaluation.<br> <br> That maps cleanly to PSEA too. The signed proof can carry or bind the<br> jti/counter/freshness material, but the high-water mark decision is<br> still verifier state. So I would treat "reject before approval-bearing<br> state mutation" as the common invariant across both profiles.<br> <br> I'll take a closer pass over the actual vectors and send concrete nits<br> off-list once the survey skeleton lands.<br> <br> Best,<br> Songbo Bu<br> <br> On Thu, 11 Jun 2026 00:38:02 -0700, Iman Schrock <<a href=3D"mailto:team= @emiliaprotocol.ai" target=3D"_blank">[email protected]</a>> wrote:= <br> > Songbo =E2=80=94<br> ><br> > The processing-model + negative-test-table framing is the<br> > right call, and the decision-matrix axes you list are exactly the comp= arison<br> > I'd want too =E2=80=94 formats will differ, the verifier decision shou= ldn't.<br> ><br> > Two concrete things from my side:<br> ><br> > - I just<br> > published EP's Class-A signoff negative vectors as a tagged battery,<b= r> > grouped on your failure classes =E2=80=94 structural, cryptographic,<b= r> > action-binding, operation/audience, lifecycle/UV =E2=80=94 and run thr= ough two<br> > independent verifiers. It's a starting table for the pass you offered;= <br> > please shred it. One honest scope line I drew, which I think matters f= or<br> > the survey: I split OFFLINE assertion checks (crypto, action- binding,= <br> > audience, UV) from SERVER-STATE checks (replay / one-time consumption = =E2=80=94<br> > the nonce is our global consumption key =E2=80=94 and enrollment-activ= e). A<br> > relying party verifying an EP receipt offline gets the former; the lat= ter<br> > live at the consume/ledger step. PSEA's counter/jti high-water-mark<br> > invariant is the same idea on your side.<br> ><br> > - Here's<br> > EP filled into your matrix, as a strawman for the joint note:<br> ><br> > - action<br> > canonicalization/hash: JCS (RFC 8785) of the action object =E2=86=92 S= HA-256<br> > context hash; non-canonical input MUST be rejected.<br> ><br> > - approver/subject<br> > binding: device-bound approver key (Class A, WebAuthn P-256); approver= _id<br> > resolved via a signed Approver Directory.<br> ><br> > - operation/audience<br> > binding: action_hash + policy ref + initiator in the signed context;<b= r> > WebAuthn rp_id for the relying party.<br> ><br> > - replay<br> > key: 128-bit CSPRNG nonce, globally unique =E2=80=94 it IS the consump= tion key.<br> ><br> > - one-time<br> > state: consumed at most once, globally; replay across<br> > sessions/operators/time terminally refused.<br> ><br> > - trust<br> > root: the signed Approver Directory; MUST NOT default to the EP operat= or.<br> ><br> > - relying-party<br> > result: the offline-verifiable Trust Receipt (signed authorization +<b= r> > consumption record + Merkle anchor).<br> ><br> > - enforcement<br> > class: Class A device-bound (recommended) vs Class C operator-custodie= d<br> > (legacy, weaker); execution-side enforcement is the strongest deployme= nt<br> > class.<br> ><br> > - residual<br> > risk: presentation attack / WYSIWYS, and key-revocation timing =E2=80= =94 both<br> > stated, neither claimed solved.<br> ><br> > The two profiles land on different rows only at assurance<br> > tier (your dedicated-hardware attestation vs. our commodity authentica= tor) and<br> > scope (we add enrollment + separation of duties). Same decision matrix= .<br> ><br> > I'll fold this into the survey skeleton I'm roughing out and<br> > send the whole thing off-list for you and Mohamad to edit.<br> ><br> > On Thu, Jun 11, 2026 at 12:24=E2=80=AFAM Songbo Bu <<a href=3D"mail= to:[email protected]" target=3D"_blank">[email protected]</a>> wro= te:<br> ><br> > Hi Mohamad, Iman, all,<br> ><br> > Thanks, Mohamad. And Iman, that is a useful data point.<br> ><br> > For PSEA, my first pass would be to make the verifier-side text more<b= r> ><br> > like a processing model, plus a small set of negative verifier cases.<= br> ><br> > That seems more useful to implementers than adding another long<br> ><br> > requirements list.<br> ><br> > For the processing model, something along these lines:<br> ><br> > > On receipt of a proof submission, the Verifier treats the signed = proof, the submitted action payload, enrollment state, and replay state as = one atomic authorization decision. The Verifier first validates the signed = token and then uses only claims from the verified payload, together with se= rver-side enrollment and replay state, to evaluate the proof. Before any co= unter advance, ledger write, acknowledgement signing, or success-bearing At= testation Result, the Verifier canonicalizes the submitted actionPayload, r= ecomputes the payload hash, and compares it byte-for-byte with the signed p= sea_payload_hash. Any mismatch, missing required signed claim, stale freshn= ess value, replayed jti/counter, inactive enrollment state, or failed cross= -replay binding is a non-approval outcome.<br> ><br> > I would then group failures into a small negative-test table rather<br> ><br> > than leave them spread across the document:<br> ><br> > - structural / cryptographic: wrong profile, unsupported alg, bad<br> ><br> > signature, unrecognized critical header;<br> ><br> > - action-binding: missing actionPayload, payload-hash mismatch,<br> ><br> > non-canonical payload, or psea_payload_hash encoding mismatch;<br> ><br> > - cross-replay: psea_tier, psea_op, aud, iss, or psea_caller_package<b= r> ><br> > not matching the expected operation context;<br> ><br> > - freshness / replay: expired proof, implausible iat, wrong eat_nonce,= <br> ><br> > reused jti, or non-increasing counter;<br> ><br> > - lifecycle / trust gate: enrollment not active, revoked/suspended<br> ><br> > key, or user-verification evidence insufficient for the required<br> ><br> > assurance level;<br> ><br> > - optional chain layer: psea_chain_prev mismatch when that layer is en= abled.<br> ><br> > The key point I would make testable is that all of those failures have= <br> ><br> > the same security effect: the verifier rejects before any state<br> ><br> > mutation that could make the proof look consumed or approved. In<br> ><br> > test-vector terms, a rejected proof should leave the counter<br> ><br> > high-water mark, finalized-jti set, chain entry, approval ledger, and<= br> ><br> > approval-bearing Attestation Result unchanged or absent.<br> ><br> > For Iman's draft, I think the useful comparison is not whether the<br> ><br> > token formats are identical, but whether the verifier decision<br> ><br> > matrices line up: action canonicalization/hash, approver or subject<br> ><br> > binding, operation/audience binding, replay key, one-time state, trust= <br> ><br> > root, relying-party result, enforcement class, and residual risk. I<br> ><br> > can take a pass over that next, especially around Class A signoff<br> ><br> > negative vectors and deployment-class claims.<br> ><br> > Best,<br> ><br> > Songbo Bu<br> ><br> > --<br> ><br> > Iman Schrock<br> > Founder & CEO<br> ><br> > EMILIA Protocol | Trust Before High-Risk Action<br> > Eye warns. EP verifies. Signoff owns.<br> ><br> > [<a href=3D"http://emiliaprotocol.ai" rel=3D"noreferrer" target=3D"_bl= ank">emiliaprotocol.ai</a>](<a href=3D"http://emiliaprotocol.ai" rel=3D"nor= eferrer" target=3D"_blank">http://emiliaprotocol.ai</a>) | GitHub<br> > <a href=3D"mailto:[email protected]" target=3D"_blank">team@emili= aprotocol.ai</a><br> </blockquote></div><div><br clear=3D"all"></div><div><br></div><span class= =3D"gmail_signature_prefix">-- </span><br><div dir=3D"ltr" class=3D"gmail_s= ignature"><div dir=3D"ltr">Iman Schrock<br>Founder & CEO<br><br>EMILIA = Protocol | Trust Before High-Risk Action<br>Eye warns. EP verifies. Signoff= owns.<br><br><a href=3D"http://emiliaprotocol.ai" target=3D"_blank">emilia= protocol.ai</a> | GitHub<br><a href=3D"mailto:[email protected]" targe= t=3D"_blank">[email protected]</a></div></div> </div></blockquote></div></div></blockquote></div> ------=_NextPart_50449147.418924146652-- --===============0126786695628512581== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Content-Disposition: inline X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KU2VjZGlzcGF0 Y2ggbWFpbGluZyBsaXN0IC0tIHNlY2Rpc3BhdGNoQGlldGYub3JnClRvIHVuc3Vic2NyaWJlIHNl bmQgYW4gZW1haWwgdG8gc2VjZGlzcGF0Y2gtbGVhdmVAaWV0Zi5vcmcK --===============0126786695628512581==--