CPZKp - Building Practical Zero-Knowledge Proofs in Rust from Scratch
— #Rust#cryptography#zero-knowledge#chaum-pedersen#ecc#curve25519#security#portfolio

“You don’t start with ZK. ZK starts with you.” — someone probably
Introduction
In late 2023, the seed for CPZKp was planted: a lightweight, modular, and no-bullshit Rust library for zero-knowledge proofs using the Chaum-Pedersen protocol. The motivation was personal and practical — to build a foundation that respects cryptographic rigor, while remaining usable in real-world systems, especially those relying on elliptic curve cryptography (ECC).
This post is not a “hello world.” It’s a journey — from group theory to Curve25519 bindings, from low-level proof serialization to full WASM exports. If you're looking for a project that goes from first principles to full-stack cryptography, buckle up.
Motivation
By 2023, a few realities had become clear:
- Most zero-knowledge implementations are either academic toys or tightly bound to specific use cases (blockchains, zkSNARKs, etc).
- Libraries like
bulletproofsorzkcryptoare excellent, but bloated when you need just authentication proofs. - There was no ergonomic, extensible, and no_std-capable Chaum-Pedersen implementation in Rust.
CPZKp was born from frustration — and fascination.
What is the Chaum-Pedersen ZKP?
It’s a proof of equality of discrete logs:
If you know x such that g^x = A and h^x = B, you can prove knowledge of x without revealing it.
This is essential in:
- Secure authentication (no password ever transmitted)
- Key exchange validation
- Voting and threshold cryptography
The challenge was: how do we express this cleanly across scalar groups and ECC, and still support Curve25519?
Designing the Library
We started with a few key design principles:
Protocols as Traits
pub trait ZkpProtocol {
type Secret;
type Public;
type Proof;
fn prove(secret: &Self::Secret, pub_input: &Self::Public) -> Self::Proof;
fn verify(public: &Self::Public, proof: &Self::Proof) -> bool;
}Protocols are swappable. This allows for different backends (secp256k1, Ristretto, ScalarGroup) and experimentation with variants.
Scalar and ECC Support
mod scalar;
mod ecc;Internally, both conform to common traits like GroupElement, enabling unified logic in proof generators and verifiers.
Serialization: Making Proofs Portable
One key requirement was to serialize proofs for transmission.
We used serde and implemented robust custom serialization for scalar and ECC formats:
#[derive(Serialize, Deserialize)]
pub struct ChaumPedersenProof {
pub t1: GroupElement,
pub t2: GroupElement,
pub challenge: Scalar,
pub response: Scalar,
}This allowed JSON/web compatibility from day one.
Testing the Unprovable
We didn’t stop at unit tests. CPZKp includes:
- 🔁 Property-based tests (
proptest) - 🧪 Negative tests (e.g., corrupt challenge / invalid response)
- 🧬 Deterministic regression seeds for CI stability
- 🔍 Manual validation of group assumptions
proptest! {
#[test]
fn prove_and_verify_should_hold(ref s in any::<Scalar>()) {
let (pk, proof) = ChaumPedersen::prove(&s);
prop_assert!(ChaumPedersen::verify(&pk, &proof));
}
}WASM and Python Bindings
We wanted this lib usable in:
- dApps (via WASM)
- Python systems (via
pyo3)
Result:
wasm_bindgenwrapper inwasm.rsmaturinbuild inbindings/python
#[wasm_bindgen]
pub fn prove_json(sk: &str, pk: &str) -> String {
...
}Now CPZKp runs in browsers and Jupyter notebooks.
CLI Tool
We implemented a command-line utility for quick usage:
cpzkp gen-key
cpzkp prove --msg "authenticate me"
cpzkp verify --proof proof.jsonBacked by clap, this made it ideal for scripting, automation, or even classroom demos.
Performance
Benchmarks were done using criterion. Example:
group time
ChaumPedersen_scalar_prove 1.2 µs
ChaumPedersen_ecc_prove 4.8 µs
ChaumPedersen_verify_scalar 0.9 µs
ChaumPedersen_verify_ecc 3.7 µsEnough for embedded use and authentication services.
What We Learned
- Traits + generic cryptographic algebra = superpowers.
- Testing edge cases in ZKP is not optional — it’s life.
- Targeting WASM early saves time later.
- Your build scripts are part of your UX.
Roadmap
- 🔒 Formal audit and fuzz testing
- 📦 Publish to crates.io and PyPI
- 🧱 Add Bulletproofs-style range proofs
- 🔄 Add MPC-friendly APIs
- 🌐 Playground (CPZKp + Monaco + WebWasm)
Conclusion
CPZKp isn’t another toy crypto lib. It’s a usable, modular ZKP toolkit built from real-world needs, shaped by frustration, and delivered with love — in Rust.
Try it. Break it. Extend it.