This folder contains a true interactive Schnorr sigma-protocol implementation in C.
- Prover secret:
x - Public key:
y = g^x mod p - Commitment:
t = g^r mod p - Challenge:
c - Response:
s = r + c*x (mod q) - Verification equation:
g^s == t * y^c (mod p)
Unlike a simple hash/password check, this is an actual proof-of-knowledge challenge-response protocol.
schnorr.h: public types and function declarationsschnorr.c: protocol and parameter generation implementationmain.c: interactive demo executabletests.c: test runner with positive/negative protocol checks
- Uses system CSPRNG (
BCryptGenRandomon Windows,/dev/urandomon POSIX) - Generates safe-prime-style parameters at runtime (
p = 2q + 1, with primality checks) - Uses multiple rounds to decrease false-accept probability for an impostor
Note: This is educational code using small (~31-bit) subgroup sizes for performance and readability in plain C without big-integer libraries.
gcc -O2 -std=c11 -Wall -Wextra -pedantic -o schnorr_demo main.c schnorr.c
gcc -O2 -std=c11 -Wall -Wextra -pedantic -o schnorr_tests tests.c schnorr.cgcc -O2 -std=c11 -Wall -Wextra -pedantic -o schnorr_demo main.c schnorr.c -lbcrypt
gcc -O2 -std=c11 -Wall -Wextra -pedantic -o schnorr_tests tests.c schnorr.c -lbcryptcl /O2 /W4 main.c schnorr.c bcrypt.lib /Fe:schnorr_demo.exe
cl /O2 /W4 tests.c schnorr.c bcrypt.lib /Fe:schnorr_tests.exe./schnorr_demoOptional rounds argument:
./schnorr_demo 20Expected behavior:
- Honest rounds print
accepted=true - Built-in tamper check prints
accepted=false
./schnorr_tests-
honest_rounds: Confirms normal prover/verifier flow succeeds across many rounds. If this fails, core protocol math is broken. -
tamper_response: Mutatessby +1 modqafter a valid proof is created. Verifier must reject. If accepted, integrity is broken. -
wrong_public_key: Verifies a valid proof against a different public key. Verifier must reject. If accepted, identity binding is broken. -
random_forgery: Tries many forged tuples(t, c, s)not produced with witnessx. Usually all reject; occasional accidental accepts are theoretically possible in finite groups.
Use tests.c as a template and add new functions with the pattern:
- Build or mutate a
SchnorrProof - Call
schnorr_verify(...) - Assert expected accept/reject outcome
- Print
[PASS]or[FAIL]
Suggested custom tests:
- Boundary challenge test: set
challenge_cto0orq-1and verify equation behavior. - Replay behavior: verify same proof multiple times against same public key (should consistently pass if unmodified).
- Cross-parameter mismatch: generate proof under one
(p,q,g)set and verify under another (must reject). - Stress run: increase honest rounds and forgery attempts for statistical confidence.
- Failure in positive test (
honest_rounds) indicates functional bug. - Failure in negative test (
tamper_response,wrong_public_key) indicates security bug. - High acceptance rate in
random_forgeryindicates serious issue; tiny nonzero rates can occur by chance in small educational parameter sizes.
gcc -O2 -std=c11 -Wall -Wextra -pedantic -o schnorr_demo main.c schnorr.c -lbcrypt; gcc -O2 -std=c11 -Wall -Wextra -pedantic -o schnorr_tests tests.c schnorr.c -lbcrypt