BIP 0324
What changes when nodes encrypt their traffic?
BIP 324 wraps the peer-to-peer protocol in opportunistic encryption that looks like random bytes on the wire. It does not tell a node who is on the other end, and it changes nothing about blocks or transactions.
Everything Bitcoin nodes relay is public: blocks, transactions, addresses. Before BIP 324, every connection carried it in the clear (v1 connections still do), over connections that were neither encrypted nor authenticated. The authors point out what that costs. A global passive eavesdropper can trivially identify the source and timing of a transaction; bytes can be altered on the fly at low cost; and every connection begins with the same magic bytes, so deep packet inspection can spot and block it.12
BIP 324, assigned in 2019 and recorded as Deployed, defines version 2 of the transport. It adds opportunistic encryption, makes the bytes on the wire look uniformly random to a passive observer, trims a little bandwidth, and leaves room to negotiate future upgrades. Only the framing changes: the messages inside are the same ones v1 carries.3456
Source details
This chapter is an independent explanation, not the proposals themselves. Preamble fields are shown as recorded in the pinned files, with author e-mail addresses omitted. Status is the proposal’s own field, not an endorsement or a sign of consensus.
BIP 324: Version 2 P2P Encrypted Transport Protocol
- BIP
- 324
- Layer
- Peer Services
- Title
- Version 2 P2P Encrypted Transport Protocol
- Authors
- Dhruv Mehta
Tim Ruffing
Jonas Schnelli
Pieter Wuille - Status
- Deployed
- Type
- Specification
- Assigned
- 2019-03-08
- License
- BSD-3-Clause
- Version
- 1.0.2
- Replaces
- 151
v1: 24 bytes besides the payload
Sent in the clear. Every v1 message begins with the network’s 4 magic bytes, and every connection opens with the command “version”.
v2: 21 bytes besides the payload
Everything is encrypted or random-looking. A ping message uses the 1-byte ID 18; types without an ID take 13 bytes (0x00 and the 12-byte name).
Field sizes from BIP 324’s packet and message structure; the ID for ping read from its message-type table (line 560).
Encryption without authentication
v2 encrypts, but it does not authenticate the peer. Nothing in the handshake tells a node who it is talking to; the BIP says so plainly. The authors argue that in a permissionless network authentication only makes sense in specific settings, such as two nodes run by the same operator, and that it should be added separately, if at all.10
What unauthenticated encryption buys, in the authors’ account, is cost. A passive eavesdropper can no longer read the messages; to read traffic it has to become active, by sitting in the middle of the connection for its whole life, downgrading it to v1, or running its own nodes and getting honest ones to connect. Active attacks at scale are more expensive, and for connections an operator sets up by hand they are in principle detectable.1112
The tools for catching them are the protocol version and the session ID. Both ends derive the session ID from the key exchange, so it is identical on both sides of an honest connection and differs if someone has spliced themselves into the middle. A node may show it to its operator to compare with the peer’s operator out of band.13
Why not use TLS or Noise? The authors’ answer is that neither treats authentication as a separate, optional layer, neither produces a pseudorandom byte stream that can be shaped to resemble other traffic, neither supports secp256k1 natively, and both hand the application a stream where Bitcoin wants packets. Fixing all of that would forfeit the benefit of reusing them.14
The handshake
Each side starts by generating a fresh ephemeral key and sending its public key as 64 bytes of ElligatorSwift encoding. Every 64-byte string is a valid encoding of some curve point, so encodings sampled uniformly look like random bytes: unlike a plain 32-byte X coordinate (a random 32-byte string is a valid one only about half the time), they give an observer nothing to test. Each side may follow its key with up to 4095 bytes of garbage, so the first message need not be exactly 64 bytes long.1617
A responder also has to keep talking to v1 peers. It watches the first 16 bytes: if they are the network magic followed by “version” and padding, the start of every v1 connection, it switches to v1.1218
Both sides then decode the other’s 64 bytes to an X coordinate and compute X-only ECDH. The result is hashed together with both 64-byte encodings exactly as sent, initiator’s first, so an attacker cannot alter an encoding without breaking the rest of the stream. HKDF-SHA256, salted with the label “bitcoin_v2_shared_secret” and the network magic, expands that secret into the session ID, four cipher keys (a length key and a payload key in each direction) and two 16-byte garbage terminators.1920
A · Interactive
Static view: the first vector with every stage shown. With JavaScript you can step through the handshake and choose among 5 published vectors.
- 1 Public keys out
- 2 Shared secret
- 3 Keys and session ID
- 4 Garbage terminator, version packet
- 5 Encrypted packets
From here on everything is encrypted packets: a 3-byte encrypted length, then ChaCha20-Poly1305 over a header byte and the contents, ending in a 16-byte tag.
This side: the initiator
- sends 64 bytes (u ‖ t)
480eacf1536b…ee625af3‖245592c11127…0d0c846a- which decode to x
014e5bdbb1d7…6c5eb2d6= x(priv · G), computed; that these 64 bytes decode to it is the vector’s claim- its garbage terminator
3ba1f51de6272aa28fd21059b91d3893
The peer: the responder
- sends 64 bytes (u ‖ t)
ffffffffffff…22d5e441‖524d571a52b3…f98457ae- which decode to x
568146140669…70109183(decoding from the vector)- its garbage terminator
faf3b317340de00e29f2181db270ff81
Derived on both sides
- X-only ECDH
105781102830…b3987ced- shared secret
e500c670f1b3…5b3433a8tagged hash of initiator’s 64 bytes ‖ responder’s 64 bytes ‖ x- initiator_L · initiator_P
67b155367abf…072176b8·93f5b4c59038…81e0d5f7- responder_L · responder_P
08fe46857ab4…c7330098·2271d5f5351a…30f9255c- session ID
d083d09c1bdf71795b39a9534601cf7c7a7e767e578c44a17dfaf43a3c18f98cthe same on both ends unless someone is in the middle
Packet 0 from the initiator
- length, 3 bytes
6aa28bencrypts 3f0000 = 63 (little-endian) - header + contents, 64 bytes, encrypted
c4b6719eca144ac33a3f17859317d5450e4978db9365ce61… - Poly1305 tag, 16 bytes
fa1cdc7eb99581c48ff2898ef92d3aa1
- nonce (packet in epoch ‖ epoch)
0000000000000000000000000 rekeys so far- associated data
- 4095 bytes: the garbage this side sent, authenticated by its first packet
- total
- 83 bytes = 3 + 1 + 63 + 16
Packet 0 in each direction is the version packet (or a decoy before it), part of the handshake; v1 has no equivalent.
Source: BIP 324 packet_encoding_test_vectors.csv, row for packet 0 (line 4). Recomputed at build time by the tested model; ECDH, secret, keys, session ID and terminators equal the vector’s, and so does the whole packet. ElligatorSwift decodings are the vector’s own.
B · Worked example
BIP 324’s packet vector for packet 0, seen from the initiator: from two public keys to one encrypted packet.
Two 64-byte public keys cross the wire
- ours
480eacf1536b52257bf8ce78d8f4ce09395d744767c6c129e7838947ee625af3245592c111275e877d5baae22584cb5f1153e67c16bcd7da767726cd0d0c846a- theirs
ffffffffffffffffffffffffffffffffffffffffffffffffffffffff22d5e441524d571a52b3def126189d3f416890a99d4da6ede2b0cde1760ce2c3f98457ae
ElligatorSwift encodings: uniformly random-looking bytes that decode to curve X coordinates.
X-only ECDH, hashed with both encodings
- x(ECDH)
10578110283044630bc13a9f12b00eb0af7cba9f53506add2b57ae07b3987ced- shared secret
e500c670f1b32f60e05009bddcdbfa7153afb19c20479583a54b43d85b3433a8
HKDF-SHA256 key schedule
- session ID
d083d09c1bdf71795b39a9534601cf7c7a7e767e578c44a17dfaf43a3c18f98c- initiator_P (our payload key)
93f5b4c59038c16c3f09793976c75e522bf994635e3f1ef9f04e628281e0d5f7
Garbage terminator, then encrypted packets
- our terminator
3ba1f51de6272aa28fd21059b91d3893
Packet 0: length, ciphertext, tag
- encrypted length
6aa28b- nonce
000000000000000000000000- tag
fa1cdc7eb99581c48ff2898ef92d3aa1
83 bytes in all for 63 bytes of contents.
Source: BIP 324 packet_encoding_test_vectors.csv (line 4); recomputed by the tested model and checked against the vector. ElligatorSwift decodings are taken from the vector; this site does not implement ElligatorSwift.
Each side sends its garbage terminator, then encrypted packets. The first packet in each direction carries the garbage that side sent as associated data, so the garbage is authenticated and cannot be changed in transit without the connection failing. Each side then sends a version packet with empty contents, possibly after decoys, to say it speaks v2; the responder’s goes out in its first flight, without waiting.2324
The authors explain that the terminator exists because the receiver has to find where the garbage ends. Scanning for a fixed 16-byte sequence is cheap, whereas trying to authenticate a packet at every offset would mean recomputing a Poly1305 tag for every byte received. A receiver reads at most 4111 bytes, 4095 of garbage and the terminator, before giving up.1725
Packets
After the handshake every byte on the wire belongs to a packet. A packet is 20 bytes plus its contents: a 3-byte length, encrypted, then ChaCha20-Poly1305 over a 1-byte header and the contents, ending in a 16-byte tag. Contents can be up to 2^24 − 1 bytes.8
The length is handled separately because a receiver must know where a packet ends before it can check it. It is encrypted with its own ChaCha20 stream, which keeps running from packet to packet, and is not covered by the tag; a wrong length still makes the packet fail authentication.26
BIP 324 defines one header flag, the highest bit, called the ignore bit; the other bits are ignored for now. A packet with it set is a decoy: the receiver checks that it decrypts and throws it away. Decoys are the protocol’s main means of shaping traffic, which the authors want to make it possible to conceal block propagation; how and when to send them is left out of scope.1227
Inside a packet, an application message starts with its type, either a one-byte ID from the BIP’s table or a zero byte and the same 12-byte ASCII name v1 uses, followed by the payload. Compared with v1’s 24-byte header, a message with a short ID carries 21 bytes of overhead; one without a short ID carries 33. The authors estimate that encrypting and authenticating costs about as much as the truncated double SHA-256 checksum it replaces.7928
Forgetting old keys
Both ciphers change their keys every 224 packets. The payload cipher’s nonce counts packets within the current epoch and numbers the epoch; after the 224th packet its new key is the first 32 bytes of encrypting 32 zero bytes under the old key, with the nonce’s packet field set to 0xffffffff. The length cipher replaces its key with the next 32 bytes of its own stream and moves to the next nonce.22
| packet | nonce: packet in epoch (4 B LE) ‖ epoch (8 B LE) | key in use |
|---|---|---|
| 0 | 00000000 0000000000000000 | fdf5a3e3e7…0b50b1 |
| 1 | 01000000 0000000000000000 | fdf5a3e3e7…0b50b1 |
| 222 | de000000 0000000000000000 | fdf5a3e3e7…0b50b1 |
| 223 | df000000 0000000000000000 | fdf5a3e3e7…0b50b1 |
| 224 | 00000000 0100000000000000 | 9eae3d0883…f6bd32 new key |
| 447 | df000000 0100000000000000 | 9eae3d0883…f6bd32 |
| 448 | 00000000 0200000000000000 | 7bf2eccbbf…c290ff new key |
Keys from BIP 324’s packet vector for packet 223 (line 5), run forward by the tested model. After every 224th packet the key is replaced by the first 32 bytes of encrypting 32 zero bytes under the old key. Keys after packet 223 are the model’s; the vectors check rekeying through the packets that follow.
This gives forward secrecy within a session: an attacker who steals the current keys cannot derive earlier ones, so it cannot read traffic recorded before the last rekey. The BIP does not refresh the key exchange during a session, and says why it does not consider that a priority.1229
Nodes advertise v2 with the NODE_P2P_V2 service flag. Because flags spread through untrusted relays, a node that is cut off as soon as it tries v2 is encouraged to retry over v1. v2 nodes keep accepting inbound v1 connections, to minimize the risk of splitting the network. Blocks, transactions, scripts and consensus rules are untouched: v2 changes how messages travel between two nodes, not what they say.61230
Evidence
Each numbered marker in the text points here, and the ↩ links lead back to every place that cites an entry. Quotes are verbatim from the BIP files at commit
3a10b5b, including their wiki markup; links open
the exact lines; the quoted text sits under each entry. Labels say what kind of statement each is: a rule, the author’s rationale, history,
a test vector, or our own inference.
-
↩ Author’s rationale The authors note that v1 peers talk over unencrypted, unauthenticated connections; the data is public but metadata can reveal private information, such as a transaction's source and timing to a global passive attacker.
Quoted source text (2)
peers talk to each other over unencrypted and unauthenticated connections.
While the relayed data itself is public in nature, the associated metadata may reveal private information and hamper privacy of users. For example, a global passive attacker eavesdropping on all Bitcoin P2P connections can trivially identify the source and timing of a transaction.
-
↩ Author’s rationale The authors note v1 connections can be tampered with cheaply, and are self-revealing because they start with fixed magic bytes, which enables censorship.
Quoted source text (2)
Since connections are unauthenticated, they can be tampered with at a low cost and often even with a low risk of detection.
The protocol is self-revealing. For example, deep packet inspection can identify a P2P connection trivially because connections start with a fixed sequence of magic bytes. The ability to detect connections enables censorship
-
↩ History BIP 324 (Mehta, Ruffing, Schnelli, Wuille), a Peer Services specification assigned in 2019, is recorded as Deployed and replaces BIP 151.
Quoted source text (1)
Layer: Peer Services Title: Version 2 P2P Encrypted Transport Protocol Authors: Dhruv Mehta <dhruv@bip324.com> Tim Ruffing <crypto@timruffing.de> Jonas Schnelli <dev@jonasschnelli.ch> Pieter Wuille <bitcoin-dev@wuille.net> Status: Deployed Type: Specification Assigned: 2019-03-08 License: BSD-3-Clause Version: 1.0.2 Replaces: 151
-
↩ Rule BIP 324 proposes a new P2P transport with opportunistic encryption, a mild bandwidth reduction, and upgrade negotiation before application messages.
Quoted source text (1)
This document proposes a new Bitcoin P2P transport protocol, which features opportunistic encryption, a mild bandwidth reduction, and the ability to negotiate upgrades before exchanging application messages.
-
↩ Author’s rationale v2 aims to raise the cost of these attacks mainly through unauthenticated, opportunistic encryption, and makes the bytestream pseudorandom to a passive eavesdropper.
Quoted source text (1)
This proposal for a new P2P protocol version (v2) aims to improve upon this by raising the costs for performing these attacks substantially, primarily through the use of unauthenticated, opportunistic transport encryption. In addition, the bytestream on the wire is made pseudorandom (i.e., indistinguishable from uniformly random bytes) to a passive eavesdropper.
-
↩ a b Editorial inference v2 changes only how P2P messages are carried: payloads and message types are those of the v1 protocol, and the BIP sits in the Peer Services layer, not consensus.
BIP 324 L3 BIP 324 L523 BIP 324 L528
Quoted source text (3)
Layer: Peer Services
An unencrypted application layer '''contents''' is composed of:
a 12-byte ASCII message type (as in the v1 P2P protocol)
-
↩ a b Test vector A v1 message header is 24 bytes (magic, command, length, checksum); a v2 packet adds 20 bytes plus a 1-byte type ID for listed types, 21 bytes in all, or 33 with a 13-byte type.
Quoted source text (2)
The total size of a packet is 20 bytes plus the length of its contents.
either a one byte ID in range ''1..255''
-
↩ a b c Rule A packet is a 3-byte encrypted length, then ChaCha20Poly1305 over a 1-byte header and the contents; a packet is 20 bytes plus its contents, which may be up to 2^24 - 1 bytes.
BIP 324 L157 BIP 324 L160–163 BIP 324 L161–162
Quoted source text (3)
The total size of a packet is 20 bytes plus the length of its contents.
* A 3-byte encrypted '''length''' field, encoding the length of the '''contents''' (between ''0'' and ''224-1''
* An authenticated encryption of the '''plaintext''', which consists of: ** A 1-byte '''header''' which consists of transport layer protocol flags. Currently, only the highest bit is defined as the '''ignore bit'''.
-
↩ a b Rule Application contents are a message type, a 1-byte ID or 0x00 plus the 12-byte ASCII type as in v1, followed by the message payload.
Quoted source text (2)
| <code>message_type</code> || 1 or 13 || either a one byte ID in range ''1..255'' or <code>b'\x00'</code> followed by a 12-byte ASCII message type (as in the v1 P2P protocol)
| <code>message_payload</code> || <code>message_length</code> || message payload
-
↩ a b Author’s rationale The proposal does not include peer authentication; the authors argue authentication should be addressed separately, if desired, and the session ID provides a basis for it.
Quoted source text (2)
The communication protocol establishing that the communication partner's identity matches who we expect them to be, through some public key mechanism. The proposal in this document does '''not''' include such a mechanism.
As a consequence, we believe that authentication should be addressed separately (if desired), and this proposal aims to provide a solid technical basis for future protocol upgrades, including the addition of optional authentication
-
↩ Author’s rationale The authors argue encryption forces an eavesdropper to become active: a persistent MitM, downgrading to v1, or running its own nodes; for manual connections, comparing session IDs would expose an attacker.
BIP 324 L34 BIP 324 L34 BIP 324 L34
Quoted source text (3)
Encryption, even when it is unauthenticated and only used when both endpoints support v2, impedes eavesdropping by forcing the attacker to become active: either by performing a persistent man-in-the-middle (MitM) attack, by downgrading connections to v1, or by spinning up their own nodes and getting honest nodes to make connections to them.
even very basic checks, e.g., operators manually comparing protocol versions and session IDs (as supported by the proposed protocol), will expose the attacker.
Active attacks at scale are more resource intensive in general, but in the case of manual, deliberate connections (as opposed to automatic, random ones), they are also in principle detectable
-
↩ a b c d e Author’s rationale Goals include confidentiality against passive attacks, a pseudorandom and shapable bytestream, forward secrecy, upgradability, accepting inbound v1, and low overhead.
BIP 324 L81 BIP 324 L83 BIP 324 L85 BIP 324 L87 BIP 324 L84
Quoted source text (5)
* Confidentiality against passive attacks: A passive attacker having recorded a v2 P2P bytestream (without timing and fragmentation information) must not be able to determine the plaintext being exchanged by the nodes.
* Pseudorandom bytestream: A passive attacker having recorded a v2 P2P bytestream (without timing information and fragmentation information) must not be able to distinguish it from a uniformly random bytestream.
* Forward secrecy: An eavesdropping attacker who compromises a peer's sessions secrets should not be able to decrypt past session traffic, except for the latest few packets.
* Compatibility: v2 clients will allow inbound v1 connections to minimize risk of network partitions.
* Shapable bytestream: It should be possible to shape the bytestream to increase resistance to traffic analysis (for example, to conceal block propagation)
-
↩ a b Author’s rationale A session ID derived from the Diffie-Hellman negotiation identifies the channel; operators can compare it out of band, so an active MitM risks detection.
Quoted source text (2)
* Observability of active attacks: A session ID identifying the encrypted channel uniquely is derived deterministically from a Diffie-Hellman negotiation. An active man-in-the-middle attacker is forced to incur a risk of being detected as peer operators can compare session IDs manually
v2 clients supporting this proposal may present the entire session ID (encoded as a hex string) to the node operator to allow for manual, out of band comparison with the peer node operator.
-
↩ Author’s rationale The authors argue TLS and Noise are unsuitable: they lack a modular treatment of authentication, a pseudorandom and shapable bytestream, native secp256k1 support, and a packet interface.
BIP 324 L62 BIP 324 L64 BIP 324 L70–73 BIP 324 L72 BIP 324 L73 BIP 324 L75
Quoted source text (6)
While it would be possible to rely on an off-the-shelf transport encryption protocol such as TLS or Noise, the specific requirements of the Bitcoin P2P network laid out above make these protocols an unsuitable choice.
The primary requirement which existing protocols fail to meet is a sufficiently modular treatment of encryption and authentication.
* Neither offers a pseudorandom bytestream. * Neither offers native support for elliptic curve cryptography on the curve secp256k1 as otherwise used in Bitcoin.
* Neither offers shapability of the bytestream.
* Both provide a stream-based interface to the application layer, whereas Bitcoin requires a packet-based interface
this would negate the benefits of using them as off-the-shelf solution
-
↩ Author’s rationale Traffic analysis of packet lengths and timing, and active attacks, can still reveal that v2 is in use.
Quoted source text (1)
Traffic analysis, e.g., observing packet lengths and timing, as well as active attacks can still reveal that the Bitcoin v2 P2P protocol is in use.
-
↩ Rule Each side sends a 64-byte ElligatorSwift encoding of an ephemeral public key; every 512-bit string is a valid encoding, so encodings sampled uniformly look random.
Quoted source text (2)
Generates a random ephemeral secp256k1 private key and sends a corresponding 64-byte ElligatorSwift
While a random 256-bit string has about 50% chance of being a valid X coordinate on the secp256k1 curve, every 512-bit string is a valid ElligatorSwift encoding of a curve point, making the encoded point indistinguishable from random when using an encoder that can sample uniformly.
-
↩ a b Rule Each side may send up to 4095 bytes of garbage after its public key, for shapability and to avoid a recognizable 64-byte pattern.
Quoted source text (2)
bytes of arbitrary data after their public key, called '''garbage''', providing a form of shapability and avoiding a recognizable pattern of exactly 64 bytes.
May send up to 4095
-
↩ Rule The responder treats a connection as v1 if the first 16 bytes match the network magic followed by "version" and padding.
Quoted source text (1)
Waits until one byte is received which does not match the 16 bytes consisting of the network magic followed by "version\x00\x00\x00\x00\x00". If the first 16 bytes do match, the connection is treated as using the v1 protocol instead.
-
↩ a b Rule Both sides compute X-only ECDH and hash it with the exact 64-byte encodings sent (initiator's first), so the encodings cannot be modified without a full MitM.
Quoted source text (2)
def v2_ecdh(priv, ellswift_theirs, ellswift_ours, initiating): ecdh_point_x32 = ellswift_ecdh_xonly(ellswift_theirs, priv) if initiating: # Initiating, place our public key encoding first. return sha256_tagged("bip324_ellswift_xonly_ecdh", ellswift_ours + ellswift_theirs + ecdh_point_x32)
This makes sure that an attacker cannot modify the public key encoding used without modifying the rest of the stream. If a third party wants the ability to modify stream bytes, they need to perform a full MitM attack on the connection.
-
↩ a b Rule HKDF-SHA256, salted with "bitcoin_v2_shared_secret" and the network magic, derives the session ID, four cipher keys and the two 16-byte garbage terminators.
Quoted source text (1)
# Include NETWORK_MAGIC to ensure a connection between nodes on different networks will immediately fail prk = HKDF_Extract(Hash=sha256, salt=b'bitcoin_v2_shared_secret' + NETWORK_MAGIC, ikm=ecdh_secret) peer.session_id = HKDF_Expand(Hash=sha256, PRK=prk, info=b'session_id', L=32) # Initialize the packet encryption ciphers. initiator_L = HKDF_Expand(Hash=sha256, PRK=prk, info=b'initiator_L', L=32) initiator_P = HKDF_Expand(Hash=sha256, PRK=prk, info=b'initiator_P', L=32) responder_L = HKDF_Expand(Hash=sha256, PRK=prk, info=b'responder_L', L=32) responder_P = HKDF_Expand(Hash=sha256, PRK=prk, info=b'responder_P', L=32) garbage_terminators = HKDF_Expand(Hash=sha256, PRK=prk, info=b'garbage_terminators', L=32) initiator_garbage_terminator = garbage_terminators[:16] responder_garbage_terminator = garbage_terminators[16:]
-
↩ a b Test vector This site's model reproduces all seven BIP 324 packet-encoding vectors: x(ours), X-only ECDH, shared secret, all HKDF outputs, session ID, the full packet for the three vectors that publish one and the published last 128 bytes for the other four, including packets after two, three and four rekeys and one of the largest size allowed (2^24 - 1 bytes of contents). ElligatorSwift decoding is not implemented: the model takes the decoded X coordinates from the vectors.
Quoted source text (1)
* [[bip-0324/packet_encoding_test_vectors.csv|Packet encoding vectors]] illustrate the lifecycle of the authenticated encryption scheme proposed in this document.
-
↩ a b c Rule Both ciphers rekey every 224 messages. The AEAD's nonce is the packet number within the epoch and the epoch number; its new key is the first 32 bytes of encrypting 32 zero bytes under the old key with the nonce's packet field set to 0xffffffff. The length cipher takes the next 32 bytes of its own keystream as its new key.
BIP 324 L414 BIP 324 L429–437 BIP 324 L435–437 BIP 324 L448
Quoted source text (4)
To provide re-keying every 224 packets, we specify two wrappers.
nonce = ((self.packet_counter % REKEY_INTERVAL).to_bytes(4, 'little') + (self.packet_counter // REKEY_INTERVAL).to_bytes(8, 'little'))
if (self.packet_counter + 1) % REKEY_INTERVAL == 0: rekey_nonce = b"\xFF\xFF\xFF\xFF" + nonce[4:] self.key = aead_chacha20_poly1305_encrypt(self.key, rekey_nonce, b"", b"\x00" * 32)[:32]
rekeying every 224 chunks using the next 32 bytes of the block function output as new key
-
↩ Rule Each side sends its 16-byte garbage terminator; the first encrypted packet in each direction authenticates the garbage sent as associated data, so a third party cannot modify it without consequence.
BIP 324 L123 BIP 324 L127 BIP 324 L127
Quoted source text (3)
#** Send their 16-byte garbage terminator.
the first encrypted packet that will be sent in that direction (regardless of it being a decoy packet or not) will make use of the associated authenticated data (AAD) feature of the AEAD to authenticate the garbage that has been sent in that direction.
Without garbage authentication, the garbage would be modifiable by a third party without consequences.
-
↩ Rule After the terminators, the responder and initiator each send a version packet with empty contents to indicate v2 support; other contents are reserved for future versions.
BIP 324 L130 BIP 324 L133 BIP 324 L149
Quoted source text (3)
Sends a '''version packet''' with empty content, to indicate support for the v2 P2P protocol proposed by this document. Any other value for content is reserved for future versions.
Sends a '''version packet''' with empty content as well, to indicate support for the v2 P2P protocol.
Note that the version negotiation phase does not need to wait for the key exchange phase to complete; version packets can be sent immediately after sending the garbage terminator.
-
↩ Author’s rationale A fixed 16-byte terminator is used because scanning for a byte sequence is much faster than trying to authenticate a packet at every offset; a receiver reads at most 4111 bytes looking for it.
Quoted source text (2)
While it is in principle possible to use the first packet after the garbage directly as a terminator (scan until a valid packet follows), this would be significantly slower than just scanning for a fixed byte sequence, as it would require recomputing a Poly1305 tag after every received byte.
#** Receive up to 4111 bytes, stopping when encountering the garbage terminator.
-
↩ Rule The length field is encrypted with a separate ChaCha20 stream but not covered by the Poly1305 tag; incorrect lengths still make the packet fail authentication.
BIP 324 L170 BIP 324 L170 BIP 324 L169
Quoted source text (3)
The Poly1305 authentication tag only covers the encrypted plaintext, and not the encrypted length field.
as incorrect lengths will still trigger authentication failure for the overall packet (the plaintext length is implicitly authenticated by ChaCha20Poly1305).
* Length encryption keeps drawing pseudorandom bytes from the same ChaCha20 cipher for multiple packets, rather than incrementing the nonce for every packet.
-
↩ Rule Packets with the ignore bit set are decoys, ignored apart from verifying they decrypt; they are the main shapability mechanism.
Quoted source text (2)
Encrypted packets have an '''ignore bit''', which makes them '''decoy packets''' if set. Decoy packets are to be ignored by the receiver apart from verifying they decrypt correctly. Either peer may send such decoy packets at any point from here on. These form the primary shapability mechanism in the protocol.
How and when to use them is out of scope for this document.
-
↩ Author’s rationale Encrypting and authenticating a v2 message costs roughly as much as v1's truncated double-SHA256 checksum.
Quoted source text (1)
Each v1 P2P message uses a double-SHA256 checksum truncated to 4 bytes. Roughly the same amount of computation power is required for encrypting and authenticating a v2 P2P message as proposed.
-
↩ Author’s rationale Rekeying gives forward secrecy within a session: an attacker who compromises current secrets cannot derive past keys; ECDH is not refreshed.
Quoted source text (2)
Re-keying ensures [https://eprint.iacr.org/2001/035.pdf forward secrecy within a session], i.e., an attacker compromising the current session secrets cannot derive past encryption keys in the same session.
We do not believe protecting against that is a priority
-
↩ Rule v2 support is signalled with the NODE_P2P_V2 = (1 << 11) service flag; clients met with immediate disconnection are encouraged to retry with v1.
Quoted source text (2)
Peers supporting the v2 transport protocol signal support by advertising the <code>NODE_P2P_V2 = (1 << 11)</code> service flag in addr relay. If met with immediate disconnection when establishing a v2 connection, clients implementing this proposal are encouraged to retry connecting using the v1 protocol.
An untrusted intermediary could falsely advertise a potential peer as supportive of v2 connections.