def verify_share(j: int, s_ij: int, Cik: List[PointG1]) -> bool: """ check share validity and return True if the share is valid, False otherwise """ r = Cik[0] for k, c in enumerate(Cik[1:]): r = add(r, multiply(c, pow(j, k + 1, CURVE_ORDER))) return normalize(multiply(G1, s_ij)) == normalize(r)
def dleq_verify(x1, y1, x2, y2, challenge, response): """ Verify the proof computed with the dleq function in G1 Args: x1: a point in G1 y1: a point in G1 x2: a point in G1 y2: a point in G1 challenge (int): the first coefficient of the proof response (int): the second coefficient of the proof Returns: True if the proof is correct, False else """ a1 = add(multiply(x1, response), multiply(y1, challenge)) a2 = add(multiply(x2, response), multiply(y2, challenge)) c = keccak_256( # pylint: disable=E1120 abi_types=["uint256"] * 12, # 12, values=[ int(v) for v in normalize(a1) + normalize(a2) + normalize(x1) + normalize(y1) + normalize(x2) + normalize(y2) ], ) c = int.from_bytes(c, "big") return c == challenge
def send_share(self, file_info): """ Send a share for a specific message along with its proof of correctness. Warning: Call the function share on CipherETHDKG, and so send a transaction. Args: row (dict): a row of the database containing the message message_id (int): the id of the message """ db = get_db() gsk_sql = db.execute("SELECT * FROM gsk WHERE user_pk=? AND round=?", (self.private_key, file_info['round'])).fetchone() gsk = int(gsk_sql['gsk']) gpk = multiply(cru.H1, gsk) ui = int(gsk_sql['ui']) db.close() c1 = (int(file_info['c1x']), int(file_info['c1y'])) c1 = cru.point_from_eth(c1) share_for_decryption = multiply(c1, gsk) proof = cru.dleq(c1, share_for_decryption, cru.H1, gpk, gsk) transaction = self.contract.functions.share_for_dec( ui, file_info['round'], cru.point_to_eth(share_for_decryption), proof ).buildTransaction( { 'chainId': 1, 'gas': 200000, 'nonce': self.w3.eth.getTransactionCount(self.account) } ) signed_tx = self.w3.eth.account.signTransaction(transaction, self.private_key) txn_hash = self.w3.eth.sendRawTransaction(signed_tx.rawTransaction) return txn_hash
def dleq_verify(x1: PointG1, y1: PointG1, x2: PointG1, y2: PointG1, challenge: int, response: int) -> bool: a1 = add(multiply(x1, response), multiply(y1, challenge)) a2 = add(multiply(x2, response), multiply(y2, challenge)) c = keccak_256( # pylint: disable=E1120 abi_types=["uint256"] * 12, # 12, values=[ int(v) for v in normalize(a1) + normalize(a2) + normalize(x1) + normalize(y1) + normalize(x2) + normalize(y2) ], ) c = int.from_bytes(c, "big") return c == challenge
def ec_pair(data: List[int]) -> List[int]: if len(data) % 192: return [] zero = (bn128.FQ2.one(), bn128.FQ2.one(), bn128.FQ2.zero()) exponent = bn128.FQ12.one() bytes_data = bytearray(data) for i in range(0, len(bytes_data), 192): x1 = extract32(bytes_data, i) y1 = extract32(bytes_data, i + 32) x2_i = extract32(bytes_data, i + 64) x2_r = extract32(bytes_data, i + 96) y2_i = extract32(bytes_data, i + 128) y2_r = extract32(bytes_data, i + 160) p1 = validate_point(x1, y1) if p1 is False: return [] for v in (x2_i, x2_r, y2_i, y2_r): if v >= bn128.field_modulus: return [] fq2_x = bn128.FQ2([x2_r, x2_i]) fq2_y = bn128.FQ2([y2_r, y2_i]) if (fq2_x, fq2_y) != (bn128.FQ2.zero(), bn128.FQ2.zero()): p2 = (fq2_x, fq2_y, bn128.FQ2.one()) if not bn128.is_on_curve(p2, bn128.b2): return [] else: p2 = zero if bn128.multiply(p2, bn128.curve_order)[-1] != bn128.FQ2.zero(): return [] exponent *= bn128.pairing(p2, p1, final_exponentiate=False) result = bn128.final_exponentiate(exponent) == bn128.FQ12.one() return [0] * 31 + [1 if result else 0]
def keygen() -> Tuple[int, PointG1]: """ Generates a random keypair on the BN128 curve. The public key is an element of the group G1. This key is used for deriving the encryption keys used to secure the shares. This is NOT a BLS key pair used for signing messages. """ sk = random_scalar() pk = multiply(G1, sk) return sk, pk
def test_jacobian_equations(): k = rand_int() a = multiply_jacobian(G2, k) _a = multiply(G2, k) assert eq(normalize_jacobian(a), _normalize(_a)) b = add_mixed_jacobian(a, G2) _b = add(_a, G2) assert eq(normalize_jacobian(b), _normalize(_b))
def dleq(x1: PointG1, y1: PointG1, x2: PointG1, y2: PointG1, alpha: int) -> Tuple[int, int]: """ DLEQ... discrete logarithm equality Proofs that the caller knows alpha such that y1 = x1**alpha and y2 = x2**alpha without revealing alpha. """ w = random_scalar() a1 = multiply(x1, w) a2 = multiply(x2, w) c = keccak_256( abi_types=["uint256"] * 12, values=[ int(v) for v in normalize(a1) + normalize(a2) + normalize(x1) + normalize(y1) + normalize(x2) + normalize(y2) ], ) c = int.from_bytes(c, "big") r = (w - alpha * c) % CURVE_ORDER return c, r
def ec_mul(data: List[int]) -> List[int]: bytes_data = bytearray(data) x = extract32(bytes_data, 0) y = extract32(bytes_data, 32) m = extract32(bytes_data, 64) p = validate_point(x, y) if p is False: return [] o = bn128.normalize(bn128.multiply(p, m)) return [safe_ord(c) for c in (encode_int32(o[0].n) + encode_int32(o[1].n))]
def map_to_g2(raw_hash: FQ2) -> G2Point: one = FQ2.one() x = raw_hash while True: y = x * x * x + b2 y = sqrt(y) if y is not None: break x += one h = multiply((x, y, one), COFACTOR_G2) assert is_on_curve(h, b2) return h
def _ecmull(data): x_bytes = pad32r(data[:32]) y_bytes = pad32r(data[32:64]) m_bytes = pad32r(data[64:96]) x = big_endian_to_int(x_bytes) y = big_endian_to_int(y_bytes) m = big_endian_to_int(m_bytes) p = validate_point(x, y) result = bn128.normalize(bn128.multiply(p, m)) return result
def _ecmull(data: bytes) -> Tuple[bn128.FQ, bn128.FQ]: x_bytes = pad32r(data[:32]) y_bytes = pad32r(data[32:64]) m_bytes = pad32r(data[64:96]) x = big_endian_to_int(x_bytes) y = big_endian_to_int(y_bytes) m = big_endian_to_int(m_bytes) p = validate_point(x, y) result = bn128.normalize(bn128.multiply(p, m)) return result
def compute_group_keys(self, round, fj_ui): self.gski[round] = sum(fj_ui) self.gpki[round] = multiply(H1, self.gski[round]) db = get_db() my_ui = self.tp_key_list[round][1] value = [str(self.gski[round]), str(my_ui), round, self.private_key] db.execute("INSERT INTO gsk (gsk, ui, round,user_pk) VALUES (?,?,?,?)", value) db.commit() db.close() print("group secret key:", self.gski) print("group public key:", self.gpki)
def hash_to_G2(m): k2 = m while 1: k1 = blake(k2) k2 = blake(k1) x1 = int.from_bytes(k1, 'big') % field_modulus x2 = int.from_bytes(k2, 'big') % field_modulus x = FQ2([x1, x2]) xcb = x**3 + b2 if xcb ** ((field_modulus ** 2 - 1) // 2) == FQ2([1,0]): break y = sqrt_fq2(xcb) return multiply((x, y, FQ2([1,0])), 2*field_modulus-curve_order)
def share_secret(secret: int, indices: List[int], threshold: int) -> Tuple[Dict[int, int], List[PointG1]]: """ Computes shares of a given secret such that at least threshold + 1 shares are required to recover the secret. Additionally returns the commitents to the coefficient of the polynom used to verify the validity of the shares. """ coefficients = [secret] + [random_scalar() for j in range(threshold)] def f(x: int) -> int: """ evaluation function for secret polynomial """ return (sum(coef * pow(x, j, CURVE_ORDER) for j, coef in enumerate(coefficients)) % CURVE_ORDER) shares = {x: f(x) for x in indices} commitments = [multiply(G1, coef) for coef in coefficients] return shares, commitments
def proc_ecmul(ext, msg): if not ext.post_metropolis_hardfork(): return 1, msg.gas, [] import py_ecc.optimized_bn128 as bn128 FQ = bn128.FQ print('ecmul proc', msg.gas) if msg.gas < opcodes.GECMUL: return 0, 0, [] x = msg.data.extract32(0) y = msg.data.extract32(32) m = msg.data.extract32(64) p = validate_point(x, y) if p is False: return 0, 0, [] o = bn128.normalize(bn128.multiply(p, m)) return (1, msg.gas - opcodes.GECMUL, [safe_ord(c) for c in (encode_int32(o[0].n) + encode_int32(o[1].n))])
def proc_ecmul(ext, msg): import py_ecc.optimized_bn128 as bn128 FQ = bn128.FQ if msg.gas < opcodes.GECMUL: return 0, 0, [] x = msg.data.extract32(0) y = msg.data.extract32(32) m = msg.data.extract32(64) p = validate_point(x, y) if p is False: return 0, 0, [] o = bn128.normalize(bn128.multiply(p, m)) return ( 1, msg.gas - opcodes.GECMUL, [safe_ord(c) for c in (encode_int32(o[0].n) + encode_int32(o[1].n))], )
def hash_to_G2(m: bytes) -> Tuple[FQ2, FQ2, FQ2]: """ WARNING: this function has not been standardized yet. """ if m in CACHE: return CACHE[m] k2 = m while 1: k1 = blake(k2) k2 = blake(k1) x1 = int.from_bytes(k1, 'big') % field_modulus x2 = int.from_bytes(k2, 'big') % field_modulus x = FQ2([x1, x2]) xcb = x**3 + b2 if xcb**((field_modulus**2 - 1) // 2) == FQ2([1, 0]): break y = sqrt_fq2(xcb) o = multiply((x, y, FQ2([1, 0])), 2 * field_modulus - curve_order) CACHE[m] = o return o
def compute_mpk(self): if len(self.gpk_0) > self.threshold and not self.has_mpk: self.has_mpk = True self.mpk = Z1 for gpki, ui in self.gpk_0[0:self.threshold + 1]: coeff = 1 for gpkj, uj in self.gpk_0[0:self.threshold + 1]: if uj != ui: coeff *= uj * sympy.mod_inverse( (uj - ui) % CURVE_ORDER, CURVE_ORDER) coeff %= CURVE_ORDER self.mpk = add(self.mpk, multiply(gpki, coeff)) self.mpk = normalize(self.mpk) db = get_db() value = (str(self.mpk[0]), str(self.mpk[1])) db.execute("INSERT INTO mpk (x,y) VALUES (?,?)", value) db.commit() db.close()
def _process_point(data_buffer: bytes, exponent: int) -> bn128.FQP: x1, y1, x2_i, x2_r, y2_i, y2_r = _extract_point(data_buffer) p1 = validate_point(x1, y1) for v in (x2_i, x2_r, y2_i, y2_r): if v >= bn128.field_modulus: raise ValidationError("value greater than field modulus") fq2_x = bn128.FQ2([x2_r, x2_i]) fq2_y = bn128.FQ2([y2_r, y2_i]) p2 = ZERO if (fq2_x, fq2_y) != (bn128.FQ2.zero(), bn128.FQ2.zero()): p2 = (fq2_x, fq2_y, bn128.FQ2.one()) if not bn128.is_on_curve(p2, bn128.b2): raise ValidationError("point is not on curve") if bn128.multiply(p2, bn128.curve_order)[-1] != bn128.FQ2.zero(): raise ValidationError("TODO: what case is this?????") return exponent * bn128.pairing(FQP_point_to_FQ2_point(p2), p1, final_exponentiate=False)
def _share_secret_int_indices(s_i: int, n: int, t: int) -> Tuple[Dict[int, int], List[PointG1]]: """ Computes n shares of a given secret such that at least t + 1 shares are required for recovery of the secret. Additionally returns the commitents to the coefficient of the polynom used to verify the validity of the shares. Assumes nodes use the indices [1, 2, ..., n]. See share_secret function of a generalized variant with arbitary indices. """ coefficients = [s_i] + [random_scalar() for j in range(t) ] # coefficients c_i0, c_i1, ..., c_it def f(x: int) -> int: """ evaluation function for secret polynomial """ return (sum(coef * pow(x, j, CURVE_ORDER) for j, coef in enumerate(coefficients)) % CURVE_ORDER) shares = {x: f(x) for x in range(1, n + 1)} commitments = [multiply(G1, coef) for coef in coefficients] return shares, commitments
def proc_ecpairing(ext, msg): if not ext.post_metropolis_hardfork(): return 1, msg.gas, [] import py_ecc.optimized_bn128 as bn128 FQ = bn128.FQ print('pairing proc', msg.gas) # Data must be an exact multiple of 192 byte if msg.data.size % 192: return 0, 0, [] gascost = opcodes.GPAIRINGBASE + msg.data.size // 192 * opcodes.GPAIRINGPERPOINT if msg.gas < gascost: return 0, 0, [] zero = (bn128.FQ2.one(), bn128.FQ2.one(), bn128.FQ2.zero()) exponent = bn128.FQ12.one() for i in range(0, msg.data.size, 192): x1 = msg.data.extract32(i) y1 = msg.data.extract32(i + 32) x2_i = msg.data.extract32(i + 64) x2_r = msg.data.extract32(i + 96) y2_i = msg.data.extract32(i + 128) y2_r = msg.data.extract32(i + 160) p1 = validate_point(x1, y1) if p1 is False: return 0, 0, [] for v in (x2_i, x2_r, y2_i, y2_r): if v >= bn128.field_modulus: return 0, 0, [] fq2_x = bn128.FQ2([x2_r, x2_i]) fq2_y = bn128.FQ2([y2_r, y2_i]) if (fq2_x, fq2_y) != (bn128.FQ2.zero(), bn128.FQ2.zero()): p2 = (fq2_x, fq2_y, bn128.FQ2.one()) if not bn128.is_on_curve(p2, bn128.b2): return 0, 0, [] else: p2 = zero if bn128.multiply(p2, bn128.curve_order)[-1] != bn128.FQ2.zero(): return 0, 0, [] exponent *= bn128.pairing(p2, p1, final_exponentiate=False) result = bn128.final_exponentiate(exponent) == bn128.FQ12.one() return 1, msg.gas - gascost, [0] * 31 + [1 if result else 0]
def _process_point(data_buffer, exponent): x1, y1, x2_i, x2_r, y2_i, y2_r = _extract_point(data_buffer) p1 = validate_point(x1, y1) for v in (x2_i, x2_r, y2_i, y2_r): if v >= bn128.field_modulus: raise ValidationError("value greater than field modulus") fq2_x = bn128.FQ2([x2_r, x2_i]) fq2_y = bn128.FQ2([y2_r, y2_i]) if (fq2_x, fq2_y) != (bn128.FQ2.zero(), bn128.FQ2.zero()): p2 = (fq2_x, fq2_y, bn128.FQ2.one()) if not bn128.is_on_curve(p2, bn128.b2): raise ValidationError("point is not on curve") else: p2 = ZERO if bn128.multiply(p2, bn128.curve_order)[-1] != bn128.FQ2.zero(): raise ValidationError("TODO: what case is this?????") return exponent * bn128.pairing(p2, p1, final_exponentiate=False)
def multiply(self, n: IntOrFE) -> "WrappedCurvePoint": return self.__class__(multiply(self.py_ecc_object, int(n)))
def privtopub(k): return compress_G1(multiply(G1, k))
def sign(m, k): return compress_G2(multiply(hash_to_G2(m), k))
def sign(m: bytes, k: int) -> Tuple[int, int]: return compress_G2(multiply(hash_to_G2(m), k))
def rand_g2(): return normalize(multiply(G2, rand_int()))
def sign(msg: Message, priv: PrivateKey) -> Signature: x, y = normalize(multiply(hash_to_g2(msg), priv)) g2 = (x, y, FQ2.one()) return G2_to_signature(g2)
def priv_to_pub(priv: PrivateKey) -> Pubkey: x, y = normalize(multiply(G1, priv)) g1 = (x, y, FQ.one()) return G1_to_pubkey(g1)
def privtopub(k: int) -> int: return compress_G1(multiply(G1, k))
def shared_key(sk_i: int, pk_j: PointG1) -> PointG1: k_ij = multiply(pk_j, sk_i) return k_ij