Skip to content

Conversation

stratospher
Copy link
Contributor

@stratospher stratospher commented Apr 3, 2022

This PR introduces support for v2 P2P encryption(BIP 324) in the existing functional test framework and adds functional tests for the same.

commits overview

  1. introduces a new class EncryptedP2PState to store the keys, functions for performing the initial v2 handshake and encryption/decryption.

  2. this class is used by P2PConnection in inbound/outbound connections to perform the initial v2 handshake before the v1 version handshake. Only after the initial v2 handshake is performed do application layer P2P messages(version, verack etc..) get exchanged. (in a v2 connection)

    • v2_state is the object of class EncryptedP2PState in P2PConnection used to store its keys, session-id etc.
    • a node advertising support for v2 P2P is different from a node actually supporting v2 P2P (differ when false advertisement of services occur)
      • introduce a boolean variable supports_v2_p2p in P2PConnection to denote if it supports v2 P2P.
      • introduce a boolean variable advertises_v2_p2p to denote whether P2PConnection which mimics peer behaviour advertises V2 P2P support. Default option is False.
    • In the test framework, you can create Inbound and Outbound connections to TestNode
      1. During Inbound Connections, P2PConnection is the initiator [TestNode <--------- P2PConnection]
        • Case 1:
          • if the TestNode advertises/signals v2 P2P support (means self.nodes[i] set up with "-v2transport=1"), different behaviour will be exhibited based on whether:
            1. P2PConnection supports v2 P2P
            2. P2PConnection does not support v2 P2P
          • In a real world scenario, the initiator node would intrinsically know if they support v2 P2P based on whatever code they choose to run. However, in the test scenario where we mimic peer behaviour, we have no way of knowing if P2PConnection should support v2 P2P or not. So supports_v2_p2p boolean variable is used as an option to enable support for v2 P2P in P2PConnection.
          • Since the TestNode advertises v2 P2P support (using "-v2transport=1"), our initiator P2PConnection would send:
            1. (if the P2PConnection supports v2 P2P) ellswift + garbage bytes to initiate the connection
            2. (if the P2PConnection does not support v2 P2P) version message to initiate the connection
        • Case 2:
          • if the TestNode doesn't signal v2 P2P support; P2PConnection being the initiator would send version message to initiate a connection.
      2. During Outbound Connections [TestNode --------> P2PConnection]
        • initiator TestNode would send:
          • (if the P2PConnection advertises v2 P2P) ellswift + garbage bytes to initiate the connection
          • (if the P2PConnection advertises v2 P2P) version message to initiate the connection
        • Suppose P2PConnection advertises v2 P2P support when it actually doesn't support v2 P2P (false advertisement scenario)
          • TestNode sends ellswift + garbage bytes
          • P2PConnection receives but can't process it and disconnects.
          • TestNode then tries using v1 P2P and sends version message
          • P2PConnection receives/processes this successfully and they communicate on v1 P2P
  3. the encrypted P2P messages follow a different format - 3 byte length + 1-13 byte message_type + payload + 16 byte MAC

  4. includes support for testing decoy messages and v2 connection downgrade(using false advertisement - when a v2 node makes an outbound connection to a node which doesn't support v2 but is advertised as v2 by some malicious
    intermediary)

run the tests

  • functional test - test/functional/p2p_v2_encrypted.py test/functional/p2p_v2_earlykeyresponse.py

I'm also super grateful to @ dhruv for his really valuable feedback on this branch.
Also written a more elaborate explanation here - https://github.com/stratospher/blogosphere/blob/main/integration_test_bip324.md

@DrahtBot
Copy link
Contributor

DrahtBot commented Apr 5, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage

For detailed information about the code coverage, see the test coverage report.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK mzumsande, theStack, naumenkogs, glozow
Concept ACK jonatack, brunoerg
Approach ACK sr-gi

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #29279 (test: p2p: check disconnect due to lack of desirable service flags by theStack)
  • #29016 (RPC: add new listmempooltransactions by niftynei)
  • #28463 (p2p: Increase inbound capacity for block-relay only connections by mzumsande)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@jonatack
Copy link
Member

jonatack commented Apr 7, 2022

Concept ACK

@brunoerg
Copy link
Contributor

brunoerg commented Apr 8, 2022

Concept ACK

@stratospher stratospher marked this pull request as draft October 3, 2022 17:17
@stratospher
Copy link
Contributor Author

converting this PR into a draft. I'll push the updated version which includes the new spec changes in BIP 324 soon.

@naumenkogs
Copy link
Member

ACK bc9283c

@DrahtBot DrahtBot removed the request for review from naumenkogs January 29, 2024 09:49
Copy link
Member

@glozow glozow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK bc9283c

Did some light code review of the P2PConnection functions, mutation testing of EncryptedP2PState, and manually changing other functional tests to use v2 connections.

@achow101 achow101 merged commit 411ba32 into bitcoin:master Jan 29, 2024
@maflcko
Copy link
Member

maflcko commented Jan 30, 2024

The test fails for me:

146/294 - p2p_v2_earlykeyresponse.py failed, Duration: 1 s

stdout:
2024-01-30T13:33:10.051000Z TestFramework (INFO): PRNG seed is: 5518845862592986813
2024-01-30T13:33:10.052000Z TestFramework (INFO): Initializing test directory /tmp/test_runner_₿_🏃_20240130_142948/p2p_v2_earlykeyresponse_133
2024-01-30T13:33:10.370000Z TestFramework (INFO): Sending ellswift bytes in parts to ensure that response from responder is received only when
2024-01-30T13:33:10.370000Z TestFramework (INFO): ellswift bytes have a mismatch from the 16 bytes(network magic followed by "version\x00\x00\x00\x00\x00")
2024-01-30T13:33:10.371000Z TestFramework (INFO): Sending first 4 bytes of ellswift which match network magic
2024-01-30T13:33:10.371000Z TestFramework (INFO): If a response is received, assertion failure would happen in our custom data_received() function
2024-01-30T13:33:10.429000Z TestFramework (INFO): Sending remaining ellswift and garbage which are different from V1_PREFIX. Since a response is
2024-01-30T13:33:10.429000Z TestFramework (INFO): expected now, our custom data_received() function wouldn't result in assertion failure
2024-01-30T13:33:10.480000Z TestFramework.p2p (WARNING): Connection lost to 127.0.0.1:12596 due to 
2024-01-30T13:33:10.518000Z TestFramework (INFO): successful disconnection when MITM happens in the key exchange phase
2024-01-30T13:33:10.519000Z TestFramework (INFO): Stopping nodes
2024-01-30T13:33:10.625000Z TestFramework (INFO): Cleaning up /tmp/test_runner_₿_🏃_20240130_142948/p2p_v2_earlykeyresponse_133 on exit
2024-01-30T13:33:10.625000Z TestFramework (INFO): Tests successful


stderr:
Fatal error: protocol.data_received() call failed.
protocol: <__main__.PeerEarlyKey object at 0x7fe7925dccb0>
transport: <_SelectorSocketTransport fd=10 read=polling write=<idle, bufsize=0>>
Traceback (most recent call last):
  File "python3.12/asyncio/selector_events.py", line 1023, in _read_ready__data_received
    self._protocol.data_received(data)
  File "test/functional/p2p_v2_earlykeyresponse.py", line 60, in data_received
    assert self.v2_state.can_data_be_received and not self.v2_state.send_net_magic
AssertionError


@stratospher
Copy link
Contributor Author

hmm weird, looking into it.

i also have a WIP branch which uses this test file and adds more v2 tests for sending excess garbage bytes, wrong garbage terminator, incorrect ellswift and non-empty version packet.

@sr-gi
Copy link
Member

sr-gi commented Jan 30, 2024

WIP branch

Is this consistently failing for you (or can you at least reproduce it)? I can see it also failing on some of the current open PRs but I can not get my head around why (nor can I reproduce it).

This should only happen if PeerEarlyKey receives any data from the node before sending anything at all, or after sending the network magic but before sending the remaining initial bytes + garbage.

To my understanding, the node should not have been able to parse enough data to be replying (with either v1 or v2) 😕

In case you can reproduce it, it may be useful to log what data_received is receiving

@mzumsande
Copy link
Contributor

mzumsande commented Jan 30, 2024

The test fails for me:

see #29352 for a fix (and a way to reproduce it).

achow101 added a commit that referenced this pull request Jan 31, 2024
9642aef test: fix intermittent failure in p2p_v2_earlykeyresponse (Martin Zumsande)

Pull request description:

  The test fails intermittently, see https://cirrus-ci.com/task/6403578080788480?logs=ci#L3521 and #24748 (comment).
  I think it's because of a race between the python NetworkThread and the actual
  test, which will both call `initiate_v2_handshake`. I could reproduce it by adding a sleep into `initiate_v2_handshake` after the line `self.sent_garbage = random.randbytes(garbage_len)`.

  Fix this by waiting for the first `initiate_v2_handshake` to have finished before calling it a second time.

ACKs for top commit:
  stratospher:
    tested ACK 9642aef.
  achow101:
    ACK 9642aef
  theStack:
    Tested ACK 9642aef

Tree-SHA512: f728bbceaf816ddefeee4957494ccb608ad4fc912cb5cbf5f2acf09836df969c4e8fa2bb441aadb94fa39b3ffbb005d4132e7b6a5a98d80811810d8bd1d624e3
glozow added a commit that referenced this pull request Feb 6, 2024
…nd few cleanups

e7fd70f [test] make v2transport arg in addconnection mandatory and few cleanups (stratospher)

Pull request description:

  - make `v2transport` argument in `addconnection` regression-testing only RPC mandatory. #24748 (comment)
  - previously it was an optional arg with default `false` value.
  - only place this RPC is used is in the [functional tests](https://github.com/bitcoin/bitcoin/blob/11b436a66af3ceaebb0f907878715f331516a0bc/test/functional/test_framework/test_node.py#L742) where we always pass the appropriate `v2transport` option to the RPC anyways. (and that too just for python dummy peer(`P2PInterface`) and bitcoind(`TestNode`) interactions)
  - rename `v2_handshake()` to `_on_data_v2_handshake()` #24748 (comment)
  - more compact return statement in `wait_for_reconnect()` #24748 (comment)
  - assertion to check that empty version packets are received from `TestNode`.

ACKs for top commit:
  glozow:
    ACK e7fd70f
  theStack:
    Code-review ACK e7fd70f
  mzumsande:
    Code Review ACK e7fd70f

Tree-SHA512: e66e29baccd91e1e4398b91f7d45c5fc7c2841d77d8a6178734586017bf2be63496721649da91848dec71da605ee31664352407d5bb896e624cc693767c61a1f
fanquake added a commit that referenced this pull request Feb 27, 2024
…ort is enabled

bf5662c test: enable v2 for python p2p depending on global --v2transport flag (Martin Zumsande)
6e9e39d test: Don't use v2transport when it's too slow. (Martin Zumsande)
87549c8 test: enable p2p_invalid_messages.py with v2transport (Martin Zumsande)
5fc9db5 test: enable p2p_sendtxrcncl.py with v2transport (Martin Zumsande)

Pull request description:

  #24748 added v2 transport to the python `P2PConnection`, but so far each test that wants to make use of it needs to enable it on an individual basis.
  This PR changes it so that if the test suite is run with `--v2transport` option, v2 is used in each test by default, not only for connections between two bitcoind instances as before, but also wherever `P2PConnection` is used. Individual tests can override this global option.

  To do that, a few tests need to be adjusted.
  In addition, I added a commit to always use v1 in a few select subtests that send a large number of large messages (e.g. large reorgs). These tests don't have a fundamental problem with v2 but become very slow due to the unoptimised python ChaCha20 implementation (~30 minutes on my computer, so probably not suitable to be run in the CI).

  As a result, `python3 test_runner.py --v2transport` should succeed and use `v2` everywhere (unless v1 is chosen explicitly).

  [Edit]: To make the "test each commit" CI pass, several test fixes were squashed into the last commit, which actually enables v2 p2p for `P2PConnection`. I have an unsquashed version at https://github.com/mzumsande/bitcoin/tree/202401_bip324_alltests_unsquashed, in case that helps with review.

ACKs for top commit:
  fjahr:
    tACK bf5662c
  vasild:
    ACK bf5662c
  stratospher:
    reACK bf5662c.
  theStack:
    Tested ACK bf5662c

Tree-SHA512: 4f5a08248ba8a755f7d0f48deb2b79bef03292345cacb7deef01be955481093800e4e56ff218ea56734eef5de1fb3ab0f04657447ea27d393bb536539d7b289d
achow101 added a commit that referenced this pull request Mar 12, 2024
0a53361 docs: add release notes for #27114 (brunoerg)
e6b8f19 test: add coverage for whitelisting manual connections (brunoerg)
c985eb8 test: add option to speed up tx relay/mempool sync (brunoerg)
66bc6e2 Accept "in" and "out" flags to -whitelist to allow whitelisting manual connections (Luke Dashjr)
8e06be3 net_processing: Move extra service flag into InitializeNode (Luke Dashjr)
9133fd6 net: Move `NetPermissionFlags::Implicit` verification to `AddWhitelistPermissionFlags` (Luke Dashjr)
2863d7d net: store `-whitelist{force}relay` values in `CConnman` (brunoerg)

Pull request description:

  Revives #17167. It allows whitelisting manual connections. Fixes #9923

  Since there are some PRs/issues around this topic, I'll list some motivations/comments for whitelisting outbound connections from them:
  - Speed-up tx relay/mempool sync for testing purposes (my personal motivation for this) - In #26970, theStack pointed out that we whitelist peers to speed up tx relay for fast mempool synchronization, however, since it applies only for inbound connections and considering the topology `node0 <--- node1 <---- node2 <--- ... <-- nodeN`,  if a tx is submitted from any node other than node0, the mempool synchronization can take quite long.
  - #29058 (comment) - "Before enabling -v2transport by default (which I'd image may happen after #24748) we could consider a way to force manual connections to be only-v1 or even only-v2 (disabling reconnect-with-v1). A possibility could be through a net permission flag, if #27114 makes it in."
  - #17167 (comment) - "This would allow us to use #25355 when making outgoing connections to all nodes, except to whitelisted ones for which we would use our persistent I2P address."
  - Force-relay/mempool permissions for a node you intentionally connected to.

ACKs for top commit:
  achow101:
    ACK 0a53361
  sr-gi:
    re-ACK [0a53361](0a53361)
  pinheadmz:
    ACK 0a53361

Tree-SHA512: 97a79bb854110da04540897d2619eda409d829016aafdf1825ab5515334b0b42ef82f33cd41587af235b3af6ddcec3f2905ca038b5ab22e4c8a03d34f27aebe1
@stratospher stratospher deleted the p2p-encryption-test branch May 17, 2024 15:21
kwvg added a commit to kwvg/dash that referenced this pull request Oct 15, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Oct 23, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Oct 23, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Oct 23, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Oct 24, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Oct 24, 2024
PastaPastaPasta added a commit to dashpay/dash that referenced this pull request Oct 24, 2024
, bitcoin#29372, bitcoin#29460, bitcoin#29358, bitcoin#29511, bitcoin#29390, bitcoin#29431, bitcoin-core/gui#788 (BIP324 backports: part 4)

4735b82 merge bitcoin#29431: disconnection scenarios during v2 handshake (Kittywhiskers Van Gogh)
cc6b88e merge bitcoin-core/gui#788: update session ID tooltip (Kittywhiskers Van Gogh)
2455862 merge bitcoin#29390: speedup bip324_cipher.py unit test (Kittywhiskers Van Gogh)
062aaf1 merge bitcoin#29511: Fix intermittent failure in rpc_net.py --v2transport (Kittywhiskers Van Gogh)
54972e8 merge bitcoin#29358: use v2 everywhere for P2PConnection if --v2transport is enabled (Kittywhiskers Van Gogh)
4cce72f test: add missing debug log assertion in `p2p_invalid_messages.py` (Kittywhiskers Van Gogh)
bd2fe61 merge bitcoin#29460: assert rpc error for addnode v2transport not enabled (Kittywhiskers Van Gogh)
5ee15fa merge bitcoin#29372: fix intermittent failure in `rpc_setban.py --v2transport` (Kittywhiskers Van Gogh)
e278818 merge bitcoin#29352: fix intermittent failure in p2p_v2_earlykeyresponse (Kittywhiskers Van Gogh)
6b2a8b5 merge bitcoin#24748: functional tests for v2 P2P encryption (Kittywhiskers Van Gogh)
32500f2 merge bitcoin#27653: add unit test coverage for Python ECDSA implementation (Kittywhiskers Van Gogh)
9f476c6 net: add Dash network message short IDs, allocate range 128 onwards (Kittywhiskers Van Gogh)

Pull request description:

  ## Additional Information

  * Depends on #6329

  * Dash-specific P2P messages have been allocated short IDs after 128 based on a prior suggestion ([source](#6280 (comment))) as there are 255 potential short IDs (ID `0` is reserved for V1 fallback, [source](https://github.com/dashpay/dash/blob/a7bbcc823d800b293e9ec54dff518fa9929c763c/src/net.cpp#L1019)) and upstream uses 28 short IDs (though Dash has left ID `5` blank as we do not implement the `FEEFILTER` message, [source](https://github.com/dashpay/dash/blob/a7bbcc823d800b293e9ec54dff518fa9929c763c/src/net.cpp#L1024)).

    As it is unlikely that upstream will utilize more than 127 short IDs (and the spec refers to IDs after 32 as "undefined", [source](https://github.com/bitcoin/bips/blob/52894e1aa7af27f4ae7836a391643124c71e2639/bip-0324.mediawiki#v2-bitcoin-p2p-message-structure)), there shouldn't be an adverse effect to utilizing IDs >=128. The unified array of short IDs are accessible through `V2ShortIDs()`.

    * As there are checks to see if an ID *can* be valid by checking against the array size (which wouldn't work here as we create an array of 256 entries combining both upstream and Dash's allocated short IDs, filling the rest with blank values and we cannot ignore blank values to know if a value is unallocated as the blanking could also signal a reservation, [source](https://github.com/dashpay/dash/blob/a7bbcc823d800b293e9ec54dff518fa9929c763c/src/net.cpp#L1048-L1052)), such a check needs to be done by using `IsValidV2ShortID()`.
    * `V2ShortIDs()` isn't as elegant as desired as `std::fill` and `std::copy` are not `constexpr` until C++20 ([source](https://en.cppreference.com/w/cpp/algorithm/fill), [source](https://en.cppreference.com/w/cpp/algorithm/copy)) and until we drop C++17 support, we have to be mindful of that.

  * `masternode connect` will now _attempt_ to establish a P2Pv2 connection if the node *initiating* the connection has opted-in using the new argument (`v2transport`) and the node was started with P2Pv2 enabled (using the launch argument, `-v2transport`).

    This mirrors changes to behavior to `addconnection` introduced in [bitcoin#24748](bitcoin#24748)

  * The oversized payload test in `p2p_invalid_messages.py` will expect an excessively large message of size of `3145729` bytes (and in P2Pv2, `3145742` bytes), as opposed to upstream's `4000001` and `4000014` bytes respectively as Dash has a lower protocol limit of 3MiB ([source](https://github.com/dashpay/dash/blob/a7bbcc823d800b293e9ec54dff518fa9929c763c/src/net.h#L80-L81)) vs Bitcoin's 4MB ([source](https://github.com/bitcoin/bitcoin/blob/225718eda89d65a7041c33e1994d3bf7bd71bbdd/src/net.h#L62-L63))

  ## Breaking Changes

  None expected.

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation **(note: N/A)**
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  PastaPastaPasta:
    utACK 4735b82
  UdjinM6:
    light ACK 4735b82

Tree-SHA512: 4a9d586133633c109e6a8f20a6c6c5e4b24185fb616bcd8568e546b6e9f886e7a8707e811fd070bbe32c40df269f89a56343a24b67242c6147f9df27275af599
@bitcoin bitcoin locked and limited conversation to collaborators May 17, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
No open projects
Status: Needs review
Development

Successfully merging this pull request may close these issues.