zk·little book

01

0
02

r
03

ga · gb = ga+b

C₁ = gm₁ mod p
C₂ = gm₂ mod p
C₁ · C₂ mod p
gm₁+m₂ mod p

g = 5, p = 1009

04

×w₁ = x · x
×w₂ = w₁ · x
+w₃ = w₂ + x
+out = w₃ + 5
witness
x
w₁ w₂ w₃
out
05

CSCA
DSC
SOD

KIM, MINJI
1990-03-15
M12345678
KOR
2031-06-01
nullifier

06

# --- Commitment: seal now, open later --------------------------
def commit(m):
    r = random_bytes(32)          # one-time blinding value
    c = sha256(r + m)             # c = H(r || m)
    return c, (m, r)              # publish c, keep (m, r)

def verify(c, m, r):
    return c == sha256(r + m)

# bare hash = fingerprint: sha256(m) is the SAME every time,
# so small secrets can be brute-forced. r blocks that (hiding),
# and the hash itself blocks changing m later (binding).
# --- Pedersen: a commitment you can add ------------------------
def pedersen(m, r):
    return m * G + r * H          # points on an elliptic curve

# the exponent rule in additive costume:
#   pedersen(m1,r1) + pedersen(m2,r2) == pedersen(m1+m2, r1+r2)
# verify sums of hidden amounts without ever seeing them.
# --- A tiny circuit: "I know x with x**3 + x + 5 == 35" --------
def make_witness(x):
    w1 = x * x                    # gate 1  (x=3 -> 9)
    w2 = w1 * x                   # gate 2  (-> 27)
    w3 = w2 + x                   # gate 3  (-> 30)
    out = w3 + 5                  # gate 4  (-> 35)
    return [1, x, out, w1, w2, w3]

# every gate is one equation; a SNARK compresses
# "all equations hold" into one short proof. The verifier
# checks the proof + out == 35, and never sees x.
# --- Passport statement (what the circuit enforces) ------------
# private: dg1, sod, dsc_cert          (never leave the phone)
# public : today, min_age, csca_root, scope

def circuit(private, public):
    assert merkle_verify(hash(private.dsc_cert), public.csca_root)
    assert verify_sig(private.dsc_cert.pub, private.sod)
    assert sha256(private.dg1) == private.sod.dg_hashes[1]
    assert age(private.dg1.dob, public.today) >= public.min_age

    secret = hash(private.dg1.doc_number, private.dg1.dob)
    return ok=1, nullifier=hash(secret, public.scope)

# the state's signature travels through the math
# into one yes/no + an unlinkable nullifier.