6.2 KiB
Interactive Zero-Knowledge Age Verification with TLSNotary
This example demonstrates privacy-preserving age verification using TLSNotary and zero-knowledge proofs. It allows a prover to demonstrate they are 18+ years old without revealing their actual birth date or any other personal information.
🔍 How It Works (simplified overview)
sequenceDiagram
participant S as Tax Server<br/>(fixture)
participant P as Prover
participant V as Verifier
P->>S: Request tax data (with auth token) (MPC-TLS)
S->>P: Tax data including `date_of_birth` (MPC-TLS)
P->>V: Share transcript with redactions
P->>V: Commit to blinded hash of birth date
P->>P: Generate ZK proof of age ≥ 18
P->>V: Send ZK proof
V->>V: Verify transcript & ZK proof
V->>V: ✅ Confirm: Prover is 18+ (no birth date revealed)
The Process
- MPC-TLS Session: The Prover fetches tax information containing their birth date, while the Verifier jointly verifies the TLS session to ensure the data comes from the authentic server.
- Selective Disclosure:
- The authorization token is redacted: the Verifier sees the plaintext request but not the token.
- The birth date is committed as a blinded hash: the Verifier cannot see the date, but the Prover is cryptographically bound to it.
(Depending on the use case more data can be redacted or revealed)
- Zero-Knowledge Proof: The Prover generates a ZK proof that the committed birth date corresponds to an age ≥ 18.
- Verification: The Verifier checks both the TLS transcript and the ZK proof, confirming age ≥ 18 without learning the actual date of birth.
Example Data
The tax server returns data like this:
{
"tax_year": 2024,
"taxpayer": {
"idnr": "12345678901",
"first_name": "Max",
"last_name": "Mustermann",
"date_of_birth": "1985-03-12",
// ...
}
}
🔐 Zero-Knowledge Proof Details
The ZK circuit proves: "I know a birth date that hashes to the committed value AND indicates I am 18+ years old"
Public Inputs:
- ✅ Verification date
- ✅ Committed blinded hash of birth date
Private Inputs (Hidden):
- 🔒 Actual birth date plaintext
- 🔒 Random blinder used in hash commitment
What the Verifier Learns:
- ✅ The prover is 18+ years old
- ✅ The birth date is authentic (from the MPC-TLS session)
Everything else remains private.
🏃 Run the Example
-
Start the test server (from repository root):
RUST_LOG=info PORT=4000 cargo run --bin tlsn-server-fixture -
Run the age verification (in a new terminal):
SERVER_PORT=4000 cargo run --release --example interactive_zk -
For detailed logs:
RUST_LOG=debug,yamux=info,uid_mux=info SERVER_PORT=4000 cargo run --release --example interactive_zk
Expected Output
Successfully verified https://test-server.io:4000/elster
Age verified in ZK: 18+ ✅
Verified sent data:
GET https://test-server.io:4000/elster HTTP/1.1
host: test-server.io
connection: close
authorization: 🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈🙈
Verified received data:
🙈🙈🙈🙈🙈🙈🙈🙈[truncated for brevity]...🙈🙈🙈🙈🙈"tax_year":2024🙈🙈🙈🙈🙈...
💡 Note: In this demo, both Prover and Verifier run on the same machine. In production, they would operate on separate systems. 💡 Note: This demo assumes that the tax server serves correct data, and that only the submitter of the tax data has access to the specified page.
🛠 Development
Project Structure
interactive_zk/
├── prover.rs # Prover implementation
├── verifier.rs # Verifier implementation
├── types.rs # Shared types
└── interactive_zk.rs # Main example runner
├── noir/ # Zero-knowledge circuit
│ ├── src/main.n # Noir circuit code
│ ├── target/ # Compiled circuit artifacts
│ └── Nargo.toml # Noir project config
│ └── Prover.toml # Example input for `nargo execute`
│ └── generate_test_data.rs # Rust script to generate Noir test data
└── README.md
Noir Circuit Commands
We use Mopro's noir_rs for ZK proof generation. The circuit is pre-compiled and ready to use. You don't need to install Noir tools to run the example. But if you want to change or test the circuit in isolation, you can use the following instructions.
Before you proceed, we recommend to double check that your Noir tooling matches the versions used in Mopro's noir_rs:
# Install correct Noir and BB versions (important for compatibility!)
noirup --version 1.0.0-beta.8
bbup -v 1.0.0-nightly.20250723
If you don't have noirup and bbup installed yet, check Noir's Quick Start.
To compile the circuit, go to the noir folder and run nargo compile.
To check and experiment with the Noir circuit, you can use these commands:
- Execute Circuit: Compile the circuit and run it with sample data from
Prover.toml:nargo execute - Generate Verification Key: Create the verification key needed to verify proofs
bb write_vk -b ./target/noir.json -o ./target - Generate Proof: Create a zero-knowledge proof using the circuit and witness data.
bb prove --bytecode_path ./target/noir.json --witness_path ./target/noir.gz -o ./target - Verify Proof: Verify that a proof is valid using the verification key.
bb verify -k ./target/vk -p ./target/proof - Run the Noir tests:
To create extra tests, you can use
nargo test --show-output./generate_test_data.rsto help with generating correct blinders and hashes.