Raise HandshakeFailure in transport

Change the exception handling flow.
Raise `SecurityUpgradeFailure` in security_multistream.
This commit is contained in:
mhchia 2019-08-21 15:12:35 +08:00
parent 80452d9589
commit 3e04480d62
No known key found for this signature in database
GPG Key ID: 389EFBEA1362589A
6 changed files with 54 additions and 21 deletions

View File

@ -10,6 +10,7 @@ def pubkey_from_protobuf(pubkey_pb: protobuf.PublicKey) -> PublicKey:
# TODO: Test against secp256k1 keys # TODO: Test against secp256k1 keys
elif pubkey_pb.key_type == protobuf.Secp256k1: elif pubkey_pb.key_type == protobuf.Secp256k1:
return Secp256k1PublicKey.from_bytes(pubkey_pb.data) return Secp256k1PublicKey.from_bytes(pubkey_pb.data)
# TODO: Support `Ed25519` and `ECDSA` in the future?
else: else:
raise ValueError( raise ValueError(
f"unsupported key_type={pubkey_pb.key_type}, data={pubkey_pb.data!r}" f"unsupported key_type={pubkey_pb.key_type}, data={pubkey_pb.data!r}"

View File

@ -1,6 +1,8 @@
class ValidationError(Exception): class BaseLibp2pError(Exception):
pass
class ValidationError(BaseLibp2pError):
""" """
Raised when something does not pass a validation check. Raised when something does not pass a validation check.
""" """
pass

View File

@ -6,7 +6,7 @@ from libp2p.peer.id import ID
from libp2p.security.base_session import BaseSession from libp2p.security.base_session import BaseSession
from libp2p.security.base_transport import BaseSecureTransport from libp2p.security.base_transport import BaseSecureTransport
from libp2p.security.secure_conn_interface import ISecureConn from libp2p.security.secure_conn_interface import ISecureConn
from libp2p.transport.exceptions import SecurityUpgradeFailure from libp2p.transport.exceptions import HandshakeFailure
from libp2p.typing import TProtocol from libp2p.typing import TProtocol
from libp2p.utils import encode_fixedint_prefixed, read_fixedint_prefixed from libp2p.utils import encode_fixedint_prefixed, read_fixedint_prefixed
@ -32,14 +32,14 @@ class InsecureSession(BaseSession):
# Verify if the given `pubkey` matches the given `peer_id` # Verify if the given `pubkey` matches the given `peer_id`
try: try:
remote_pubkey = pubkey_from_protobuf(remote_msg.pubkey) remote_pubkey = pubkey_from_protobuf(remote_msg.pubkey)
except ValueError as error: except ValueError:
raise SecurityUpgradeFailure( raise HandshakeFailure(
f"unknown protocol of remote_msg.pubkey={remote_msg.pubkey}" f"unknown `key_type` of remote_msg.pubkey={remote_msg.pubkey}"
) from error )
remote_peer_id = ID(remote_msg.id) remote_peer_id = ID(remote_msg.id)
remote_peer_id_from_pubkey = ID.from_pubkey(remote_pubkey) remote_peer_id_from_pubkey = ID.from_pubkey(remote_pubkey)
if remote_peer_id_from_pubkey != remote_peer_id: if remote_peer_id_from_pubkey != remote_peer_id:
raise SecurityUpgradeFailure( raise HandshakeFailure(
"peer id and pubkey from the remote mismatch: " "peer id and pubkey from the remote mismatch: "
f"remote_peer_id={remote_peer_id}, remote_pubkey={remote_pubkey}, " f"remote_peer_id={remote_peer_id}, remote_pubkey={remote_pubkey}, "
f"remote_peer_id_from_pubkey={remote_peer_id_from_pubkey}" f"remote_peer_id_from_pubkey={remote_peer_id_from_pubkey}"
@ -76,10 +76,9 @@ class InsecureTransport(BaseSecureTransport):
""" """
session = InsecureSession(self, conn, peer_id) session = InsecureSession(self, conn, peer_id)
await session.run_handshake() await session.run_handshake()
# TODO: Check if `remote_public_key is not None`. If so, check if `session.remote_peer`
received_peer_id = session.get_remote_peer() received_peer_id = session.get_remote_peer()
if received_peer_id != peer_id: if session.remote_permanent_pubkey is not None and received_peer_id != peer_id:
raise SecurityUpgradeFailure( raise HandshakeFailure(
"remote peer sent unexpected peer ID. " "remote peer sent unexpected peer ID. "
f"expected={peer_id} received={received_peer_id}" f"expected={peer_id} received={received_peer_id}"
) )

View File

@ -4,11 +4,15 @@ from typing import Mapping
from libp2p.network.connection.raw_connection_interface import IRawConnection from libp2p.network.connection.raw_connection_interface import IRawConnection
from libp2p.peer.id import ID from libp2p.peer.id import ID
from libp2p.protocol_muxer.multiselect import Multiselect from libp2p.protocol_muxer.multiselect import Multiselect, MultiselectError
from libp2p.protocol_muxer.multiselect_client import MultiselectClient from libp2p.protocol_muxer.multiselect_client import (
MultiselectClient,
MultiselectClientError,
)
from libp2p.protocol_muxer.multiselect_communicator import RawConnectionCommunicator from libp2p.protocol_muxer.multiselect_communicator import RawConnectionCommunicator
from libp2p.security.secure_conn_interface import ISecureConn from libp2p.security.secure_conn_interface import ISecureConn
from libp2p.security.secure_transport_interface import ISecureTransport from libp2p.security.secure_transport_interface import ISecureTransport
from libp2p.transport.exceptions import HandshakeFailure, SecurityUpgradeFailure
from libp2p.typing import TProtocol from libp2p.typing import TProtocol
@ -63,8 +67,18 @@ class SecurityMultistream(ABC):
for an inbound connection (i.e. we are not the initiator) for an inbound connection (i.e. we are not the initiator)
:return: secure connection object (that implements secure_conn_interface) :return: secure connection object (that implements secure_conn_interface)
""" """
try:
transport = await self.select_transport(conn, False) transport = await self.select_transport(conn, False)
except MultiselectError as error:
raise SecurityUpgradeFailure(
"failed to negotiate the secure protocol"
) from error
try:
secure_conn = await transport.secure_inbound(conn) secure_conn = await transport.secure_inbound(conn)
except HandshakeFailure as error:
raise SecurityUpgradeFailure(
"failed to secure the inbound transport"
) from error
return secure_conn return secure_conn
async def secure_outbound(self, conn: IRawConnection, peer_id: ID) -> ISecureConn: async def secure_outbound(self, conn: IRawConnection, peer_id: ID) -> ISecureConn:
@ -73,8 +87,18 @@ class SecurityMultistream(ABC):
for an inbound connection (i.e. we are the initiator) for an inbound connection (i.e. we are the initiator)
:return: secure connection object (that implements secure_conn_interface) :return: secure connection object (that implements secure_conn_interface)
""" """
try:
transport = await self.select_transport(conn, True) transport = await self.select_transport(conn, True)
except MultiselectClientError as error:
raise SecurityUpgradeFailure(
"failed to negotiate the secure protocol"
) from error
try:
secure_conn = await transport.secure_outbound(conn, peer_id) secure_conn = await transport.secure_outbound(conn, peer_id)
except HandshakeFailure as error:
raise SecurityUpgradeFailure(
"failed to secure the outbound transport"
) from error
return secure_conn return secure_conn
async def select_transport( async def select_transport(

View File

@ -1,7 +1,14 @@
from libp2p.exceptions import BaseLibp2pError
# TODO: Add `BaseLibp2pError` and `UpgradeFailure` can inherit from it? # TODO: Add `BaseLibp2pError` and `UpgradeFailure` can inherit from it?
class UpgradeFailure(Exception): class UpgradeFailure(BaseLibp2pError):
pass pass
class SecurityUpgradeFailure(UpgradeFailure): class SecurityUpgradeFailure(UpgradeFailure):
pass pass
class HandshakeFailure(BaseLibp2pError):
pass

View File

@ -4,9 +4,9 @@ import pytest
from libp2p import new_node from libp2p import new_node
from libp2p.crypto.rsa import create_new_key_pair from libp2p.crypto.rsa import create_new_key_pair
from libp2p.protocol_muxer.multiselect_client import MultiselectClientError
from libp2p.security.insecure.transport import InsecureSession, InsecureTransport from libp2p.security.insecure.transport import InsecureSession, InsecureTransport
from libp2p.security.simple.transport import SimpleSecurityTransport from libp2p.security.simple.transport import SimpleSecurityTransport
from libp2p.transport.exceptions import SecurityUpgradeFailure
from tests.configs import LISTEN_MADDR from tests.configs import LISTEN_MADDR
from tests.utils import cleanup, connect from tests.utils import cleanup, connect
@ -161,7 +161,7 @@ async def test_multiple_security_none_the_same_fails():
def assertion_func(_): def assertion_func(_):
assert False assert False
with pytest.raises(MultiselectClientError): with pytest.raises(SecurityUpgradeFailure):
await perform_simple_test( await perform_simple_test(
assertion_func, transports_for_initiator, transports_for_noninitiator assertion_func, transports_for_initiator, transports_for_noninitiator
) )