示例#1
0
def signature_validator(msg: rpc_pb2.Message) -> bool:
    """
    Verify the message against the given public key.

    :param pubkey: the public key which signs the message.
    :param msg: the message signed.
    """
    # Check if signature is attached
    if msg.signature == b"":
        logger.debug("Reject because no signature attached for msg: %s", msg)
        return False

    # Validate if message sender matches message signer,
    # i.e., check if `msg.key` matches `msg.from_id`
    msg_pubkey = deserialize_public_key(msg.key)
    if ID.from_pubkey(msg_pubkey) != msg.from_id:
        logger.debug(
            "Reject because signing key does not match sender ID for msg: %s", msg
        )
        return False
    # First, construct the original payload that's signed by 'msg.key'
    msg_without_key_sig = rpc_pb2.Message(
        data=msg.data, topicIDs=msg.topicIDs, from_id=msg.from_id, seqno=msg.seqno
    )
    payload = PUBSUB_SIGNING_PREFIX.encode() + msg_without_key_sig.SerializeToString()
    try:
        return msg_pubkey.verify(payload, msg.signature)
    except Exception:
        return False
示例#2
0
 def __init__(
     self,
     local_key_pair: KeyPair,
     secure_bytes_provider: Callable[[int], bytes] = default_secure_bytes_provider,
 ) -> None:
     self.local_private_key = local_key_pair.private_key
     self.local_peer = ID.from_pubkey(local_key_pair.public_key)
     self.secure_bytes_provider = secure_bytes_provider
示例#3
0
    async def handshake_outbound(self, conn: IRawConnection,
                                 remote_peer: ID) -> ISecureConn:
        noise_state = self.create_noise_state()

        read_writer = NoiseHandshakeReadWriter(conn, noise_state)
        noise_state.set_as_initiator()
        noise_state.start_handshake()
        handshake_state = noise_state.noise_protocol.handshake_state

        # Send msg#1, which is *not* encrypted.
        msg_1 = b""
        await read_writer.write_msg(msg_1)

        # Read msg#2 from the remote, which contains the public key of the peer.
        msg_2 = await read_writer.read_msg()
        peer_handshake_payload = NoiseHandshakePayload.deserialize(msg_2)

        if handshake_state.rs is None:
            raise NoiseStateError(
                "something is wrong in the underlying noise `handshake_state`: "
                "we received and consumed msg#3, which should have included the"
                " remote static public key, but it is not present in the handshake_state"
            )
        remote_pubkey = self._get_pubkey_from_noise_keypair(handshake_state.rs)

        if not verify_handshake_payload_sig(peer_handshake_payload,
                                            remote_pubkey):
            raise InvalidSignature
        remote_peer_id_from_pubkey = ID.from_pubkey(
            peer_handshake_payload.id_pubkey)
        if remote_peer_id_from_pubkey != remote_peer:
            raise PeerIDMismatchesPubkey(
                "peer id does not correspond to the received pubkey: "
                f"remote_peer={remote_peer}, "
                f"remote_peer_id_from_pubkey={remote_peer_id_from_pubkey}")

        # Send msg#3, which includes our encrypted payload and our noise static key.
        our_payload = self.make_handshake_payload()
        msg_3 = our_payload.serialize()
        await read_writer.write_msg(msg_3)

        if not noise_state.handshake_finished:
            raise HandshakeHasNotFinished(
                "handshake is done but it is not marked as finished in `noise_state`"
            )
        transport_read_writer = NoiseTransportReadWriter(conn, noise_state)
        return SecureSession(
            local_peer=self.local_peer,
            local_private_key=self.libp2p_privkey,
            remote_peer=remote_peer_id_from_pubkey,
            remote_permanent_pubkey=remote_pubkey,
            is_initiator=True,
            conn=transport_read_writer,
        )
示例#4
0
def test_id_from_public_key():
    key_pair = create_new_key_pair()
    public_key = key_pair.public_key

    key_bin = public_key.serialize()
    algo = multihash.Func.sha2_256
    mh_digest = multihash.digest(key_bin, algo)
    expected = ID(mh_digest.encode())

    actual = ID.from_pubkey(public_key)

    assert actual == expected
示例#5
0
    async def run_handshake(self) -> None:
        """Raise `HandshakeFailure` when handshake failed."""
        msg = make_exchange_message(self.local_private_key.get_public_key())
        msg_bytes = msg.SerializeToString()
        encoded_msg_bytes = encode_fixedint_prefixed(msg_bytes)
        try:
            await self.write(encoded_msg_bytes)
        except RawConnError:
            raise HandshakeFailure("connection closed")

        try:
            remote_msg_bytes = await read_fixedint_prefixed(self.conn)
        except RawConnError:
            raise HandshakeFailure("connection closed")
        remote_msg = plaintext_pb2.Exchange()
        remote_msg.ParseFromString(remote_msg_bytes)
        received_peer_id = ID(remote_msg.id)

        # Verify if the receive `ID` matches the one we originally initialize the session.
        # We only need to check it when we are the initiator, because only in that condition
        # we possibly knows the `ID` of the remote.
        if self.is_initiator and self.remote_peer_id != received_peer_id:
            raise HandshakeFailure(
                "remote peer sent unexpected peer ID. "
                f"expected={self.remote_peer_id} received={received_peer_id}"
            )

        # Verify if the given `pubkey` matches the given `peer_id`
        try:
            received_pubkey = deserialize_public_key(
                remote_msg.pubkey.SerializeToString()
            )
        except ValueError:
            raise HandshakeFailure(
                f"unknown `key_type` of remote_msg.pubkey={remote_msg.pubkey}"
            )
        except MissingDeserializerError as error:
            raise HandshakeFailure(error)
        peer_id_from_received_pubkey = ID.from_pubkey(received_pubkey)
        if peer_id_from_received_pubkey != received_peer_id:
            raise HandshakeFailure(
                "peer id and pubkey from the remote mismatch: "
                f"received_peer_id={received_peer_id}, remote_pubkey={received_pubkey}, "
                f"peer_id_from_received_pubkey={peer_id_from_received_pubkey}"
            )

        # Nothing is wrong. Store the `pubkey` and `peer_id` in the session.
        self.remote_permanent_pubkey = received_pubkey
        # Only need to set peer's id when we don't know it before,
        # i.e. we are not the connection initiator.
        if not self.is_initiator:
            self.remote_peer_id = received_peer_id
示例#6
0
async def test_create_secure_session(nursery):
    local_nonce = b"\x01" * NONCE_SIZE
    local_key_pair = create_new_key_pair(b"a")
    local_peer = ID.from_pubkey(local_key_pair.public_key)

    remote_nonce = b"\x02" * NONCE_SIZE
    remote_key_pair = create_new_key_pair(b"b")
    remote_peer = ID.from_pubkey(remote_key_pair.public_key)

    async with raw_conn_factory(nursery) as conns:
        local_conn, remote_conn = conns

        local_secure_conn, remote_secure_conn = None, None

        async def local_create_secure_session():
            nonlocal local_secure_conn
            local_secure_conn = await create_secure_session(
                local_nonce,
                local_peer,
                local_key_pair.private_key,
                local_conn,
                remote_peer,
            )

        async def remote_create_secure_session():
            nonlocal remote_secure_conn
            remote_secure_conn = await create_secure_session(
                remote_nonce, remote_peer, remote_key_pair.private_key,
                remote_conn)

        async with trio.open_nursery() as nursery_1:
            nursery_1.start_soon(local_create_secure_session)
            nursery_1.start_soon(remote_create_secure_session)

        msg = b"abc"
        await local_secure_conn.write(msg)
        received_msg = await remote_secure_conn.read(MAX_READ_LEN)
        assert received_msg == msg
示例#7
0
def test_peer_id_interop():
    private_key_protobuf_bytes = base64.b64decode(
        PRIVATE_KEY_PROTOBUF_SERIALIZATION)
    private_key_protobuf = pb.PrivateKey()
    private_key_protobuf.ParseFromString(private_key_protobuf_bytes)

    private_key_data = private_key_protobuf.data

    private_key_impl = RSA.import_key(private_key_data)
    private_key = RSAPrivateKey(private_key_impl)
    public_key = private_key.get_public_key()

    peer_id = ID.from_pubkey(public_key)
    assert peer_id == EXPECTED_PEER_ID
示例#8
0
async def test_create_secure_session():
    local_nonce = b"\x01" * NONCE_SIZE
    local_key_pair = create_new_key_pair(b"a")
    local_peer = ID.from_pubkey(local_key_pair.public_key)

    remote_nonce = b"\x02" * NONCE_SIZE
    remote_key_pair = create_new_key_pair(b"b")
    remote_peer = ID.from_pubkey(remote_key_pair.public_key)

    local_conn = InMemoryConnection(local_peer, is_initiator=True)
    remote_conn = InMemoryConnection(remote_peer)

    local_pipe_task = asyncio.ensure_future(
        create_pipe(local_conn, remote_conn))
    remote_pipe_task = asyncio.ensure_future(
        create_pipe(remote_conn, local_conn))

    local_session_builder = create_secure_session(local_nonce, local_peer,
                                                  local_key_pair.private_key,
                                                  local_conn, remote_peer)
    remote_session_builder = create_secure_session(remote_nonce, remote_peer,
                                                   remote_key_pair.private_key,
                                                   remote_conn)
    local_secure_conn, remote_secure_conn = await asyncio.gather(
        local_session_builder, remote_session_builder)

    msg = b"abc"
    await local_secure_conn.write(msg)
    received_msg = await remote_secure_conn.read()
    assert received_msg == msg

    await asyncio.gather(local_secure_conn.close(), remote_secure_conn.close())

    local_pipe_task.cancel()
    remote_pipe_task.cancel()
    await local_pipe_task
    await remote_pipe_task
示例#9
0
    async def handshake_inbound(self, conn: IRawConnection) -> ISecureConn:
        noise_state = self.create_noise_state()
        noise_state.set_as_responder()
        noise_state.start_handshake()
        handshake_state = noise_state.noise_protocol.handshake_state
        read_writer = NoiseHandshakeReadWriter(conn, noise_state)

        # Consume msg#1.
        await read_writer.read_msg()

        # Send msg#2, which should include our handshake payload.
        our_payload = self.make_handshake_payload()
        msg_2 = our_payload.serialize()
        await read_writer.write_msg(msg_2)

        # Receive and consume msg#3.
        msg_3 = await read_writer.read_msg()
        peer_handshake_payload = NoiseHandshakePayload.deserialize(msg_3)

        if handshake_state.rs is None:
            raise NoiseStateError(
                "something is wrong in the underlying noise `handshake_state`: "
                "we received and consumed msg#3, which should have included the"
                " remote static public key, but it is not present in the handshake_state"
            )
        remote_pubkey = self._get_pubkey_from_noise_keypair(handshake_state.rs)

        if not verify_handshake_payload_sig(peer_handshake_payload,
                                            remote_pubkey):
            raise InvalidSignature
        remote_peer_id_from_pubkey = ID.from_pubkey(
            peer_handshake_payload.id_pubkey)

        if not noise_state.handshake_finished:
            raise HandshakeHasNotFinished(
                "handshake is done but it is not marked as finished in `noise_state`"
            )
        transport_read_writer = NoiseTransportReadWriter(conn, noise_state)
        return SecureSession(
            local_peer=self.local_peer,
            local_private_key=self.libp2p_privkey,
            remote_peer=remote_peer_id_from_pubkey,
            remote_permanent_pubkey=remote_pubkey,
            is_initiator=False,
            conn=transport_read_writer,
        )
示例#10
0
 def calculate_peer_id(self) -> PeerID:
     return PeerID.from_pubkey(self.public_key)
示例#11
0
def node_peer_id(node_key_pair):
    return PeerID.from_pubkey(node_key_pair.public_key)
示例#12
0
 def peer_id(self) -> ID:
     return ID.from_pubkey(self.node_privkey.get_public_key())
示例#13
0
def make_exchange_message(pubkey: PublicKey) -> plaintext_pb2.Exchange:
    pubkey_pb = crypto_pb2.PublicKey(
        key_type=pubkey.get_type().value, data=pubkey.to_bytes()
    )
    id_bytes = ID.from_pubkey(pubkey).to_bytes()
    return plaintext_pb2.Exchange(id=id_bytes, pubkey=pubkey_pb)
示例#14
0
async def run_handshake(
    local_peer: ID,
    local_private_key: PrivateKey,
    conn: IRawConnection,
    is_initiator: bool,
    remote_peer_id: ID,
) -> ISecureConn:
    """Raise `HandshakeFailure` when handshake failed."""
    msg = make_exchange_message(local_private_key.get_public_key())
    msg_bytes = msg.SerializeToString()
    read_writer = PlaintextHandshakeReadWriter(conn)
    try:
        await read_writer.write_msg(msg_bytes)
    except RawConnError as e:
        raise HandshakeFailure("connection closed") from e

    try:
        remote_msg_bytes = await read_writer.read_msg()
    except RawConnError as e:
        raise HandshakeFailure("connection closed") from e
    remote_msg = plaintext_pb2.Exchange()
    remote_msg.ParseFromString(remote_msg_bytes)
    received_peer_id = ID(remote_msg.id)

    # Verify if the receive `ID` matches the one we originally initialize the session.
    # We only need to check it when we are the initiator, because only in that condition
    # we possibly knows the `ID` of the remote.
    if is_initiator and remote_peer_id != received_peer_id:
        raise HandshakeFailure(
            "remote peer sent unexpected peer ID. "
            f"expected={remote_peer_id} received={received_peer_id}"
        )

    # Verify if the given `pubkey` matches the given `peer_id`
    try:
        received_pubkey = deserialize_public_key(remote_msg.pubkey.SerializeToString())
    except ValueError as e:
        raise HandshakeFailure(
            f"unknown `key_type` of remote_msg.pubkey={remote_msg.pubkey}"
        ) from e
    except MissingDeserializerError as error:
        raise HandshakeFailure() from error
    peer_id_from_received_pubkey = ID.from_pubkey(received_pubkey)
    if peer_id_from_received_pubkey != received_peer_id:
        raise HandshakeFailure(
            "peer id and pubkey from the remote mismatch: "
            f"received_peer_id={received_peer_id}, remote_pubkey={received_pubkey}, "
            f"peer_id_from_received_pubkey={peer_id_from_received_pubkey}"
        )

    secure_conn = InsecureSession(
        local_peer=local_peer,
        local_private_key=local_private_key,
        remote_peer=received_peer_id,
        remote_permanent_pubkey=received_pubkey,
        is_initiator=is_initiator,
        conn=conn,
    )

    # TODO: Store `pubkey` and `peer_id` to `PeerStore`

    return secure_conn
示例#15
0
def generate_peer_id_from(key_pair: KeyPair) -> ID:
    public_key = key_pair.public_key
    return ID.from_pubkey(public_key)
示例#16
0
    def __init__(
        self,
        local_node_key: PrivateKey,
        eth2_config: Eth2Config,
        clock: Clock,
        chain: BaseBeaconChain,
        validator_api_port: int,
        client_identifier: str,
        p2p_maddr: Multiaddr,
        preferred_nodes: Collection[Multiaddr],
        bootstrap_nodes: Collection[Multiaddr],
    ) -> None:
        self._local_key_pair = create_new_key_pair(local_node_key.to_bytes())
        self._eth2_config = eth2_config

        self._clock = clock
        self._chain = chain

        self._block_pool: Set[SignedBeaconBlock] = set()
        self._slashable_block_pool: Set[SignedBeaconBlock] = set()

        # FIXME: can we provide `p2p_maddr` as a default listening interface for `_mk_host`?
        peer_id = PeerID.from_pubkey(self._local_key_pair.public_key)
        if "p2p" in p2p_maddr:
            existing_peer_id = p2p_maddr.value_for_protocol("p2p")
            existing_p2p_maddr = Multiaddr(f"/p2p/{existing_peer_id}")
            self.logger.warning(
                "peer identity derived from local key pair %s overriding given identity %s",
                peer_id,
                existing_peer_id,
            )
            p2p_maddr = p2p_maddr.decapsulate(existing_p2p_maddr)
        self._p2p_maddr = p2p_maddr.encapsulate(Multiaddr(f"/p2p/{peer_id}"))

        # TODO: persist metadata and handle updates...
        self._metadata_provider = lambda: MetaData.create()
        self._peer_updater, self._peer_updates = trio.open_memory_channel[
            Tuple[PeerID, Any]](0)
        self._host = Host(
            self._local_key_pair,
            peer_id,
            self._accept_peer_updates,
            self._get_status,
            self._get_finalized_root_by_epoch,
            self._get_block_by_slot,
            self._get_block_by_root,
            self._metadata_provider,
            self._get_fork_digest,
            self._eth2_config,
        )
        self._preferred_nodes = preferred_nodes
        self._bootstrap_nodes = bootstrap_nodes

        self._sync_notifier, self._sync_requests = trio.open_memory_channel[
            SyncRequest](0)
        self._syncer = _mk_syncer()

        api_context = Context(
            client_identifier,
            eth2_config,
            self._syncer,
            self._chain,
            self._clock,
            _mk_block_broadcaster(self),
        )
        self.validator_api_port = validator_api_port
        self._validator_api_server = _mk_validator_api_server(
            self.validator_api_port, api_context)