def reconstruct(signatures): r = b.Z2 for i, sig in signatures.items(): sig_point = signature_to_G2(sig) coef = 1 for j in signatures: if j != i: coef = -coef * (j + 1) * prime_field_inv( i - j, b.curve_order) % b.curve_order r = b.add(r, b.multiply(sig_point, coef)) return G2_to_signature(r)
def test_verify_and_deposit_fails_with_incorrect_signature( proxy_contract, deposit_contract, bls_public_key, withdrawal_credentials, signature, deposit_data_root, public_key_witness, signature_witness, deposit_amount, assert_tx_failed, signing_root, bls_private_key, ): assert ( int.from_bytes( deposit_contract.functions.get_deposit_count().call(), byteorder="little" ) == 0 ) assert ( deposit_contract.functions.get_deposit_root().call().hex() == EMPTY_DEPOSIT_ROOT ) public_key_witness_repr = _convert_int_to_fp_repr(public_key_witness) another_message = hashlib.sha256(b"not the signing root").digest() assert signing_root != another_message signature = G2ProofOfPossession.Sign(bls_private_key, another_message) group_element = signature_to_G2(signature) normalized_group_element = normalize(group_element) signature_witness = normalized_group_element[1] signature_witness_repr = _convert_int_to_fp2_repr(signature_witness) amount_in_wei = deposit_amount * 10 ** 9 txn = proxy_contract.functions.verifyAndDeposit( bls_public_key, withdrawal_credentials, signature, deposit_data_root, public_key_witness_repr, signature_witness_repr, ) assert_tx_failed(lambda: txn.transact({"value": amount_in_wei})) assert ( int.from_bytes( deposit_contract.functions.get_deposit_count().call(), byteorder="little" ) == 0 )
def test_bls_pairing_check(proxy_contract, signing_root, bls_public_key, signature): public_key_point = pubkey_to_G1(bls_public_key) public_key = normalize(public_key_point) public_key_repr = ( _convert_int_to_fp_repr(public_key[0]), _convert_int_to_fp_repr(public_key[1]), ) # skip some data wrangling by calling contract function for this... message_on_curve = proxy_contract.functions.hashToCurve(signing_root).call() projective_signature_point = signature_to_G2(signature) signature_point = normalize(projective_signature_point) signature_repr = ( _convert_int_to_fp2_repr(signature_point[0]), _convert_int_to_fp2_repr(signature_point[1]), ) assert proxy_contract.functions.blsPairingCheck( public_key_repr, message_on_curve, signature_repr ).call()
def test_bls_signature_is_valid_fails_with_invalid_signature( proxy_contract, bls_public_key, signing_root, public_key_witness, bls_private_key ): public_key_witness_repr = _convert_int_to_fp_repr(public_key_witness) another_message = hashlib.sha256(b"not the signing root").digest() assert signing_root != another_message signature = G2ProofOfPossession.Sign(bls_private_key, another_message) group_element = signature_to_G2(signature) normalized_group_element = normalize(group_element) signature_witness = normalized_group_element[1] signature_witness_repr = _convert_int_to_fp2_repr(signature_witness) assert not proxy_contract.functions.blsSignatureIsValid( signing_root, bls_public_key, signature, public_key_witness_repr, signature_witness_repr, ).call()
def verify_signature_files(signature_files:Sequence[str], threshold:int=None) -> bool: # Get data from signature share file parties_ids = [] signature_shares = {} master_pubkey = None msg = None derivation_index = None for sig_file in signature_files: in_data = {} try: in_file = open(sig_file, "r") in_data = json.load(in_file) in_file.close() except: raise GenVerErrorBasic(f'Reading key file {sig_file}') try: # Verify same master pubkey curr_master_pubkey = bytes.fromhex(in_data['master_pubkey']) if master_pubkey: if not master_pubkey == curr_master_pubkey: raise GenVerErrorBasic(f'Different master pubkey in different files') else: master_pubkey = curr_master_pubkey # Verify same message curr_msg = in_data['message'] if msg: if not msg == curr_msg: raise GenVerErrorBasic(f'Different messages in different files') else: msg = curr_msg # Verify same derivation indx curr_index = in_data['derivation_index'] if derivation_index: if not derivation_index == curr_index: raise GenVerErrorBasic(f'Different derivation index in different files') else: derivation_index = curr_index curr_id = int(in_data['signer_id']) parties_ids.append(curr_id) signature_shares[curr_id] = bytes.fromhex(in_data['signature']) except: raise GenVerErrorBasic(f'Parsing key file {sig_file}') msg_str = msg['payload'] if msg['hex']: try: msg_bytes = bytes.fromhex(msg['payload']) except: raise GenVerErrorBasic(f'Message string is not hex') else: msg_bytes = bytes(msg_str, 'ascii') # Convert signature shares to G2 elements to later interpolate on G2_signature_shares = {} for id, sig in signature_shares.items(): try: G2_signature_shares[id] = bls_conv.signature_to_G2(sig) except: raise GenVerErrorBasic(f'Invalid encoding of signature for id {id}') if not threshold: threshold = len(parties_ids) der_path = index_to_path(derivation_index) derived_pubkey_address = bls_conv.G1_to_pubkey(derive_public_child(master_pubkey, der_path)) print(f'Verifying signing threshold {threshold} out of {len(parties_ids)} parties') print("Message:", colored(msg_str, "green")) print("Public Key:", colored(derived_pubkey_address.hex(), "green")) # Verify joining threshold singature shares gives a valid signature auth_signature = b'' for auth_ids in itertools.combinations(parties_ids, threshold): auth_signature = bls_conv.G2_to_signature(_interpolate_in_group({id : G2_signature_shares[id] for id in auth_ids}, bls_curve.G2)) if not bls_basic.Verify(derived_pubkey_address, msg_bytes, auth_signature): raise GenVerErrorBasic(f'Failed verification of combined signature for id {auth_ids}.') print("Signature:", colored(auth_signature.hex(), "green")) print(colored("Success!", "green")) return True
def verify_key_file(key_file:str, passphrase:str, rsa_priv_key_file:str=None): try: in_file = open(key_file, "r") data = json.load(in_file) in_file.close() except: raise GenVerErrorBasic(f'Reading key file {key_file}') try: master_pubkey = bytes.fromhex(data['master_pubkey']) integrity_checksum = bytes.fromhex(data['integrity_checksum']) threshold = data['threshold'] my_id = data['key_id'] parties = data['parties'] encrypted_private_key_share = bytes.fromhex(parties[f'{my_id}']['encrypted_master_private_key_share']) encrypted_integrity_passphrase = bytes.fromhex(parties[f'{my_id}']['encrypted_integrity_passphrase']) parties_ids = [] test_signature_shares = {} for id_str, party in parties.items(): try: id = int(id_str) except: raise GenVerErrorBasic(f'Invalid id {id_str}') parties_ids.append(id) test_signature_shares[id] = bytes.fromhex(party['test_signature_share']) except: raise GenVerErrorBasic(f'Error parsing key file') # If Given RSA private key, use it to get integrity passphrase (if no file, assume integrity passphrase is given) master_private_key_share = None if rsa_priv_key_file is None: integrity_passphrase = bytes(passphrase, 'utf-8') else: try: in_file = open(rsa_priv_key_file, 'r') rsa_key = RSA.importKey(in_file.read(), passphrase=passphrase) cipher = PKCS1_OAEP.new(rsa_key) in_file.close() except: raise GenVerErrorBasic(f'Importing RSA key from file {rsa_priv_key_file} (perhaps wrong passphrase)') if not rsa_key.has_private(): raise GenVerErrorBasic(f'Not a private RSA key file {rsa_priv_key_file}') try: master_private_key_share = cipher.decrypt(encrypted_private_key_share) except: raise GenVerErrorBasic(f'Invalid decryption of private key share from key file') try: integrity_passphrase = cipher.decrypt(encrypted_integrity_passphrase) except: raise GenVerErrorBasic(f'Invalid decryption of integrity passphrase from key file') # Sanity checks if threshold > len(parties_ids) or threshold < 1: raise GenVerErrorBasic(f'Invalid threhsold {threshold} for ids {parties_ids}') if (len(parties_ids) != len(set(parties_ids))): raise GenVerErrorBasic(f'Non-unique ids in verification file') test_derivation_path = _get_test_path() derived_public_key = derive_public_child(master_pubkey, test_derivation_path) derived_pubkey_address = bls_conv.G1_to_pubkey(derived_public_key) test_msg, _ = _get_msg_for_address(derived_pubkey_address) # If decrypted master_private_key Verify my own signature wasn't modified by signing again if master_private_key_share: derived_private_key_share = derive_private_child(master_private_key_share, test_derivation_path, master_pubkey) if not test_signature_shares[my_id] == bls_basic.Sign(derived_private_key_share, test_msg): raise GenVerErrorBasic(f'Modified signature share for my key id {my_id}') else: print(colored('No RSA key - not verifying private key share validity!', "cyan")) # After getting scrypt integrity passphrase validate master pubkey wasn't changed computed_checksum = _compute_scrypt_checksum(integrity_passphrase, master_pubkey) if not computed_checksum == integrity_checksum: raise GenVerErrorBasic(f'Failure in validating master public key integrity checksum (perhaps wrong passphrase)') # Convert to group elements to allow interpolation of signatures G2_signature_shares = {} for id, sig in test_signature_shares.items(): try: G2_signature_shares[id] = bls_conv.signature_to_G2(sig) except: raise GenVerErrorBasic(f'Invalid encoding of signature shares for id {id}') # For each authorized set of the above, combine signature shares and verify print(f'Verifying signing threshold {threshold} out of {len(parties_ids)} parties...') for auth_ids in itertools.combinations(parties_ids, threshold): auth_signature = bls_conv.G2_to_signature(_interpolate_in_group({id : G2_signature_shares[id] for id in auth_ids}, bls_curve.G2)) if not bls_basic.Verify(derived_pubkey_address, test_msg, auth_signature): raise GenVerErrorBasic(f'Failed verification of combined signature for ids {auth_ids}') # Sanity check: check un-authorized set can't get valid signature (less then threhsold) for auth_ids in itertools.combinations(parties_ids, threshold-1): auth_signature = bls_conv.G2_to_signature(_interpolate_in_group({id : G2_signature_shares[id] for id in auth_ids}, bls_curve.G2)) if bls_basic.Verify(derived_pubkey_address, test_msg, auth_signature): raise GenVerErrorBasic(f'Valid signature for unauthorized ids {auth_ids}') print(colored("Success!", "green")) return True
def sign_with_sk(sk, msg): return signature_to_G2(bls.Sign(sk.to_bytes(32, config.ENDIANNESS), msg))
def test_G2_signature_encode_decode(): G2_point = multiply(G2, 42) signature = G2_to_signature(G2_point) assert (normalize(signature_to_G2(signature)) == normalize(G2_point))
def test_decompress_G2_with_no_modular_square_root_found(): with pytest.raises(ValueError, match="Failed to find a modular squareroot"): signature_to_G2(b'\xA0' + b'\x11' * 95)
def signature_witness(signature): group_element = signature_to_G2(signature) normalized_group_element = normalize(group_element) return normalized_group_element[1]