def OpBLS_Sign(arg): op = json.loads(arg) private_key = to_int(op['priv']) if private_key == 0 or private_key >= 52435875175126190479447740508185965837690552500527637822603658699938581184513: return cleartext = bytes.fromhex(op['cleartext']) aug = bytes.fromhex(op['aug']) msg = aug + cleartext signature = bls_pop.Sign(private_key, msg) signature = signature_to_G2(signature) x = signature[0] / signature[2] y = signature[1] / signature[2] signature = [[str(x.coeffs[0]), str(y.coeffs[0])], [str(x.coeffs[1]), str(y.coeffs[1])]] public_key = bls_pop.SkToPk(private_key) point = pubkey_to_G1(public_key) point = [str(point[0]), str(point[1])] j = {} j['signature'] = signature j['pub'] = point r = json.dumps(j) return bytes(r, 'utf-8')
def sign_deposit_data(deposit_data: DepositMessage, sk: int) -> Deposit: ''' Given a DepositMessage, it signs its root and returns a Deposit ''' assert bls.PrivToPub(sk) == deposit_data.pubkey domain = compute_domain() signing_root = compute_signing_root(deposit_data, domain) signed_deposit_data = Deposit(**deposit_data.as_dict(), signature=bls.Sign(sk, signing_root)) return signed_deposit_data
def test_verify_and_deposit_fails_with_incorrect_public_key( proxy_contract, deposit_contract, withdrawal_credentials, signature, deposit_data_root, public_key_witness, signature_witness, deposit_amount, assert_tx_failed, seed, ): 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 ) another_seed = "another-secret".encode() assert seed != another_seed another_private_key = G2ProofOfPossession.KeyGen(another_seed) public_key = G2ProofOfPossession.SkToPk(another_private_key) group_element = pubkey_to_G1(public_key) normalized_group_element = normalize(group_element) public_key_witness = normalized_group_element[1] public_key_witness_repr = _convert_int_to_fp_repr(public_key_witness) signature_witness_repr = _convert_int_to_fp2_repr(signature_witness) amount_in_wei = deposit_amount * 10 ** 9 txn = proxy_contract.functions.verifyAndDeposit( 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 AggregateVerify( signature: BLSSignature, public_keys: Tuple[BLSPubkey, ...], messages: Tuple[Hash32, ...], ) -> bool: return G2ProofOfPossession.AggregateVerify(public_keys, messages, signature)
def OpBLS_Verify(arg): op = json.loads(arg) verified = False g1_x = to_int(op['g1_x']) g1_y = to_int(op['g1_y']) g1 = [FQ(g1_x), FQ(g1_y), FQ.one()] if is_on_curve(g1, b) == False: r = json.dumps(verified) return bytes(r, 'utf-8') g1 = G1_to_pubkey(g1) g2_v = to_int(op['g2_v']) g2_w = to_int(op['g2_w']) g2_x = to_int(op['g2_x']) g2_y = to_int(op['g2_y']) g2 = (FQ2((g2_v, g2_x)), FQ2((g2_w, g2_y)), FQ2.one()) try: g2 = G2_to_signature(g2) except: r = json.dumps(verified) return bytes(r, 'utf-8') msg = bytes.fromhex(op['cleartext']) verified = bls_pop.Verify(g1, msg, g2) r = json.dumps(verified) return bytes(r, 'utf-8')
def verify_deposit(deposit_data_dict: Dict[str, Any]) -> bool: ''' Checks whether a deposit is valid based on the eth2 rules. https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#deposits ''' pubkey = BLSPubkey(bytes.fromhex(deposit_data_dict['pubkey'])) withdrawal_credentials = bytes.fromhex(deposit_data_dict['withdrawal_credentials']) amount = deposit_data_dict['amount'] signature = BLSSignature(bytes.fromhex(deposit_data_dict['signature'])) deposit_data_root = bytes.fromhex(deposit_data_dict['signed_deposit_data_root']) # Verify deposit amount if not MIN_DEPOSIT_AMOUNT < amount <= MAX_DEPOSIT_AMOUNT: return False # Verify deposit signature && pubkey deposit_message = DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount) domain = compute_domain(domain_type=DOMAIN_DEPOSIT) signing_root = compute_signing_root(deposit_message, domain) if not bls.Verify(pubkey, signing_root, signature): return False # Verify Deposit Root deposit = Deposit(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, signature=signature) return deposit.hash_tree_root == deposit_data_root
def encrypt( cls, *, secret: bytes, password: str, path: str = '', kdf_salt: bytes = randbits(256).to_bytes(32, 'big'), aes_iv: bytes = randbits(128).to_bytes(16, 'big') ) -> 'Keystore': """ Encrypt a secret (BLS SK) as an EIP 2335 Keystore. """ keystore = cls() keystore.uuid = str(uuid4()) keystore.crypto.kdf.params['salt'] = kdf_salt decryption_key = keystore.kdf(password=cls._process_password(password), **keystore.crypto.kdf.params) keystore.crypto.cipher.params['iv'] = aes_iv cipher = AES_128_CTR(key=decryption_key[:16], **keystore.crypto.cipher.params) keystore.crypto.cipher.message = cipher.encrypt(secret) keystore.crypto.checksum.message = SHA256( decryption_key[16:32] + keystore.crypto.cipher.message) keystore.pubkey = bls.SkToPk(int.from_bytes(secret, 'big')).hex() keystore.path = path return keystore
def signed_deposit(self) -> DepositData: domain = compute_deposit_domain(fork_version=self.fork_version) signing_root = compute_signing_root(self.deposit_message, domain) signed_deposit = DepositData(**self.deposit_message.as_dict(), signature=bls.Sign( self.signing_sk, signing_root)) return signed_deposit
def signed_deposit(self) -> DepositData: domain = compute_deposit_domain( fork_version=self.chain_setting.GENESIS_FORK_VERSION) signing_root = compute_signing_root(self.deposit_message, domain) signed_deposit = DepositData(**self.deposit_message.as_dict(), signature=bls.Sign( self.signing_sk, signing_root)) return signed_deposit
def encrypt(cls, *, secret: bytes, password: str, path: str='', kdf_salt: bytes=randbits(256).to_bytes(32, 'big'), aes_iv: bytes=randbits(128).to_bytes(16, 'big')): keystore = cls() keystore.crypto.kdf.params['salt'] = kdf_salt decryption_key = keystore.kdf(password=password, **keystore.crypto.kdf.params) keystore.crypto.cipher.params['iv'] = aes_iv cipher = AES_128_CTR(key=decryption_key[:16], **keystore.crypto.cipher.params) keystore.crypto.cipher.message = cipher.encrypt(secret) keystore.crypto.checksum.message = SHA256(decryption_key[16:32] + keystore.crypto.cipher.message) keystore.pubkey = bls.PrivToPub(int.from_bytes(secret, 'big')).hex() keystore.path = path return keystore
def test_bls_signature_is_valid_fails_with_invalid_public_key( proxy_contract, seed, signing_root, signature, signature_witness ): another_seed = "another-secret".encode() assert seed != another_seed another_private_key = G2ProofOfPossession.KeyGen(another_seed) public_key = G2ProofOfPossession.SkToPk(another_private_key) group_element = pubkey_to_G1(public_key) normalized_group_element = normalize(group_element) public_key_witness = normalized_group_element[1] public_key_witness_repr = _convert_int_to_fp_repr(public_key_witness) signature_witness_repr = _convert_int_to_fp2_repr(signature_witness) assert not proxy_contract.functions.blsSignatureIsValid( signing_root, public_key, signature, public_key_witness_repr, signature_witness_repr, ).call()
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 OpBLS_PrivateToPublic(arg): op = json.loads(arg) private_key = to_int(op['priv']) if private_key > 115792089237316195423570985008687907853269984665640564039457584007913129639935: point = ['0', '0'] r = json.dumps(point) return bytes(r, 'utf-8') private_key %= 52435875175126190479447740508185965837690552500527637822603658699938581184513 if private_key == 0: point = ['0', '0'] r = json.dumps(point) return bytes(r, 'utf-8') public_key = bls_pop.SkToPk(private_key) point = pubkey_to_G1(public_key) point = [str(point[0]), str(point[1])] r = json.dumps(point) return bytes(r, 'utf-8')
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 test_hkdf_mod_r_key_info(seed: bytes, key_info: bytes) -> None: assert bls.KeyGen(seed, key_info) == _HKDF_mod_r(IKM=seed, key_info=key_info)
def test_hkdf_mod_r(test) -> None: seed = bytes.fromhex(test['seed']) assert bls.KeyGen(seed) == _HKDF_mod_r(IKM=seed)
def Aggregate(signatures: Sequence[BLSSignature]) -> BLSSignature: # py_ecc use a different EMPTY_SIGNATURE. Return the Trinity one here: if len(signatures) == 0: return EMPTY_SIGNATURE return G2ProofOfPossession.Aggregate(signatures)
def SkToPk(SK: int) -> BLSPubkey: return G2ProofOfPossession.SkToPk(SK)
def signing_pk(self) -> bytes: return bls.SkToPk(self.signing_sk)
def withdrawal_pk(self): return bls.PrivToPub(self.withdrawal_sk)
def Sign(SK: int, message: Hash32) -> BLSSignature: return G2ProofOfPossession.Sign(SK, message)
def test_fast_aggregate_verify(SKs, message): PKs = [G2ProofOfPossession.PrivToPub(sk) for sk in SKs] signatures = [G2ProofOfPossession.Sign(sk, message) for sk in SKs] aggregate_signature = G2ProofOfPossession.Aggregate(signatures) assert G2ProofOfPossession.FastAggregateVerify(PKs, message, aggregate_signature)
from py_ecc.bls import G2ProofOfPossession as bls # Enough keys for 256 validators per slot in worst-case epoch length privkeys = [i + 1 for i in range(32 * 256)] pubkeys = [bls.SkToPk(privkey) for privkey in privkeys] pubkey_to_privkey = { pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys) }
def FastAggregateVerify(PKs: Sequence[BLSPubkey], message: Hash32, signature: BLSSignature) -> bool: return G2ProofOfPossession.FastAggregateVerify(PKs, message, signature)
def test_pop(sk): pk = G2ProofOfPossession.PrivToPub(sk) proof = G2ProofOfPossession.PopProve(sk) assert G2ProofOfPossession.PopVerify(pk, proof)
def withdrawal_pk(self) -> bytes: return bls.SkToPk(self.withdrawal_sk)
def test_aggregate_pks(signature_points, result_point): signatures = [G1_to_pubkey(pt) for pt in signature_points] result_signature = G1_to_pubkey(result_point) assert G2ProofOfPossession._AggregatePKs(signatures) == result_signature
def Verify(PK: BLSPubkey, message: Hash32, signature: BLSSignature) -> bool: return G2ProofOfPossession.Verify(PK, message, signature)
def validate_deposit(deposit_data_dict: Dict[str, Any], credential: Credential) -> bool: ''' Checks whether a deposit is valid based on the eth2 rules. https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#deposits ''' pubkey = BLSPubkey(bytes.fromhex(deposit_data_dict['pubkey'])) withdrawal_credentials = bytes.fromhex( deposit_data_dict['withdrawal_credentials']) amount = deposit_data_dict['amount'] signature = BLSSignature(bytes.fromhex(deposit_data_dict['signature'])) deposit_message_root = bytes.fromhex( deposit_data_dict['deposit_data_root']) fork_version = bytes.fromhex(deposit_data_dict['fork_version']) # Verify pubkey if len(pubkey) != 48: return False if pubkey != credential.signing_pk: return False # Verify withdrawal credential if len(withdrawal_credentials) != 32: return False if withdrawal_credentials[: 1] == BLS_WITHDRAWAL_PREFIX == credential.withdrawal_prefix: if withdrawal_credentials[1:] != SHA256(credential.withdrawal_pk)[1:]: return False elif withdrawal_credentials[: 1] == ETH1_ADDRESS_WITHDRAWAL_PREFIX == credential.withdrawal_prefix: if withdrawal_credentials[1:12] != b'\x00' * 11: return False if credential.eth1_withdrawal_address is None: return False if withdrawal_credentials[12:] != credential.eth1_withdrawal_address: return False else: return False # Verify deposit amount if not MIN_DEPOSIT_AMOUNT < amount <= MAX_DEPOSIT_AMOUNT: return False # Verify deposit signature && pubkey deposit_message = DepositMessage( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount) domain = compute_deposit_domain(fork_version) signing_root = compute_signing_root(deposit_message, domain) if not bls.Verify(pubkey, signing_root, signature): return False # Verify Deposit Root signed_deposit = DepositData( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, signature=signature, ) return signed_deposit.hash_tree_root == deposit_message_root
from py_ecc.bls import G2ProofOfPossession as bls from eth2spec.phase0 import spec privkeys = [i + 1 for i in range(spec.SLOTS_PER_EPOCH * 16)] pubkeys = [bls.PrivToPub(privkey) for privkey in privkeys] pubkey_to_privkey = { pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys) }