attestation-gated, unlinkable authorization tokens.
Two post-quantum layers work together:
- FAEST-128f — the attester signs short-lived issuance authorization after hardware evidence passes your policy (CVM quote, App Attest, TPM bundle, …).
- PoMFRIT — the issuer blind-signs anonymous spend tokens (MAYO +
VOLE-in-the-head, Privacy Pass–shaped wire format, token type
0x4550).
A server should accept requests only from clients running an attested build it trusts — without learning which client. Minting is gated on a valid unified-quote measurement the attester allowlists; spending stays unlinkable because the issuer never sees the unblinded token.
it is the open analog of google's aratea / blindsignauth anonymous tokens and apple's private access tokens / arc, with one change that matters: issuance is gated on hardware attestation (an eat), not on an account.
client (attested build) attester (FAEST) issuer (PoMFRIT) origin
─────────────────────── ──────────────── ──────────────── ──────
collect evidence
blind a random nonce ───▶ verify evidence
check policy
sign authorization ─▶ blind-sign nonce
◀────────────────────────── blind signatures
finalize → token
POST + token ──────────────────────────────────────────────────────▶ verify token
spend nonce
- FAEST binds this mint to this attestation + policy decision — the issuer only signs when it holds a valid attester authorization.
- PoMFRIT binds the spend token to the blinded nonce — publicly verifiable, offline-checkable, one-time-spendable (~7 KiB on the wire).
- the issuer never sees the unblinded token, so it cannot link an issued token to a redemption. attestation proves eligibility, not identity.
- the origin only needs the issuer's public key to gate a route. no callback, no per-request attestation, no shared secret.
[ cvm-agent ] agent platform / product
[ attestation-service ] /attest flow, stage0→stage1
[ eat-pass ] anonymous authorization gated on an eat ◀── this repo
[ unified-quote ] the eat / quote format + verifier
[ attested-workload ] the in-tee runtime
eat-pass consumes unified-quote's portable verifier to check the gate, and emits tokens any origin can verify with nothing but a public key.
the eat-pass binary is issuer, client, origin, and redeemer in one. there is
no dev/insecure mode: issuance is always gated on a real hardware attestation
and key-transparency is mandatory — there are no flags to weaken either. the
client runs inside the attested CVM and collects a genuine SEV-SNP vTPM quote
bound to each request.
# attester: verifies attestation and signs short-lived issuance authorizations.
# Policy JSON replaces inline --allow/--class (see policy/examples/).
# Optional: EATPASS_POLICY_TRUSTED_PUB + policy.json.sig for signed sidecars.
export EATPASS_ATTESTER_SEED=$(openssl rand -hex 32)
eat-pass attester --gate azure --policy policy/examples/uqaz1-example.json
# issuer: PoMFRIT blind-signs tokens after FAEST-128f attester authorization.
export EATPASS_KT_SEED=$(openssl rand -hex 32)
eat-pass issuer --listen 127.0.0.1:8088
# prints "kt log pubkey <hex>" → clients AND origins MUST pin it.
# redeemer: the central double-spend authority every origin replica shares.
eat-pass redeem --listen 127.0.0.1:8100
# origin: gates GET /resource on a valid PrivateToken (RFC 9577), trusting only
# issuer keys included in the transparency log signed by the pinned key.
# Each 401 issues a fresh challenge with a 32-byte redemption_context (default).
# --redeemer is required: double-spend is always enforced centrally, never
# origin-locally (which would let a token be spent once per replica).
eat-pass origin --issuer http://127.0.0.1:8088 \
--redeemer http://127.0.0.1:8100 --kt-log-pub <hex>
# client (inside the attested CVM): fetch origin challenge, collect quote, mint, present
eat-pass token --kt-log-pub <hex> \
--uq-collect "sudo /home/azureuser/unified-quote/target/release/uq azure collect" \
--count 2 --present http://127.0.0.1:8099/resource
# operator policy tooling
eat-pass policy validate --file policy/examples/uqaz1-example.json
eat-pass policy diff --left old.json --right new.json
# verify a captured live azure node to the AMD root, through the gate
eat-pass verify-azure-tls --cert live-leaf.der --binding <value_x_hex>the full protocol can be exercised without TEE hardware in tests only via
cargo test -p eat-pass-cli --features dev-sim; the dev-sim attestation stand-ins are compiled out of every shipped binary.
- m0 / m0.5 — done. the credential layer: PoMFRIT spend tokens
(
PoMFRIT-MAYO1-FV1-128, token type0x4550), the Privacy Pass–shaped RFC 9577 http flow,TokenChallengeorigin binding,token_key_idpinning, measurement-class anonymity sets, rate-limiting, and an epoched double-spend store. Attester authorization, KT log, and policy sidecars use FAEST-128f. Native build requires Linux x86_64 (seescripts/build-pomfrit-deps.sh). - m1 — done. the
eat-passbinary above (issuer service, client, origin example) with an end-to-end test and a cross-platform release. - m2 — done. the real attestation gate.
eat-pass-gateverifies a genuine unified-quote attestation (UqVerifierfor the cbor eat;AzureUqVerifier/AzureTlsVerifierfor the azure sev-snp vtpm path) to the AMD/Intel hardware root and extracts the gated measurement; the issuer selects it with--gate uq|azure|azure-tls(there is no dev/software gate). verified end-to-end against the liveattest.secure.buildsev-snp node (eat-pass verify-azure-tls). plus mandatory key transparency — the issuer publishes a signed, append-only key log at/ktand both the client and origin pin it with--kt-log-pub(inclusion + consistency checks; not optional), and a central redeemer (eat-pass redeem+origin --redeemer) for shared cross-replica double-spend. (gcp/tdx node still blocked; the tdx verifier path is compiled and ready.) - m3 — done. operational hardening. a networked redis backend (cargo
feature
redis) behind the same atomicSpentStore/RateLimitertraits —eat-pass redeem --backend redis://…andissuer --rate-backend redis://…give a multi-replica issuer/redeemer shared, durable state (fail-closed on outage); key rotation end-to-end —POST /rotate(admin-gated) mints a new signing key, appends it to the transparency log, and keeps serving the old one at/keys/{version}, while the client proves the log stayed consistent across the rotation with--kt-known-head; and the token + auth-header parsers are fuzzed (libFuzzer harness incore/fuzz/+ an always-on deterministic smoke test). verified live: a clean in-CVM--attest azuremint (the client bindsvalue_x = channel bindingvia the vTPM AK quote) gated throughAzureUqVerifierto the AMD Milan root onattest.secure.build. - m4 — done. reach. a GitHub Pages site
in the family style, and
eat-pass-mobile— the client credential math exposed to Android (Kotlin) and iOS (Swift) via UniFFI (HTTP + attestation stay host-native; blinding secrets never cross FFI). - still blocked. the gcp/tdx node; the tdx verifier path is compiled and ready and slots into the same gate the moment a live node is available.
the protocol, crypto choice, and crate layout are specified in PLAN.md,
grounded in google's decompiled anonymous-tokens surface, chromium/android
private-access-token sources, and the relevant ietf rfcs.
These papers inform the policy and challenge layers only — we did not
redesign attester/issuer split, coupled mint, or unified-quote verifiers.
Full mapping: docs/literature-pull-in.md.
| Topic | Citation | Pull-in |
|---|---|---|
| Coupled mint / redemption context | Hanff, Lehmann, Özbay. ACM CCS 2025. DOI 10.1145/3719027.3765172 | Fresh 32-byte redemption_context per origin challenge; channel binding in attestation |
| Privacy Pass formal verification | Ivanova et al. ePrint 2025/2022 | Audit KT pin + key rotation path |
| Attestation results (EAR) | Fossati et al. draft-ietf-rats-ear | /authorize returns AppraisalResult (EAR-shaped summary) |
| Reference-value manifests | Ferro & Lioy. ITASEC 2024 (Veraison/CoRIM). CEUR Vol-3731 | Operator JSON policy + optional FAEST-128f .json.sig sidecar |
| Policy validate/simulate/diff | Lin et al. USENIX Security 2025 (Verdict) | eat-pass policy validate|simulate|diff |
| CVM trust boundaries | Galanou et al. ACSAC 2025 / arXiv:2503.08256 | Policy notes field for operator trust assumptions |
| Proof of Cloud (awareness) | Rezabek & Passerat-Palmbach. arXiv:2510.12469 | Documented in policy notes — quote ≠ location |
| Android attestation replay | Fahl et al. ASIACCS 2023 | Server nonce = channel binding; reject stale challenges |
| Mobile app identity | Leierzopf et al. SPICES 2025 (AVBTestKeyInTheWild) | Mobile allow entries use app_id_hash only |
| RATS / EAT / policy shape | RFC 9334, RFC 9711, IETF CoRIM draft | Roles, attestation results, reference values |
- EAT — raw attestation evidence you collect (
uq collect, mobile bundle). RFC 9711. - EAR — verifier result after checks (pass/fail, appraisal). IETF draft; eat-pass exposes a small EAR-shaped
AppraisalResulton/authorize, not in the PrivateToken.
| Google PAT | Apple ARC / PAT | eat-pass | |
|---|---|---|---|
| Token crypto | Blind RSA (+ extensions) | Blind RSA (RFC 9474 type 2) | PoMFRIT (MAYO1 + VOLE-in-the-head) |
| Abuse / trust coloring | Private metadata bit — issuer-only secret at mint/redeem; can flag bad actors without client visibility | Platform attester + partner issuers; device/account signals | No private metadata bit — explicit VerificationPolicy + registry + appraisal logs |
| Attestation gate | Platform / mediator signals | Apple Secure Enclave + iCloud attester | Hardware EAT (CVM / mobile) |
Google private metadata bit: issuers can embed secret issuance metadata read only at redemption to re-score or throttle clients opaquely (ePrint 2020/072). eat-pass deliberately does not implement PMB — operators revoke via policy/registry instead of hidden issuer coloring.
Post-quantum: eat-pass ships PQ spend tokens (PoMFRIT) and FAEST-128f attester
authorization today — see docs/post-quantum-roadmap.md.
Full comparison: docs/competitive.md.
mit
