def get_valid_early_derived_secret_reveal(spec, state, epoch=None): current_epoch = spec.get_current_epoch(state) revealed_index = spec.get_active_validator_indices(state, current_epoch)[-1] masker_index = spec.get_active_validator_indices(state, current_epoch)[0] if epoch is None: epoch = current_epoch + spec.CUSTODY_PERIOD_TO_RANDAO_PADDING # Generate the secret that is being revealed domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch) signing_root = spec.compute_signing_root(spec.Epoch(epoch), domain) reveal = bls.Sign(privkeys[revealed_index], signing_root) # Generate the mask (any random 32 bytes that don't reveal the masker's secret will do) mask = spec.hash(reveal) # Generate masker's signature on the mask signing_root = spec.compute_signing_root(mask, domain) masker_signature = bls.Sign(privkeys[masker_index], signing_root) masked_reveal = bls.Aggregate([reveal, masker_signature]) return spec.EarlyDerivedSecretReveal( revealed_index=revealed_index, epoch=epoch, reveal=masked_reveal, masker_index=masker_index, mask=mask, )
def test_invalid_sig_other_version(spec, state): validator_index = len(state.validators) amount = spec.MAX_EFFECTIVE_BALANCE pubkey = pubkeys[validator_index] privkey = privkeys[validator_index] withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:] # Go through the effort of manually signing, not something normally done. This sig domain will be invalid. deposit_message = spec.DepositMessage( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount) domain = spec.compute_domain(domain_type=spec.DOMAIN_DEPOSIT, fork_version=spec.Version('0xaabbccdd')) deposit_data = spec.DepositData( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, signature=bls.Sign(privkey, spec.compute_signing_root(deposit_message, domain))) deposit, root, _ = deposit_from_context(spec, [deposit_data], 0) state.eth1_deposit_index = 0 state.eth1_data.deposit_root = root state.eth1_data.deposit_count = 1 yield from run_deposit_processing(spec, state, deposit, validator_index, valid=True, effective=False)
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': { 'privkey': int_to_hex(privkey), 'message': encode_hex(message), }, 'output': encode_hex(sig) } # Edge case: privkey == 0 expect_exception(bls.Sign, ZERO_PRIVKEY, message) 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 get_valid_custody_slashing(spec, state, attestation, shard_transition, custody_secret, data, data_index=0): beacon_committee = spec.get_beacon_committee( state, attestation.data.slot, attestation.data.index, ) malefactor_index = beacon_committee[0] whistleblower_index = beacon_committee[-1] slashing = spec.CustodySlashing( data_index=data_index, malefactor_index=malefactor_index, malefactor_secret=custody_secret, whistleblower_index=whistleblower_index, shard_transition=shard_transition, attestation=attestation, data=data, ) slashing_domain = spec.get_domain(state, spec.DOMAIN_CUSTODY_BIT_SLASHING) slashing_root = spec.compute_signing_root(slashing, slashing_domain) signed_slashing = spec.SignedCustodySlashing( message=slashing, signature=bls.Sign(privkeys[whistleblower_index], slashing_root)) return signed_slashing
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(aggregate_sig), } # Invalid pubkeys -- len(pubkeys) == 0 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-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 get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None): if validator_index is None: validator_index = 0 if withdrawal_pubkey is None: key_index = -1 - validator_index withdrawal_pubkey = pubkeys[key_index] withdrawal_privkey = privkeys[key_index] else: withdrawal_privkey = pubkey_to_privkey[withdrawal_pubkey] domain = spec.get_domain(state, spec.DOMAIN_BLS_TO_EXECUTION_CHANGE) address_change = spec.BLSToExecutionChange( validator_index=validator_index, from_bls_pubkey=withdrawal_pubkey, to_execution_address=b'\x42' * 20, ) signing_root = spec.compute_signing_root(address_change, domain) return spec.SignedBLSToExecutionChange( message=address_change, signature=bls.Sign(withdrawal_privkey, signing_root), )
def create_signed_exit(index): exit = spec.VoluntaryExit( epoch=spec.get_current_epoch(state), validator_index=index, ) signing_root = spec.compute_signing_root(exit, domain) return spec.SignedVoluntaryExit(message=exit, signature=bls.Sign(privkeys[index], signing_root))
def sign_voluntary_exit(spec, state, voluntary_exit, privkey): domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch) signing_root = spec.compute_signing_root(voluntary_exit, domain) return spec.SignedVoluntaryExit( message=voluntary_exit, signature=bls.Sign(privkey, signing_root) )
def compute_sync_committee_signature(spec, state, slot, privkey): domain = spec.get_domain(state, spec.DOMAIN_SYNC_COMMITTEE, spec.compute_epoch_at_slot(slot)) if slot == state.slot: block_root = build_empty_block_for_next_slot(spec, state).parent_root else: block_root = spec.get_block_root_at_slot(state, slot) signing_root = spec.compute_signing_root(block_root, domain) return bls.Sign(privkey, signing_root)
def sign_deposit_data(spec, deposit_data, privkey): deposit_message = spec.DepositMessage( pubkey=deposit_data.pubkey, withdrawal_credentials=deposit_data.withdrawal_credentials, amount=deposit_data.amount) domain = spec.compute_domain(spec.DOMAIN_DEPOSIT) signing_root = spec.compute_signing_root(deposit_message, domain) deposit_data.signature = bls.Sign(privkey, signing_root)
def sign_block_header(spec, state, header, privkey): domain = spec.get_domain( state=state, domain_type=spec.DOMAIN_BEACON_PROPOSER, ) signing_root = spec.compute_signing_root(header, domain) signature = bls.Sign(privkey, signing_root) return spec.SignedBeaconBlockHeader(message=header, signature=signature)
def get_custody_secret(spec, state, validator_index, epoch=None): period = spec.get_custody_period_for_validator( validator_index, epoch if epoch is not None else spec.get_current_epoch(state)) epoch_to_sign = spec.get_randao_epoch_for_custody_period( period, validator_index) domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch_to_sign) signing_root = spec.compute_signing_root(spec.Epoch(epoch_to_sign), domain) return bls.Sign(privkeys[validator_index], signing_root)
def sign_shard_block(spec, beacon_state, shard, block, proposer_index=None): slot = block.message.slot if proposer_index is None: proposer_index = spec.get_shard_proposer_index(beacon_state, slot, shard) privkey = privkeys[proposer_index] domain = spec.get_domain(beacon_state, spec.DOMAIN_SHARD_PROPOSAL, spec.compute_epoch_at_slot(slot)) signing_root = spec.compute_signing_root(block.message, domain) block.signature = bls.Sign(privkey, signing_root)
def apply_sig(spec, state, signed_block, proposer_index=None): block = signed_block.message proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index) privkey = privkeys[proposer_index] domain = spec.get_domain(state, spec.DOMAIN_BEACON_PROPOSER, spec.compute_epoch_at_slot(block.slot)) signing_root = spec.compute_signing_root(block, domain) signed_block.signature = bls.Sign(privkey, signing_root)
def get_attestation_custody_signature(spec, state, attestation_data, block_index, bit, privkey): domain = spec.get_domain(state, spec.DOMAIN_BEACON_ATTESTER, attestation_data.target.epoch) signing_root = spec.compute_signing_root( spec.AttestationCustodyBitWrapper( attestation_data_root=attestation_data.hash_tree_root(), block_index=block_index, bit=bit, ), domain, ) return bls.Sign(privkey, signing_root)
def case01_sign(): for privkey in PRIVKEYS: for message in MESSAGES: sig = bls.Sign(privkey, message) identifier = f'{int_to_hex(privkey)}_{encode_hex(message)}' yield f'sign_case_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { 'input': { 'privkey': int_to_hex(privkey), 'message': encode_hex(message), }, 'output': encode_hex(sig) }
def apply_randao_reveal(spec, state, block, proposer_index=None): assert state.slot <= block.slot proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index) privkey = privkeys[proposer_index] domain = spec.get_domain(state, spec.DOMAIN_RANDAO, spec.compute_epoch_at_slot(block.slot)) signing_root = spec.compute_signing_root( spec.compute_epoch_at_slot(block.slot), domain) block.body.randao_reveal = bls.Sign(privkey, signing_root)
def test_invalid_block_sig(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state) domain = spec.get_domain(state, spec.DOMAIN_BEACON_PROPOSER, spec.compute_epoch_at_slot(block.slot)) signing_root = spec.compute_signing_root(block, domain) invalid_signed_block = spec.SignedBeaconBlock( message=block, signature=bls.Sign(123456, signing_root) ) expect_assertion_error(lambda: spec.state_transition(state, invalid_signed_block)) yield 'blocks', [invalid_signed_block] yield 'post', None
def test_get_sync_committee_message(spec, state): validator_index = 0 block_root = spec.Root(b'\x12' * 32) sync_committee_message = spec.get_sync_committee_message( state=state, block_root=block_root, validator_index=validator_index, privkey=privkeys[validator_index], ) assert sync_committee_message.slot == state.slot assert sync_committee_message.beacon_block_root == block_root assert sync_committee_message.validator_index == validator_index epoch = spec.get_current_epoch(state) domain = spec.get_domain(state, spec.DOMAIN_SYNC_COMMITTEE, epoch) signing_root = spec.compute_signing_root(block_root, domain) signature = bls.Sign(privkeys[validator_index], signing_root) assert sync_committee_message.signature == signature
def get_valid_custody_key_reveal(spec, state, period=None): current_epoch = spec.get_current_epoch(state) revealer_index = spec.get_active_validator_indices(state, current_epoch)[0] revealer = state.validators[revealer_index] if period is None: period = revealer.next_custody_secret_to_reveal epoch_to_sign = spec.get_randao_epoch_for_custody_period( period, revealer_index) # Generate the secret that is being revealed domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch_to_sign) signing_root = spec.compute_signing_root(spec.Epoch(epoch_to_sign), domain) reveal = bls.Sign(privkeys[revealer_index], signing_root) return spec.CustodyKeyReveal( revealer_index=revealer_index, reveal=reveal, )
def _get_sync_committee_signature( spec, state, target_slot, target_block_root, subcommittee_index, index_in_subcommittee, ): subcommittee_size = spec.SYNC_COMMITTEE_SIZE // spec.SYNC_COMMITTEE_SUBNET_COUNT sync_committee_index = (subcommittee_index * subcommittee_size + index_in_subcommittee) pubkey = state.current_sync_committee.pubkeys[sync_committee_index] privkey = pubkey_to_privkey[pubkey] domain = spec.get_domain( state, spec.DOMAIN_SYNC_COMMITTEE, ) signing_data = spec.compute_signing_root(target_block_root, domain) return bls.Sign(privkey, spec.hash_tree_root(signing_data))
def case03_aggregate(): for message in MESSAGES: sigs = [bls.Sign(privkey, message) for privkey in PRIVKEYS] yield f'aggregate_{encode_hex(message)}', { 'input': [encode_hex(sig) for sig in sigs], 'output': encode_hex(bls.Aggregate(sigs)), } # Invalid pubkeys -- len(pubkeys) == 0 try: bls.Aggregate([]) except Exception: pass else: raise Exception("Should have been INVALID") yield f'aggregate_na_pubkeys', { 'input': [], 'output': None, }
def case03_aggregate(): for message in MESSAGES: sigs = [bls.Sign(privkey, message) for privkey in PRIVKEYS] yield f'aggregate_{encode_hex(message)}', { 'input': [encode_hex(sig) for sig in sigs], 'output': encode_hex(bls.Aggregate(sigs)), } # Invalid pubkeys -- len(pubkeys) == 0 try: bls.Aggregate([]) except Exception: pass else: raise Exception("Should have been INVALID") # 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 yield f'aggregate_na_signatures', { 'input': [], 'output': None, }
def test_voluntary_exit(spec, state): validator_index = spec.get_active_validator_indices( state, spec.get_current_epoch(state))[-1] # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH yield 'pre', state voluntary_exit = spec.VoluntaryExit( epoch=spec.get_current_epoch(state), validator_index=validator_index, ) domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT) signing_root = spec.compute_signing_root(voluntary_exit, domain) signed_voluntary_exit = spec.SignedVoluntaryExit( message=voluntary_exit, signature=bls.Sign(privkeys[validator_index], signing_root)) # Add to state via block transition initiate_exit_block = build_empty_block_for_next_slot(spec, state) initiate_exit_block.body.voluntary_exits.append(signed_voluntary_exit) signed_initiate_exit_block = state_transition_and_sign_block( spec, state, initiate_exit_block) assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH # Process within epoch transition exit_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) signed_exit_block = state_transition_and_sign_block( spec, state, exit_block) yield 'blocks', [signed_initiate_exit_block, signed_exit_block] yield 'post', state assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
def get_valid_bit_challenge(spec, state, attestation, invalid_custody_bit=False): beacon_committee = spec.get_beacon_committee( state, attestation.data.slot, attestation.data.crosslink.shard, ) responder_index = beacon_committee[0] challenger_index = beacon_committee[-1] epoch = spec.get_randao_epoch_for_custody_period( attestation.data.target.epoch, responder_index) # Generate the responder key domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch) signing_root = spec.compute_signing_root(spec.Epoch(epoch), domain) responder_key = bls.Sign(privkeys[responder_index], signing_root) chunk_count = spec.get_custody_chunk_count(attestation.data.crosslink) chunk_bits = bitlist_from_int(spec.MAX_CUSTODY_CHUNKS, chunk_count, 0) n = 0 while spec.get_chunk_bits_root( chunk_bits) == attestation.custody_bits[0] ^ invalid_custody_bit: chunk_bits = bitlist_from_int(spec.MAX_CUSTODY_CHUNKS, chunk_count, n) n += 1 return spec.CustodyBitChallenge( responder_index=responder_index, attestation=attestation, challenger_index=challenger_index, responder_key=responder_key, chunk_bits=chunk_bits, )
def case07_eth_fast_aggregate_verify(): """ Similar to `case04_fast_aggregate_verify` except for the empty case """ for i, message in enumerate(MESSAGES): privkeys = PRIVKEYS[:i + 1] sigs = [bls.Sign(privkey, message) for privkey in privkeys] aggregate_signature = bls.Aggregate(sigs) pubkeys = [bls.SkToPk(privkey) for privkey in privkeys] pubkeys_serial = [encode_hex(pubkey) for pubkey in pubkeys] # Valid signature identifier = f'{pubkeys_serial}_{encode_hex(message)}' assert spec.eth_fast_aggregate_verify(pubkeys, message, aggregate_signature) yield f'eth_fast_aggregate_verify_valid_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { 'input': { 'pubkeys': pubkeys_serial, 'message': encode_hex(message), 'signature': encode_hex(aggregate_signature), }, 'output': True, } # Invalid signature -- extra pubkey pubkeys_extra = pubkeys + [bls.SkToPk(PRIVKEYS[-1])] pubkeys_extra_serial = [encode_hex(pubkey) for pubkey in pubkeys_extra] identifier = f'{pubkeys_extra_serial}_{encode_hex(message)}' assert not spec.eth_fast_aggregate_verify(pubkeys_extra, message, aggregate_signature) yield f'eth_fast_aggregate_verify_extra_pubkey_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { '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' identifier = f'{pubkeys_serial}_{encode_hex(message)}' assert not spec.eth_fast_aggregate_verify(pubkeys, message, tampered_signature) yield f'eth_fast_aggregate_verify_tampered_signature_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { 'input': { 'pubkeys': pubkeys_serial, 'message': encode_hex(message), 'signature': encode_hex(tampered_signature), }, 'output': False, } # NOTE: Unlike `FastAggregateVerify`, len(pubkeys) == 0 and signature == G2_POINT_AT_INFINITY is VALID assert spec.eth_fast_aggregate_verify([], message, G2_POINT_AT_INFINITY) yield f'eth_fast_aggregate_verify_na_pubkeys_and_infinity_signature', { 'input': { 'pubkeys': [], 'message': encode_hex(message), 'signature': encode_hex(G2_POINT_AT_INFINITY), }, 'output': True, } # Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == 0x00... assert not spec.eth_fast_aggregate_verify([], message, ZERO_SIGNATURE) yield f'eth_fast_aggregate_verify_na_pubkeys_and_zero_signature', { 'input': { 'pubkeys': [], 'message': encode_hex(message), 'signature': encode_hex(ZERO_SIGNATURE), }, 'output': False, } # Invalid pubkeys and signature -- pubkeys contains point at infinity pubkeys = PUBKEYS.copy() pubkeys_with_infinity = pubkeys + [G1_POINT_AT_INFINITY] signatures = [bls.Sign(privkey, SAMPLE_MESSAGE) for privkey in PRIVKEYS] aggregate_signature = bls.Aggregate(signatures) assert not spec.eth_fast_aggregate_verify( pubkeys_with_infinity, SAMPLE_MESSAGE, aggregate_signature) yield f'eth_fast_aggregate_verify_infinity_pubkey', { 'input': { 'pubkeys': [encode_hex(pubkey) for pubkey in pubkeys_with_infinity], 'message': encode_hex(SAMPLE_MESSAGE), 'signature': encode_hex(aggregate_signature), }, 'output': False, }
def get_attestation_signature(spec, state, attestation_data, privkey): domain = spec.get_domain(state, spec.DOMAIN_BEACON_ATTESTER, attestation_data.target.epoch) signing_root = spec.compute_signing_root(attestation_data, domain) return bls.Sign(privkey, signing_root)
def case02_verify(): for i, privkey in enumerate(PRIVKEYS): for message in MESSAGES: # Valid signature signature = bls.Sign(privkey, message) pubkey = bls.SkToPk(privkey) assert milagro_bls.SkToPk(to_bytes(privkey)) == pubkey assert milagro_bls.Sign(to_bytes(privkey), message) == signature identifier = f'{encode_hex(pubkey)}_{encode_hex(message)}' assert bls.Verify(pubkey, message, signature) assert milagro_bls.Verify(pubkey, message, signature) yield f'verify_valid_case_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { 'input': { 'pubkey': encode_hex(pubkey), 'message': encode_hex(message), 'signature': encode_hex(signature), }, 'output': True, } # Invalid signatures -- wrong pubkey wrong_pubkey = bls.SkToPk(PRIVKEYS[(i + 1) % len(PRIVKEYS)]) identifier = f'{encode_hex(wrong_pubkey)}_{encode_hex(message)}' assert not bls.Verify(wrong_pubkey, message, signature) assert not milagro_bls.Verify(wrong_pubkey, message, signature) yield f'verify_wrong_pubkey_case_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { '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' identifier = f'{encode_hex(pubkey)}_{encode_hex(message)}' assert not bls.Verify(pubkey, message, tampered_signature) assert not milagro_bls.Verify(pubkey, message, tampered_signature) yield f'verify_tampered_signature_case_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { 'input': { 'pubkey': encode_hex(pubkey), 'message': encode_hex(message), 'signature': encode_hex(tampered_signature), }, 'output': False, } # 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 case05_aggregate_verify(): pubkeys = [] pubkeys_serial = [] messages = [] messages_serial = [] sigs = [] for privkey, message in zip(PRIVKEYS, MESSAGES): sig = bls.Sign(privkey, message) pubkey = bls.SkToPk(privkey) pubkeys.append(pubkey) pubkeys_serial.append(encode_hex(pubkey)) messages.append(message) messages_serial.append(encode_hex(message)) sigs.append(sig) aggregate_signature = bls.Aggregate(sigs) assert bls.AggregateVerify(pubkeys, messages, aggregate_signature) assert milagro_bls.AggregateVerify(pubkeys, messages, aggregate_signature) yield f'aggregate_verify_valid', { 'input': { 'pubkeys': pubkeys_serial, 'messages': messages_serial, 'signature': encode_hex(aggregate_signature), }, 'output': True, } tampered_signature = aggregate_signature[:4] + b'\xff\xff\xff\xff' assert not bls.AggregateVerify(pubkey, messages, tampered_signature) assert not milagro_bls.AggregateVerify(pubkeys, messages, tampered_signature) yield f'aggregate_verify_tampered_signature', { 'input': { 'pubkeys': pubkeys_serial, 'messages': messages_serial, 'signature': encode_hex(tampered_signature), }, 'output': False, } # Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == Z1_SIGNATURE assert not bls.AggregateVerify([], [], Z2_SIGNATURE) assert not milagro_bls.AggregateVerify([], [], Z2_SIGNATURE) yield f'aggregate_verify_na_pubkeys_and_infinity_signature', { 'input': { 'pubkeys': [], 'messages': [], 'signature': encode_hex(Z2_SIGNATURE), }, 'output': False, } # Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == 0x00... assert not bls.AggregateVerify([], [], NO_SIGNATURE) assert not milagro_bls.AggregateVerify([], [], NO_SIGNATURE) yield f'aggregate_verify_na_pubkeys_and_na_signature', { 'input': { 'pubkeys': [], 'messages': [], 'signature': encode_hex(NO_SIGNATURE), }, '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 case04_fast_aggregate_verify(): for i, message in enumerate(MESSAGES): privkeys = PRIVKEYS[:i + 1] sigs = [bls.Sign(privkey, message) for privkey in privkeys] aggregate_signature = bls.Aggregate(sigs) pubkeys = [bls.SkToPk(privkey) for privkey in privkeys] pubkeys_serial = [encode_hex(pubkey) for pubkey in pubkeys] # Valid signature identifier = f'{pubkeys_serial}_{encode_hex(message)}' assert bls.FastAggregateVerify(pubkeys, message, aggregate_signature) assert milagro_bls.FastAggregateVerify(pubkeys, message, aggregate_signature) yield f'fast_aggregate_verify_valid_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { 'input': { 'pubkeys': pubkeys_serial, 'message': encode_hex(message), 'signature': encode_hex(aggregate_signature), }, 'output': True, } # Invalid signature -- extra pubkey pubkeys_extra = pubkeys + [bls.SkToPk(PRIVKEYS[-1])] pubkeys_extra_serial = [encode_hex(pubkey) for pubkey in pubkeys_extra] identifier = f'{pubkeys_extra_serial}_{encode_hex(message)}' assert not bls.FastAggregateVerify(pubkeys_extra, message, aggregate_signature) assert not milagro_bls.FastAggregateVerify(pubkeys_extra, message, aggregate_signature) yield f'fast_aggregate_verify_extra_pubkey_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { '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' identifier = f'{pubkeys_serial}_{encode_hex(message)}' assert not bls.FastAggregateVerify(pubkeys, message, tampered_signature) assert not milagro_bls.FastAggregateVerify(pubkeys, message, tampered_signature) yield f'fast_aggregate_verify_tampered_signature_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', { 'input': { 'pubkeys': pubkeys_serial, 'message': encode_hex(message), 'signature': encode_hex(tampered_signature), }, 'output': False, } # Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == Z1_SIGNATURE assert not bls.FastAggregateVerify([], message, Z2_SIGNATURE) assert not milagro_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(pubkeys) == 0 and signature == 0x00... assert not bls.FastAggregateVerify([], message, NO_SIGNATURE) assert not milagro_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, } # 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) 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], 'message': encode_hex(SAMPLE_MESSAGE), 'signature': encode_hex(aggregate_signature), }, 'output': False, }