Instantly share code, notes, and snippets.

What would you like to do?

  BIP: 151
  Layer: Peer Services
  Title: Peer-to-Peer Communication Encryption
  Author: Jonas Schnelli <>
  Comments-Summary: Controversial; some recommendation, and some discouragement
  Status: Draft
  Type: Standards Track
  Created: 2016-03-23
  License: PD

Table of Contents


This BIP describes a new Bitcoin peer to peer transport protocol with opportunistic encryption.


The Bitcoin network does not encrypt communication between peers today. This opens up security issues (eg: traffic manipulation by others) and allows for mass surveillance / analysis of bitcoin users. Mostly this is negligible because of the nature of Bitcoins trust model, however for SPV nodes this can have significant privacy impacts [1] and could reduce the censorship-resistance of a peer.

Encrypting peer traffic will make analysis and specific user targeting more difficult than it currently is. Today it's trivial for a network provider or any other men-in-the-middle to identify a Bitcoin user and its controlled addresses/keys (and link with his Google profile, etc.). Just created and broadcasted transactions will reveal the amount and the payee to the network provider.

With the current unencrypted protocol, BGP hijack and block delay attacks have little cost and can be executed in a covert way (undetectable MITM).

This BIP also describes a way that data manipulation (blocking commands by an intercepting TCP/IP node) would be identifiable by the communicating peers.

Analyzing the type of p2p communication would still be possible because of the characteristics (size, sending-interval, etc.) of the encrypted messages.

Encrypting traffic between peers is already possible with VPN, tor, stunnel, curveCP or any other encryption mechanism on a deeper OSI level, however, most of those solutions require significant knowhow in how to setup such a secure channel.


The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119[1].

A peer that supports encryption MUST accept encryption requests from all peers.

Both communication direction share the same shared-secret but have different symmetric cipher keys.

The encryption handshake must happen before sending any other messages to the responding peer.

If the responding peer closes the connection after sending the handshake request, the requesting peer MAY try to connect again without encryption. Such reconnects allow an attacker to "downgrade" the encryption to plaintext communication and MUST not be done when the Bitcoin p2p network uses almost only encrypted communications.


Peers supporting encryption after this proposal MUST signal NODE_ENCRYPTED

NODE_ENCRYPTED = (1 << 11)

A peer usually learns an address along with the expected service flags which MAY be used to filter possible outbound peers.

A peer signaling NODE_ENCRYPTED must accept encrypted communication specified in this proposal.

Peers MAY only make outbound connections to peers supporting NODE_ENCRYPTED.


 | Initiator                             Responder                                      |
 |                                                                                      |
 | x, X         := SECP256k1_KEYGEN()                                                   |
 | CLIENT_HDATA := X                                                                    |
 |                                                                                      |
 |               --- CLIENT_HDATA --->                                                  |
 |                                                                                      |
 |                                       y, Y           := SECP256k1_KEYGEN()           |
 |                                       ECDH_KEY       := SECP256k1_ECDH(X,y)          |
 |                                       SERVER_HDATA   := Y                            |
 |                                                                                      |
 |               <-- SERVER_HDATA ----                                                  |
 |                                                                                      |
 | ECDH_KEY     := SECP256k1_ECDH(x,Y)                                                  |

To request encrypted communication (only possible if no other messages have been sent or received), the requesting peer generates an EC secp256k1 ephemeral-key and sends a 32-byte public key to the responding peer and waits for the remote 32-byte public key from the counterparty.

ODD secp256k1 public keys MUST be used (public keys starting with 0x02). If the public key from the generated ephemeral key is an EVEN public key (starting with 0x03), negating the key and recalculating its public key SHOULD be done. Only using ODD public keys will make it more complex to identify the handshake based on analyzing the traffic.

The handshake request and response message are raw 32byte messages containing no header or length (the pure 32byte payload) and MUST be sent before anything else.

Public keys starting with the 4-byte network magic are forbidden and MUST lead to re-generate an ephemeral-key.

Pseudocode for the ephemeral-key generation

do {
    if (ecdh_key.GetPubKey()[0] == 3) {
} while (m_ecdh_key.GetPubKey()[0..3] == NETWORK_MAGIC);

Once a peer has received the counterparties public key, the shared secret MUST be calculated by using secp256k1 ECDH (own public key x remote pub key). Private keys will never be transmitted. The shared secret can only be calculated if an attacker knows at least one private key and the remote peer's public key.

After a successful handshake, the messages format must use the "encrypted messages structure". Non-encrypted messages from the requesting peer must lead to a connection termination.

After a successful handshake, both peers MUST cleanse the ephemeral-session-key from memory.

A peer not supporting this proposal will not perform the described handshake and thus send a v1 version message. Peers supporting this BIP MAY optionally allow unencrypted v1 communication by detecting a v1 version message by the initial 11-byte sequence of 4byte net magic || "version".

Symmetric Encryption Cipher Keys

The symmetric encryption cipher keys will be calculated with ECDH/HKDF by sharing the public keys of an ephemeral key. Once the ECDH secret (ECDH_KEY) is calculated on each side, the symmetric encryption cipher keys must be derived with HKDF [2] after the following specification:

1. HKDF extraction PRK = HKDF_EXTRACT(hash=SHA256, salt="BitcoinSharedSecret", ikm=ECDH_KEY).

2. Derive Key1A (communication direction X) K1A = HKDF_EXPAND(prk=PRK, hash=SHA256, info="BitcoinK1A", L=32)

2. Derive Key1B (communication direction X) K1B = HKDF_EXPAND(prk=PRK, hash=SHA256, info="BitcoinK1B", L=32)

3. Derive Key2A (communication direction Y) K2 = HKDF_EXPAND(prk=PRK, hash=SHA256, info="BitcoinK2A", L=32)

3. Derive Key2B (communication direction Y) K2 = HKDF_EXPAND(prk=PRK, hash=SHA256, info="BitcoinK2B", L=32)

Session ID

Both parties MUST also calculate the 256bit session-id using SID = HKDF_EXPAND(prk=PRK, hash=SHA256, info="BitcoinSessionID", L=32). The session-id can be used for linking the encryption-session to an identity check.

The session-id MUST be presented to the user on request.

ChaCha20-Poly1305 Cipher Suite

ChaCha20 is a stream cipher designed by Daniel Bernstein [3]. It operates by permuting 128 fixed bits, 128 or 256 bits of key, a 64-bit nonce and a 64-bit counter into 64 bytes of output. This output is used as a keystream, with any unused bytes simply discarded.

Poly1305, also by Daniel Bernstein [4], is a one-time Carter-Wegman MAC that computes a 128 bit integrity tag given a message and a single-use 256-bit secret key.

The chacha20-poly1305@bitcoin specified and defined in this proposal combines these two primitives into an authenticated encryption mode. The construction used is based on that proposed for TLS by Adam Langley [6], but differs in the layout of data passed to the MAC and in the addition of encryption of the packet lengths.

K1A MUST be used to encrypt/decrypt the payload and length of the (encrypted) message.

K1B MUST be used in conjunction with poly1305 to build an AEAD.

K2 and K2B MUST be used for the same AEAD construct in the other communication direction.

The requesting peer MUST use K1A,K1B to encrypt messages on the send channel, K2A,K2B MUST be used to decrypt messages on the receive channel.

The responding peer MUST use K2A,K2B to decrypt messages on the receive channel, K1A,K1B MUST be used to encrypt messages on the send channel.

Optimized implementations of ChaCha20-Poly1305 are very fast in general, therefore it is very likely that encrypted messages require not more CPU cycles per bytes then the current unencrypted p2p message format (ChaCha20/Poly1305 versus double SHA256).

ChaCha20 must never reuse a {key, nonce} for encryption nor may it be used to encrypt more than 2^70 bytes under the same {key, nonce}.

We use a message sequence number for both communication directions as ChaCha20 nonce.

 | Initiator                          Responder                                         |
 |                                                                                      |
 | BIP151_AEAD() = ChaCha20Poly1305()                                                   |
 | MSG_A_CIPH = BIP151_AEAD(k=K1A,K1B, n=0, msg)                                        |
 |                                                                                      |
 |                         --- MSG_CIPH --->                                            |
 |                                                                                      |
 |                                    msg   := BIP151_AEAD(k=K1A,K1B, n=0, MSG_A_CIPH)  |
 |                                                                                      |

Encrypted Messages Structure (v2 message)

Field Size Description Data type Comments
3 length & flag 23 + 1 bits Encrypted length of ciphertext payload (not counting the MAC tag) in number of bytes (only 2^23 is usable, most significant bit is the rekey-flag)
1-13 encrypted command variable ASCII command (or one byte short command ID)
? encrypted payload ? The actual data
16 MAC tag ? 128bit MAC-tag

Encrypted messages do not have the 4byte network magic.

The maximum message size is 2^23 (8’388’608) bytes. Future communication MAY exceed this limit and thus MUST be split into different messages.

Processing the message before the authentication succeeds (MAC verified) MUST not be done.

The 4byte sha256 checksum is no longer required because the AEAD (MAC).

Both peers MUST keep track of the message sequence number (uint32) of sent and received messages for building a 64-bit symmetric cipher IV.

The command field MUST start with a byte that defines the length of the ASCII command string up to 12 chars (1 to 12) or a short command ID (see below).

Short Command ID

To save valuable bandwidth, the v2 message format supports message-command short IDs for message types with high frequency. The ID/string mapping is a peer to peer arrangement and MAY be negotiated between the requesting and responding peer. A peer conforming to this proposal MUST support receiving commands based on the table below and SHOULD use short command IDs for outgoing messages.

Number Command
13 INV

Length comparisons between v1 and v2 messages

v1 in: 4(Magic)+12(Command)+4(MessageSize)+4(Checksum)+36(Payload) == 60
v2 inv: 3(MessageSize&Flag)+1(Command)+36(Payload)+16(MAC) == 56

v1 ping: 4(Magic)+12(Command)+4(MessageSize)+4(Checksum)+8(Payload) == 32
v2 pong: 3(MessageSize&Flag)+1(Command)+8(Payload)+16(MAC) == 28

v1 block: 4(Magic)+12(Command)+4(MessageSize)+4(Checksum)+1’048’576(Payload) = 1’048’600
v2 block: 3(MessageSize&Flag)+6(CommandStr)+8(Payload)+16(MAC) == 28 = 1’048’601


Re-keying can be signaled by setting the most significant bit in the envelope length field. A peer signaling a rekey MUST use the next key for encryption messages AFTER the message where the signaling has been done.

A peer identifying a rekey by checking the most significant bit in the envelope length must use the next key for decrypt messages AFTER the message where the signaling has been detected.

The next symmetric cipher key MUST be calculated by SHA256(SHA256(session ID || old_symmetric_cipher_key)).

Re-Keying interval is a peer policy with a minimum timespan of 10 seconds.

The Re-Keying must be done after every 1GB of data sent or received (recommended by RFC4253 SSH Transport) or if the last rekey was more than an hour ago.


The encryption does not include an identity authentication scheme. This BIP does not cover a proposal to avoid MITM attacks during the encryption initialization. However, peers MUST show the session-id on request by the user which allows to identify a MITM by a manual out-of-band verification.

Optional identity authentication will be covered by other BIPs and will presume communication encryption after this BIP.


This proposal is backward compatible. Non-supporting peers can still use unencrypted communications.

Reference implementation



  • Pieter Wuille and Gregory Maxwell for most of the ideas in this BIP.


This work is placed in the public domain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment