def FQP_point_to_FQ2_point(pt: Tuple[FQP, FQP, FQP]) -> Tuple[FQ2, FQ2, FQ2]: """ Transform FQP to FQ2 for type hinting. """ return ( FQ2(pt[0].coeffs), FQ2(pt[1].coeffs), FQ2(pt[2].coeffs), )
def hash_to_G2(message: bytes, domain: int) -> Tuple[FQ2, FQ2, FQ2]: x_coordinate = _get_x_coordinate(message, domain) # Test candidate y coordinates until a one is found while 1: y_coordinate_squared = x_coordinate**3 + FQ2( [4, 4]) # The curve is y^2 = x^3 + 4(i + 1) y_coordinate = modular_squareroot(y_coordinate_squared) if y_coordinate is not None: # Check if quadratic residue found break x_coordinate += FQ2([1, 0]) # Add 1 and try again return multiply((x_coordinate, y_coordinate, FQ2([1, 0])), G2_cofactor)
def test_get_x_coordinate(message, domain): x_coordinate = _get_x_coordinate(message, domain) domain_in_bytes = domain.to_bytes(8, 'big') assert x_coordinate == FQ2([ big_endian_to_int(hash_eth2(message + domain_in_bytes + b'\x01')), big_endian_to_int(hash_eth2(message + domain_in_bytes + b'\x02')), ])
def hash_to_G2(message: bytes, domain: int) -> Tuple[FQ2, FQ2, FQ2]: domain_in_bytes = domain.to_bytes(8, 'big') # Initial candidate x coordinate x_re = big_endian_to_int(hash_eth2(domain_in_bytes + b'\x01' + message)) x_im = big_endian_to_int(hash_eth2(domain_in_bytes + b'\x02' + message)) x_coordinate = FQ2([x_re, x_im]) # x_re + x_im * i # Test candidate y coordinates until a one is found while 1: y_coordinate_squared = x_coordinate**3 + FQ2( [4, 4]) # The curve is y^2 = x^3 + 4(i + 1) y_coordinate = modular_squareroot(y_coordinate_squared) if y_coordinate is not None: # Check if quadratic residue found break x_coordinate += FQ2([1, 0]) # Add 1 and try again return multiply((x_coordinate, y_coordinate, FQ2([1, 0])), G2_cofactor)
def _get_x_coordinate(message: bytes, domain: int) -> FQ2: domain_in_bytes = domain.to_bytes(8, 'big') # Initial candidate x coordinate x_re = big_endian_to_int(hash_eth2(message + domain_in_bytes + b'\x01')) x_im = big_endian_to_int(hash_eth2(message + domain_in_bytes + b'\x02')) x_coordinate = FQ2([x_re, x_im]) # x_re + x_im * i return x_coordinate
def decompress_G2(p: Tuple[int, int]) -> Tuple[FQP, FQP, FQP]: x1 = p[0] % 2**383 y1_mod_2 = p[0] // 2**383 x2 = p[1] x = FQ2([x1, x2]) if x == FQ2([0, 0]): return FQ2([1, 0]), FQ2([1, 0]), FQ2([0, 0]) y = modular_squareroot(x**3 + b2) if y.coeffs[0] % 2 != y1_mod_2: y = FQ2((y * -1).coeffs) if not is_on_curve((x, y, FQ2([1, 0])), b2): raise ValueError( "The given point is not on the twisted curve over FQ**2") return x, y, FQ2([1, 0])
def modular_squareroot(value: int) -> FQP: """ ``modular_squareroot(x)`` returns the value ``y`` such that ``y**2 % q == x``, and None if this is not possible. In cases where there are two solutions, the value with higher imaginary component is favored; if both solutions have equal imaginary component the value with higher real component is favored. """ candidate_squareroot = value**((FQ2_order + 8) // 16) check = candidate_squareroot**2 / value if check in eighth_roots_of_unity[::2]: x1 = candidate_squareroot / eighth_roots_of_unity[ eighth_roots_of_unity.index(check) // 2] x2 = FQ2([-x1.coeffs[0], -x1.coeffs[1]]) # x2 = -x1 return x1 if (x1.coeffs[1], x1.coeffs[0]) > (x2.coeffs[1], x2.coeffs[0]) else x2 return None
def modular_squareroot(value: FQ2) -> FQP: """ ``modular_squareroot(x)`` returns the value ``y`` such that ``y**2 % q == x``, and None if this is not possible. In cases where there are two solutions, the value with higher imaginary component is favored; if both solutions have equal imaginary component the value with higher real component is favored. """ candidate_squareroot = value**((FQ2_order + 8) // 16) check = candidate_squareroot**2 / value if check in eighth_roots_of_unity[::2]: x1 = candidate_squareroot / eighth_roots_of_unity[ eighth_roots_of_unity.index(check) // 2] # TODO: fix pending upstream fix https://github.com/ethereum/py_ecc/issues/47 # % q shouldn't be needed x2 = FQ2([-x1.coeffs[0] % q, -x1.coeffs[1] % q]) # x2 = -x1 return x1 if (x1.coeffs[1], x1.coeffs[0]) > (x2.coeffs[1], x2.coeffs[0]) else x2 return None
def _convert_fp2_to_int(fp2_repr): a, b = fp2_repr return FQ2((_convert_fp_to_int(a), _convert_fp_to_int(b)))
ValidationError, ) from py_ecc.optimized_bls12_381 import ( # NOQA G1, G2, Z1, Z2, neg, add, multiply, FQ, FQ2, FQ12, FQP, pairing, normalize, field_modulus as q, b, b2, is_on_curve, curve_order, final_exponentiate) from eth2.beacon._utils.hash import hash_eth2 from eth2.beacon.typing import ( BLSPubkey, BLSSignature, ) G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109 # noqa: E501 FQ2_order = q**2 - 1 eighth_roots_of_unity = [FQ2([1, 1])**((FQ2_order * k) // 8) for k in range(8)] # # Helpers # def FQP_point_to_FQ2_point(pt: Tuple[FQP, FQP, FQP]) -> Tuple[FQ2, FQ2, FQ2]: """ Transform FQP to FQ2 for type hinting. """ return ( FQ2(pt[0].coeffs), FQ2(pt[1].coeffs), FQ2(pt[2].coeffs), )