def test_signature_non_interactive_proof(): mG = BilinearGroupPair() keypair = BBSPlusKeypair.generate(mG, 9) messages = [Bn(30), Bn(31), Bn(32)] pk, sk = keypair.pk, keypair.sk generators, h0 = keypair.generators, keypair.h0 creator = BBSPlusSignatureCreator(pk) lhs = creator.commit(messages) presignature = sk.sign(lhs.com_message) signature = creator.obtain_signature(presignature) e, s, m1, m2, m3 = (Secret() for _ in range(5)) secret_dict = { e: signature.e, s: signature.s, m1: messages[0], m2: messages[1], m3: messages[2], } p1 = BBSPlusSignatureStmt([e, s, m1, m2, m3], pk, signature) tr = p1.prove(secret_dict) p1 = BBSPlusSignatureStmt([Secret() for _ in range(5)], pk) assert p1.verify(tr)
def test_signature_proof(): mG = BilinearGroupPair() keypair = BBSPlusKeypair.generate(mG, 9) messages = [Bn(30), Bn(31), Bn(32)] pk, sk = keypair.pk, keypair.sk generators, h0 = keypair.generators, keypair.h0 creator = BBSPlusSignatureCreator(pk) lhs = creator.commit(messages) presignature = sk.sign(lhs.com_message) signature = creator.obtain_signature(presignature) e, s, m1, m2, m3 = (Secret() for _ in range(5)) secret_dict = { e: signature.e, s: signature.s, m1: messages[0], m2: messages[1], m3: messages[2], } p1 = BBSPlusSignatureStmt([e, s, m1, m2, m3], pk, signature) prover = p1.get_prover(secret_dict) p2 = BBSPlusSignatureStmt([Secret() for _ in range(5)], pk) verifier = p2.get_verifier() pc = prover.precommit() verifier.process_precommitment(pc) com = prover.commit() chal = verifier.send_challenge(com) resp = prover.compute_response(chal) assert verifier.verify(resp)
def point_double(a, b, p, x, y): """Define "doubling" an EC point. A special case, when a point needs to be added to itself. Reminder: lam = (3 * xp ^ 2 + a) * (2 * yp) ^ -1 (mod p) xr = lam ^ 2 - 2 * xp yr = lam * (xp - xr) - yp (mod p) Returns the point representing the double of the input (x, y). """ if x is None and y is None: return None, None # ADD YOUR CODE BELOW lam1 = ((x.mod_mul(x,p)).mod_mul(Bn(3), p)).mod_add(a,p) lam2 = (y.mod_mul(Bn(2),p)).mod_inverse(m=p) lam = lam1.mod_mul(lam2,p) xr1 = lam.mod_pow(Bn(2),p) xr2 = x.mod_mul(Bn(2),p) xr = xr1.mod_sub(xr2,p) yr1 = x.mod_sub(xr,p) yr2 = lam.mod_mul(yr1,p) yr = yr2.mod_sub(y,p) return xr, yr
def point_double(a, b, p, x, y): """Define "doubling" an EC point. A special case, when a point needs to be added to itself. Reminder: lam = (3 * xp ^ 2 + a) * (2 * yp) ^ -1 (mod p) xr = lam ^ 2 - 2 * xp yr = lam * (xp - xr) - yp (mod p) Returns the point representing the double of the input (x, y). """ # ADD YOUR CODE BELOW if x is None and y is None: return None, None xsq = x.mod_mul(x, p) xsq3 = Bn(3).mod_mul(xsq, p) num = xsq3.mod_add(a, p) y2 = Bn(2).mod_mul(y, p) y2inv = y2.mod_inverse(m=p) lam = num.mod_mul(y2inv, p) xr = lam.mod_mul(lam, p) xr = xr.mod_sub(x, p) xr = xr.mod_sub(x, p) yr = lam.mod_mul(x.mod_sub(xr, p), p) yr = yr.mod_sub(y, p) return (xr, yr) xr, yr = None, None return xr, yr
def hashG1(self, sbin): """ Hashes a byte string into a point of G1. Example: >>> G = BpGroup() >>> g1 = G.gen1() >>> g1p = G.hashG1(b"Hello") >>> x = g1 + g1p """ if self.p is None: p = Bn() a, b = Bn(), Bn() _check(_C.BP_GROUP_get_curve(self.bpg, p.bn, a.bn, b.bn, _FFI.NULL)) self.p = p pt = G1Elem(self) xhash = sbin y = 1 ret = 0 while ret == 0: xhash = sha512(xhash).digest() x = Bn.from_binary(xhash) % self.p ret = _C.G1_ELEM_set_compressed_coordinates( self.bpg, pt.elem, x.bn, y, _FFI.NULL) _check(_C.G1_ELEM_is_on_curve(self.bpg, pt.elem, _FFI.NULL)) return pt
def test_poly_expand(): ck = setup() (G, g, h, o) = ck p1 = [Bn(1), Bn(2)] p2 = poly_expand(o, p1, 10) assert len(p2) == 10 assert p2[:2] == p1
def test_poly_add(): ck = setup() (G, g, h, o) = ck p1 = [Bn(1), Bn(2)] p2 = poly_add(o, p1, p1) assert len(p2) == len(p1) assert p2 == [2, 4]
def precommit(self): """ Commit to the bit-decomposition of the value. """ actual_value = ensure_bn(self.x.value) value_as_bits = decompose_into_n_bits(actual_value, self.num_bits) # Set true value to computed secrets for rand in self.randomizers: rand.value = self.order.random() precommitment = {} precommitment["Cs"] = [ b * self.g + r.value * self.h for b, r in zip(value_as_bits, self.randomizers) ] # Compute revealed randomizer rand = Bn(0) power = Bn(1) for r in self.randomizers: rand = rand.mod_add(r.value * power, self.order) power *= 2 rand = rand.mod_sub(self.randomizer.value, self.order) precommitment["rand"] = rand return precommitment
def compute_denominators(k, q): """Computes denominators for Lagrange basis polynomials. Uses distinct points 1, ...,k Arguments: k -- number of basis polynomials q -- the order of the group """ denominators = [] temp = Bn(1) for i in range(1, k + 1): if i == 1: for j in range(2, k + 1): elem = i - j temp = temp.mod_mul(elem, q) elif i == k: elem = 1 - k temp = temp.mod_mul(elem, q) else: inverse = Bn(i - 1 - k) inverse = inverse.mod_inverse(q) elem = i - 1 temp = temp.mod_mul(elem, q) temp = temp.mod_mul(inverse, q) denominators.append(temp) return denominators
def point_double(a, b, p, x, y): """Define "doubling" an EC point. A special case, when a point needs to be added to itself. Reminder: lam = (3 * xp ^ 2 + a) * (2 * yp) ^ -1 (mod p) xr = lam ^ 2 - 2 * xp yr = lam * (xp - xr) - yp (mod p) Returns the point representing the double of the input (x, y). """ if x is None and y is None: return None, None xr, yr = None, None lam_part1 = x.mod_pow(2, p).mod_mul(Bn(3), p).mod_add(a, p) lam_part2 = Bn(2).mod_mul(y, p).mod_inverse(p) lam = lam_part1.mod_mul(lam_part2, p) xr = lam.mod_pow(2, p).mod_sub(Bn(2).mod_mul(x, p), p) yr = x.mod_sub(xr, p).mod_mul(lam, p).mod_sub(y, p) return xr, yr
def test_pedersen_commit_invalid_proof(): values = [Bn(2651), Bn(1), Bn(98)] pparam = CommitParam(hs_size=len(values)) pcommit, prand = pparam.commit(values) prand = pparam.q.random() assert not pcommit.verify(pparam, prand, values) proof = pcommit.prove_knowledge(pparam, prand, values) assert not pcommit.verify_proof(pparam, proof)
def test_same_random_values_in_commitments(group): (g, ) = make_generators(1, group) generators = [g, g, g] pub = group.wsum([Bn(100), Bn(100), Bn(100)], generators) x = Secret() p = DLRep(pub, wsum_secrets([x, x, x], generators)) prover = p.get_prover({x: 100}) commitments = prover.commit()
def test_decompose_n_bits(): n4 = Bn(4) assert decompose_into_n_bits(n4, 4) == [0, 0, 1, 0] n3 = Bn(3) assert decompose_into_n_bits(n3, 6) == [1, 1, 0, 0, 0, 0] n5 = Bn(5) assert decompose_into_n_bits(n5, 4) == [1, 0, 1, 0]
def mul(params, pub, c1, alpha): """ Given a ciphertext compute the ciphertext of the product of the plaintext time alpha """ assert isCiphertext(params, c1) (a, b) = c1 c3 = (Bn(alpha) * a, Bn(alpha) * b) return c3
def keygen(self): # 调用petlib.bn中的Bn,生成两个[0,p)的随机数,作为私钥sk self.x = Bn().from_decimal(str(self.p)).random() self.y = Bn().from_decimal(str(self.p)).random() # 将私钥中两个元素x,y分别与g2做标量乘法 self.X = self.x * self.g2 self.Y = self.y * self.g2 sk = (self.x, self.y) vk = (self.X, self.Y) return sk, vk
def generate_locally(gk, g1_chipows, g2_chipows, g1_beta_chipows, n): """ Computes (Θ(n ^ 2)) the following collections: [l_i]_1, 1<=i<=n+1 [l_i * l_(n+1)]_1, 1<=i<=n+1 [l_i ^ 2]_1, 1<=i<=n+1 [l_i]_2, 1<=i<=n+1 [β * l_i]_1, 1<=i<=n+1 Computations follow directly from Theorem 7, C.2, pg. 26 """ q = gk.q omega = (q - 1) // (n + 1) div_nplus1 = lambda _: _.mul(Bn(n + 1).mod_inverse(q)) div_nplus1_sq = lambda _: _.mul(Bn((n + 1)**2).mod_inverse(q)) g1_zero = 0 * gk.g1 g2_zero = 0 * gk.g2 def compute_g1_elij_prod(i, j): g1_el_ij = g1_zero for s in range(0, n + 1): for t in range(0, n + 1): omega_pow_inv = pow(omega, i * s + j * t, q).mod_inverse(q) g1_el_ij += g1_chipows[s + t].mul(omega_pow_inv) return g1_el_ij g1_elinplus1_prods = [] g1_el_squares = [] g1_els = [] g2_els = [] g1_beta_els = [] for i in range(1, n + 2): g1_el_i = g1_zero g2_el_i = g2_zero g1_beta_el_i = g1_zero for j in range(0, n + 1): omega_ij_inv = pow(omega, i * j, q).mod_inverse(q) g1_el_i += g1_chipows[j].mul(omega_ij_inv) g2_el_i += g2_chipows[j].mul(omega_ij_inv) g1_beta_el_i += g1_beta_chipows[j].mul(omega_ij_inv) g1_el_square = compute_g1_elij_prod(i, i) g1_elinplus1_prod = compute_g1_elij_prod(i, n + 1) g1_els.append(div_nplus1(g1_el_i)) g1_el_squares.append(div_nplus1_sq(g1_el_square)) g1_elinplus1_prods.append(div_nplus1_sq(g1_elinplus1_prod)) g2_els.append(div_nplus1(g2_el_i)) g1_beta_els.append(div_nplus1(g1_beta_el_i)) return g1_els, g1_elinplus1_prods, g1_el_squares, g2_els, g1_beta_els
def __lagrange(i, index_list, p): ''' Calculate lagrange coefficient ''' top = Bn(1) bottom = Bn(1) for j in index_list: if j != i: top = (top * j) bottom = (bottom * (j-i)) return top.mod_mul(bottom.mod_inverse(p), p)
def test_bl_pedersen_with_acl_params_valid(): group = EcGroup(DEFAULT_GROUP_ID) g1 = group.hash_to_point(b"test_z") g2 = group.hash_to_point(b"test_h2") values = [Bn(123), Bn(456), 'hello', b"world"] param = BlindedPedersenParam(hs_size=len(values), Z=g1, H_2=g2 ) bcommit, bpriv = param.blind_commit(values) bproof = bcommit.prove_values(bpriv, reveal_mask=[True, False, True, True]) assert bcommit.verify_proof(param, bproof) assert bproof.revealed_values == [Bn(123), None, 'hello', b"world"]
def test_bl_pedersen_valid(): values = [Bn(123), Bn(456), 'hello', b"world"] param = BlindedPedersenParam(hs_size=len(values)) # reveal nothing bcommit, bpriv = param.blind_commit(values) bproof = bcommit.prove_values(bpriv) assert bcommit.verify_proof(param, bproof) # revealing some values bproof2 = bcommit.prove_values(bpriv, reveal_mask=[True, False, True, True]) assert bcommit.verify_proof(param, bproof2) assert bproof2.revealed_values == [Bn(123), None, 'hello', b"world"]
def pederson_commitment_example(): values = [Bn(2651), Bn(1), Bn(98)] pparam = CommitParam(hs_size=len(values)) pcommit, prand = pparam.commit(values) # reveal the opening valid = pcommit.verify(pparam, prand, values) assert valid # Prove the knowledge of opening with a nzkp proof = pcommit.prove_knowledge(pparam, prand, values) valid = pcommit.verify_proof(pparam, proof) print(valid)
def test_signature_and_dlrne_fails_on_wrong_secret(): """ We manually modify a secret in the DLNE member, i.e we wrongfully claim to use the same "s" i the signature and in the DLNE. Should be detected and raise an Exception. """ mG = BilinearGroupPair() keypair = BBSPlusKeypair.generate(mG, 9) messages = [Bn(30), Bn(31), Bn(32)] pk, sk = keypair.pk, keypair.sk generators, h0 = keypair.generators, keypair.h0 creator = BBSPlusSignatureCreator(pk) lhs = creator.commit(messages) presignature = sk.sign(lhs.com_message) signature = creator.obtain_signature(presignature) e, s, m1, m2, m3 = (Secret() for _ in range(5)) secret_dict = { e: signature.e, s: signature.s, m1: messages[0], m2: messages[1], m3: messages[2], } sigproof = BBSPlusSignatureStmt([e, s, m1, m2, m3], pk, signature) g1 = mG.G1.generator() pg1 = signature.s * g1 pg2, g2 = mG.G1.order().random() * g1, mG.G1.order().random() * g1 dneq = DLNotEqual((pg1, g1), (pg2, g2), s, bind=True) secrets = [Secret() for _ in range(5)] sigproof1 = BBSPlusSignatureStmt(secrets, pk, signature) dneq1 = DLNotEqual((pg1, g1), (pg2, g2), secrets[1], bind=True) andp = sigproof & dneq andp1 = sigproof1 & dneq1 prov = andp.get_prover(secret_dict) prov.subs[1].secret_values[s] = signature.s + 1 ver = andp1.get_verifier() ver.process_precommitment(prov.precommit()) commitment = prov.commit() challenge = ver.send_challenge(commitment) responses = prov.compute_response(challenge) with pytest.raises(ValidationError): ver.verify(responses)
def blinded_pederson_commitment_example(): values = [Bn(123), Bn(456), "hello", b"world"] param = BlindedPedersenParam(hs_size=len(values)) # reveal nothing bcommit, bpriv = param.blind_commit(values) bproof = bcommit.prove_values(bpriv) assert bcommit.verify_proof(param, bproof) # revealing some values bproof2 = bcommit.prove_values(bpriv, reveal_mask=[True, False, True, True]) assert bcommit.verify_proof(param, bproof) print(bproof2.revealed_values)
def test_signature_setup(): mG = BilinearGroupPair() keypair = BBSPlusKeypair.generate(mG, 9) messages = [Bn(30), Bn(31), Bn(32), Bn(12)] pk, sk = keypair.pk, keypair.sk generators, h0 = keypair.generators, keypair.h0 creator = BBSPlusSignatureCreator(pk) com = creator.commit(messages, zkp=True) presignature = sk.sign(com.com_message) signature = creator.obtain_signature(presignature) assert com.verify_blinding(pk) and signature.verify_signature(pk, messages)
def test_signature_and_dlrne_does_not_fail_on_wrong_secret_when_non_binding(): """ Manually modify a secret in the DLNE member, i.e we wrongfully claim to use the same "s" i the signature and in the DLNE. Should not be detected since bindings in the DLNE are False. """ mG = BilinearGroupPair() keypair = BBSPlusKeypair.generate(mG, 9) messages = [Bn(30), Bn(31), Bn(32)] pk, sk = keypair.pk, keypair.sk generators, h0 = keypair.generators, keypair.h0 creator = BBSPlusSignatureCreator(pk) lhs = creator.commit(messages) presignature = sk.sign(lhs.com_message) signature = creator.obtain_signature(presignature) e, s, m1, m2, m3 = (Secret() for _ in range(5)) secret_dict = { e: signature.e, s: signature.s, m1: messages[0], m2: messages[1], m3: messages[2], } sigproof = BBSPlusSignatureStmt([e, s, m1, m2, m3], pk, signature) g1 = mG.G1.generator() pg1 = signature.s * g1 + g1 pg2, g2 = mG.G1.order().random() * g1, mG.G1.order().random() * g1 splus = Secret(signature.s + 1) dneq = DLNotEqual((pg1, g1), (pg2, g2), splus, bind=False) secrets = [Secret() for _ in range(5)] sigproof1 = BBSPlusSignatureStmt(secrets, pk, signature) # Note difference: dneq above uses an independent secret for dneq, # here it is bound to the secret s (secrets[1]) from the signature proof dneq1 = DLNotEqual((pg1, g1), (pg2, g2), secrets[1]) andp = sigproof & dneq andp1 = sigproof1 & dneq1 prov = andp.get_prover(secret_dict) ver = andp1.get_verifier() ver.process_precommitment(prov.precommit()) commitment = prov.commit() challenge = ver.send_challenge(commitment) responses = prov.compute_response(challenge) assert ver.verify(responses)
def simulate_precommit(self): randomizers = [self.order.random() for _ in range(self.num_bits)] precommitment = {} precommitment["Cs"] = [r * self.h for r in randomizers] precommitment["Cs"][0] += self.com # Compute revealed randomizer rand = Bn(0) power = Bn(1) for r in randomizers: rand = rand.mod_add(r * power, self.order) power *= 2 precommitment["rand"] = rand return precommitment
def CommitG2(self, y): try: ttype = y["type"] if ttype == "bas": return self.G2bas() elif ttype == "unt": return self.G2unt() value = y["value"] if type(value) == G2Elem: if ttype == "pub": return self.G2pub(value) elif ttype == "enc": return self.G2enc(value) elif ttype == "com": return self.G2com(value) if type(value) == Bn or type(value) == int: if type(value) == int: value = Bn(value) if ttype == "sca": return self.G2sca(value) except Exception as e: print("Error G2 commit", e)
def CommitG1(self, x): try: ttype = x["type"] if ttype == "unt": return self.G1unt() elif ttype == "bas": return self.G1bas() value = x["value"] if type(value) == G1Elem: if ttype == "pub": return self.G1pub(value) elif ttype == "enc": return self.G1enc(value) elif ttype == "com": return self.G1com(value) if type(value) == Bn or type(value) == int: if type(value) == int: value = Bn(value) if ttype == "sca": return self.G1sca(value) except Exception as e: print("Error G1 commit", e)
def verifyEq(self, eq, x, b, a, y, c, t): #print("verifyEq") if eq in ["PPE", "PN1", "PC1", "PN2", "PC2"]: #print("eq in [\"PPE\", \"PN1\", \"PC1\", \"PN2\", \"PC2\"]") T = GTElem.zero(self.G) for i in range(min(len(x), len(b))): T = T * self.e(x[i]["value"], b[i]["value"]) for j in range(min(len(a), len(y))): T = T * self.e(a[j]["value"], y[j]["value"]) for i in range(len(c)): for j in range(len(c[i])): T = T * self.e(c[i][j] * x[i]["value"], y[j]["value"]) return T.eq(t) else: #print("eq NOT in [\"PPE\", \"PN1\", \"PC1\", \"PN2\", \"PC2\"]") T = Bn(0) if eq in ["ME1", "MN1", "MC1", "ML1"]: T = G1Elem.inf(self.G) elif eq in ["ME2", "MN2", "MC2", "ML2"]: T = G2Elem.inf(self.G) elif eq not in ["QE", "QC1", "QC2"]: print("eq error", eq) return 0 for i in range(min(len(x), len(b))): T += x[i]["value"] * b[i]["value"] for j in range(min(len(a), len(y))): T += a[j]["value"] * y[j]["value"] for i in range(len(c)): for j in range(len(c[i])): if c[i][j] != 0: T += c[i][j] * x[i]["value"] * y[j]["value"] return T.eq(t)
def get_prover(self, secrets_dict=None): """ Get a prover for the current proof statement. Args: secrets_dict: Optional mapping from secrets or secret names to their values. Returns: :py:class:`DLRepProver` or None: Prover object if all secret values are known. """ if secrets_dict is None: secrets_dict = {} # First we update the dictionary we have with the additional secrets, and process it self.secret_values.update(secrets_dict) secrets_dict = self.secret_values # If missing secrets or simulation parameter set, return now if (self.set_simulated() or secrets_dict == {} or any(sec not in secrets_dict.keys() for sec in set(self.secret_vars))): # TODO: Make this raise: # raise IncompleteValuesError(self.secret_vars) return None # We check everything is indeed a big number, else we cast it for name, sec in secrets_dict.items(): if not isinstance(sec, Bn): secrets_dict[name] = Bn(sec) return DLRepProver(self, secrets_dict)
def cred_show_check(params, publics, secrets, creds, sig, cred_show_proof=cred_show_proof, xenv={}): # Parse the inputs G, g, h, _ = params sk, _ = secrets Cx0, iparams = publics (u, Cmis, Cup) = creds n = len(iparams) ## Recompute a V V = sk[0] * u + (- Cup) for xi, Cmi in zip(sk[1:], Cmis): V = V + xi * Cmi # Define the proof, and instanciate it with variables zk = cred_show_proof(params, n) env = ZKEnv(zk) env.u = u env.g, env.h = g, h env.V = V env.minus1 = -Bn(1) env.Xi = iparams env.Cmi = Cmis if xenv: xenv(env) # Return the result of the verification return zk.verify_proof(env.get(), sig)