Merge pull request #2080 from ethereum/bls_v4
Bump IETF BLS spec version draft 03 -> draft 04
This commit is contained in:
commit
d264ad8af7
4
setup.py
4
setup.py
|
@ -536,8 +536,8 @@ setup(
|
|||
"eth-utils>=1.3.0,<2",
|
||||
"eth-typing>=2.1.0,<3.0.0",
|
||||
"pycryptodome==3.9.4",
|
||||
"py_ecc==4.0.0",
|
||||
"milagro_bls_binding==1.3.0",
|
||||
"py_ecc==5.0.0",
|
||||
"milagro_bls_binding==1.4.0",
|
||||
"dataclasses==0.6",
|
||||
"remerkleable==0.1.17",
|
||||
"ruamel.yaml==0.16.5",
|
||||
|
|
|
@ -605,7 +605,7 @@ def bytes_to_uint64(data: bytes) -> uint64:
|
|||
|
||||
#### BLS Signatures
|
||||
|
||||
Eth2 makes use of BLS signatures as specified in the [IETF draft BLS specification draft-irtf-cfrg-bls-signature-03](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03). Specifically, eth2 uses the `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_` ciphersuite which implements the following interfaces:
|
||||
Eth2 makes use of BLS signatures as specified in the [IETF draft BLS specification draft-irtf-cfrg-bls-signature-04](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04). Specifically, eth2 uses the `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_` ciphersuite which implements the following interfaces:
|
||||
|
||||
- `def Sign(SK: int, message: Bytes) -> BLSSignature`
|
||||
- `def Verify(PK: BLSPubkey, message: Bytes, signature: BLSSignature) -> bool`
|
||||
|
|
|
@ -10,6 +10,7 @@ bls = py_ecc_bls
|
|||
|
||||
STUB_SIGNATURE = b'\x11' * 96
|
||||
STUB_PUBKEY = b'\x22' * 48
|
||||
Z1_PUBKEY = b'\xc0' + b'\x00' * 47
|
||||
Z2_SIGNATURE = b'\xc0' + b'\x00' * 95
|
||||
STUB_COORDINATES = _signature_to_G2(Z2_SIGNATURE)
|
||||
|
||||
|
@ -66,6 +67,11 @@ def AggregateVerify(pubkeys, messages, signature):
|
|||
|
||||
@only_with_bls(alt_return=True)
|
||||
def FastAggregateVerify(pubkeys, message, signature):
|
||||
# TODO: remove it when milagro_bls_binding is fixed
|
||||
# https://github.com/ChihChengLiang/milagro_bls_binding/issues/19
|
||||
if Z1_PUBKEY in pubkeys:
|
||||
return False
|
||||
|
||||
try:
|
||||
result = bls.FastAggregateVerify(list(pubkeys), message, signature)
|
||||
except Exception:
|
||||
|
@ -81,6 +87,9 @@ def Aggregate(signatures):
|
|||
|
||||
@only_with_bls(alt_return=STUB_SIGNATURE)
|
||||
def Sign(SK, message):
|
||||
# TODO: remove it when https://github.com/sigp/milagro_bls/issues/39 is fixed
|
||||
if SK == 0:
|
||||
raise Exception("SK should not be zero")
|
||||
if bls == py_ecc_bls:
|
||||
return bls.Sign(SK, message)
|
||||
else:
|
||||
|
@ -99,4 +108,7 @@ def AggregatePKs(pubkeys):
|
|||
|
||||
@only_with_bls(alt_return=STUB_SIGNATURE)
|
||||
def SkToPk(SK):
|
||||
return bls.SkToPk(SK)
|
||||
if bls == py_ecc_bls:
|
||||
return bls.SkToPk(SK)
|
||||
else:
|
||||
return bls.SkToPk(SK.to_bytes(32, 'big'))
|
||||
|
|
|
@ -10,7 +10,7 @@ The test data is declared in a `data.yaml` file:
|
|||
input:
|
||||
privkey: bytes32 -- the private key used for signing
|
||||
message: bytes32 -- input message to sign (a hash)
|
||||
output: bytes96 -- expected signature
|
||||
output: BLS Signature -- expected output, single BLS signature or empty.
|
||||
```
|
||||
|
||||
All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`.
|
||||
|
|
|
@ -40,6 +40,7 @@ MESSAGES = [
|
|||
bytes(b'\x56' * 32),
|
||||
bytes(b'\xab' * 32),
|
||||
]
|
||||
SAMPLE_MESSAGE = b'\x12' * 32
|
||||
|
||||
PRIVKEYS = [
|
||||
# Curve order is 256 so private keys are 32 bytes at most.
|
||||
|
@ -48,16 +49,30 @@ PRIVKEYS = [
|
|||
hex_to_int('0x0000000000000000000000000000000047b8192d77bf871b62e87859d653922725724a5c031afeabc60bcef5ff665138'),
|
||||
hex_to_int('0x00000000000000000000000000000000328388aff0d4a5b7dc9205abd374e7e98f3cd9f3418edb4eafda5fb16473d216'),
|
||||
]
|
||||
PUBKEYS = [bls.SkToPk(privkey) for privkey in PRIVKEYS]
|
||||
|
||||
Z1_PUBKEY = b'\xc0' + b'\x00' * 47
|
||||
NO_SIGNATURE = b'\x00' * 96
|
||||
Z2_SIGNATURE = b'\xc0' + b'\x00' * 95
|
||||
ZERO_PRIVKEY = 0
|
||||
ZERO_PRIVKEY_BYTES = b'\x00' * 32
|
||||
|
||||
|
||||
def expect_exception(func, *args):
|
||||
try:
|
||||
func(*args)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
raise Exception("should have raised exception")
|
||||
|
||||
|
||||
def case01_sign():
|
||||
# Valid cases
|
||||
for privkey in PRIVKEYS:
|
||||
for message in MESSAGES:
|
||||
sig = bls.Sign(privkey, message)
|
||||
assert sig == milagro_bls.Sign(to_bytes(privkey), message) # double-check with milagro
|
||||
identifier = f'{int_to_hex(privkey)}_{encode_hex(message)}'
|
||||
yield f'sign_case_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
|
@ -66,6 +81,17 @@ def case01_sign():
|
|||
},
|
||||
'output': encode_hex(sig)
|
||||
}
|
||||
# Edge case: privkey == 0
|
||||
expect_exception(bls.Sign, ZERO_PRIVKEY, message)
|
||||
# TODO enable it when milagro_bls is ready for IETF BLS draft 04
|
||||
# expect_exception(milagro_bls.Sign, ZERO_PRIVKEY_BYTES, message)
|
||||
yield f'sign_case_zero_privkey', {
|
||||
'input': {
|
||||
'privkey': encode_hex(ZERO_PRIVKEY_BYTES),
|
||||
'message': encode_hex(message),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
|
||||
def case02_verify():
|
||||
|
@ -120,42 +146,46 @@ def case02_verify():
|
|||
'output': False,
|
||||
}
|
||||
|
||||
# Valid pubkey and signature with the point at infinity
|
||||
assert bls.Verify(Z1_PUBKEY, message, Z2_SIGNATURE)
|
||||
assert milagro_bls.Verify(Z1_PUBKEY, message, Z2_SIGNATURE)
|
||||
yield f'verify_infinity_pubkey_and_infinity_signature', {
|
||||
'input': {
|
||||
'pubkey': encode_hex(Z1_PUBKEY),
|
||||
'message': encode_hex(message),
|
||||
'signature': encode_hex(Z2_SIGNATURE),
|
||||
},
|
||||
'output': True,
|
||||
}
|
||||
# Invalid pubkey and signature with the point at infinity
|
||||
assert not bls.Verify(Z1_PUBKEY, SAMPLE_MESSAGE, Z2_SIGNATURE)
|
||||
assert not milagro_bls.Verify(Z1_PUBKEY, SAMPLE_MESSAGE, Z2_SIGNATURE)
|
||||
yield f'verify_infinity_pubkey_and_infinity_signature', {
|
||||
'input': {
|
||||
'pubkey': encode_hex(Z1_PUBKEY),
|
||||
'message': encode_hex(SAMPLE_MESSAGE),
|
||||
'signature': encode_hex(Z2_SIGNATURE),
|
||||
},
|
||||
'output': False,
|
||||
}
|
||||
|
||||
|
||||
def case03_aggregate():
|
||||
for message in MESSAGES:
|
||||
sigs = [bls.Sign(privkey, message) for privkey in PRIVKEYS]
|
||||
aggregate_sig = bls.Aggregate(sigs)
|
||||
assert aggregate_sig == milagro_bls.Aggregate(sigs)
|
||||
yield f'aggregate_{encode_hex(message)}', {
|
||||
'input': [encode_hex(sig) for sig in sigs],
|
||||
'output': encode_hex(bls.Aggregate(sigs)),
|
||||
'output': encode_hex(aggregate_sig),
|
||||
}
|
||||
|
||||
# Invalid pubkeys -- len(pubkeys) == 0
|
||||
try:
|
||||
bls.Aggregate([])
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
raise Exception("Should have been INVALID")
|
||||
|
||||
expect_exception(bls.Aggregate, [])
|
||||
# No signatures to aggregate. Follow IETF BLS spec, return `None` to represent INVALID.
|
||||
# https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-02#section-2.8
|
||||
# https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.8
|
||||
yield f'aggregate_na_signatures', {
|
||||
'input': [],
|
||||
'output': None,
|
||||
}
|
||||
|
||||
# Valid to aggregate G2 point at infinity
|
||||
aggregate_sig = bls.Aggregate([Z2_SIGNATURE])
|
||||
assert aggregate_sig == milagro_bls.Aggregate([Z2_SIGNATURE]) == Z2_SIGNATURE
|
||||
yield f'aggregate_infinity_signature', {
|
||||
'input': [encode_hex(Z2_SIGNATURE)],
|
||||
'output': encode_hex(aggregate_sig),
|
||||
}
|
||||
|
||||
|
||||
def case04_fast_aggregate_verify():
|
||||
for i, message in enumerate(MESSAGES):
|
||||
|
@ -231,6 +261,23 @@ def case04_fast_aggregate_verify():
|
|||
'output': False,
|
||||
}
|
||||
|
||||
# Invalid pubkeys and signature -- pubkeys contains point at infinity
|
||||
pubkeys = PUBKEYS.copy()
|
||||
pubkeys_with_infinity = pubkeys + [Z1_PUBKEY]
|
||||
signatures = [bls.Sign(privkey, SAMPLE_MESSAGE) for privkey in PRIVKEYS]
|
||||
aggregate_signature = bls.Aggregate(signatures)
|
||||
assert not bls.FastAggregateVerify(pubkeys_with_infinity, SAMPLE_MESSAGE, aggregate_signature)
|
||||
# TODO enable it when milagro_bls is ready for IETF BLS draft 04
|
||||
# assert not milagro_bls.FastAggregateVerify(pubkeys_with_infinity, SAMPLE_MESSAGE, aggregate_signature)
|
||||
yield f'fast_aggregate_verify_infinity_pubkey', {
|
||||
'input': {
|
||||
'pubkeys': [encode_hex(pubkey) for pubkey in pubkeys_with_infinity],
|
||||
'messages': encode_hex(SAMPLE_MESSAGE),
|
||||
'signature': encode_hex(aggregate_signature),
|
||||
},
|
||||
'output': False,
|
||||
}
|
||||
|
||||
|
||||
def case05_aggregate_verify():
|
||||
pubkeys = []
|
||||
|
@ -295,6 +342,20 @@ def case05_aggregate_verify():
|
|||
'output': False,
|
||||
}
|
||||
|
||||
# Invalid pubkeys and signature -- pubkeys contains point at infinity
|
||||
pubkeys_with_infinity = pubkeys + [Z1_PUBKEY]
|
||||
messages_with_sample = messages + [SAMPLE_MESSAGE]
|
||||
assert not bls.AggregateVerify(pubkeys_with_infinity, messages_with_sample, aggregate_signature)
|
||||
assert not milagro_bls.AggregateVerify(pubkeys_with_infinity, messages_with_sample, aggregate_signature)
|
||||
yield f'aggregate_verify_infinity_pubkey', {
|
||||
'input': {
|
||||
'pubkeys': [encode_hex(pubkey) for pubkey in pubkeys_with_infinity],
|
||||
'messages': [encode_hex(message) for message in messages_with_sample],
|
||||
'signature': encode_hex(aggregate_signature),
|
||||
},
|
||||
'output': False,
|
||||
}
|
||||
|
||||
|
||||
def create_provider(handler_name: str,
|
||||
test_case_fn: Callable[[], Iterable[Tuple[str, Dict[str, Any]]]]) -> gen_typing.TestProvider:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
py_ecc==4.0.0
|
||||
py_ecc==5.0.0
|
||||
eth-utils==1.6.0
|
||||
../../core/gen_helpers
|
||||
../../../
|
||||
|
|
Loading…
Reference in New Issue