async def _handshake( initiator: "HandshakeInitiator", reader: asyncio.StreamReader, writer: asyncio.StreamWriter, token: CancelToken, ) -> Tuple[bytes, bytes, keccak_256, keccak_256]: """See the handshake() function above. This code was factored out into this helper so that we can create Peers with directly connected readers/writers for our tests. """ initiator_nonce = keccak(os.urandom(HASH_LEN)) auth_msg = initiator.create_auth_message(initiator_nonce) auth_init = initiator.encrypt_auth_message(auth_msg) writer.write(auth_init) auth_ack = await token.cancellable_wait( reader.read(ENCRYPTED_AUTH_ACK_LEN), timeout=REPLY_TIMEOUT) if reader.at_eof(): # This is what happens when Parity nodes have blacklisted us # (https://github.com/ethereum/py-evm/issues/901). raise HandshakeFailure("%s disconnected before sending auth ack", repr(initiator.remote)) ephemeral_pubkey, responder_nonce = initiator.decode_auth_ack_message( auth_ack) aes_secret, mac_secret, egress_mac, ingress_mac = initiator.derive_secrets( initiator_nonce, responder_nonce, ephemeral_pubkey, auth_init, auth_ack) return aes_secret, mac_secret, egress_mac, ingress_mac
async def handshake_for_version(remote: Node, factory): """Perform the auth and P2P handshakes (without sub-protocol handshake) with the given remote. Disconnect after initial hello message exchange, and return version id """ try: ( aes_secret, mac_secret, egress_mac, ingress_mac, reader, writer, ) = await auth.handshake(remote, factory.privkey, factory.cancel_token) except (ConnectionRefusedError, OSError) as e: raise UnreachablePeer() from e connection = PeerConnection( reader=reader, writer=writer, aes_secret=aes_secret, mac_secret=mac_secret, egress_mac=egress_mac, ingress_mac=ingress_mac, ) peer = factory.create_peer(remote=remote, connection=connection, inbound=False) # see await peer.do_p2p_handshake() peer.base_protocol.send_handshake() try: cmd, msg = await peer.read_msg(timeout=peer.conn_idle_timeout) except rlp.DecodingError: raise HandshakeFailure("Got invalid rlp data during handshake") except MalformedMessage as e: raise HandshakeFailure("Got malformed message during handshake") from e if isinstance(cmd, Disconnect): msg = cast(Dict[str, Any], msg) raise HandshakeDisconnectedFailure( "disconnected before completing sub-proto handshake: {}".format( msg["reason_name"] ) ) msg = cast(Dict[str, Any], msg) if not isinstance(cmd, Hello): await peer.disconnect(DisconnectReason.bad_protocol) raise HandshakeFailure( "Expected a Hello msg, got {}, disconnecting".format(cmd) ) return msg["client_version_string"]
async def do_sub_proto_handshake(self) -> None: """ overrides BasePeer.do_sub_proto_handshake() """ self.secure_peer = SecurePeer(self) Logger.info("starting peer hello exchange") start_state = await self.secure_peer.start() if start_state: # returns None if successful raise HandshakeFailure( "hello message exchange failed: {}".format(start_state))
async def process_p2p_handshake(self, cmd: protocol.Command, msg: protocol.PayloadType) -> None: msg = cast(Dict[str, Any], msg) if not isinstance(cmd, Hello): await self.disconnect(DisconnectReason.bad_protocol) raise HandshakeFailure( f"Expected a Hello msg, got {cmd}, disconnecting") remote_capabilities = msg["capabilities"] try: self.sub_proto = self.select_sub_protocol(remote_capabilities) except NoMatchingPeerCapabilities: await self.disconnect(DisconnectReason.useless_peer) raise HandshakeFailure( f"No matching capabilities between us ({self.capabilities}) and {self.remote} " f"({remote_capabilities}), disconnecting") self.logger.debug( "Finished P2P handshake with %s, using sub-protocol %s", self.remote, self.sub_proto, )
async def do_p2p_handshake(self) -> None: """Perform the handshake for the P2P base protocol. Raises HandshakeFailure if the handshake is not successful. """ self.base_protocol.send_handshake() try: cmd, msg = await self.read_msg() except rlp.DecodingError: raise HandshakeFailure("Got invalid rlp data during handshake") except MalformedMessage as e: raise HandshakeFailure( "Got malformed message during handshake") from e if isinstance(cmd, Disconnect): msg = cast(Dict[str, Any], msg) # Peers sometimes send a disconnect msg before they send the initial P2P handshake. raise HandshakeFailure( f"{self} disconnected before completing sub-proto handshake: {msg['reason_name']}" ) await self.process_p2p_handshake(cmd, msg)
async def do_sub_proto_handshake(self) -> None: """Perform the handshake for the sub-protocol agreed with the remote peer. Raises HandshakeFailure if the handshake is not successful. """ await self.send_sub_proto_handshake() cmd, msg = await self.read_msg() if isinstance(cmd, Ping): # Parity sends a Ping before the sub-proto handshake, so respond to that and read the # next one, which hopefully will be the actual handshake. self.base_protocol.send_pong() cmd, msg = await self.read_msg() if isinstance(cmd, Disconnect): msg = cast(Dict[str, Any], msg) # Peers sometimes send a disconnect msg before they send the sub-proto handshake. raise HandshakeFailure( f"{self} disconnected before completing sub-proto handshake: {msg['reason_name']}" ) await self.process_sub_proto_handshake(cmd, msg) self.logger.debug("Finished %s handshake with %s", self.sub_proto, self.remote)