A proof of concept implementation of Chaum-Pedersen ZKP(Zero Knowledge Proof) based authentication system in C++, Python, and Rust implementations. The POC system allows password-less authentication where the server never learns user passwords.
- C++ Version (
cpp/
directory): High-performance implementation using Crypto++ - Python Version (
python/
directory): Easy-to-use implementation with pure Python - Rust Version (
rust/
directory): Memory-safe, concurrent implementation with modern tooling
- Zero-Knowledge Authentication: Server verifies user identity without learning the password
- Chaum-Pedersen Protocol: Non-interactive proofs via Fiat-Shamir transform
- Secure Storage: SQLite database stores only public keys and salts
- RESTful API: HTTP server with session management
- CLI Client: Easy-to-use command-line interface
- Replay Attack Prevention: One-time nonce challenges with short TTL
- 2048-bit Security: Uses safe primes for cryptographic operations
Best for: High performance, embedded systems
- Faster cryptographic operations
- Lower memory footprint
- Compiled binary distribution
Best for: Rapid prototyping, research, education, quick deployment
- Easier to understand and modify
- No compilation required
- Cross-platform without rebuilding
Best for: Systems requiring safety, concurrent applications, modern development
- Memory safety without garbage collection
- Fearless concurrency
- Modern tooling (cargo, clippy, rustfmt)
- Zero-cost abstractions
- Language: C++17
- Crypto Library: Crypto++ (libcrypto++)
- Database: SQLite3
- HTTP Server: cpp-httplib (header-only)
- JSON: nlohmann/json (header-only)
- Build System: CMake
- Testing: Catch2 (optional)
- Language: Python 3.8+
- Crypto: Built-in hashlib, secrets
- Primes: sympy
- Database: sqlite3 (built-in)
- HTTP Server: Flask
- HTTP Client: requests
- Testing: pytest
- Language: Rust 2021 edition
- Crypto: num-bigint, sha2, rand
- Database: rusqlite (bundled SQLite)
- HTTP Server: Actix-web (async)
- HTTP Client: reqwest
- Build System: Cargo
- Testing: Built-in test framework
# Install dependencies
cd python
pip install -r requirements.txt
# Terminal 1: Start server
cd server
python main.py
# Terminal 2: Use client (client handles /params and /challenge)
cd client
python cli_client.py register alice password123
python cli_client.py login alice password123
# Install Rust (if needed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Build and run
cd rust
cargo build --release
# Terminal 1: Start server (2048-bit predefined)
./target/release/zkp-server
# Terminal 2: Use client
./target/release/zkp-client register alice password123
./target/release/zkp-client login alice password123
See detailed build instructions below.
sudo apt-get update
sudo apt-get install -y \
build-essential \
cmake \
libcrypto++-dev \
libsqlite3-dev \
libcatch2-dev \
pkg-config
sudo dnf install -y \
gcc-c++ \
cmake \
cryptopp-devel \
sqlite-devel \
catch-devel \
pkg-config
# Python 3.8 or higher required
python3 --version
# Install dependencies
cd python
pip install -r requirements.txt
# Navigate to cpp directory
cd cpp
# Create build directory
mkdir build && cd build
# Configure with CMake
cmake ..
# Build the project
make -j$(nproc)
This will create two executables:
auth_server
- Authentication serverauth_client
- CLI client
# Start server on default port 8080 with default database
./auth_server
# Specify custom port and database
./auth_server --port 9000 --db custom_users.db
# Use 4096-bit parameters (generation mode recommended)
./auth_server --bits 4096 --params-mode generate
# Show help
./auth_server --help
./auth_client register alice mypassword123
./auth_client login alice mypassword123
This will return a session token that you can use for authenticated requests.
./auth_client verify <your-session-token>
./auth_client register alice password --host 192.168.1.100 --port 9000
./auth_client login alice password --host 192.168.1.100 --port 9000
cd python/server
python main.py
# Specify custom port and database
python main.py --port 9000 --db custom_users.db
cd python/client
# Register a new user
python cli_client.py register alice mypassword123
# Login (Authenticate)
python cli_client.py login alice mypassword123
# Verify session token
python cli_client.py verify <your-session-token>
# Custom server connection
python cli_client.py register alice password --host 192.168.1.100 --port 9000
Note: First startup may take 30-60 seconds while generating 2048-bit safe primes. This is normal.
Fetch group parameters p, q, g, h
(hex) used for proof generation/verification.
Register by submitting client-computed public keys and salt:
{ "username": "alice", "y1": "<hex>", "y2": "<hex>", "salt": "<hex>" }
.
Request a one-time nonce
for { "username": "alice" }
. Response includes { y1, y2, salt, nonce, expires_at }
.
Submit proof { "username": "alice", "a1": "<hex>", "a2": "<hex>", "s": "<hex>", "nonce": "<hex>" }
.
On success returns { message, token, username }
.
Verify a session token.
Request Headers:
Authorization: Bearer <token>
Response (200 OK):
{
"valid": true,
"message": "Session is valid"
}
If Catch2 is installed, tests will be automatically built.
# From the build directory
./test_zkp
# Or using CTest
ctest --output-on-failure
cd python
pytest tests/
# With verbose output
pytest -v tests/
cd rust
cargo test
# With output
cargo test -- --nocapture
# Release mode (faster)
cargo test --release
- Safe Prime Generation: Uses 2048-bit safe primes (p = 2q + 1)
- Random Salt: Each user gets a unique 256-bit random salt
- Password Hashing: Passwords are hashed with username and salt
- Nonce Challenges: One-time, short-lived server nonces prevent replay attacks
- Session Expiry: Session tokens expire after 1 hour
- No Password Transmission: Passwords are never sent to or stored on the server
- Client derives secret
x = Hash(username || password || salt)
- Client computes
y1 = g^x mod p
andy2 = h^x mod p
- Server stores
(username, y1, y2, salt)
in database
- Client requests a one-time
nonce
from/challenge
- Client generates random
k
and computes commitmentsa1 = g^k mod p
,a2 = h^k mod p
- Client computes challenge
c = Hash(hex(a1)|hex(a2)|hex(y1)|hex(y2)|nonce)
(Fiat-Shamir) - Client computes response
s = k - c*x mod q
- Server verifies:
g^s * y1^c = a1
andh^s * y2^c = a2
, and consumes the nonce
- 2048-bit (default): ~112-bit security, fastest startup
- 4096-bit: higher security, slower startup and heavier CPU
- Python:
python server/main.py --bits 4096 --params-mode generate
- C++:
./auth_server --bits 4096 --params-mode generate
- Rust:
zkp-server --bits 4096 --params-mode generate
- Server creates session token upon successful verification
zkp-auth-system/
├── README.md # This file
├── QUICKSTART.md # Quick start guide
├── IMPLEMENTATION_COMPARISON.md # C++ vs Python comparison
├── docs/ # Documentation
│ ├── API.md # REST API documentation
│ ├── ZKP_THEORY.md # Zero-Knowledge Proof theory
│ ├── CHAUM_PEDERSEN.md # Chaum-Pedersen protocol details
│ └── IMPLEMENTATION.md # Mathematical implementation details
├── cpp/ # C++ Implementation
│ ├── CMakeLists.txt # Build configuration
│ ├── README.md # C++ specific documentation
│ ├── src/
│ │ ├── zkp/ # ZKP cryptography
│ │ │ ├── chaum_pedersen.hpp
│ │ │ └── chaum_pedersen.cpp
│ │ ├── db/ # Database layer
│ │ │ ├── user_database.hpp
│ │ │ └── user_database.cpp
│ │ ├── server/ # HTTP server
│ │ │ ├── auth_server.hpp
│ │ │ ├── auth_server.cpp
│ │ │ └── main.cpp
│ │ └── client/ # CLI client
│ │ └── cli_client.cpp
│ ├── tests/ # C++ tests
│ │ └── test_zkp.cpp
│ └── third_party/ # Header-only libraries
│ ├── httplib.h
│ └── nlohmann/
│ └── json.hpp
├── python/ # Python Implementation
│ ├── README.md # Python specific documentation
│ ├── requirements.txt # Python dependencies
│ ├── setup.py # Package setup
│ ├── run_server.sh # Quick start script
│ ├── run_tests.sh # Test runner
│ ├── zkp/ # ZKP cryptography
│ │ ├── __init__.py
│ │ └── chaum_pedersen.py
│ ├── db/ # Database layer
│ │ ├── __init__.py
│ │ └── user_database.py
│ ├── server/ # Flask server
│ │ ├── __init__.py
│ │ ├── auth_server.py
│ │ └── main.py
│ ├── client/ # CLI client
│ │ ├── __init__.py
│ │ └── cli_client.py
│ └── tests/ # Python tests
│ ├── __init__.py
│ └── test_zkp.py
└── rust/ # Rust Implementation
├── Cargo.toml # Dependencies and config
├── README.md # Rust specific documentation
├── src/
│ ├── lib.rs # Library root
│ ├── zkp/ # ZKP cryptography
│ │ └── mod.rs
│ ├── db/ # Database layer
│ │ └── mod.rs
│ ├── server/ # Actix-web server
│ │ └── mod.rs
│ ├── client/ # HTTP client
│ │ └── mod.rs
│ └── bin/ # Binary executables
│ ├── server.rs # Server binary
│ └── client.rs # Client binary
└── tests/ # Integration tests
└── integration_tests.rs
- QUICKSTART.md - Get started in 5 minutes
- IMPLEMENTATION_COMPARISON.md - C++ vs Python detailed comparison
- cpp/README.md - C++ implementation guide
- python/README.md - Python implementation guide
- rust/README.md - Rust implementation guide
- docs/VISUAL_GUIDE.md - 👈 START HERE! Visual introduction for beginners
- docs/ZKP_THEORY.md - Zero-Knowledge Proof theory and foundations
- docs/CHAUM_PEDERSEN.md - Chaum-Pedersen protocol specification
- docs/IMPLEMENTATION.md - Mathematical implementation details with examples
- docs/API.md - REST API documentation
This project demonstrates Zero-Knowledge Proofs (ZKP) in action. Here's what makes it special:
New to ZKP? Check out our Visual Guide with diagrams, analogies, and step-by-step illustrations!
The server can verify you know your password without ever learning what the password is. It's like proving you know a secret without revealing the secret itself.
- No Password Transmission: Your password never leaves your device
- No Password Storage: Server only stores public keys, never passwords
- Mathematical Proof: Uses the Chaum-Pedersen protocol based on discrete logarithms
- Non-Interactive: Uses Fiat-Shamir transform for one-shot proofs
- Replay Protection: One-time nonces prevent reuse of old proofs
Registration:
Password → Secret (x) → Public Keys (y₁ = g^x, y₂ = h^x)
Server stores: Public Keys only!
Authentication:
1. Client generates random k
2. Client computes commitments: a₁ = g^k, a₂ = h^k
3. Client computes challenge: c = Hash(commitments, public_keys, nonce)
4. Client computes response: s = k - c·x
5. Server verifies: g^s · y₁^c = a₁ and h^s · y₂^c = a₂
Server learns: Proof is valid ✓
Server never learns: Password or secret x ✗
For deep dive into the mathematics, see docs/ZKP_THEORY.md and docs/CHAUM_PEDERSEN.md.
This project was developed as POC for research purposes.
- Chaum, D., & Pedersen, T. P. (1992). "Wallet databases with observers." In Advances in Cryptology - CRYPTO.
- Fiat, A., & Shamir, A. (1986). "How to prove yourself: Practical solutions to identification and signature problems." In Advances in Cryptology - CRYPTO.
- Schnorr, C. P. (1991). "Efficient signature generation by smart cards." Journal of Cryptology, 4(3), 161-174.
- Goldwasser, S., Micali, S., & Rackoff, C. (1989). "The knowledge complexity of interactive proof systems." SIAM Journal on Computing.