[Secdispatch] Re: [saag] Re: Re: draft-yossif-psea-0 2 posted — re-scoped as an EAT token profile (diff)
Iman Schrock <[email protected]> Thu, 11 Jun 2026 00:38:02 -0700
| Newsgroups | gmane.ietf.secdispatch,gmane.ietf.saag |
|---|---|
| Message-ID | <CAOfgHgrf7Aps1vr1zENiPKnoe=CW7CgP3GtWK-VWLj=UxQPN1Q@mail.gmail.com> |
--===============3462044757711891890==
Content-Type: multipart/alternative; boundary="0000000000006160c60653f571f1"
--0000000000006160c60653f571f1
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
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 comparison I'd want too =
=E2=80=94
formats will differ, the verifier decision shouldn't.
Two concrete things from my side:
1. I just published EP's Class-A signoff negative vectors as a tagged
battery, grouped on your failure classes =E2=80=94 structural, cryptogra=
phic,
action-binding, operation/audience, lifecycle/UV =E2=80=94 and run throu=
gh 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 a=
t
the consume/ledger step. PSEA's counter/jti high-water-mark invariant is
the same idea on your side.
2. 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 t=
he
consumption 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 th=
e
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=E2=80=AFAM Songbo Bu <[email protected]> w=
rote:
> 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
> atomic authorization decision. The Verifier first validates the signed
> token and then uses only claims from the verified payload, together with
> server-side enrollment and replay state, to evaluate the proof. Before an=
y
> counter advance, ledger write, acknowledgement signing, or success-bearin=
g
> Attestation Result, the Verifier canonicalizes the submitted actionPayloa=
d,
> recomputes the payload hash, and compares it byte-for-byte with the signe=
d
> psea_payload_hash. Any mismatch, missing required signed claim, stale
> freshness value, replayed jti/counter, inactive enrollment state, or fail=
ed
> cross-replay 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
> enabled.
>
> 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
>
--=20
Iman Schrock
Founder & CEO
EMILIA Protocol | Trust Before High-Risk Action
Eye warns. EP verifies. Signoff owns.
emiliaprotocol.ai | GitHub
[email protected]
--0000000000006160c60653f571f1
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><p class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-he=
ight:115%;font-size:12pt;font-family:Aptos,sans-serif">Songbo =E2=80=94</p>
<p class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font-si=
ze:12pt;font-family:Aptos,sans-serif">The processing-model + negative-test-=
table framing is the
right call, and the decision-matrix axes you list are exactly the compariso=
n
I'd want too =E2=80=94 formats will differ, the verifier decision shoul=
dn't.</p>
<p class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font-si=
ze:12pt;font-family:Aptos,sans-serif">Two concrete things from my side:</p>
<ol style=3D"margin-top:0in;margin-bottom:0in" start=3D"1" type=3D"1">
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font-=
size:12pt;font-family:Aptos,sans-serif">I just
published EP's Class-A signoff negative vectors as a tagged batter=
y,
grouped on your failure classes =E2=80=94 structural, cryptographic,
action-binding, operation/audience, lifecycle/UV =E2=80=94 and run thr=
ough two
independent verifiers. It's a starting table for the pass you offe=
red;
please shred it. One honest scope line I drew, which I think matters f=
or
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-activ=
e). A
relying party verifying an EP receipt offline gets the former; the lat=
ter
live at the consume/ledger step. PSEA's counter/jti high-water-mar=
k
invariant is the same idea on your side.</li>
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font-=
size:12pt;font-family:Aptos,sans-serif">Here's
EP filled into your matrix, as a strawman for the joint note:</li>
<ul style=3D"margin-top:0in;margin-bottom:0in" type=3D"circle">
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font=
-size:12pt;font-family:Aptos,sans-serif">action
canonicalization/hash: JCS (RFC 8785) of the action object =E2=86=92 =
SHA-256
context hash; non-canonical input MUST be rejected.</li>
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font=
-size:12pt;font-family:Aptos,sans-serif">approver/subject
binding: device-bound approver key (Class A, WebAuthn P-256); approve=
r_id
resolved via a signed Approver Directory.</li>
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font=
-size:12pt;font-family:Aptos,sans-serif">operation/audience
binding: action_hash + policy ref + initiator in the signed context;
WebAuthn rp_id for the relying party.</li>
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font=
-size:12pt;font-family:Aptos,sans-serif">replay
key: 128-bit CSPRNG nonce, globally unique =E2=80=94 it IS the consum=
ption key.</li>
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font=
-size:12pt;font-family:Aptos,sans-serif">one-time
state: consumed at most once, globally; replay across
sessions/operators/time terminally refused.</li>
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font=
-size:12pt;font-family:Aptos,sans-serif">trust
root: the signed Approver Directory; MUST NOT default to the EP opera=
tor.</li>
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font=
-size:12pt;font-family:Aptos,sans-serif">relying-party
result: the offline-verifiable Trust Receipt (signed authorization +
consumption record + Merkle anchor).</li>
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font=
-size:12pt;font-family:Aptos,sans-serif">enforcement
class: Class A device-bound (recommended) vs Class C operator-custodi=
ed
(legacy, weaker); execution-side enforcement is the strongest deploym=
ent
class.</li>
<li class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font=
-size:12pt;font-family:Aptos,sans-serif">residual
risk: presentation attack / WYSIWYS, and key-revocation timing =E2=80=
=94 both
stated, neither claimed solved.</li>
</ul>
</ol>
<p class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font-si=
ze:12pt;font-family:Aptos,sans-serif">The two profiles land on different ro=
ws only at assurance
tier (your dedicated-hardware attestation vs. our commodity authenticator) =
and
scope (we add enrollment + separation of duties). Same decision matrix.</p>
<p class=3D"MsoNormal" style=3D"margin:0in 0in 8pt;line-height:115%;font-si=
ze:12pt;font-family:Aptos,sans-serif">I'll fold this into the survey sk=
eleton I'm roughing out and
send the whole thing off-list for you and Mohamad to edit.</p></div><br><di=
v class=3D"gmail_quote gmail_quote_container"><div dir=3D"ltr" class=3D"gma=
il_attr">On Thu, Jun 11, 2026 at 12:24=E2=80=AFAM Songbo Bu <<a href=3D"=
mailto:[email protected]">[email protected]</a>> wrote:<br></div><=
blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-l=
eft:1px solid rgb(204,204,204);padding-left:1ex">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<br>
like a processing model, plus a small set of negative verifier cases.<br>
That seems more useful to implementers than adding another long<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 a=
tomic authorization decision. The Verifier first validates the signed token=
and then uses only claims from the verified payload, together with server-=
side enrollment and replay state, to evaluate the proof. Before any counter=
advance, ledger write, acknowledgement signing, or success-bearing Attesta=
tion Result, the Verifier canonicalizes the submitted actionPayload, recomp=
utes the payload hash, and compares it byte-for-byte with the signed psea_p=
ayload_hash. Any mismatch, missing required signed claim, stale freshness v=
alue, replayed jti/counter, inactive enrollment state, or failed cross-repl=
ay binding is a non-approval outcome.<br>
<br>
I would then group failures into a small negative-test table rather<br>
than leave them spread across the document:<br>
<br>
- structural / cryptographic: wrong profile, unsupported alg, bad<br>
signature, unrecognized critical header;<br>
- action-binding: missing actionPayload, payload-hash mismatch,<br>
non-canonical payload, or psea_payload_hash encoding mismatch;<br>
- cross-replay: psea_tier, psea_op, aud, iss, or psea_caller_package<br>
not matching the expected operation context;<br>
- freshness / replay: expired proof, implausible iat, wrong eat_nonce,<br>
reused jti, or non-increasing counter;<br>
- lifecycle / trust gate: enrollment not active, revoked/suspended<br>
key, or user-verification evidence insufficient for the required<br>
assurance level;<br>
- optional chain layer: psea_chain_prev mismatch when that layer is enabled=
.<br>
<br>
The key point I would make testable is that all of those failures have<br>
the same security effect: the verifier rejects before any state<br>
mutation that could make the proof look consumed or approved. In<br>
test-vector terms, a rejected proof should leave the counter<br>
high-water mark, finalized-jti set, chain entry, approval ledger, and<br>
approval-bearing Attestation Result unchanged or absent.<br>
<br>
For Iman's draft, I think the useful comparison is not whether the<br>
token formats are identical, but whether the verifier decision<br>
matrices line up: action canonicalization/hash, approver or subject<br>
binding, operation/audience binding, replay key, one-time state, trust<br>
root, relying-party result, enforcement class, and residual risk. I<br>
can take a pass over that next, especially around Class A signoff<br>
negative vectors and deployment-class claims.<br>
<br>
Best,<br>
Songbo Bu<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>
--0000000000006160c60653f571f1--
--===============3462044757711891890==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: inline
X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KU2VjZGlzcGF0
Y2ggbWFpbGluZyBsaXN0IC0tIHNlY2Rpc3BhdGNoQGlldGYub3JnClRvIHVuc3Vic2NyaWJlIHNl
bmQgYW4gZW1haWwgdG8gc2VjZGlzcGF0Y2gtbGVhdmVAaWV0Zi5vcmcK
--===============3462044757711891890==--