|
|
@@ -0,0 +1,408 @@
|
|
|
+ProofWitness = namedtuple("ProofWitness", [
|
|
|
+ "v1", "v2", "v3", "v4", "r", "b", "σ"
|
|
|
+])
|
|
|
+
|
|
|
+class ProofPublic:
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self.C = None
|
|
|
+ self.D = None
|
|
|
+ self.X = None
|
|
|
+ self.Y = None
|
|
|
+ self.Z = None
|
|
|
+
|
|
|
+class ProofCommits:
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self.v1 = None
|
|
|
+ self.v2 = None
|
|
|
+ self.v3 = None
|
|
|
+ self.v4 = None
|
|
|
+ self.r = None
|
|
|
+ self.b = None
|
|
|
+ self.σ = None
|
|
|
+
|
|
|
+ self.σ_G1 = None
|
|
|
+ self.σ_G2 = None
|
|
|
+ self.v3_G1 = None
|
|
|
+ self.v4_G2 = None
|
|
|
+ self.blind_x = None
|
|
|
+ self.blind_y = None
|
|
|
+ self.blind_z = None
|
|
|
+
|
|
|
+ # Used by inner product
|
|
|
+ self.C0 = None
|
|
|
+ self.C1 = None
|
|
|
+
|
|
|
+ def transcript(self):
|
|
|
+ points = [
|
|
|
+ self.v1, self.v2, self.v3, self.v4, self.r, self.b, #self.σ,
|
|
|
+ self.σ_G1, self.σ_G2, self.v3_G1, self.v4_G2,
|
|
|
+ self.blind_x, self.blind_y, self.blind_z, self.C0, self.C1
|
|
|
+ ]
|
|
|
+ assert all(P is not None for P in points)
|
|
|
+ points = [P.xy() for P in points]
|
|
|
+ return list(zip(*points))
|
|
|
+
|
|
|
+class ProofResponses:
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self.v1 = None
|
|
|
+ self.v2 = None
|
|
|
+ self.v3 = None
|
|
|
+ self.v4 = None
|
|
|
+ self.r = None
|
|
|
+ self.b = None
|
|
|
+ self.σ = None
|
|
|
+
|
|
|
+ self.blind_x = None
|
|
|
+ self.blind_y = None
|
|
|
+ self.blind_z = None
|
|
|
+
|
|
|
+ self.txy = None
|
|
|
+
|
|
|
+Proof = namedtuple("Proof", [
|
|
|
+ "R", "s", "boolean_check"
|
|
|
+])
|
|
|
+
|
|
|
+RingProof = namedtuple("RingProof", [
|
|
|
+ "c0", "s0", "s1"
|
|
|
+])
|
|
|
+
|
|
|
+def make_proof(Ei, witness):
|
|
|
+ G1, G2, G3, G4, H = gens[Ei]
|
|
|
+ S = Scalar[Ei]
|
|
|
+
|
|
|
+ blind_x = int(S.random_element())
|
|
|
+ blind_y = int(S.random_element())
|
|
|
+ blind_xy = int(S.random_element())
|
|
|
+ # We want that blind_xy + blind_z == witness.b
|
|
|
+ blind_z = int(S(witness.b - blind_xy))
|
|
|
+
|
|
|
+ k_v1 = int(S.random_element())
|
|
|
+ k_v2 = int(S.random_element())
|
|
|
+ k_v3 = int(S.random_element())
|
|
|
+ k_v4 = int(S.random_element())
|
|
|
+ k_r = int(S.random_element())
|
|
|
+ k_b = int(S.random_element())
|
|
|
+ k_σ = int(S.random_element())
|
|
|
+
|
|
|
+ k_blind_x = int(S.random_element())
|
|
|
+ k_blind_y = int(S.random_element())
|
|
|
+ k_blind_xy = int(S.random_element())
|
|
|
+ k_blind_z = int(S.random_element())
|
|
|
+
|
|
|
+ # Used for inner product
|
|
|
+ k_t0 = int(S.random_element())
|
|
|
+ k_t1 = int(S.random_element())
|
|
|
+
|
|
|
+ R = ProofCommits()
|
|
|
+ R.v1 = k_v1 * G1
|
|
|
+ R.v2 = k_v2 * G2
|
|
|
+ R.v3 = k_v3 * G3
|
|
|
+ R.v4 = k_v4 * G4
|
|
|
+ R.r = k_r * H
|
|
|
+ R.b = k_b * H
|
|
|
+
|
|
|
+ # Used for 2nd proof
|
|
|
+ R.σ_G1 = k_σ * G1
|
|
|
+ R.σ_G2 = k_σ * G2
|
|
|
+ R.v3_G1 = k_v3 * G1
|
|
|
+ R.v4_G2 = k_v4 * G2
|
|
|
+ R.blind_x = k_blind_x * H
|
|
|
+ R.blind_y = k_blind_y * H
|
|
|
+ R.blind_xy = k_blind_xy * H
|
|
|
+ R.blind_z = k_blind_z * H
|
|
|
+
|
|
|
+ # σ (v1 - v3)
|
|
|
+ # σ (v2 - v4)
|
|
|
+ # (k_σ + c σ)(k_v1 - k_v3 + c*(v1 - v3))
|
|
|
+ #
|
|
|
+ # sage: var("k_σ c σ k_v1 k_v3 v1 v3")
|
|
|
+ # sage: ((k_σ + c*σ)*(k_v1 - k_v3 + c*(v1 - v3))).expand().collect(c)
|
|
|
+ # (v1*σ - v3*σ)*c^2 + (k_σ*v1 - k_σ*v3 + k_v1*σ - k_v3*σ)*c + k_v1*k_σ - k_v3*k_σ
|
|
|
+ R.C0 = (
|
|
|
+ (k_v3*k_σ - k_v1*k_σ) * G1 +
|
|
|
+ (k_v4*k_σ - k_v2*k_σ) * G2 +
|
|
|
+ k_t0 * H
|
|
|
+ )
|
|
|
+ R.C1 = (
|
|
|
+ (k_σ*witness.v3 - k_σ*witness.v1 + k_v3*witness.σ - k_v1*witness.σ) * G1 +
|
|
|
+ (k_σ*witness.v4 - k_σ*witness.v2 + k_v4*witness.σ - k_v2*witness.σ) * G2 +
|
|
|
+ k_t1 * H
|
|
|
+ )
|
|
|
+
|
|
|
+ c = hash_scalar(Ei, R.transcript())
|
|
|
+
|
|
|
+ s = ProofResponses()
|
|
|
+ s.v1 = int( k_v1 + c*witness.v1 )
|
|
|
+ s.v2 = int( k_v2 + c*witness.v2 )
|
|
|
+ s.v3 = int( k_v3 + c*witness.v3 )
|
|
|
+ s.v4 = int( k_v4 + c*witness.v4 )
|
|
|
+ s.r = int( k_r + c*witness.r )
|
|
|
+ s.b = int( k_b + c*witness.b )
|
|
|
+ s.σ = int( k_σ + c*witness.σ )
|
|
|
+
|
|
|
+ s.blind_x = int(k_blind_x + c*blind_x)
|
|
|
+ s.blind_y = int(k_blind_y + c*blind_y)
|
|
|
+ s.blind_xy = int(k_blind_xy + c*blind_xy)
|
|
|
+ s.blind_z = int(k_blind_z + c*blind_z)
|
|
|
+
|
|
|
+ s.txy = c**2 * blind_xy + c * k_t1 + k_t0
|
|
|
+
|
|
|
+ public = ProofPublic()
|
|
|
+ public.X = ((witness.v3 - witness.v1) * G1 +
|
|
|
+ (witness.v4 - witness.v2) * G2 +
|
|
|
+ blind_x * H)
|
|
|
+ public.Y = witness.σ * G1 + witness.σ * G2 + blind_y * H
|
|
|
+ public.XY = (
|
|
|
+ witness.σ * (witness.v3 - witness.v1) * G1 +
|
|
|
+ witness.σ * (witness.v4 - witness.v2) * G2 +
|
|
|
+ blind_xy * H
|
|
|
+ )
|
|
|
+ public.Z = witness.v1 * G1 + witness.v2 * G2 + blind_z * H
|
|
|
+ assert witness.σ in (0, 1)
|
|
|
+ if witness.σ == 0:
|
|
|
+ assert public.XY == blind_xy * H
|
|
|
+ assert (
|
|
|
+ public.XY + public.Z
|
|
|
+ ==
|
|
|
+ witness.v1*G1 + witness.v2*G2 + (blind_xy + blind_z)*H
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ assert witness.σ == 1
|
|
|
+ assert (
|
|
|
+ public.XY
|
|
|
+ ==
|
|
|
+ (witness.v3 - witness.v1) * G1 +
|
|
|
+ (witness.v4 - witness.v2) * G2 +
|
|
|
+ blind_xy * H
|
|
|
+ )
|
|
|
+ assert (
|
|
|
+ public.XY + public.Z
|
|
|
+ ==
|
|
|
+ witness.v3*G1 + witness.v4*G2 + (blind_xy + blind_z)*H
|
|
|
+ )
|
|
|
+ assert blind_xy + blind_z == witness.b
|
|
|
+
|
|
|
+ P1 = public.Y
|
|
|
+ P2 = public.Y - G1 - G2
|
|
|
+ assert blind_y*H == [P1, P2][witness.σ]
|
|
|
+ if witness.σ == 0:
|
|
|
+ assert blind_y*H == P1
|
|
|
+ assert blind_y*H - G1 - G2 == P2
|
|
|
+ else:
|
|
|
+ assert witness.σ == 1
|
|
|
+ assert blind_y*H + G1 + G2 == P1
|
|
|
+ assert blind_y*H == P2
|
|
|
+ boolean_check = make_ring_sig(Ei, [P1, P2], blind_y, int(witness.σ))
|
|
|
+ assert verify_ring_sig(Ei, boolean_check, [P1, P2])
|
|
|
+
|
|
|
+ return Proof(R, s, boolean_check), public
|
|
|
+
|
|
|
+def make_ring_sig(Ei, public_keys, secret, j):
|
|
|
+ H, S = gens[Ei][-1], Scalar[Ei]
|
|
|
+ assert len(public_keys) == 2
|
|
|
+ assert secret*H == public_keys[j]
|
|
|
+ assert j in (0, 1)
|
|
|
+
|
|
|
+ k0 = int(S.random_element())
|
|
|
+ R0 = k0*H
|
|
|
+
|
|
|
+ c1 = hash_scalar(Ei, R0.xy())
|
|
|
+ s1 = int(S.random_element())
|
|
|
+ R1 = s1*H - c1*public_keys[(j + 1) % 2]
|
|
|
+
|
|
|
+ c0 = hash_scalar(Ei, R1.xy())
|
|
|
+ s0 = k0 + c0*secret
|
|
|
+
|
|
|
+ if j == 1:
|
|
|
+ c0 = c1
|
|
|
+ s0, s1 = s1, s0
|
|
|
+
|
|
|
+ proof = RingProof(c0, s0, s1)
|
|
|
+ return proof
|
|
|
+
|
|
|
+def verify_ring_sig(Ei, proof, public_keys):
|
|
|
+ H = gens[Ei][-1]
|
|
|
+ S = Scalar[Ei]
|
|
|
+ assert len(public_keys) == 2
|
|
|
+
|
|
|
+ R1 = proof.s0*H - proof.c0*public_keys[0]
|
|
|
+ c1 = hash_scalar(Ei, R1.xy())
|
|
|
+
|
|
|
+ R2 = proof.s1*H - c1*public_keys[1]
|
|
|
+ c2 = hash_scalar(Ei, R2.xy())
|
|
|
+
|
|
|
+ return c2 == proof.c0
|
|
|
+
|
|
|
+def verify_proof(Ei, proof, public):
|
|
|
+ G1, G2, G3, G4, H = gens[Ei]
|
|
|
+ S = Scalar[Ei]
|
|
|
+
|
|
|
+ R, s = proof.R, proof.s
|
|
|
+
|
|
|
+ c = hash_scalar(Ei, R.transcript())
|
|
|
+
|
|
|
+ if (s.v1 * G1 +
|
|
|
+ s.v2 * G2 +
|
|
|
+ s.v3 * G3 +
|
|
|
+ s.v4 * G4 +
|
|
|
+ s.r * H
|
|
|
+ !=
|
|
|
+ R.v1 + R.v2 + R.v3 + R.v4 + R.r + c*public.C
|
|
|
+ ):
|
|
|
+ return False
|
|
|
+
|
|
|
+ # Now we want to prove that
|
|
|
+ # D = v1 G1 + v2 G2 + b H
|
|
|
+ # or
|
|
|
+ # D = v3 G1 + v4 G2 + b H
|
|
|
+
|
|
|
+ # We do this by checking:
|
|
|
+ # X = (v1 - v2)G + b_X H
|
|
|
+ # Y = σ G + b_Y H
|
|
|
+ # D = xy G + v2 G + b_D H
|
|
|
+ # σ ∈ {0, 1}
|
|
|
+
|
|
|
+ # X = (v1 - v2)G + b_X H
|
|
|
+ if (s.v3 * G1 - s.v1 * G1 +
|
|
|
+ s.v4 * G2 - s.v2 * G2 +
|
|
|
+ s.blind_x * H
|
|
|
+ !=
|
|
|
+ R.v3_G1 - R.v1 + R.v4_G2 - R.v2 + R.blind_x + c*public.X
|
|
|
+ ):
|
|
|
+ return False
|
|
|
+
|
|
|
+ # Y = σ G + b_Y H
|
|
|
+ if (s.σ * G1 + s.σ * G2 + s.blind_y * H
|
|
|
+ !=
|
|
|
+ R.σ_G1 + R.σ_G2 + R.blind_y + c*public.Y
|
|
|
+ ):
|
|
|
+ return False
|
|
|
+
|
|
|
+ # Z = v1 G1 + v2 G2
|
|
|
+ if (s.v1 * G1 + s.v2 * G2 + s.blind_z * H
|
|
|
+ !=
|
|
|
+ R.v1 + R.v2 + R.blind_z + c*public.Z
|
|
|
+ ):
|
|
|
+ return False
|
|
|
+
|
|
|
+ # Inner product verification. We select either P1 or P2
|
|
|
+ # prove D1 = x1 y1 G1 + b1 H
|
|
|
+ if (s.σ*(s.v3 - s.v1)*G1 + s.σ*(s.v4 - s.v2)*G2 + s.txy*H
|
|
|
+ !=
|
|
|
+ c**2*public.XY + c*R.C1 + R.C0
|
|
|
+ ):
|
|
|
+ return False
|
|
|
+
|
|
|
+ # check D is correct
|
|
|
+ if public.D != public.XY + public.Z:
|
|
|
+ return False
|
|
|
+
|
|
|
+ # boolean check proof for s
|
|
|
+ P1 = public.Y
|
|
|
+ P2 = public.Y - G1 - G2
|
|
|
+ if not verify_ring_sig(Ei, proof.boolean_check, [P1, P2]):
|
|
|
+ return False
|
|
|
+
|
|
|
+ return True
|
|
|
+
|
|
|
+def hash_scalar(Ei, values):
|
|
|
+ S = Scalar[Ei]
|
|
|
+
|
|
|
+ hasher = hashlib.sha256()
|
|
|
+ for value in values:
|
|
|
+ hasher.update(str(value).encode())
|
|
|
+
|
|
|
+ return S(int(hasher.hexdigest(), 16))
|
|
|
+
|
|
|
+# Test proving system
|
|
|
+def test_proof():
|
|
|
+ G1, G2, G3, G4, H = gens[E1]
|
|
|
+ S = Scalar[E1]
|
|
|
+
|
|
|
+ P1, P2 = [E[E2].random_point() for _ in range(2)]
|
|
|
+ (P1_x, P1_y), (P2_x, P2_y) = P1.xy(), P2.xy()
|
|
|
+ r, b = [S.random_element() for _ in range(2)]
|
|
|
+
|
|
|
+ C = hash_nodes(E1, P1, P2, r)
|
|
|
+ # σ = 0 for P1, or σ = 1 for P2
|
|
|
+ σ = S(1)
|
|
|
+ D = hash_point(E1, P2, b)
|
|
|
+
|
|
|
+ proof, public = make_proof(
|
|
|
+ E1,
|
|
|
+ ProofWitness(
|
|
|
+ P1_x,
|
|
|
+ P1_y,
|
|
|
+ P2_x,
|
|
|
+ P2_y,
|
|
|
+ r,
|
|
|
+ b,
|
|
|
+ σ
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+ public.C = C
|
|
|
+ public.D = D
|
|
|
+
|
|
|
+ assert verify_proof(E1, proof, public)
|
|
|
+
|
|
|
+ # Now try the other side too
|
|
|
+ σ = S(0)
|
|
|
+ D = hash_point(E1, P1, b)
|
|
|
+
|
|
|
+ proof, public = make_proof(
|
|
|
+ E1,
|
|
|
+ ProofWitness(
|
|
|
+ P1_x,
|
|
|
+ P1_y,
|
|
|
+ P2_x,
|
|
|
+ P2_y,
|
|
|
+ r,
|
|
|
+ b,
|
|
|
+ σ
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+ public.C = C
|
|
|
+ public.D = D
|
|
|
+
|
|
|
+ assert verify_proof(E1, proof, public)
|
|
|
+
|
|
|
+ # Test the ring sigs too
|
|
|
+ secret = int(S.random_element())
|
|
|
+ P1 = secret*H
|
|
|
+ P2 = E[E1].random_point()
|
|
|
+ proof = make_ring_sig(E1, [P1, P2], secret, 0)
|
|
|
+ assert verify_ring_sig(E1, proof, [P1, P2])
|
|
|
+ # Also try in reverse
|
|
|
+ P1, P2 = P2, P1
|
|
|
+ proof = make_ring_sig(E1, [P1, P2], secret, 1)
|
|
|
+ assert verify_ring_sig(E1, proof, [P1, P2])
|
|
|
+
|
|
|
+ # Ring sigs is our boolean proof for σ
|
|
|
+ σ = S(0)
|
|
|
+ b = S.random_element()
|
|
|
+
|
|
|
+ # Verifier only has P
|
|
|
+ # We prove that σ ∈ {0, 1}
|
|
|
+ P = σ*G1 + b*H
|
|
|
+ # They can only make a ring signature on H
|
|
|
+ # if σ is 0 or 1
|
|
|
+ # P1 = P represents σ = 0
|
|
|
+ P1 = P
|
|
|
+ # P2 = P - G1 represents σ = 1
|
|
|
+ P2 = P - G1
|
|
|
+
|
|
|
+ proof = make_ring_sig(E1, [P1, P2], b, 0)
|
|
|
+ assert verify_ring_sig(E1, proof, [P1, P2])
|
|
|
+ # Also try σ = 1
|
|
|
+ σ = S(1)
|
|
|
+ P = σ*G1 + b*H
|
|
|
+ P1 = P
|
|
|
+ P2 = P - G1
|
|
|
+ proof = make_ring_sig(E1, [P1, P2], b, 1)
|
|
|
+ assert verify_ring_sig(E1, proof, [P1, P2])
|
|
|
+
|