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.