base = 2
# φ(a + b) = φ(a) × φ(b)
a, b = 3, 5
lhs = base ** (a + b) # 2^8 = 256
rhs = base**a * base**b # 8 × 32 = 256
assert lhs == rhs
# Addition in integers maps to multiplication in powers!
print(f"2^({a}+{b}) = 2^{a+b} = {lhs}")
print(f"2^{a} × 2^{b} = {base**a} × {base**b} = {rhs}")
Addition on one side becomes multiplication on the other.
This is the basis of how elliptic curve scalar multiplication works!
Important Clarifications
One-directional: φ maps A → B, but there's no requirement for B → A
Not always exists: arbitrary groups may have no homomorphism between them
Not necessarily bijective: φ doesn't need to hit every element of B
Trivial homomorphism: mapping everything to the identity always works (but is useless)
Multiple homomorphisms: there can be more than one φ between A and B
The trivial homomorphism: φ(a) = eB for all a.
It's valid (φ(a□b) = e = e ■ e = φ(a)■φ(b)) but tells us nothing useful.
Rationals → Finite Field
φ(a/b) = a × b-1 (mod p)
p = 17
def phi(num, den):
return (num * pow(den, -1, p)) % p
# 1/3 + 3/5 = 14/15 in regular math
a = phi(1, 3) # φ(1/3)
b = phi(3, 5) # φ(3/5)
c = phi(14, 15) # φ(14/15)
print(f"φ(1/3) = {a}") # 6
print(f"φ(3/5) = {b}") # 4
print(f"φ(14/15) = {c}") # 10
# Check: φ(1/3) + φ(3/5) ≡ φ(14/15) (mod 17)?
print(f"φ(1/3) + φ(3/5) = {(a + b) % p}") # 10
assert (a + b) % p == c # ✓ Homomorphism preserved!
Homomorphic Encryption
If φ is computationally difficult to invert,
then φ homomorphically encrypts the elements of A.
φ maps from "secret space" A to "public space" B
We can verify computations in B without knowing values in A
Because φ preserves structure, computations in B reflect computations in A
Because φ is hard to invert, nobody can recover the original values
This is the fundamental mechanism of zero knowledge proofs!
Zero Knowledge Proof: Addition
Claim: "I know two numbers x and y such that x + y = 15"
# Setup: φ maps integers to elliptic curve points
# φ(x) = x × G (scalar multiplication by generator)
# This is hard to invert (discrete logarithm problem)
# Prover sends:
# A = φ(x) = xG
# B = φ(y) = yG
# claim: x + y = 15
# Verifier checks:
# A ■ B ==? φ(15)
# xG + yG ==? 15G
#
# By the homomorphism property:
# φ(x + y) = φ(x) + φ(y) = xG + yG
#
# If xG + yG = 15G, then x + y = 15 ✓
# The verifier is convinced WITHOUT knowing x or y!
Zero Knowledge Proof: Scalar Multiplication
Claim: "I know a and b, and b = 5a"
# Prover sends: φ(a) and φ(b)
# i.e., aG and bG
# Verifier checks:
# φ(a) + φ(a) + φ(a) + φ(a) + φ(a) ==? φ(b)
# 5 × (aG) ==? bG
#
# Remember: "5 × (aG)" is repeated ADDITION
# That's just 5aG
# If 5(aG) = bG, then 5a = b ✓
# Pseudocode:
# A = multiply(G1, a) # prover computes aG
# B = multiply(G1, b) # prover computes bG
# assert multiply(A, 5) == B # verifier checks 5(aG) = bG
"Multiplication by a constant" is really repeated addition.
Repeated addition = scalar multiplication on the curve.
Why Elliptic Curve Points?
What makes EC points perfect for φ?
Group structure: EC points form a finite cyclic abelian group
Homomorphism: φ(x) = xG preserves addition
Hard to invert: given xG, finding x is the discrete logarithm problem
Efficient: point addition and scalar multiplication are fast
Compact: points can be represented with ~256 bits
EC points give us a one-way homomorphism:
Easy: integer → EC point (compute xG)
Hard: EC point → integer (discrete log)
The Big Picture
"Elliptic curve points in a finite field under addition are a finite cyclic group, and integers under addition are homomorphic to this group."
You now understand every word of this sentence:
EC points under addition: closed, associative, has identity, has inverses
Finite cyclic: bounded number of elements, all generated from G
Integers under addition: the familiar group we know
This one sentence encodes EVERYTHING you need to know about how ZK proofs work at a high level.
Demo: Homomorphism in Action
# Verify the homomorphism property in Python
# φ(a + b) = φ(a) + φ(b) where φ(x) = 2^x
base = 2
# Test with many pairs
import random
for _ in range(5):
a = random.randint(1, 20)
b = random.randint(1, 20)
# Path 1: add then map
path1 = base ** (a + b)
# Path 2: map then multiply
path2 = (base ** a) * (base ** b)
assert path1 == path2
print(f"a={a:2d}, b={b:2d}: "
f"{base}^({a}+{b})={path1}, "
f"{base}^{a}×{base}^{b}={path2} ✓")
Advanced: Rationals → Finite Field (Multiplicative)
p = 17
def phi(num, den):
return (num * pow(den, -1, p)) % p
# Multiplicative homomorphism:
# φ(1/3 × 3/5) = φ(1/3) × φ(3/5)
# 1/3 × 3/5 = 1/5 in regular math
a = phi(1, 3) # φ(1/3) = 6
b = phi(3, 5) # φ(3/5) = 4
c = phi(1, 5) # φ(1/5) = 7
print(f"φ(1/3) × φ(3/5) = {a} × {b} = {(a*b) % p}") # 24 mod 17 = 7
print(f"φ(1/5) = {c}") # 7
assert (a * b) % p == c # ✓ Works for multiplication too!
The same φ works for both additive and multiplicative homomorphisms.
This is because finite fields have both group structures.