def dleq(g1: PointG1, h1: PointG1, g2: PointG1, h2: PointG1, alpha: int) -> Tuple[int, int]: """ dleq... discrete logarithm equality proofs that the caller knows alpha such that h1 = g1**alpha and h2 = g2**alpha without revealing alpha """ w = random_scalar() a1 = multiply(g1, w) a2 = multiply(g2, w) c = soliditySha3( # pylint: disable=E1120 abi_types=["uint256"] * 12, # 12, values=[ a1[0], a1[1], a2[0], a2[1], g1[0], g1[1], h1[0], h1[1], g2[0], g2[1], h2[0], h2[1], ], ) c = int.from_bytes(c, "big") r = (w - alpha * c) % CURVE_ORDER return c, r
def shared_key_proof(sk, other_pk: PointG1) -> Tuple[int, int]: """ non-iteractive zero-knowledge proof showing that shared_key(sk, other_pk) is indeed the correct encryption/decryption key """ pk = multiply(G1, sk) shared_key = multiply(other_pk, sk) return dleq(G1, pk, other_pk, shared_key, alpha=sk)
def recover_point(id_and_point_list: List[Tuple[int, PointG1]]) -> PointG1: ids = [j for j, _ in id_and_point_list] i, sig = id_and_point_list[0] result = multiply(sig, lagrange_coefficient(i, ids)) for i, sig in id_and_point_list[1:]: t = multiply(sig, lagrange_coefficient(i, ids)) result = add(result, t) return result
def test_verify_decryption_key(): sk1, sk2 = random_scalar(), random_scalar() pk1, pk2 = multiply(G1, sk1), multiply(G1, sk2) shared_key = vss.shared_key(sk1, pk2) chal, resp = vss.shared_key_proof(sk1, pk2) assert vss.dleq_verify(G1, pk1, pk2, shared_key, chal, resp) assert contract.verify_decryption_key(shared_key, [chal, resp], pk1, pk2)
def test_dleq(): sk1 = random_scalar(seed=1) sk2 = random_scalar(seed=2) pk1 = multiply(G1, sk1) pk2 = multiply(G1, sk2) shared_key = multiply(pk2, sk1) assert shared_key == multiply(pk1, sk2) challenge, response = vss.dleq(G1, pk1, pk2, shared_key, alpha=sk1) assert vss.dleq_verify(G1, pk1, pk2, shared_key, challenge, response) response += 1 assert not vss.dleq_verify(G1, pk1, pk2, shared_key, challenge, response)
def derive_group_keys(self) -> None: """ computes the group public keys and the personal group key for the node itself """ if not hasattr(self, "group"): # load_dispute_infos() was not called yet, we use call nodes which provided shares for our group # i.e. use the empty set for loading disputes self.load_dispute_infos(set()) self.group_sk = sum([node.share for node in self.group ]) # maybe needs to be product instead self.group_pk = multiply(G1, self.group_sk) self.group_bls_pk = multiply(neg(G2), self.group_sk) self.master_pk = crypto.sum_points([node.pk for node in self.group]) self.master_bls_pk = crypto.sum_points( [node.bls_pk for node in self.group])
def F(x): """ public polynomial """ result = public_coefficients[0] for j, coef in enumerate(public_coefficients[1:]): result = add(result, multiply(coef, pow(x, j + 1, CURVE_ORDER))) return result
def test_sk_knowledge_with_account(): sk = random_scalar() pk = multiply(G1, sk) challenge, response = vss.prove_sk_knowledge( sk, pk, account="0xe3D320ea4AF151Fc309568884C217f634E9c38f5") assert vss.verify_sk_knowledge( pk, challenge, response, account="0xe3D320ea4AF151Fc309568884C217f634E9c38f5")
def verify_sk_knowledge(pk: PointG1, challenge: int, response: int, account: str = None) -> bool: t = add(multiply(G1, response), multiply(pk, challenge)) types = ["uint256"] * 6 values = list(G1 + pk + t) if account is not None: types.append("address") values.append(account) c = soliditySha3(abi_types=types, values=values) c = int.from_bytes(c, "big") print("values", values) print("t", t) print("c", c) return c == challenge
def verify(share_id: int, share: int, public_coefficients: List[PointG1]) -> bool: """ check share validity and return True if the share is valid, False otherwise """ def F(x): """ public polynomial """ result = public_coefficients[0] for j, coef in enumerate(public_coefficients[1:]): result = add(result, multiply(coef, pow(x, j + 1, CURVE_ORDER))) return result return F(share_id) == multiply(G1, share)
def keygen(seed=None): """ generated a new bls keypair """ if seed is None: sk = random_scalar() else: sk = hash_to_scalar(seed) # compute the corresponding public key # for bls_pk, we use neg(G2) which flips the sign of the y coordinates to be compatible with the ethereum # implementation of the pairing check bls_pk = multiply(neg(G2), sk) return sk, bls_pk
def dleq_verify(g1: PointG1, h1: PointG1, g2: PointG1, h2: PointG1, challenge: int, response: int): a1 = add(multiply(g1, response), multiply(h1, challenge)) a2 = add(multiply(g2, response), multiply(h2, challenge)) c = soliditySha3( # pylint: disable=E1120 abi_types=["uint256"] * 12, # 12, values=[ a1[0], a1[1], a2[0], a2[1], g1[0], g1[1], h1[0], h1[1], g2[0], g2[1], h2[0], h2[1], ], ) c = int.from_bytes(c, "big") return c == challenge
def test_verify_sk_knowledge(): sk = random_scalar() pk = multiply(G1, sk) addr = w3.eth.accounts[0] proof = vss.prove_sk_knowledge(sk, pk, addr) assert vss.verify_sk_knowledge(pk, proof[0], proof[1], addr) print("sk", sk) print("pk", pk) print("account", addr) print("proof", proof) assert contract.verify_sk_knowledge(pk, proof)
def share(secret: int, n: int, t: int) -> Tuple[List[int], List[PointG1]]: """ computes n shares of a given secret such that at least t shares are required for recovery of the secret additionally returns the public_coefficients used to verify the validity of the shares """ coefficients = [secret] + [ hash_to_scalar(f"vss:coefficient:{secret}:{j}") for j in range(1, t) ] def f(x): """ secret polynomial """ return sum(coef * pow(x, j, CURVE_ORDER) for j, coef in enumerate(coefficients)) % CURVE_ORDER shares = [f(id) for id in range(1, n + 1)] public_coefficients = [multiply(G1, coef) for coef in coefficients] return shares, public_coefficients
def prove_sk_knowledge(sk: int, pk: PointG1, account: str = None) -> Tuple[int, int]: """ proofs that the caller knows the discreate logarithm of pk to the generator g1 (and that links the proof to an Ethereum account if provided) """ w = random_scalar() t = multiply(G1, w) types = ["uint256"] * 6 values = list(G1 + pk + t) if account is not None: types.append("address") values.append(account) c = soliditySha3(abi_types=types, values=values) c = int.from_bytes(c, "big") r = (w - sk * c) % CURVE_ORDER return c, r
def sign_message(msg, SECRET): E = Curve(0xffffffffffffffffffffffffffffffff7fffffff, 0xffffffffffffffffffffffffffffffff7ffffffc, 0x1c97befc54bd7a8b65acf89f81d4d4adc565fa45) G = Point(E, 0x4a96b5688ef573284664698968c38bb913cbfc82, 0x23a628553168947d59dcc912042351377ac5fb32) q = 0x100000000000000000001f4c8f927aed3ca752257 h = int(sha1(msg).hexdigest(), 16) while True: k = rand(1, q) Q = normalize(multiply(G, k)) r = Q.x % q if r != 0: break s = invmod(k, q) * (h + r * SECRET) % q return (r, s, k & 0x1f)
def keygen(self, seed=None): """ generates a new secret/public key pair for the node """ self.sk, self.bls_pk = bls.keygen(seed) self.pk = multiply(G1, self.sk)
def sign(sk: int, message: Any) -> PointG1: return multiply(hash_to_G1(message), sk)
def test_add(): a = multiply(G1, 5) b = multiply(G1, 10) s = add(a, b) assert contract.bn128_add([a[0], a[1], b[0], b[1]]) == list(s)
def test_sk_knowledge_invalid_response(): sk = random_scalar() pk = multiply(G1, sk) challenge, response = vss.prove_sk_knowledge(sk, pk) response += 1 assert not vss.verify_sk_knowledge(pk, challenge, response)
def test_check_pairing(): P1 = multiply(G1, 5) Q1 = G2 Q2 = multiply(neg(G2), 5) P2 = G1 assert check_pairing(P1, Q1, P2, Q2)
def test_multiply(): assert G1 == (1, 2) assert contract.bn128_multiply([1, 2, 5]) == list(multiply(G1, 5))
def shared_key(sk, other_pk: PointG1) -> PointG1: """ Computes a shared key between given a node's secret key and and some other node public key. Used for individual encryption/decryption of the shares. """ return multiply(other_pk, sk)