A demo implementation of an anonymous credential system in the commit-and-prove paradigm, following the approach described in "Vision: A Modular Framework for Anonymous Credential Systems" by A. Lehmann, A. Sidorenko, and A. Zacharakis.
The concrete use case is age verification: a credential holder proves they are 18 years old without revealing any other attribute, and without the verifier being able to track or link individual presentations.
The workspace contains four main components:
crypto/modular-ac/ # Core cryptographic library for the commit-and-prove anonymous credential scheme
crypto/agever/ # Age-verification scheme built on modular-ac + Android UniFFI bindings
web/ # Axum web server — issuer + verifier demo
wallet/ # Android app (Jetpack Compose) — holder demo
Implements the full commit-and-prove anonymous credential scheme on BLS12-381 / BBS+ 23:
Issuer— signs credentials using BBS+ 23, embedding a holder P-256 public key.CommittedDisclosurePresenter— produces aPresentationcomposed of three sub-proofs:- Base proof — PoK of BBS+ signature with selective disclosure and Pedersen commitments to the hidden messages.
- Proof of validity — Bulletproofs++ range proof that
nbf < today < expon the committed timestamps. - Holder-binding proof — equality-across-groups proofs (tom256 ↔ BLS12-381) plus a PoK of ECDSA signature under the committed P-256 holder key.
CommittedDisclosureVerifier— verifies all three sub-proofs.
Implements the age-verification credential scheme on top of modular-ac, and exposes it
to Android via UniFFI:
- Credential scheme — defines a fixed schema (
header,above16,above18) and providesAgeVerIssuer,AgeVerPresenter, andAgeVerVerifierthat wire up themodular-acprimitives for the age-verification use case. - UniFFI bindings — compiles to
libagever.so(cdylib) and generates Kotlin bindings via theuniffi-bindgenbinary.
An Axum HTTP server that acts as both issuer and verifier in the demo flow:
| Endpoint | Role |
|---|---|
POST /issue |
Verify Android Key Attestation cert chain, issue a JWT credential |
GET /ageverification |
Create a session and display a QR code / deep-link |
POST /validate |
Verify a AgeVerPresentation ZK proof, mark session valid |
The /issue endpoint verifies the Android StrongBox Key Attestation certificate chain
(Google EC + RSA roots) before issuing a credential, binding the credential to the
hardware-attested P-256 key of the wallet.
| Variable | Default | Description |
|---|---|---|
ISSUER_SECRET |
(OS RNG) | Exactly 32-byte ASCII string used as the seed for deterministic issuer keypair generation. If unset, the keypair is sampled from the OS RNG on every start. |
REQUIRE_ATTEST |
true |
Set to false or 0 to skip the Android StrongBox security-level check. |
EXTRA_TRUST_CERTS_PEM_FILE |
(none) | Path to a PEM file containing additional X.509 trust anchors. |
An Android application (min SDK 31, target SDK 37) that:
- Generates a P-256 key pair in the StrongBox secure element, if available.
- Requests and stores a JWT credential from the
/issueendpoint. - Scans a QR code from the verifier web page and submits a ZK presentation to
/validate.
The following commands build and run the different components of the demo from the root of the workspace.
Note that the builds require a signigicant amount disk space (10-12 GB when all targets
are built) due to the dependency on the docknetwork/crypto suite, which includes
multiple large Rust libraries and their build artifacts.
# Run all crypto tests
cargo test -p modular-ac> Dependencies:
- Rust toolchain (2024 edition)
# Run locally (listens on :3000)
cargo run --bin web> Dependencies:
A local OpenSSL installation is required for the web server to verify the Android Key Attestation
certificate chain. The server expects the openssl CLI tool to be available in the system PATH
and uses it to perform certificate parsing and chain verification.
# Build and run with Docker
docker build -t agever-demo-srv:latest .
docker run -p 3000:3000 agever-demo-srv> Dependencies:
- Docker (for the containerized build and run)
cd wallet
./gradlew assembleDebug> Dependencies:
- Android SDK 37, NDK
30.0.14904198 - Gradle 8+ with Kotlin DSL
- JNA
5.18.1for the UniFFI bridge - Rust targets
aarch64-linux-androidandx86_64-linux-android
The gradle build should compile the Rust bindings for both aarch64 and x86_64 Android
targets as a part of the buildRust task.