Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump IETF BLS spec version draft 03 -> draft 04 #2080

Merged
merged 6 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion specs/phase0/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,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`
Expand Down
5 changes: 4 additions & 1 deletion tests/core/pyspec/eth2spec/utils/bls.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,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'))
2 changes: 1 addition & 1 deletion tests/formats/bls/sign.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
101 changes: 81 additions & 20 deletions tests/generators/bls/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def hex_to_int(x: str) -> int:
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.
Expand All @@ -48,16 +49,30 @@ def hex_to_int(x: str) -> int:
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': {
Expand All @@ -66,6 +81,16 @@ def case01_sign():
},
'output': encode_hex(sig)
}
# Edge case: privkey == 0
expect_exception(bls.Sign, ZERO_PRIVKEY, message)
# expect_exception(milagro_bls.Sign, ZERO_PRIVKEY_BYTES, message) # TODO: enable it when milagro is ready
yield f'sign_case_zero_privkey', {
'input': {
'privkey': encode_hex(ZERO_PRIVKEY_BYTES),
'message': encode_hex(message),
},
'output': None
}


def case02_verify():
Expand Down Expand Up @@ -120,42 +145,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) # TODO: enable it when milagro is ready
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
hwwhww marked this conversation as resolved.
Show resolved Hide resolved
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):
Expand Down Expand Up @@ -231,6 +260,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 is ready
# 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 = []
Expand Down Expand Up @@ -295,6 +341,21 @@ 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)
# TODO: enable it when milagro is ready
# 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:
Expand Down