2019-03-27 16:32:13 +00:00
|
|
|
"""
|
|
|
|
BLS test vectors generator
|
|
|
|
"""
|
|
|
|
|
2019-07-27 01:07:37 +00:00
|
|
|
from typing import Tuple, Iterable, Any, Callable, Dict
|
2019-03-27 16:32:13 +00:00
|
|
|
|
2019-04-11 09:15:23 +00:00
|
|
|
from eth_utils import (
|
2019-07-16 06:27:34 +00:00
|
|
|
encode_hex,
|
|
|
|
int_to_big_endian,
|
2019-04-11 09:15:23 +00:00
|
|
|
)
|
2019-07-27 01:07:37 +00:00
|
|
|
from gen_base import gen_runner, gen_typing
|
2019-03-27 16:32:13 +00:00
|
|
|
|
2020-05-14 16:45:26 +00:00
|
|
|
from eth2spec.utils import bls
|
2019-09-08 18:36:09 +00:00
|
|
|
from hashlib import sha256
|
|
|
|
|
2020-04-02 07:23:20 +00:00
|
|
|
from eth2spec.test.context import PHASE0
|
2019-09-08 18:36:09 +00:00
|
|
|
|
|
|
|
def hash(x):
|
|
|
|
return sha256(x).digest()
|
|
|
|
|
2019-03-27 16:32:13 +00:00
|
|
|
|
2019-07-27 01:07:37 +00:00
|
|
|
def int_to_hex(n: int, byte_length: int = None) -> str:
|
2019-06-14 16:15:54 +00:00
|
|
|
byte_value = int_to_big_endian(n)
|
|
|
|
if byte_length:
|
|
|
|
byte_value = byte_value.rjust(byte_length, b'\x00')
|
2019-07-16 06:27:34 +00:00
|
|
|
return encode_hex(byte_value)
|
2019-03-27 16:32:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
def hex_to_int(x: str) -> int:
|
|
|
|
return int(x, 16)
|
|
|
|
|
|
|
|
|
|
|
|
MESSAGES = [
|
2019-04-11 09:15:23 +00:00
|
|
|
bytes(b'\x00' * 32),
|
|
|
|
bytes(b'\x56' * 32),
|
|
|
|
bytes(b'\xab' * 32),
|
2019-03-27 16:32:13 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
PRIVKEYS = [
|
|
|
|
# Curve order is 256 so private keys are 32 bytes at most.
|
|
|
|
# Also not all integers is a valid private key, so using pre-generated keys
|
|
|
|
hex_to_int('0x00000000000000000000000000000000263dbd792f5b1be47ed85f8938c0f29586af0d3ac7b977f21c278fe1462040e3'),
|
|
|
|
hex_to_int('0x0000000000000000000000000000000047b8192d77bf871b62e87859d653922725724a5c031afeabc60bcef5ff665138'),
|
|
|
|
hex_to_int('0x00000000000000000000000000000000328388aff0d4a5b7dc9205abd374e7e98f3cd9f3418edb4eafda5fb16473d216'),
|
|
|
|
]
|
|
|
|
|
2020-05-14 16:45:26 +00:00
|
|
|
NO_PUBKEY = b'\x00' * 48
|
|
|
|
Z1_PUBKEY = b'\xc0' + b'\x00' * 47
|
|
|
|
NO_SIGNATURE = b'\x00' * 96
|
|
|
|
Z2_SIGNATURE = b'\xc0' + b'\x00' * 95
|
2019-03-27 16:32:13 +00:00
|
|
|
|
2020-01-23 00:56:50 +00:00
|
|
|
def case01_sign():
|
2019-03-27 16:32:13 +00:00
|
|
|
for privkey in PRIVKEYS:
|
|
|
|
for message in MESSAGES:
|
2020-05-14 16:45:26 +00:00
|
|
|
sig = bls.Sign(privkey, message)
|
2020-02-09 00:04:19 +00:00
|
|
|
identifier = f'{int_to_hex(privkey)}_{encode_hex(message)}'
|
|
|
|
yield f'sign_case_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
2020-01-16 18:09:58 +00:00
|
|
|
'input': {
|
|
|
|
'privkey': int_to_hex(privkey),
|
|
|
|
'message': encode_hex(message),
|
|
|
|
},
|
|
|
|
'output': encode_hex(sig)
|
|
|
|
}
|
2019-04-11 09:15:23 +00:00
|
|
|
|
2019-07-27 01:07:37 +00:00
|
|
|
|
2020-01-23 00:56:50 +00:00
|
|
|
def case02_verify():
|
2020-01-21 17:28:31 +00:00
|
|
|
for i, privkey in enumerate(PRIVKEYS):
|
|
|
|
for message in MESSAGES:
|
|
|
|
# Valid signature
|
2020-05-14 16:45:26 +00:00
|
|
|
signature = bls.Sign(privkey, message)
|
|
|
|
pubkey = bls.SkToPk(privkey)
|
2020-02-09 00:04:19 +00:00
|
|
|
identifier = f'{encode_hex(pubkey)}_{encode_hex(message)}'
|
2020-05-14 16:45:26 +00:00
|
|
|
assert bls.Verify(pubkey, message, signature)
|
2020-02-09 00:04:19 +00:00
|
|
|
yield f'verify_valid_case_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
2020-01-21 17:28:31 +00:00
|
|
|
'input': {
|
|
|
|
'pubkey': encode_hex(pubkey),
|
|
|
|
'message': encode_hex(message),
|
|
|
|
'signature': encode_hex(signature),
|
|
|
|
},
|
|
|
|
'output': True,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Invalid signatures -- wrong pubkey
|
2020-05-14 16:45:26 +00:00
|
|
|
wrong_pubkey = bls.SkToPk(PRIVKEYS[(i + 1) % len(PRIVKEYS)])
|
2020-02-09 00:04:19 +00:00
|
|
|
identifier = f'{encode_hex(wrong_pubkey)}_{encode_hex(message)}'
|
2020-05-14 16:45:26 +00:00
|
|
|
assert not bls.Verify(wrong_pubkey, message, signature)
|
2020-02-09 00:04:19 +00:00
|
|
|
yield f'verify_wrong_pubkey_case_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
2020-01-21 17:28:31 +00:00
|
|
|
'input': {
|
|
|
|
'pubkey': encode_hex(wrong_pubkey),
|
|
|
|
'message': encode_hex(message),
|
|
|
|
'signature': encode_hex(signature),
|
|
|
|
},
|
|
|
|
'output': False,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Invalid signature -- tampered with signature
|
|
|
|
tampered_signature = signature[:-4] + b'\xFF\xFF\xFF\xFF'
|
2020-02-09 00:04:19 +00:00
|
|
|
identifier = f'{encode_hex(pubkey)}_{encode_hex(message)}'
|
2020-05-14 16:45:26 +00:00
|
|
|
assert not bls.Verify(pubkey, message, tampered_signature)
|
2020-02-09 00:04:19 +00:00
|
|
|
yield f'verify_tampered_signature_case_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
2020-01-21 17:28:31 +00:00
|
|
|
'input': {
|
|
|
|
'pubkey': encode_hex(pubkey),
|
|
|
|
'message': encode_hex(message),
|
|
|
|
'signature': encode_hex(tampered_signature),
|
|
|
|
},
|
|
|
|
'output': False,
|
|
|
|
}
|
2019-03-27 16:32:13 +00:00
|
|
|
|
2020-05-14 16:45:26 +00:00
|
|
|
# Valid pubkey and signature with the point at infinity
|
|
|
|
assert 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,
|
|
|
|
}
|
2019-03-27 16:32:13 +00:00
|
|
|
|
2020-01-23 00:56:50 +00:00
|
|
|
def case03_aggregate():
|
2020-01-16 18:09:58 +00:00
|
|
|
for message in MESSAGES:
|
2020-05-14 16:45:26 +00:00
|
|
|
sigs = [bls.Sign(privkey, message) for privkey in PRIVKEYS]
|
2020-01-23 00:56:50 +00:00
|
|
|
yield f'aggregate_{encode_hex(message)}', {
|
2020-01-16 18:09:58 +00:00
|
|
|
'input': [encode_hex(sig) for sig in sigs],
|
2020-05-14 16:45:26 +00:00
|
|
|
'output': encode_hex(bls.Aggregate(sigs)),
|
2020-01-16 18:09:58 +00:00
|
|
|
}
|
2019-04-11 09:15:23 +00:00
|
|
|
|
2019-07-27 01:07:37 +00:00
|
|
|
|
2020-01-23 00:56:50 +00:00
|
|
|
def case04_fast_aggregate_verify():
|
2020-01-21 17:28:31 +00:00
|
|
|
for i, message in enumerate(MESSAGES):
|
|
|
|
privkeys = PRIVKEYS[:i + 1]
|
2020-05-14 16:45:26 +00:00
|
|
|
sigs = [bls.Sign(privkey, message) for privkey in privkeys]
|
|
|
|
aggregate_signature = bls.Aggregate(sigs)
|
|
|
|
pubkeys = [bls.SkToPk(privkey) for privkey in privkeys]
|
2020-01-21 17:28:31 +00:00
|
|
|
pubkeys_serial = [encode_hex(pubkey) for pubkey in pubkeys]
|
|
|
|
|
|
|
|
# Valid signature
|
2020-02-09 00:04:19 +00:00
|
|
|
identifier = f'{pubkeys_serial}_{encode_hex(message)}'
|
2020-05-14 16:45:26 +00:00
|
|
|
assert bls.FastAggregateVerify(pubkeys, message, aggregate_signature)
|
2020-02-09 00:04:19 +00:00
|
|
|
yield f'fast_aggregate_verify_valid_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
2020-01-21 17:28:31 +00:00
|
|
|
'input': {
|
|
|
|
'pubkeys': pubkeys_serial,
|
|
|
|
'message': encode_hex(message),
|
|
|
|
'signature': encode_hex(aggregate_signature),
|
|
|
|
},
|
|
|
|
'output': True,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Invalid signature -- extra pubkey
|
2020-05-14 16:45:26 +00:00
|
|
|
pubkeys_extra = pubkeys + [bls.SkToPk(PRIVKEYS[-1])]
|
2020-01-25 21:16:32 +00:00
|
|
|
pubkeys_extra_serial = [encode_hex(pubkey) for pubkey in pubkeys_extra]
|
2020-02-09 00:04:19 +00:00
|
|
|
identifier = f'{pubkeys_extra_serial}_{encode_hex(message)}'
|
2020-05-14 16:45:26 +00:00
|
|
|
assert not bls.FastAggregateVerify(pubkeys_extra, message, aggregate_signature)
|
2020-02-09 00:04:19 +00:00
|
|
|
yield f'fast_aggregate_verify_extra_pubkey_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
2020-01-21 17:28:31 +00:00
|
|
|
'input': {
|
|
|
|
'pubkeys': pubkeys_extra_serial,
|
|
|
|
'message': encode_hex(message),
|
|
|
|
'signature': encode_hex(aggregate_signature),
|
|
|
|
},
|
|
|
|
'output': False,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Invalid signature -- tampered with signature
|
|
|
|
tampered_signature = aggregate_signature[:-4] + b'\xff\xff\xff\xff'
|
2020-02-09 00:04:19 +00:00
|
|
|
identifier = f'{pubkeys_serial}_{encode_hex(message)}'
|
2020-05-14 16:45:26 +00:00
|
|
|
assert not bls.FastAggregateVerify(pubkeys, message, tampered_signature)
|
2020-02-09 00:04:19 +00:00
|
|
|
yield f'fast_aggregate_verify_tampered_signature_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
2020-01-21 17:28:31 +00:00
|
|
|
'input': {
|
|
|
|
'pubkeys': pubkeys_serial,
|
|
|
|
'message': encode_hex(message),
|
|
|
|
'signature': encode_hex(tampered_signature),
|
|
|
|
},
|
|
|
|
'output': False,
|
|
|
|
}
|
|
|
|
|
2020-05-14 16:45:26 +00:00
|
|
|
# Invalid pubkeys and signature -- len(pubkey) == 0 and signature == Z1_SIGNATURE
|
|
|
|
assert not bls.FastAggregateVerify([], message, Z2_SIGNATURE)
|
|
|
|
yield f'fast_aggregate_verify_na_pubkeys_and_infinity_signature', {
|
|
|
|
'input': {
|
|
|
|
'pubkeys': [],
|
|
|
|
'message': encode_hex(message),
|
|
|
|
'signature': encode_hex(Z2_SIGNATURE),
|
|
|
|
},
|
|
|
|
'output': False,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Invalid pubkeys and signature -- len(pubkey) == 0 and signature == 0x00...
|
|
|
|
assert not bls.FastAggregateVerify([], message, NO_SIGNATURE)
|
|
|
|
yield f'fast_aggregate_verify_na_pubkeys_and_na_signature', {
|
|
|
|
'input': {
|
|
|
|
'pubkeys': [],
|
|
|
|
'message': encode_hex(message),
|
|
|
|
'signature': encode_hex(NO_SIGNATURE),
|
|
|
|
},
|
|
|
|
'output': False,
|
|
|
|
}
|
2020-01-21 17:28:31 +00:00
|
|
|
|
2020-01-23 00:56:50 +00:00
|
|
|
def case05_aggregate_verify():
|
2020-05-14 16:45:26 +00:00
|
|
|
pubekys = []
|
|
|
|
pubkeys_serial = []
|
|
|
|
messages = []
|
|
|
|
messages_serial = []
|
2020-01-21 17:28:31 +00:00
|
|
|
sigs = []
|
|
|
|
for privkey, message in zip(PRIVKEYS, MESSAGES):
|
2020-05-14 16:45:26 +00:00
|
|
|
sig = bls.Sign(privkey, message)
|
|
|
|
pubkey = bls.SkToPk(privkey)
|
|
|
|
pubekys.append(pubkey)
|
|
|
|
pubkeys_serial.append(encode_hex(pubkey))
|
|
|
|
messages.append(message)
|
|
|
|
messages_serial.append(encode_hex(message))
|
2020-01-21 17:28:31 +00:00
|
|
|
sigs.append(sig)
|
|
|
|
|
2020-05-14 16:45:26 +00:00
|
|
|
aggregate_signature = bls.Aggregate(sigs)
|
|
|
|
assert bls.AggregateVerify(pubekys, messages, aggregate_signature)
|
2020-02-05 05:04:51 +00:00
|
|
|
yield f'aggregate_verify_valid', {
|
2020-01-21 17:28:31 +00:00
|
|
|
'input': {
|
2020-05-14 16:45:26 +00:00
|
|
|
'pubkeys': pubkeys_serial,
|
|
|
|
'messages': messages_serial,
|
2020-01-21 17:28:31 +00:00
|
|
|
'signature': encode_hex(aggregate_signature),
|
|
|
|
},
|
|
|
|
'output': True,
|
|
|
|
}
|
|
|
|
|
|
|
|
tampered_signature = aggregate_signature[:4] + b'\xff\xff\xff\xff'
|
2020-05-14 16:45:26 +00:00
|
|
|
assert not bls.AggregateVerify(pubkey, messages, tampered_signature)
|
2020-02-05 05:04:51 +00:00
|
|
|
yield f'aggregate_verify_tampered_signature', {
|
2020-01-21 17:28:31 +00:00
|
|
|
'input': {
|
2020-05-14 16:45:26 +00:00
|
|
|
'pubkeys': pubkeys_serial,
|
|
|
|
'messages': messages_serial,
|
2020-01-21 17:28:31 +00:00
|
|
|
'signature': encode_hex(tampered_signature),
|
|
|
|
},
|
|
|
|
'output': False,
|
|
|
|
}
|
|
|
|
|
2020-05-14 16:45:26 +00:00
|
|
|
# Invalid pubkeys and signature -- len(pubkey) == 0 and signature == Z1_SIGNATURE
|
|
|
|
assert not bls.AggregateVerify([], [], Z2_SIGNATURE)
|
|
|
|
yield f'aggregate_verify_na_pubkeys_and_infinity_signature', {
|
|
|
|
'input': {
|
|
|
|
'pubkeys': [],
|
|
|
|
'message': [],
|
|
|
|
'signature': encode_hex(Z2_SIGNATURE),
|
|
|
|
},
|
|
|
|
'output': False,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Invalid pubkeys and signature -- len(pubkey) == 0 and signature == 0x00...
|
|
|
|
assert not bls.AggregateVerify([], [], NO_SIGNATURE)
|
|
|
|
yield f'aggregate_verify_na_pubkeys_and_na_signature', {
|
|
|
|
'input': {
|
|
|
|
'pubkeys': [],
|
|
|
|
'messages': [],
|
|
|
|
'signature': encode_hex(NO_SIGNATURE),
|
|
|
|
},
|
|
|
|
'output': False,
|
|
|
|
}
|
2019-04-11 09:15:23 +00:00
|
|
|
|
2019-07-27 01:07:37 +00:00
|
|
|
def create_provider(handler_name: str,
|
|
|
|
test_case_fn: Callable[[], Iterable[Tuple[str, Dict[str, Any]]]]) -> gen_typing.TestProvider:
|
|
|
|
|
|
|
|
def prepare_fn(configs_path: str) -> str:
|
2019-07-30 01:11:59 +00:00
|
|
|
# Nothing to load / change in spec. Maybe in future forks.
|
|
|
|
# Put the tests into the general config category, to not require any particular configuration.
|
|
|
|
return 'general'
|
2019-07-27 01:07:37 +00:00
|
|
|
|
|
|
|
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
|
|
|
for data in test_case_fn():
|
|
|
|
print(data)
|
|
|
|
(case_name, case_content) = data
|
|
|
|
yield gen_typing.TestCase(
|
2020-04-02 07:23:20 +00:00
|
|
|
fork_name=PHASE0,
|
2019-07-27 01:07:37 +00:00
|
|
|
runner_name='bls',
|
|
|
|
handler_name=handler_name,
|
|
|
|
suite_name='small',
|
|
|
|
case_name=case_name,
|
|
|
|
case_fn=lambda: [('data', 'data', case_content)]
|
|
|
|
)
|
|
|
|
|
|
|
|
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
2019-04-11 09:15:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2019-04-11 09:25:00 +00:00
|
|
|
gen_runner.run_generator("bls", [
|
2020-01-23 00:56:50 +00:00
|
|
|
create_provider('sign', case01_sign),
|
|
|
|
create_provider('verify', case02_verify),
|
|
|
|
create_provider('aggregate', case03_aggregate),
|
|
|
|
create_provider('fast_aggregate_verify', case04_fast_aggregate_verify),
|
|
|
|
create_provider('aggregate_verify', case05_aggregate_verify),
|
2019-04-11 09:15:23 +00:00
|
|
|
])
|