mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2025-12-31 20:36:24 +00:00
* fix pyproject.toml , add ruff * rm lock * make progress * add poetry lock ignore * fix type issues * fix tcp type errors * fix text example - type error - wrong args * add setuptools to dev * test ci * fix docs build * fix type issues for new_swarm & new_host * fix types in gossipsub * fix type issues in noise * wip: factories * revert factories * fix more type issues * more type fixes * fix: add null checks for noise protocol initialization and key handling * corrected argument-errors in peerId and Multiaddr in peer tests * fix: Noice - remove redundant type casts in BaseNoiseMsgReadWriter * fix: update test_notify.py to use SwarmFactory.create_batch_and_listen, fix type hints, and comment out ClosedStream assertions * Fix type checks for pubsub module Signed-off-by: sukhman <sukhmansinghsaluja@gmail.com> * Fix type checks for pubsub module-tests Signed-off-by: sukhman <sukhmansinghsaluja@gmail.com> * noise: add checks for uninitialized protocol and key states in PatternXX Signed-off-by: varun-r-mallya <varunrmallya@gmail.com> * pubsub: add None checks for optional fields in FloodSub and Pubsub Signed-off-by: varun-r-mallya <varunrmallya@gmail.com> * Fix type hints and improve testing Signed-off-by: varun-r-mallya <varunrmallya@gmail.com> * remove redundant checks Signed-off-by: varun-r-mallya <varunrmallya@gmail.com> * fix build issues * add optional to trio service * fix types * fix type errors * Fix type errors Signed-off-by: varun-r-mallya <varunrmallya@gmail.com> * fixed more-type checks in crypto and peer_data files * wip: factories * replaced union with optional * fix: type-error in interp-utils and peerinfo * replace pyright with pyrefly * add pyrefly.toml * wip: fix multiselect issues * try typecheck * base check * mcache test fixes , typecheck ci update * fix ci * will this work * minor fix * use poetry * fix wokflow * use cache,fix err * fix pyrefly.toml * fix pyrefly.toml * fix cache in ci * deploy commit * add main baseline * update to v5 * improve typecheck ci (#14) * fix typo * remove holepunching code (#16) * fix gossipsub typeerrors (#17) * fix: ensure initiator user includes remote peer id in handshake (#15) * fix ci (#19) * typefix: custom_types | core/peerinfo/test_peer_info | io/abc | pubsub/floodsub | protocol_muxer/multiselect (#18) * fix: Typefixes in PeerInfo (#21) * fix minor type issue (#22) * fix type errors in pubsub (#24) * fix: Minor typefixes in tests (#23) * Fix failing tests for type-fixed test/pubsub (#8) * move pyrefly & ruff to pyproject.toml & rm .project-template (#28) * move the async_context file to tests/core * move crypto test to crypto folder * fix: some typefixes (#25) * fix type errors * fix type issues * fix: update gRPC API usage in autonat_pb2_grpc.py (#31) * md: typecheck ci * rm comments * clean up : from review suggestions * use | None over Optional as per new python standards * drop supporto for py3.9 * newsfragments --------- Signed-off-by: sukhman <sukhmansinghsaluja@gmail.com> Signed-off-by: varun-r-mallya <varunrmallya@gmail.com> Co-authored-by: acul71 <luca.pisani@birdo.net> Co-authored-by: kaneki003 <sakshamchauhan707@gmail.com> Co-authored-by: sukhman <sukhmansinghsaluja@gmail.com> Co-authored-by: varun-r-mallya <varunrmallya@gmail.com> Co-authored-by: varunrmallya <100590632+varun-r-mallya@users.noreply.github.com> Co-authored-by: lla-dane <abhinavagarwalla6@gmail.com> Co-authored-by: Collins <ArtemisfowlX@protonmail.com> Co-authored-by: Abhinav Agarwalla <120122716+lla-dane@users.noreply.github.com> Co-authored-by: guha-rahul <52607971+guha-rahul@users.noreply.github.com> Co-authored-by: Sukhman Singh <63765293+sukhman-sukh@users.noreply.github.com> Co-authored-by: acul71 <34693171+acul71@users.noreply.github.com> Co-authored-by: pacrob <5199899+pacrob@users.noreply.github.com>
215 lines
7.5 KiB
Python
215 lines
7.5 KiB
Python
from abc import (
|
|
ABC,
|
|
abstractmethod,
|
|
)
|
|
|
|
from cryptography.hazmat.primitives import (
|
|
serialization,
|
|
)
|
|
from noise.backends.default.keypairs import KeyPair as NoiseKeyPair
|
|
from noise.connection import (
|
|
Keypair as NoiseKeypairEnum,
|
|
NoiseConnection as NoiseState,
|
|
)
|
|
|
|
from libp2p.abc import (
|
|
IRawConnection,
|
|
ISecureConn,
|
|
)
|
|
from libp2p.crypto.ed25519 import (
|
|
Ed25519PublicKey,
|
|
)
|
|
from libp2p.crypto.keys import (
|
|
PrivateKey,
|
|
PublicKey,
|
|
)
|
|
from libp2p.peer.id import (
|
|
ID,
|
|
)
|
|
from libp2p.security.secure_session import (
|
|
SecureSession,
|
|
)
|
|
|
|
from .exceptions import (
|
|
HandshakeHasNotFinished,
|
|
InvalidSignature,
|
|
NoiseStateError,
|
|
PeerIDMismatchesPubkey,
|
|
)
|
|
from .io import (
|
|
NoiseHandshakeReadWriter,
|
|
NoiseTransportReadWriter,
|
|
)
|
|
from .messages import (
|
|
NoiseHandshakePayload,
|
|
make_handshake_payload_sig,
|
|
verify_handshake_payload_sig,
|
|
)
|
|
|
|
|
|
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
|
|
early_data: bytes | None
|
|
|
|
def create_noise_state(self) -> NoiseState:
|
|
noise_state = NoiseState.from_name(self.protocol_name)
|
|
noise_state.set_keypair_from_private_bytes(
|
|
NoiseKeypairEnum.STATIC, self.noise_static_key.to_bytes()
|
|
)
|
|
if noise_state.noise_protocol is None:
|
|
raise NoiseStateError("noise_protocol is not initialized")
|
|
return noise_state
|
|
|
|
def make_handshake_payload(self) -> NoiseHandshakePayload:
|
|
signature = make_handshake_payload_sig(
|
|
self.libp2p_privkey, self.noise_static_key.get_public_key()
|
|
)
|
|
return NoiseHandshakePayload(self.libp2p_privkey.get_public_key(), signature)
|
|
|
|
|
|
class PatternXX(BasePattern):
|
|
def __init__(
|
|
self,
|
|
local_peer: ID,
|
|
libp2p_privkey: PrivateKey,
|
|
noise_static_key: PrivateKey,
|
|
early_data: bytes | None = None,
|
|
) -> 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
|
|
self.early_data = early_data
|
|
|
|
async def handshake_inbound(self, conn: IRawConnection) -> ISecureConn:
|
|
noise_state = self.create_noise_state()
|
|
noise_state.set_as_responder()
|
|
noise_state.start_handshake()
|
|
if noise_state.noise_protocol is None:
|
|
raise NoiseStateError("noise_protocol is not initialized")
|
|
handshake_state = noise_state.noise_protocol.handshake_state
|
|
if handshake_state is None:
|
|
raise NoiseStateError("Handshake state is not initialized")
|
|
|
|
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,
|
|
)
|
|
|
|
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()
|
|
if noise_state.noise_protocol is None:
|
|
raise NoiseStateError("noise_protocol is not initialized")
|
|
handshake_state = noise_state.noise_protocol.handshake_state
|
|
if handshake_state is None:
|
|
raise NoiseStateError("Handshake state is not initialized")
|
|
|
|
# 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,
|
|
)
|
|
|
|
@staticmethod
|
|
def _get_pubkey_from_noise_keypair(key_pair: NoiseKeyPair) -> PublicKey:
|
|
# Use `Ed25519PublicKey` since 25519 is used in our pattern.
|
|
if key_pair.public is None:
|
|
raise NoiseStateError("public key is not initialized")
|
|
raw_bytes = key_pair.public.public_bytes(
|
|
serialization.Encoding.Raw, serialization.PublicFormat.Raw
|
|
)
|
|
return Ed25519PublicKey.from_bytes(raw_bytes)
|