eth2.0-specs/tests/core/pyspec/eth2spec/utils/bls.py

398 lines
11 KiB
Python
Raw Normal View History

from py_ecc.bls import G2ProofOfPossession as py_ecc_bls
2024-05-17 06:38:58 +00:00
from py_ecc.bls.g2_primitives import signature_to_G2 as _signature_to_G2
from py_ecc.utils import prime_field_inv as py_ecc_prime_field_inv
from py_ecc.optimized_bls12_381 import ( # noqa: F401
G1 as py_ecc_G1,
G2 as py_ecc_G2,
Z1 as py_ecc_Z1,
2024-01-05 12:31:09 +00:00
Z2 as py_ecc_Z2,
add as py_ecc_add,
multiply as py_ecc_mul,
neg as py_ecc_neg,
pairing as py_ecc_pairing,
final_exponentiate as py_ecc_final_exponentiate,
FQ12 as py_ecc_GT,
2024-04-27 04:11:29 +00:00
FQ,
FQ2,
)
from py_ecc.bls.g2_primitives import ( # noqa: F401
2023-03-07 08:52:09 +00:00
curve_order as BLS_MODULUS,
G1_to_pubkey as py_ecc_G1_to_bytes48,
pubkey_to_G1 as py_ecc_bytes48_to_G1,
G2_to_signature as py_ecc_G2_to_bytes96,
signature_to_G2 as py_ecc_bytes96_to_G2,
)
from py_arkworks_bls12381 import (
G1Point as arkworks_G1,
G2Point as arkworks_G2,
Scalar as arkworks_Scalar,
GT as arkworks_GT,
)
import milagro_bls_binding as milagro_bls # noqa: F401 for BLS switching option
2019-05-13 21:15:02 +00:00
import py_arkworks_bls12381 as arkworks_bls # noqa: F401 for BLS switching option
class py_ecc_Scalar(FQ):
field_modulus = BLS_MODULUS
def __init__(self, value):
"""
Force underlying value to be a native integer.
"""
super().__init__(int(value))
def pow(self, exp):
"""
Raises the self to the power of the given exponent.
"""
return self**int(exp)
def inverse(self):
"""
Computes the modular inverse of self.
"""
return py_ecc_Scalar(py_ecc_prime_field_inv(self.n, self.field_modulus))
class fastest_bls:
G1 = arkworks_G1
G2 = arkworks_G2
Scalar = arkworks_Scalar
GT = arkworks_GT
_AggregatePKs = milagro_bls._AggregatePKs
Sign = milagro_bls.Sign
Verify = milagro_bls.Verify
Aggregate = milagro_bls.Aggregate
AggregateVerify = milagro_bls.AggregateVerify
FastAggregateVerify = milagro_bls.FastAggregateVerify
SkToPk = milagro_bls.SkToPk
2019-05-13 21:15:02 +00:00
# Flag to make BLS active or not. Used for testing, do not ignore BLS in production unless you know what you are doing.
bls_active = True
2023-10-16 09:43:23 +00:00
# Default to fastest_bls
bls = fastest_bls
Scalar = fastest_bls.Scalar
STUB_SIGNATURE = b'\x11' * 96
STUB_PUBKEY = b'\x22' * 48
G2_POINT_AT_INFINITY = b'\xc0' + b'\x00' * 95
STUB_COORDINATES = _signature_to_G2(G2_POINT_AT_INFINITY)
2019-05-13 21:15:02 +00:00
def use_milagro():
"""
Shortcut to use Milagro as BLS library
"""
global bls
bls = milagro_bls
global Scalar
Scalar = py_ecc_Scalar
def use_arkworks():
"""
Shortcut to use Arkworks as BLS library
"""
global bls
bls = arkworks_bls
global Scalar
Scalar = arkworks_Scalar
def use_py_ecc():
"""
Shortcut to use Py-ecc as BLS library
"""
global bls
bls = py_ecc_bls
global Scalar
Scalar = py_ecc_Scalar
def use_fastest():
"""
Shortcut to use Milagro for signatures and Arkworks for other BLS operations
"""
global bls
bls = fastest_bls
global Scalar
Scalar = fastest_bls.Scalar
def only_with_bls(alt_return=None):
"""
Decorator factory to make a function only run when BLS is active. Otherwise return the default.
"""
def runner(fn):
def entry(*args, **kw):
if bls_active:
return fn(*args, **kw)
else:
return alt_return
return entry
return runner
@only_with_bls(alt_return=True)
2019-12-17 10:04:56 +00:00
def Verify(PK, message, signature):
try:
if bls == arkworks_bls: # no signature API in arkworks
result = py_ecc_bls.Verify(PK, message, signature)
else:
result = bls.Verify(PK, message, signature)
except Exception:
result = False
finally:
return result
2019-12-17 10:04:56 +00:00
2019-12-17 13:33:37 +00:00
@only_with_bls(alt_return=True)
def AggregateVerify(pubkeys, messages, signature):
try:
if bls == arkworks_bls: # no signature API in arkworks
result = py_ecc_bls.AggregateVerify(list(pubkeys), list(messages), signature)
else:
result = bls.AggregateVerify(list(pubkeys), list(messages), signature)
except Exception:
result = False
finally:
return result
2019-05-13 21:15:02 +00:00
@only_with_bls(alt_return=True)
def FastAggregateVerify(pubkeys, message, signature):
try:
if bls == arkworks_bls: # no signature API in arkworks
result = py_ecc_bls.FastAggregateVerify(list(pubkeys), message, signature)
else:
result = bls.FastAggregateVerify(list(pubkeys), message, signature)
except Exception:
result = False
finally:
return result
2019-05-13 21:15:02 +00:00
@only_with_bls(alt_return=STUB_SIGNATURE)
2019-12-17 10:04:56 +00:00
def Aggregate(signatures):
if bls == arkworks_bls: # no signature API in arkworks
return py_ecc_bls.Aggregate(signatures)
2020-01-07 19:52:20 +00:00
return bls.Aggregate(signatures)
2019-05-15 16:37:11 +00:00
@only_with_bls(alt_return=STUB_SIGNATURE)
2019-12-17 10:04:56 +00:00
def Sign(SK, message):
if bls == arkworks_bls: # no signature API in arkworks
return py_ecc_bls.Sign(SK, message)
elif bls == py_ecc_bls:
return bls.Sign(SK, message)
else:
return bls.Sign(SK.to_bytes(32, 'big'), message)
@only_with_bls(alt_return=STUB_COORDINATES)
2019-12-20 06:41:46 +00:00
def signature_to_G2(signature):
2020-01-07 19:52:20 +00:00
return _signature_to_G2(signature)
2020-04-23 07:08:36 +00:00
@only_with_bls(alt_return=STUB_PUBKEY)
def AggregatePKs(pubkeys):
if bls == py_ecc_bls:
assert all(bls.KeyValidate(pubkey) for pubkey in pubkeys)
elif bls == milagro_bls:
# milagro_bls._AggregatePKs checks KeyValidate internally
pass
if bls == arkworks_bls: # no signature API in arkworks
return py_ecc_bls._AggregatePKs(list(pubkeys))
return bls._AggregatePKs(list(pubkeys))
@only_with_bls(alt_return=STUB_SIGNATURE)
def SkToPk(SK):
if bls == py_ecc_bls or bls == arkworks_bls: # no signature API in arkworks
return py_ecc_bls.SkToPk(SK)
else:
return bls.SkToPk(SK.to_bytes(32, 'big'))
def pairing_check(values):
if bls == arkworks_bls or bls == fastest_bls:
p_q_1, p_q_2 = values
g1s = [p_q_1[0], p_q_2[0]]
g2s = [p_q_1[1], p_q_2[1]]
return arkworks_GT.multi_pairing(g1s, g2s) == arkworks_GT.one()
else:
p_q_1, p_q_2 = values
final_exponentiation = py_ecc_final_exponentiate(
py_ecc_pairing(p_q_1[1], p_q_1[0], final_exponentiate=False)
* py_ecc_pairing(p_q_2[1], p_q_2[0], final_exponentiate=False)
)
return final_exponentiation == py_ecc_GT.one()
def add(lhs, rhs):
"""
Performs point addition of `lhs` and `rhs`.
The points can either be in G1 or G2.
"""
if bls == arkworks_bls or bls == fastest_bls:
return lhs + rhs
return py_ecc_add(lhs, rhs)
def multiply(point, scalar):
"""
Performs Scalar multiplication between
`point` and `scalar`.
`point` can either be in G1 or G2
"""
if bls == arkworks_bls or bls == fastest_bls:
if not isinstance(scalar, arkworks_Scalar):
return point * arkworks_Scalar(int(scalar))
return point * scalar
return py_ecc_mul(point, int(scalar))
2024-04-23 12:38:04 +00:00
def multi_exp(points, scalars):
2024-04-23 11:44:50 +00:00
"""
Performs a multi-scalar multiplication between
`points` and `scalars`.
2024-04-23 13:53:06 +00:00
`points` can either be in G1 or G2.
2024-04-23 11:44:50 +00:00
"""
2024-04-23 13:58:32 +00:00
# Since this method accepts either G1 or G2, we need to know
# the type of the point to return. Hence, we need at least one point.
if not points or not scalars:
raise Exception("Cannot call multi_exp with zero points or zero scalars")
2024-04-23 13:58:32 +00:00
2024-04-23 11:44:50 +00:00
if bls == arkworks_bls or bls == fastest_bls:
# If using py_ecc Scalars, convert to arkworks Scalars.
if not isinstance(scalars[0], arkworks_Scalar):
scalars = [arkworks_Scalar(int(s)) for s in scalars]
# Check if we need to perform a G1 or G2 multiexp
if isinstance(points[0], arkworks_G1):
return arkworks_G1.multiexp_unchecked(points, scalars)
elif isinstance(points[0], arkworks_G2):
return arkworks_G2.multiexp_unchecked(points, scalars)
else:
raise Exception("Invalid point type")
2024-04-23 12:38:04 +00:00
result = None
2024-04-27 04:11:29 +00:00
if isinstance(points[0][0], FQ):
2024-04-23 13:53:06 +00:00
result = Z1()
2024-04-27 04:11:29 +00:00
elif isinstance(points[0][0], FQ2):
2024-04-23 13:53:06 +00:00
result = Z2()
else:
raise Exception("Invalid point type")
for point, scalar in zip(points, scalars):
2024-04-23 12:38:04 +00:00
result = add(result, multiply(point, scalar))
return result
def neg(point):
"""
Returns the point negation of `point`
`point` can either be in G1 or G2
"""
if bls == arkworks_bls or bls == fastest_bls:
return -point
return py_ecc_neg(point)
def Z1():
"""
Returns the identity point in G1
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G1.identity()
return py_ecc_Z1
2024-01-05 12:31:09 +00:00
def Z2():
"""
Returns the identity point in G2
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G2.identity()
return py_ecc_Z2
def G1():
"""
Returns the chosen generator point in G1
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G1()
return py_ecc_G1
def G2():
"""
Returns the chosen generator point in G2
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G2()
return py_ecc_G2
def G1_to_bytes48(point):
"""
Serializes a point in G1.
Returns a bytearray of size 48 as
we use the compressed format
"""
if bls == arkworks_bls or bls == fastest_bls:
return bytes(point.to_compressed_bytes())
return py_ecc_G1_to_bytes48(point)
def G2_to_bytes96(point):
"""
Serializes a point in G2.
Returns a bytearray of size 96 as
we use the compressed format
"""
if bls == arkworks_bls or bls == fastest_bls:
return bytes(point.to_compressed_bytes())
return py_ecc_G2_to_bytes96(point)
def bytes48_to_G1(bytes48):
"""
Deserializes a purported compressed serialized
point in G1.
- No subgroup checks are performed
- If the bytearray is not a valid serialization
of a point in G1, then this method will raise
an exception
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G1.from_compressed_bytes_unchecked(bytes48)
return py_ecc_bytes48_to_G1(bytes48)
def bytes96_to_G2(bytes96):
"""
Deserializes a purported compressed serialized
point in G2.
- No subgroup checks are performed
- If the bytearray is not a valid serialization
of a point in G2, then this method will raise
an exception
"""
if bls == arkworks_bls or bls == fastest_bls:
return arkworks_G2.from_compressed_bytes_unchecked(bytes96)
return py_ecc_bytes96_to_G2(bytes96)
@only_with_bls(alt_return=True)
def KeyValidate(pubkey):
return py_ecc_bls.KeyValidate(pubkey)