fix(sdjwt): enforce aud/nonce binding on every KB hop, fail closed on terminal - #313
fix(sdjwt): enforce aud/nonce binding on every KB hop, fail closed on terminal#313SashaMIT wants to merge 3 commits into
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
d50e31d to
b0ffebe
Compare
|
@googlebot I signed it! (commits re-authored to the signing email) |
|
@googlebot rescan |
6244749 to
44ad0bb
Compare
|
@googlebot rescan |
0679ba5 to
fef421f
Compare
|
@googlebot rescan |
|
CI note: the Lint Code Base failure is the repo-wide Biome run (75 errors in |
fef421f to
62e9a81
Compare
… terminal Two gaps in KB-SD-JWT verification: 1. aud/nonce were only checked on terminal hops; intermediate hops skipped the check even when the caller passed expected values (the repo's own test_verify_rejects_aud_mismatch / _nonce_mismatch tests were failing on main because of this). 2. A terminal hop carrying aud/nonce verified fine when the caller passed no expected_aud/expected_nonce. Per RFC 9901 section 7.3 a verifier MUST confirm a key-binding token's aud identifies itself; the AP2 profile always issues terminal hops with aud/nonce, so accepting one unbound turned every captured presentation into a replayable bearer credential (exp is optional, so replay could be indefinite). verify() now checks expected claims on all hops when provided, and a terminal hop that carries aud/nonce but is verified without binding them raises. Tests updated to bind at verify time (the previously-unsafe pattern), plus two regression tests for the fail-closed behavior. Signed-off-by: SashaMIT <sash@ela.city> Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…om words Signed-off-by: SashaMIT <sash.t.mitchell@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
62e9a81 to
f2074f5
Compare
Summary
In plain terms: a presentation token's
aud/nonceare what bind it to a specific recipient and session. Two gaps in KB-SD-JWT verification weakened that binding:aud/nonceeven when the caller passed expected values — the check only ran on the terminal hop. (The repo's owntest_verify_rejects_aud_mismatch/test_verify_rejects_nonce_mismatchwere failing on main because of exactly this.)aud/nonceverified fine with no expected values passed at all. Per RFC 9901 §7.3 a verifier MUST confirm a key-binding token'saudidentifies itself; the AP2 profile always issues terminal hops withaud/nonce, andexpis optional — so a verifier that forgot to bind turned every captured presentation into a replayable bearer credential, potentially indefinitely.Fix (
kb_sd_jwt.verify)expected_aud/expected_nonceare now honored on every hop when provided (fixes the two failing tests).aud/noncebut is verified without binding them raises (fail-closed on the spec MUST). Root-only credential verification is unaffected.Test plan
expected_aud/expected_nonce— the previously-unsafe patternaud/noncerejects an unbound verifierCompatibility note
Callers who verified terminal presentations without passing
expected_aud/expected_noncewill now get an error instead of a silent pass — that is the intended behavior change; those verifiers were accepting replays.Made with Cursor
Made with Cursor