From 897e66b7e1f3ba1c537b543935b61b73c2fb1b1f Mon Sep 17 00:00:00 2001 From: mhchia Date: Fri, 7 Feb 2020 17:47:50 +0800 Subject: [PATCH 01/10] Add the skeletons of noise transport and conn --- libp2p/security/insecure/transport.py | 3 +- libp2p/security/noise/__init__.py | 0 libp2p/security/noise/transport.py | 69 +++++++++++++++++++++++++++ libp2p/tools/factories.py | 57 ++++++++++++++++++++-- tests/security/test_noise.py | 15 ++++++ 5 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 libp2p/security/noise/__init__.py create mode 100644 libp2p/security/noise/transport.py create mode 100644 tests/security/test_noise.py diff --git a/libp2p/security/insecure/transport.py b/libp2p/security/insecure/transport.py index 103a580..f452e53 100644 --- a/libp2p/security/insecure/transport.py +++ b/libp2p/security/insecure/transport.py @@ -35,9 +35,8 @@ class InsecureSession(BaseSession): super().__init__(local_peer, local_private_key, is_initiator, peer_id) self.conn = conn - async def write(self, data: bytes) -> int: + async def write(self, data: bytes) -> None: await self.conn.write(data) - return len(data) async def read(self, n: int = None) -> bytes: return await self.conn.read(n) diff --git a/libp2p/security/noise/__init__.py b/libp2p/security/noise/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libp2p/security/noise/transport.py b/libp2p/security/noise/transport.py new file mode 100644 index 0000000..291fb81 --- /dev/null +++ b/libp2p/security/noise/transport.py @@ -0,0 +1,69 @@ +from libp2p.crypto.keys import KeyPair, PrivateKey +from libp2p.io.msgio import ReadWriteCloser +from libp2p.network.connection.raw_connection_interface import IRawConnection +from libp2p.peer.id import ID +from libp2p.security.base_session import BaseSession +from libp2p.security.secure_conn_interface import ISecureConn +from libp2p.security.secure_transport_interface import ISecureTransport +from libp2p.typing import TProtocol + +PROTOCOL_ID = TProtocol("/") + + +class NoiseConnection(BaseSession): + conn: ReadWriteCloser + + def __init__( + self, + local_peer: ID, + local_private_key: PrivateKey, + remote_peer: ID, + conn: ReadWriteCloser, + is_initiator: bool, + ) -> None: + super().__init__(local_peer, local_private_key, is_initiator, remote_peer) + self.conn = conn + + async def read(self, n: int = None) -> bytes: + return await self.conn.read(n) + + async def write(self, data: bytes) -> int: + return await self.conn.write(data) + + async def close(self) -> None: + await self.conn.close() + + +class Transport(ISecureTransport): + libp2p_privkey: PrivateKey + noise_privkey: PrivateKey + local_peer: ID + early_data: bytes + with_noise_pipes: bool + + def __init__( + self, + libp2p_keypair: KeyPair, + noise_privkey: PrivateKey = None, + early_data: bytes = None, + with_noise_pipes: bool = False, + ) -> None: + self.libp2p_privkey = libp2p_keypair.private_key + self.noise_privkey = noise_privkey + self.local_peer = ID.from_pubkey(libp2p_keypair.public_key) + self.early_data = early_data + self.with_noise_pipes = with_noise_pipes + + async def secure_inbound(self, conn: IRawConnection) -> ISecureConn: + # TODO: SecureInbound attempts to complete a noise-libp2p handshake initiated + # by a remote peer over the given InsecureConnection. + return NoiseConnection(self.local_peer, self.libp2p_privkey, None, conn, False) + + async def secure_outbound(self, conn: IRawConnection, peer_id: ID) -> ISecureConn: + # TODO: Validate libp2p pubkey with `peer_id`. Abort if not correct. + # NOTE: Implementations that support Noise Pipes must decide whether to use + # an XX or IK handshake based on whether they possess a cached static + # Noise key for the remote peer. + return NoiseConnection( + self.local_peer, self.libp2p_privkey, peer_id, conn, False + ) diff --git a/libp2p/tools/factories.py b/libp2p/tools/factories.py index 67e2651..40d644e 100644 --- a/libp2p/tools/factories.py +++ b/libp2p/tools/factories.py @@ -8,7 +8,9 @@ from multiaddr import Multiaddr import trio from libp2p import generate_new_rsa_identity, generate_peer_id_from -from libp2p.crypto.keys import KeyPair +from libp2p.crypto.ed25519 import create_new_key_pair as create_ed25519_key_pair +from libp2p.crypto.keys import KeyPair, PrivateKey +from libp2p.crypto.secp256k1 import create_new_key_pair as create_secp256k1_key_pair from libp2p.host.basic_host import BasicHost from libp2p.host.host_interface import IHost from libp2p.host.routed_host import RoutedHost @@ -26,9 +28,12 @@ from libp2p.pubsub.floodsub import FloodSub from libp2p.pubsub.gossipsub import GossipSub from libp2p.pubsub.pubsub import Pubsub from libp2p.routing.interfaces import IPeerRouting -from libp2p.security.base_transport import BaseSecureTransport from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, InsecureTransport +import libp2p.security.noise.transport as noise +from libp2p.security.noise.transport import Transport as NoiseTransport import libp2p.security.secio.transport as secio +from libp2p.security.secure_conn_interface import ISecureConn +from libp2p.security.secure_transport_interface import ISecureTransport from libp2p.stream_muxer.mplex.mplex import MPLEX_PROTOCOL_ID, Mplex from libp2p.stream_muxer.mplex.mplex_stream import MplexStream from libp2p.tools.constants import GOSSIPSUB_PARAMS @@ -58,13 +63,26 @@ def initialize_peerstore_with_our_keypair(self_id: ID, key_pair: KeyPair) -> Pee def security_transport_factory( is_secure: bool, key_pair: KeyPair -) -> Dict[TProtocol, BaseSecureTransport]: +) -> Dict[TProtocol, ISecureTransport]: if not is_secure: return {PLAINTEXT_PROTOCOL_ID: InsecureTransport(key_pair)} else: return {secio.ID: secio.Transport(key_pair)} +def noise_static_key_factory() -> PrivateKey: + return create_ed25519_key_pair().private_key + + +def noise_transport_factory() -> NoiseTransport: + return noise.Transport( + libp2p_keypair=create_secp256k1_key_pair(), + noise_privkey=noise_static_key_factory(), + early_data=None, + with_noise_pipes=False, + ) + + @asynccontextmanager async def raw_conn_factory( nursery: trio.Nursery @@ -88,6 +106,39 @@ async def raw_conn_factory( yield conn_0, conn_1 +@asynccontextmanager +async def noise_conn_factory( + nursery: trio.Nursery +) -> AsyncIterator[Tuple[ISecureConn, ISecureConn]]: + local_transport = noise_transport_factory() + remote_transport = noise_transport_factory() + + local_secure_conn: ISecureConn = None + remote_secure_conn: ISecureConn = None + + async def upgrade_local_conn() -> None: + nonlocal local_secure_conn + local_secure_conn = await local_transport.secure_outbound( + local_conn, local_transport.local_peer + ) + + async def upgrade_remote_conn() -> None: + nonlocal remote_secure_conn + remote_secure_conn = await remote_transport.secure_inbound(remote_conn) + + async with raw_conn_factory(nursery) as conns: + local_conn, remote_conn = conns + async with trio.open_nursery() as nursery: + nursery.start_soon(upgrade_local_conn) + nursery.start_soon(upgrade_remote_conn) + if local_secure_conn is None or remote_secure_conn is None: + raise Exception( + "local or remote secure conn has not been successfully upgraded" + f"local_secure_conn={local_secure_conn}, remote_secure_conn={remote_secure_conn}" + ) + yield local_secure_conn, remote_secure_conn + + class SwarmFactory(factory.Factory): class Meta: model = Swarm diff --git a/tests/security/test_noise.py b/tests/security/test_noise.py new file mode 100644 index 0000000..b912f98 --- /dev/null +++ b/tests/security/test_noise.py @@ -0,0 +1,15 @@ +import pytest + +from libp2p.tools.factories import noise_conn_factory + + +@pytest.mark.trio +async def test_noise_transport(nursery): + async with noise_conn_factory(nursery): + pass + + +@pytest.mark.trio +async def test_noise_connection(): + async with noise_conn_factory() as conns: + local_conn, remote_conn = conns From 23ece34157ec148897ccf6f01adb7b24f0f1cd8a Mon Sep 17 00:00:00 2001 From: mhchia Date: Fri, 7 Feb 2020 18:17:15 +0800 Subject: [PATCH 02/10] test_noise_connection: fix missing nursery --- tests/security/test_noise.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/security/test_noise.py b/tests/security/test_noise.py index b912f98..eb34e13 100644 --- a/tests/security/test_noise.py +++ b/tests/security/test_noise.py @@ -3,6 +3,9 @@ import pytest from libp2p.tools.factories import noise_conn_factory +DATA = b"testing_123" + + @pytest.mark.trio async def test_noise_transport(nursery): async with noise_conn_factory(nursery): @@ -10,6 +13,9 @@ async def test_noise_transport(nursery): @pytest.mark.trio -async def test_noise_connection(): - async with noise_conn_factory() as conns: +async def test_noise_connection(nursery): + async with noise_conn_factory(nursery) as conns: local_conn, remote_conn = conns + await local_conn.write(DATA) + read_data = await remote_conn.read(len(DATA)) + assert read_data == DATA From 1152f9b703c9d4da40875dfe7dbadd40ca95ab54 Mon Sep 17 00:00:00 2001 From: Kevin Mai-Husan Chia Date: Sat, 8 Feb 2020 10:14:35 +0800 Subject: [PATCH 03/10] Update libp2p/security/noise/transport.py Co-Authored-By: Alex Stokes --- libp2p/security/noise/transport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/security/noise/transport.py b/libp2p/security/noise/transport.py index 291fb81..a567eb2 100644 --- a/libp2p/security/noise/transport.py +++ b/libp2p/security/noise/transport.py @@ -7,7 +7,7 @@ from libp2p.security.secure_conn_interface import ISecureConn from libp2p.security.secure_transport_interface import ISecureTransport from libp2p.typing import TProtocol -PROTOCOL_ID = TProtocol("/") +PROTOCOL_ID = TProtocol("/noise") class NoiseConnection(BaseSession): From fb53edbc0466e87d6e08b6ccf7d33689e6b5d6ac Mon Sep 17 00:00:00 2001 From: mhchia Date: Fri, 7 Feb 2020 18:33:15 +0800 Subject: [PATCH 04/10] Change `async def write` To return `None` instead of `int. `Writer.write` *does* write all data in all use case. --- libp2p/io/abc.py | 2 +- libp2p/io/msgio.py | 3 +-- libp2p/network/stream/net_stream.py | 4 ++-- libp2p/security/noise/transport.py | 4 ++-- libp2p/security/secio/transport.py | 3 +-- libp2p/stream_muxer/mplex/mplex.py | 4 +--- libp2p/stream_muxer/mplex/mplex_stream.py | 4 ++-- 7 files changed, 10 insertions(+), 14 deletions(-) diff --git a/libp2p/io/abc.py b/libp2p/io/abc.py index 8f2c758..265663a 100644 --- a/libp2p/io/abc.py +++ b/libp2p/io/abc.py @@ -14,7 +14,7 @@ class Reader(ABC): class Writer(ABC): @abstractmethod - async def write(self, data: bytes) -> int: + async def write(self, data: bytes) -> None: ... diff --git a/libp2p/io/msgio.py b/libp2p/io/msgio.py index 32c0d09..82c55ac 100644 --- a/libp2p/io/msgio.py +++ b/libp2p/io/msgio.py @@ -31,9 +31,8 @@ class MsgIOWriter(WriteCloser): def __init__(self, write_closer: WriteCloser) -> None: self.write_closer = write_closer - async def write(self, data: bytes) -> int: + async def write(self, data: bytes) -> None: await self.write_msg(data) - return len(data) async def write_msg(self, msg: bytes) -> None: data = encode_msg_with_length(msg) diff --git a/libp2p/network/stream/net_stream.py b/libp2p/network/stream/net_stream.py index 72d5c6a..dab1920 100644 --- a/libp2p/network/stream/net_stream.py +++ b/libp2p/network/stream/net_stream.py @@ -51,14 +51,14 @@ class NetStream(INetStream): except MuxedStreamReset as error: raise StreamReset() from error - async def write(self, data: bytes) -> int: + async def write(self, data: bytes) -> None: """ write to stream. :return: number of bytes written """ try: - return await self.muxed_stream.write(data) + await self.muxed_stream.write(data) except MuxedStreamClosed as error: raise StreamClosed() from error diff --git a/libp2p/security/noise/transport.py b/libp2p/security/noise/transport.py index a567eb2..51b00d0 100644 --- a/libp2p/security/noise/transport.py +++ b/libp2p/security/noise/transport.py @@ -27,8 +27,8 @@ class NoiseConnection(BaseSession): async def read(self, n: int = None) -> bytes: return await self.conn.read(n) - async def write(self, data: bytes) -> int: - return await self.conn.write(data) + async def write(self, data: bytes) -> None: + await self.conn.write(data) async def close(self) -> None: await self.conn.close() diff --git a/libp2p/security/secio/transport.py b/libp2p/security/secio/transport.py index 9e98873..d8216b8 100644 --- a/libp2p/security/secio/transport.py +++ b/libp2p/security/secio/transport.py @@ -135,9 +135,8 @@ class SecureSession(BaseSession): raise DecryptionFailedException() from e return decrypted_msg - async def write(self, data: bytes) -> int: + async def write(self, data: bytes) -> None: await self.write_msg(data) - return len(data) async def write_msg(self, msg: bytes) -> None: encrypted_data = self.local_encrypter.encrypt(msg) diff --git a/libp2p/stream_muxer/mplex/mplex.py b/libp2p/stream_muxer/mplex/mplex.py index 4f62e15..defe0b8 100644 --- a/libp2p/stream_muxer/mplex/mplex.py +++ b/libp2p/stream_muxer/mplex/mplex.py @@ -160,7 +160,7 @@ class Mplex(IMuxedConn): return await self.write_to_stream(_bytes) - async def write_to_stream(self, _bytes: bytes) -> int: + async def write_to_stream(self, _bytes: bytes) -> None: """ writes a byte array to a secured connection. @@ -174,8 +174,6 @@ class Mplex(IMuxedConn): "failed to write message to the underlying connection" ) from e - return len(_bytes) - async def handle_incoming(self) -> None: """Read a message off of the secured connection and add it to the corresponding message buffer.""" diff --git a/libp2p/stream_muxer/mplex/mplex_stream.py b/libp2p/stream_muxer/mplex/mplex_stream.py index 7967574..9c724cc 100644 --- a/libp2p/stream_muxer/mplex/mplex_stream.py +++ b/libp2p/stream_muxer/mplex/mplex_stream.py @@ -134,7 +134,7 @@ class MplexStream(IMuxedStream): self._buf = self._buf[len(payload) :] return bytes(payload) - async def write(self, data: bytes) -> int: + async def write(self, data: bytes) -> None: """ write to stream. @@ -147,7 +147,7 @@ class MplexStream(IMuxedStream): if self.is_initiator else HeaderTags.MessageReceiver ) - return await self.muxed_conn.send_message(flag, data, self.stream_id) + await self.muxed_conn.send_message(flag, data, self.stream_id) async def close(self) -> None: """Closing a stream closes it for writing and closes the remote end for From f27db83a1408fcd388c9fca0825cf9169d515caa Mon Sep 17 00:00:00 2001 From: mhchia Date: Sat, 8 Feb 2020 10:48:29 +0800 Subject: [PATCH 05/10] Noise: add TODO comments --- libp2p/security/noise/transport.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libp2p/security/noise/transport.py b/libp2p/security/noise/transport.py index 51b00d0..906e880 100644 --- a/libp2p/security/noise/transport.py +++ b/libp2p/security/noise/transport.py @@ -25,9 +25,11 @@ class NoiseConnection(BaseSession): self.conn = conn async def read(self, n: int = None) -> bytes: + # TODO: Add decryption logic here return await self.conn.read(n) async def write(self, data: bytes) -> None: + # TODO: Add encryption logic here await self.conn.write(data) async def close(self) -> None: From 0324a69841e77fe6c01f6ef78f9e4db3dacfdb27 Mon Sep 17 00:00:00 2001 From: mhchia Date: Sun, 9 Feb 2020 00:33:26 +0800 Subject: [PATCH 06/10] Noise: add `PatternXX` --- libp2p/security/noise/connection.py | 30 +++++++ libp2p/security/noise/patterns.py | 116 +++++++++++++++++++++++++++ libp2p/security/noise/pb/__init__.py | 0 libp2p/security/noise/pb/noise.proto | 5 ++ libp2p/security/noise/transport.py | 48 ++++------- libp2p/tools/factories.py | 3 +- setup.py | 1 + tests/security/test_noise.py | 1 - 8 files changed, 169 insertions(+), 35 deletions(-) create mode 100644 libp2p/security/noise/connection.py create mode 100644 libp2p/security/noise/patterns.py create mode 100644 libp2p/security/noise/pb/__init__.py create mode 100644 libp2p/security/noise/pb/noise.proto diff --git a/libp2p/security/noise/connection.py b/libp2p/security/noise/connection.py new file mode 100644 index 0000000..940d807 --- /dev/null +++ b/libp2p/security/noise/connection.py @@ -0,0 +1,30 @@ +from libp2p.crypto.keys import PrivateKey +from libp2p.network.connection.raw_connection_interface import IRawConnection +from libp2p.peer.id import ID +from libp2p.security.base_session import BaseSession + + +class NoiseConnection(BaseSession): + conn: IRawConnection + + def __init__( + self, + local_peer: ID, + local_private_key: PrivateKey, + remote_peer: ID, + conn: IRawConnection, + is_initiator: bool, + ) -> None: + super().__init__(local_peer, local_private_key, is_initiator, remote_peer) + self.conn = conn + + async def read(self, n: int = None) -> bytes: + # TODO: Add decryption logic here + return await self.conn.read(n) + + async def write(self, data: bytes) -> None: + # TODO: Add encryption logic here + await self.conn.write(data) + + async def close(self) -> None: + await self.conn.close() diff --git a/libp2p/security/noise/patterns.py b/libp2p/security/noise/patterns.py new file mode 100644 index 0000000..1d812b2 --- /dev/null +++ b/libp2p/security/noise/patterns.py @@ -0,0 +1,116 @@ +from abc import ABC, abstractmethod + +from noise.connection import Keypair as NoiseKeypair +from noise.connection import NoiseConnection as NoiseState + +from libp2p.crypto.keys import PrivateKey +from libp2p.network.connection.raw_connection_interface import IRawConnection +from libp2p.peer.id import ID +from libp2p.security.secure_conn_interface import ISecureConn + +from .connection import NoiseConnection + +# FIXME: Choose a serious bound number. +NUM_BYTES_TO_READ = 2048 + + +# TODO: Merged into `BasePattern`? +class PreHandshakeConnection: + conn: IRawConnection + + def __init__(self, conn: IRawConnection) -> None: + self.conn = conn + + async def write_msg(self, data: bytes) -> None: + # TODO: + await self.conn.write(data) + + async def read_msg(self) -> bytes: + return await self.conn.read(NUM_BYTES_TO_READ) + + +class IPattern(ABC): + @abstractmethod + async def handshake_inbound(self, conn: IRawConnection) -> ISecureConn: + ... + + @abstractmethod + async def handshake_outbound( + self, conn: IRawConnection, remote_peer: ID + ) -> ISecureConn: + ... + + +class BasePattern(IPattern): + protocol_name: bytes + noise_static_key: PrivateKey + local_peer: ID + libp2p_privkey: PrivateKey + + def create_noise_state(self) -> NoiseState: + noise_state = NoiseState.from_name(self.protocol_name) + noise_state.set_keypair_from_private_bytes( + NoiseKeypair.STATIC, self.noise_static_key.to_bytes() + ) + return noise_state + + +class PatternXX(BasePattern): + def __init__( + self, local_peer: ID, libp2p_privkey: PrivateKey, noise_static_key: PrivateKey + ) -> None: + self.protocol_name = b"Noise_XX_25519_ChaChaPoly_SHA256" + self.local_peer = local_peer + self.libp2p_privkey = libp2p_privkey + self.noise_static_key = noise_static_key + + async def handshake_inbound(self, conn: IRawConnection) -> ISecureConn: + noise_state = self.create_noise_state() + handshake_conn = PreHandshakeConnection(conn) + noise_state.set_as_responder() + noise_state.start_handshake() + msg_0_encrypted = await handshake_conn.read_msg() + msg_0 = noise_state.read_message(msg_0_encrypted) + # TODO: Parse and save the payload from the other side. + + # TODO: Send our payload. + our_payload = b"server" + msg_1_encrypted = noise_state.write_message(our_payload) + await handshake_conn.write_msg(msg_1_encrypted) + + msg_2_encrypted = await handshake_conn.read_msg() + msg_2 = noise_state.read_message(msg_2_encrypted) + # TODO: Parse and save another payload from the other side. + + # TODO: Add a specific exception + if not noise_state.handshake_finished: + raise Exception + + # FIXME: `remote_peer` should be derived from the messages. + return NoiseConnection(self.local_peer, self.libp2p_privkey, None, conn, False) + + async def handshake_outbound( + self, conn: IRawConnection, remote_peer: ID + ) -> ISecureConn: + noise_state = self.create_noise_state() + handshake_conn = PreHandshakeConnection(conn) + noise_state.set_as_initiator() + noise_state.start_handshake() + msg_0 = noise_state.write_message() + await handshake_conn.write_msg(msg_0) + msg_1_encrypted = await handshake_conn.read_msg() + msg_1 = noise_state.read_message(msg_1_encrypted) + # TODO: Parse and save the payload from the other side. + + # TODO: Send our payload. + our_payload = b"client" + msg_2_encrypted = noise_state.write_message(our_payload) + await handshake_conn.write_msg(msg_2_encrypted) + + # TODO: Add a specific exception + if not noise_state.handshake_finished: + raise Exception + + return NoiseConnection( + self.local_peer, self.libp2p_privkey, remote_peer, conn, False + ) diff --git a/libp2p/security/noise/pb/__init__.py b/libp2p/security/noise/pb/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libp2p/security/noise/pb/noise.proto b/libp2p/security/noise/pb/noise.proto new file mode 100644 index 0000000..54cef92 --- /dev/null +++ b/libp2p/security/noise/pb/noise.proto @@ -0,0 +1,5 @@ +message NoiseHandshakePayload { + optional bytes identity_key = 1; + optional bytes identity_sig = 2; + optional bytes data = 3; +} diff --git a/libp2p/security/noise/transport.py b/libp2p/security/noise/transport.py index 906e880..37e15e8 100644 --- a/libp2p/security/noise/transport.py +++ b/libp2p/security/noise/transport.py @@ -1,47 +1,22 @@ from libp2p.crypto.keys import KeyPair, PrivateKey -from libp2p.io.msgio import ReadWriteCloser from libp2p.network.connection.raw_connection_interface import IRawConnection from libp2p.peer.id import ID -from libp2p.security.base_session import BaseSession from libp2p.security.secure_conn_interface import ISecureConn from libp2p.security.secure_transport_interface import ISecureTransport from libp2p.typing import TProtocol +from .patterns import IPattern, PatternXX + PROTOCOL_ID = TProtocol("/noise") -class NoiseConnection(BaseSession): - conn: ReadWriteCloser - - def __init__( - self, - local_peer: ID, - local_private_key: PrivateKey, - remote_peer: ID, - conn: ReadWriteCloser, - is_initiator: bool, - ) -> None: - super().__init__(local_peer, local_private_key, is_initiator, remote_peer) - self.conn = conn - - async def read(self, n: int = None) -> bytes: - # TODO: Add decryption logic here - return await self.conn.read(n) - - async def write(self, data: bytes) -> None: - # TODO: Add encryption logic here - await self.conn.write(data) - - async def close(self) -> None: - await self.conn.close() - - class Transport(ISecureTransport): libp2p_privkey: PrivateKey noise_privkey: PrivateKey local_peer: ID early_data: bytes with_noise_pipes: bool + # TODO: A storage of seen noise static keys for pattern IK? def __init__( self, @@ -56,16 +31,25 @@ class Transport(ISecureTransport): self.early_data = early_data self.with_noise_pipes = with_noise_pipes + if self.with_noise_pipes: + raise NotImplementedError + + def get_pattern(self) -> IPattern: + if self.with_noise_pipes: + raise NotImplementedError + else: + return PatternXX(self.local_peer, self.libp2p_privkey, self.noise_privkey) + async def secure_inbound(self, conn: IRawConnection) -> ISecureConn: # TODO: SecureInbound attempts to complete a noise-libp2p handshake initiated # by a remote peer over the given InsecureConnection. - return NoiseConnection(self.local_peer, self.libp2p_privkey, None, conn, False) + pattern = self.get_pattern() + return await pattern.handshake_inbound(conn) async def secure_outbound(self, conn: IRawConnection, peer_id: ID) -> ISecureConn: # TODO: Validate libp2p pubkey with `peer_id`. Abort if not correct. # NOTE: Implementations that support Noise Pipes must decide whether to use # an XX or IK handshake based on whether they possess a cached static # Noise key for the remote peer. - return NoiseConnection( - self.local_peer, self.libp2p_privkey, peer_id, conn, False - ) + pattern = self.get_pattern() + return await pattern.handshake_outbound(conn, peer_id) diff --git a/libp2p/tools/factories.py b/libp2p/tools/factories.py index 40d644e..75a37a3 100644 --- a/libp2p/tools/factories.py +++ b/libp2p/tools/factories.py @@ -29,7 +29,6 @@ from libp2p.pubsub.gossipsub import GossipSub from libp2p.pubsub.pubsub import Pubsub from libp2p.routing.interfaces import IPeerRouting from libp2p.security.insecure.transport import PLAINTEXT_PROTOCOL_ID, InsecureTransport -import libp2p.security.noise.transport as noise from libp2p.security.noise.transport import Transport as NoiseTransport import libp2p.security.secio.transport as secio from libp2p.security.secure_conn_interface import ISecureConn @@ -75,7 +74,7 @@ def noise_static_key_factory() -> PrivateKey: def noise_transport_factory() -> NoiseTransport: - return noise.Transport( + return NoiseTransport( libp2p_keypair=create_secp256k1_key_pair(), noise_privkey=noise_static_key_factory(), early_data=None, diff --git a/setup.py b/setup.py index e808b95..5e5553c 100644 --- a/setup.py +++ b/setup.py @@ -78,6 +78,7 @@ install_requires = [ "async-service>=0.1.0a6", "async-exit-stack==1.0.1", "trio-typing>=0.3.0,<0.4.0", + "noiseprotocol>=0.3.0,<0.4.0", ] diff --git a/tests/security/test_noise.py b/tests/security/test_noise.py index eb34e13..c60f83c 100644 --- a/tests/security/test_noise.py +++ b/tests/security/test_noise.py @@ -2,7 +2,6 @@ import pytest from libp2p.tools.factories import noise_conn_factory - DATA = b"testing_123" From d7fabab3e1c6228a359eda9560b4a0e165f3efe0 Mon Sep 17 00:00:00 2001 From: mhchia Date: Sun, 9 Feb 2020 13:23:12 +0800 Subject: [PATCH 07/10] Noise: add compiled pb2.py --- Makefile | 1 + libp2p/security/noise/pb/noise_pb2.py | 83 ++++++++++++++++++++++++++ libp2p/security/noise/pb/noise_pb2.pyi | 41 +++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 libp2p/security/noise/pb/noise_pb2.py create mode 100644 libp2p/security/noise/pb/noise_pb2.pyi diff --git a/Makefile b/Makefile index 5620e59..203391f 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ PB = libp2p/crypto/pb/crypto.proto \ libp2p/pubsub/pb/rpc.proto \ libp2p/security/insecure/pb/plaintext.proto \ libp2p/security/secio/pb/spipe.proto \ + libp2p/security/noise/pb/noise.proto \ libp2p/identity/identify/pb/identify.proto PY = $(PB:.proto=_pb2.py) PYI = $(PB:.proto=_pb2.pyi) diff --git a/libp2p/security/noise/pb/noise_pb2.py b/libp2p/security/noise/pb/noise_pb2.py new file mode 100644 index 0000000..d7b92ae --- /dev/null +++ b/libp2p/security/noise/pb/noise_pb2.py @@ -0,0 +1,83 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: libp2p/security/noise/pb/noise.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='libp2p/security/noise/pb/noise.proto', + package='', + syntax='proto2', + serialized_pb=_b('\n$libp2p/security/noise/pb/noise.proto\"Q\n\x15NoiseHandshakePayload\x12\x14\n\x0cidentity_key\x18\x01 \x01(\x0c\x12\x14\n\x0cidentity_sig\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c') +) + + + + +_NOISEHANDSHAKEPAYLOAD = _descriptor.Descriptor( + name='NoiseHandshakePayload', + full_name='NoiseHandshakePayload', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='identity_key', full_name='NoiseHandshakePayload.identity_key', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='identity_sig', full_name='NoiseHandshakePayload.identity_sig', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='data', full_name='NoiseHandshakePayload.data', index=2, + number=3, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=40, + serialized_end=121, +) + +DESCRIPTOR.message_types_by_name['NoiseHandshakePayload'] = _NOISEHANDSHAKEPAYLOAD +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +NoiseHandshakePayload = _reflection.GeneratedProtocolMessageType('NoiseHandshakePayload', (_message.Message,), dict( + DESCRIPTOR = _NOISEHANDSHAKEPAYLOAD, + __module__ = 'libp2p.security.noise.pb.noise_pb2' + # @@protoc_insertion_point(class_scope:NoiseHandshakePayload) + )) +_sym_db.RegisterMessage(NoiseHandshakePayload) + + +# @@protoc_insertion_point(module_scope) diff --git a/libp2p/security/noise/pb/noise_pb2.pyi b/libp2p/security/noise/pb/noise_pb2.pyi new file mode 100644 index 0000000..eea8fda --- /dev/null +++ b/libp2p/security/noise/pb/noise_pb2.pyi @@ -0,0 +1,41 @@ +# @generated by generate_proto_mypy_stubs.py. Do not edit! +import sys +from google.protobuf.descriptor import ( + Descriptor as google___protobuf___descriptor___Descriptor, +) + +from google.protobuf.message import ( + Message as google___protobuf___message___Message, +) + +from typing import ( + Optional as typing___Optional, +) + +from typing_extensions import ( + Literal as typing_extensions___Literal, +) + + +class NoiseHandshakePayload(google___protobuf___message___Message): + DESCRIPTOR: google___protobuf___descriptor___Descriptor = ... + identity_key = ... # type: bytes + identity_sig = ... # type: bytes + data = ... # type: bytes + + def __init__(self, + *, + identity_key : typing___Optional[bytes] = None, + identity_sig : typing___Optional[bytes] = None, + data : typing___Optional[bytes] = None, + ) -> None: ... + @classmethod + def FromString(cls, s: bytes) -> NoiseHandshakePayload: ... + def MergeFrom(self, other_msg: google___protobuf___message___Message) -> None: ... + def CopyFrom(self, other_msg: google___protobuf___message___Message) -> None: ... + if sys.version_info >= (3,): + def HasField(self, field_name: typing_extensions___Literal[u"data",u"identity_key",u"identity_sig"]) -> bool: ... + def ClearField(self, field_name: typing_extensions___Literal[u"data",u"identity_key",u"identity_sig"]) -> None: ... + else: + def HasField(self, field_name: typing_extensions___Literal[u"data",b"data",u"identity_key",b"identity_key",u"identity_sig",b"identity_sig"]) -> bool: ... + def ClearField(self, field_name: typing_extensions___Literal[u"data",b"data",u"identity_key",b"identity_key",u"identity_sig",b"identity_sig"]) -> None: ... From 95959725db2fd6d53753d0505ae3424bafb4bb0d Mon Sep 17 00:00:00 2001 From: mhchia Date: Sat, 15 Feb 2020 12:18:19 +0800 Subject: [PATCH 08/10] Noise pattern: Fix flake8 --- libp2p/security/noise/patterns.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libp2p/security/noise/patterns.py b/libp2p/security/noise/patterns.py index 1d812b2..dee9a61 100644 --- a/libp2p/security/noise/patterns.py +++ b/libp2p/security/noise/patterns.py @@ -70,8 +70,8 @@ class PatternXX(BasePattern): noise_state.set_as_responder() noise_state.start_handshake() msg_0_encrypted = await handshake_conn.read_msg() - msg_0 = noise_state.read_message(msg_0_encrypted) # TODO: Parse and save the payload from the other side. + _ = noise_state.read_message(msg_0_encrypted) # TODO: Send our payload. our_payload = b"server" @@ -79,8 +79,8 @@ class PatternXX(BasePattern): await handshake_conn.write_msg(msg_1_encrypted) msg_2_encrypted = await handshake_conn.read_msg() - msg_2 = noise_state.read_message(msg_2_encrypted) # TODO: Parse and save another payload from the other side. + _ = noise_state.read_message(msg_2_encrypted) # TODO: Add a specific exception if not noise_state.handshake_finished: @@ -99,8 +99,8 @@ class PatternXX(BasePattern): msg_0 = noise_state.write_message() await handshake_conn.write_msg(msg_0) msg_1_encrypted = await handshake_conn.read_msg() - msg_1 = noise_state.read_message(msg_1_encrypted) # TODO: Parse and save the payload from the other side. + _ = noise_state.read_message(msg_1_encrypted) # TODO: Send our payload. our_payload = b"client" From f4c545ed68cd221be2980d4749c18b9ebf956569 Mon Sep 17 00:00:00 2001 From: mhchia Date: Sat, 15 Feb 2020 12:18:44 +0800 Subject: [PATCH 09/10] isort: add `noise` to 3rd party config --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a03627e..b1e5cdc 100644 --- a/tox.ini +++ b/tox.ini @@ -12,7 +12,7 @@ envlist = combine_as_imports=False force_sort_within_sections=True include_trailing_comma=True -known_third_party=anyio,factory,lru,p2pclient,pytest +known_third_party=anyio,factory,lru,p2pclient,pytest,noise known_first_party=libp2p line_length=88 multi_line_output=3 From 4620544d4578eeb300f0431bf6eb94f62b946a05 Mon Sep 17 00:00:00 2001 From: mhchia Date: Sat, 15 Feb 2020 12:35:36 +0800 Subject: [PATCH 10/10] Noise: fix docs --- docs/libp2p.security.noise.pb.rst | 22 +++++++++++++++ docs/libp2p.security.noise.rst | 45 +++++++++++++++++++++++++++++ docs/libp2p.security.rst | 47 ++++++++++++++++--------------- 3 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 docs/libp2p.security.noise.pb.rst create mode 100644 docs/libp2p.security.noise.rst diff --git a/docs/libp2p.security.noise.pb.rst b/docs/libp2p.security.noise.pb.rst new file mode 100644 index 0000000..1088136 --- /dev/null +++ b/docs/libp2p.security.noise.pb.rst @@ -0,0 +1,22 @@ +libp2p.security.noise.pb package +================================ + +Submodules +---------- + +libp2p.security.noise.pb.noise\_pb2 module +------------------------------------------ + +.. automodule:: libp2p.security.noise.pb.noise_pb2 + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: libp2p.security.noise.pb + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/libp2p.security.noise.rst b/docs/libp2p.security.noise.rst new file mode 100644 index 0000000..a6aaa49 --- /dev/null +++ b/docs/libp2p.security.noise.rst @@ -0,0 +1,45 @@ +libp2p.security.noise package +============================= + +Subpackages +----------- + +.. toctree:: + + libp2p.security.noise.pb + +Submodules +---------- + +libp2p.security.noise.connection module +--------------------------------------- + +.. automodule:: libp2p.security.noise.connection + :members: + :undoc-members: + :show-inheritance: + +libp2p.security.noise.patterns module +------------------------------------- + +.. automodule:: libp2p.security.noise.patterns + :members: + :undoc-members: + :show-inheritance: + +libp2p.security.noise.transport module +-------------------------------------- + +.. automodule:: libp2p.security.noise.transport + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: libp2p.security.noise + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/libp2p.security.rst b/docs/libp2p.security.rst index de60977..32dd329 100644 --- a/docs/libp2p.security.rst +++ b/docs/libp2p.security.rst @@ -6,8 +6,9 @@ Subpackages .. toctree:: - libp2p.security.insecure - libp2p.security.secio + libp2p.security.insecure + libp2p.security.noise + libp2p.security.secio Submodules ---------- @@ -16,55 +17,55 @@ libp2p.security.base\_session module ------------------------------------ .. automodule:: libp2p.security.base_session - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: libp2p.security.base\_transport module -------------------------------------- .. automodule:: libp2p.security.base_transport - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: libp2p.security.exceptions module --------------------------------- .. automodule:: libp2p.security.exceptions - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: libp2p.security.secure\_conn\_interface module ---------------------------------------------- .. automodule:: libp2p.security.secure_conn_interface - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: libp2p.security.secure\_transport\_interface module --------------------------------------------------- .. automodule:: libp2p.security.secure_transport_interface - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: libp2p.security.security\_multistream module -------------------------------------------- .. automodule:: libp2p.security.security_multistream - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: libp2p.security - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: