feat: decouple the TAKT from the AAT and gate verification behind one entrypoint - #884
feat: decouple the TAKT from the AAT and gate verification behind one entrypoint#884kilianglas wants to merge 9 commits into
Conversation
|
@codex review |
|
cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 89db6b7. Configure here.
There was a problem hiding this comment.
Pull request overview
Moves the Trust Anchor Key Token (TAKT) out of the AAT payload and into the AAT COSE_Sign1 unprotected header (takt), aligning the Rust encoder, Noir circuits, and test vectors around the new binding model (TAKT self-authentication + shared assertion_key equality). Also updates the claim set to rename -80000 from signal to cdh and adds/adjusts KATs to pin the exact signed Sig_structure bytes.
Changes:
- Rust: remove nested TAKT (
submods.takt) from the AAT payload; carry TAKT bytes in the unprotected header; rename claimsignal→cdh; enforce fixed-width[2^16, 2^32)exprange. - Noir: shrink
AAT_SIG_STRUCTURE_LENand updateaat_sig_structure; remove TAKT reserialization; enforce TAKTexpCBOR-uint32 constraint inverify_takt. - Tests/docs: add cross-implementation AAT KAT; update deterministic CBOR map order tests and Noir README guidance.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/proof/src/authenticator_attestation/tests.rs | Updates AAT payload assertions (no submods), checks takt in unprotected header, adds AAT Sig_structure/signature KAT, adds exp range tests. |
| crates/proof/src/authenticator_attestation/mod.rs | Implements new on-wire layout (TAKT in unprotected header), renames claim to cdh, and adds exp fixed-width validation. |
| crates/proof/noir/README.md | Documents the new binding model and TAKT placement for Noir developers. |
| crates/proof/noir/authenticator-attestation/src/takt.nr | Moves the exp CBOR-uint32 constraint into verify_takt and removes TAKT serialization/KAT. |
| crates/proof/noir/authenticator-attestation/src/aat.nr | Updates Sig_structure template and constants to match the smaller payload and claim set (no nested TAKT, cdh claim). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| .unprotected | ||
| .rest | ||
| .iter() | ||
| .find(|(label, _)| *label == coset::Label::Text("takt".to_string())) |
| /// Validates that `exp` fits the fixed 4-byte CBOR uint encoding mandated by | ||
| /// WIP-106 (`2^16 <= exp < 2^32`, dates between 1971 and 2106), so the claim | ||
| /// offsets the circuits rely on stay constant. |
| @@ -24,14 +29,15 @@ pub struct AuthenticatorAssertionToken { | |||
| exp: Field, | |||
| // WIP-106 fixes `exp` to a 4-byte CBOR uint. The circuit no longer | ||
| // serializes the TAKT (it rides in the AAT's unprotected header), so this | ||
| // is the only place that constraint is still enforced -- the Rust | ||
| // constructor only range-checks the CWT numeric date. Must precede the | ||
| // freshness check so an out-of-range `exp` reports the encoding error. |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
| - `authenticator-attestation/` — WIP-106 Authenticator Attestation library | ||
| (Trust Anchor Key Token + Authenticator Assertion Token verification). | ||
|
|
||
| The TAKT is **not** nested in the AAT. It travels in the AAT's `COSE_Sign1` |
There was a problem hiding this comment.
nit, let's remove the line breaks and delegate to IDEs to render this in the screen
| /// proof that calls this function. | ||
| pub fn verify_takt(input: TrustAnchorKeyToken, now: Field) { | ||
| // CBOR uint32 encoding of `exp` is checked later in `serialize_takt`. | ||
| // WIP-106 fixes `exp` to a 4-byte CBOR uint. The circuit no longer |
There was a problem hiding this comment.
I'd remove the comment, it's not super informative. furthermore, the last part is a bit misleading because constraint failures aren't surfaced to rust
There was a problem hiding this comment.
Yeah, didn't cleanup claude comments yet.
| assert_eq!(submods[0].1, Value::Bytes(takt)); | ||
| // The TAKT is NOT in the payload: it rides in the unprotected header, so it | ||
| // is outside the AAT signature (WIP-106 AAT section 6). | ||
| assert!( |
There was a problem hiding this comment.
this seems like a very specific assertion to cover a previous design, I'd drop it, there's multiple things that could be in here that shouldn't. or perhaps a more generic test that ensures only the expected keys are present
c5f92a9 to
cce9ee0
Compare
Aligns the WIP-106 attestation library with the current spec and makes it consumable from another Noir package.
The TAKT is fully decoupled from the AAT
serialize_takt,compress_bjj_point,FIELD_HALFand its serialization KAT are deleted. 50742 → 49113 ACIR (−3.2%).AuthenticatorAssertionToken::newnow validates AAT claims independently; it no longer parses a TAKT or retains its attested key. Pairing and key-consistency checks belong to the carrying/verifying layer.-80000renamedsignal→cdh.aat_known_answer_sig_structure_and_signatureKAT. The TAKT had one, the AAT did not, which is how the Noir fixture could silently drift from the Rust encoder. It caught a bug on first run: a 159-byte payload takes the 1-byte CBOR length, not the 2-byte form the old 417-byte payload needed.One public entrypoint
The composite feeds
verify_aatthe key read from the TAKT it just verified, so the binding holds by construction.Struct fields are now
pub: they were module-private, so no external package could construct either token.Rust
expvalidationThe AAT constructor range-checked
aud, but neither token constructor constrainedexpto the fixed-width range. An out-of-range value encodes at a different CBOR width and silently breaks the fixed offsets the circuits depend on.Callers MUST
trust_anchor_key_x/_yandnowas public inputs — a privatenowlets the prover pick a time at which any token is fresh; a private anchor key lets it sign its own TAKT;aat.nonceandaat.cdhto the surrounding proof;sec_flagsandauthenticator_metathe RP needs.None of these are enforceable from inside the library. Documented with a worked
mainincrates/proof/noir/README.md.