diff options
author | stratospher <44024636+stratospher@users.noreply.github.com> | 2022-02-05 23:08:49 +0530 |
---|---|---|
committer | stratospher <44024636+stratospher@users.noreply.github.com> | 2024-01-25 11:09:52 +0530 |
commit | 05bddb20f5cc9036fd680500bde8ece70dbf0646 (patch) | |
tree | 78356190c1fd4e7a89dfb8eb6c8379c734e249ff /test/functional/test_framework | |
parent | a049d1bd08c8cdb3b693520f24f8a82572dcaab1 (diff) | |
download | bitcoin-05bddb20f5cc9036fd680500bde8ece70dbf0646.tar.xz |
[test] Perform initial v2 handshake
Diffstat (limited to 'test/functional/test_framework')
-rwxr-xr-x | test/functional/test_framework/p2p.py | 51 |
1 files changed, 50 insertions, 1 deletions
diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py index 004a9edc1c..308cefdbc2 100755 --- a/test/functional/test_framework/p2p.py +++ b/test/functional/test_framework/p2p.py @@ -232,13 +232,62 @@ class P2PConnection(asyncio.Protocol): self.recvbuf = b"" self.on_close() + # v2 handshake method + def v2_handshake(self): + """v2 handshake performed before P2P messages are exchanged (see BIP324). P2PConnection is the initiator + (in inbound connections to TestNode) and the responder (in outbound connections from TestNode). + Performed by: + * initiator using `initiate_v2_handshake()`, `complete_handshake()` and `authenticate_handshake()` + * responder using `respond_v2_handshake()`, `complete_handshake()` and `authenticate_handshake()` + + `initiate_v2_handshake()` is immediately done by the initiator when the connection is established in + `connection_made()`. The rest of the initial v2 handshake functions are handled here. + """ + if not self.v2_state.peer: + if not self.v2_state.initiating and not self.v2_state.sent_garbage: + # if the responder hasn't sent garbage yet, the responder is still reading ellswift bytes + # reads ellswift bytes till the first mismatch from 12 bytes V1_PREFIX + length, send_handshake_bytes = self.v2_state.respond_v2_handshake(BytesIO(self.recvbuf)) + self.recvbuf = self.recvbuf[length:] + if send_handshake_bytes == -1: + self.v2_state = None + return + elif send_handshake_bytes: + self.send_raw_message(send_handshake_bytes) + elif send_handshake_bytes == b"": + return # only after send_handshake_bytes are sent can `complete_handshake()` be done + + # `complete_handshake()` reads the remaining ellswift bytes from recvbuf + # and sends response after deriving shared ECDH secret using received ellswift bytes + length, response = self.v2_state.complete_handshake(BytesIO(self.recvbuf)) + self.recvbuf = self.recvbuf[length:] + if response: + self.send_raw_message(response) + else: + return # only after response is sent can `authenticate_handshake()` be done + + # `self.v2_state.peer` is instantiated only after shared ECDH secret/BIP324 derived keys and ciphers + # is derived in `complete_handshake()`. + # so `authenticate_handshake()` which uses the BIP324 derived ciphers gets called after `complete_handshake()`. + assert self.v2_state.peer + length, is_mac_auth = self.v2_state.authenticate_handshake(self.recvbuf) + if not is_mac_auth: + raise ValueError("invalid v2 mac tag in handshake authentication") + self.recvbuf = self.recvbuf[length:] + while self.v2_state.tried_v2_handshake and self.queue_messages: + message = self.queue_messages.pop(0) + self.send_message(message) + # Socket read methods def data_received(self, t): """asyncio callback when data is read from the socket.""" if len(t) > 0: self.recvbuf += t - self._on_data() + if self.supports_v2_p2p and not self.v2_state.tried_v2_handshake: + self.v2_handshake() + else: + self._on_data() def _on_data(self): """Try to read P2P messages from the recv buffer. |