From a87b42c8fda61c6bf192d3e7b5039bc38dfc024d Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 28 Mar 2024 13:41:17 -0700 Subject: [PATCH 1/3] Refactor to proxy and protocol packages --- Cargo.lock | 34 ++++++++++++------- Cargo.toml | 31 ++--------------- protocol/Cargo.toml | 26 ++++++++++++++ {src => protocol/src}/chacha20poly1305.rs | 0 .../src}/chacha20poly1305/chacha20.rs | 0 .../src}/chacha20poly1305/poly1305.rs | 0 {src => protocol/src}/error.rs | 0 {src => protocol/src}/hkdf.rs | 0 {src => protocol/src}/lib.rs | 0 {src => protocol/src}/types.rs | 0 {tests => protocol/tests}/round_trips.rs | 0 proxy/Cargo.toml | 15 ++++++++ {examples => proxy/src/bin}/v1.rs | 0 examples/proxy.rs => proxy/src/main.rs | 0 14 files changed, 65 insertions(+), 41 deletions(-) create mode 100644 protocol/Cargo.toml rename {src => protocol/src}/chacha20poly1305.rs (100%) rename {src => protocol/src}/chacha20poly1305/chacha20.rs (100%) rename {src => protocol/src}/chacha20poly1305/poly1305.rs (100%) rename {src => protocol/src}/error.rs (100%) rename {src => protocol/src}/hkdf.rs (100%) rename {src => protocol/src}/lib.rs (100%) rename {src => protocol/src}/types.rs (100%) rename {tests => protocol/tests}/round_trips.rs (100%) create mode 100644 proxy/Cargo.toml rename {examples => proxy/src/bin}/v1.rs (100%) rename examples/proxy.rs => proxy/src/main.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 9762edf..5e3c4a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,15 +25,15 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" -version = "0.3.70" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95d8e92cac0961e91dbd517496b00f7e9b92363dbe6d42c3198268323798860c" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -54,11 +54,19 @@ checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" name = "bip324" version = "0.1.0" dependencies = [ - "bitcoin", "bitcoin_hashes", "hex-conservative 0.2.0", "rand", "secp256k1", +] + +[[package]] +name = "bip324-proxy" +version = "0.1.0" +dependencies = [ + "bip324", + "bitcoin", + "hex-conservative 0.2.0", "tokio", ] @@ -106,9 +114,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.86" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg-if" @@ -178,9 +186,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "miniz_oxide" @@ -371,9 +379,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.53" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", @@ -382,9 +390,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 3fd1017..4daa6c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,28 +1,3 @@ -[package] -name = "bip324" -version = "0.1.0" -edition = "2021" -license-file = "LICENSE" -description = "Encrypted messaging over the Bitcoin P2P Protocol as specified by BIP 324" -repository = "https://github.com/rustaceanrob/bip324" -readme = "README.md" -rust-version = "1.56.1" - -[features] -default = ["std"] -std = ["secp256k1/std", "rand/std", "rand/std_rng"] - -[dependencies] -secp256k1 = { version="0.28.2", default-features = false} -rand = { version = "0.8.4", default-features = false } -bitcoin_hashes = { version = "0.13.0", default-features = false } - -[dev-dependencies] -bitcoin = "0.31.1" -tokio = { version = "1.36.0", features = ["full"] } -hex = { package = "hex-conservative", version = "0.2.0" } - -[lib] -name = "bip324" -path = "src/lib.rs" - +[workspace] +members = ["protocol", "proxy"] +resolver = "2" diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml new file mode 100644 index 0000000..cd992ec --- /dev/null +++ b/protocol/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "bip324" +version = "0.1.0" +edition = "2021" +license-file = "LICENSE" +description = "Encrypted messaging over the Bitcoin P2P Protocol as specified by BIP 324" +repository = "https://github.com/rustaceanrob/bip324" +readme = "README.md" +rust-version = "1.56.1" + +[features] +default = ["std"] +std = ["secp256k1/std", "rand/std", "rand/std_rng"] + +[dependencies] +secp256k1 = { version="0.28.2", default-features = false} +rand = { version = "0.8.4", default-features = false } +bitcoin_hashes = { version = "0.13.0", default-features = false } + +[dev-dependencies] +hex = { package = "hex-conservative", version = "0.2.0" } + +[lib] +name = "bip324" +path = "src/lib.rs" + diff --git a/src/chacha20poly1305.rs b/protocol/src/chacha20poly1305.rs similarity index 100% rename from src/chacha20poly1305.rs rename to protocol/src/chacha20poly1305.rs diff --git a/src/chacha20poly1305/chacha20.rs b/protocol/src/chacha20poly1305/chacha20.rs similarity index 100% rename from src/chacha20poly1305/chacha20.rs rename to protocol/src/chacha20poly1305/chacha20.rs diff --git a/src/chacha20poly1305/poly1305.rs b/protocol/src/chacha20poly1305/poly1305.rs similarity index 100% rename from src/chacha20poly1305/poly1305.rs rename to protocol/src/chacha20poly1305/poly1305.rs diff --git a/src/error.rs b/protocol/src/error.rs similarity index 100% rename from src/error.rs rename to protocol/src/error.rs diff --git a/src/hkdf.rs b/protocol/src/hkdf.rs similarity index 100% rename from src/hkdf.rs rename to protocol/src/hkdf.rs diff --git a/src/lib.rs b/protocol/src/lib.rs similarity index 100% rename from src/lib.rs rename to protocol/src/lib.rs diff --git a/src/types.rs b/protocol/src/types.rs similarity index 100% rename from src/types.rs rename to protocol/src/types.rs diff --git a/tests/round_trips.rs b/protocol/tests/round_trips.rs similarity index 100% rename from tests/round_trips.rs rename to protocol/tests/round_trips.rs diff --git a/proxy/Cargo.toml b/proxy/Cargo.toml new file mode 100644 index 0000000..e2ccc24 --- /dev/null +++ b/proxy/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bip324-proxy" +version = "0.1.0" +edition = "2021" +license-file = "LICENSE" +description = "BIP324 proxy enabling v1-only clients to use the v2 Bitcoin P2P Protocol" +repository = "https://github.com/rustaceanrob/bip324" +readme = "README.md" +rust-version = "1.56.1" + +[dependencies] +bitcoin = "0.31.1" +tokio = { version = "1.36.0", features = ["full"] } +hex = { package = "hex-conservative", version = "0.2.0" } +bip324 = { path = "../protocol", version = "0.1.0" } diff --git a/examples/v1.rs b/proxy/src/bin/v1.rs similarity index 100% rename from examples/v1.rs rename to proxy/src/bin/v1.rs diff --git a/examples/proxy.rs b/proxy/src/main.rs similarity index 100% rename from examples/proxy.rs rename to proxy/src/main.rs From fc919013e806c52dcb58af9112b9f7c77b230a5c Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 28 Mar 2024 13:43:41 -0700 Subject: [PATCH 2/3] Update CI --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d857938..a688084 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,16 +24,16 @@ jobs: rustup component add --toolchain ${{ matrix.toolchain }} clippy rustup update ${{ matrix.toolchain }} - name: Build - run: cargo build --verbose + run: cargo build --all --verbose - name: Lint run: cargo clippy --all --all-targets - name: Format run: cargo fmt --all -- --check - name: Test - run: cargo test --verbose + run: cargo test --all --verbose - name: Check No Standard Library Support run: | rustup target add --toolchain ${{ matrix.toolchain }} thumbv7m-none-eabi cargo install cross - cross build --target thumbv7m-none-eabi --no-default-features + cross build --package bip324 --target thumbv7m-none-eabi --no-default-features From da9f6df24f75d8fa514ee38cbec576d7070410fc Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 28 Mar 2024 13:49:53 -0700 Subject: [PATCH 3/3] Add readmes --- README.md | 50 +--------------------------------------------- protocol/README.md | 47 +++++++++++++++++++++++++++++++++++++++++++ proxy/README.md | 3 +++ 3 files changed, 51 insertions(+), 49 deletions(-) create mode 100644 protocol/README.md create mode 100644 proxy/README.md diff --git a/README.md b/README.md index 0b1906b..a6da0cd 100644 --- a/README.md +++ b/README.md @@ -2,52 +2,4 @@ [BIP324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki) describes an encrypted communication protocol over the Bitcoin P2P network. Encrypted messages offer a number of benefits over plaintext communication, even though the data exchanged over the Bitcoin P2P network is public to some degree. For instance, plaintext message tampering without detection is trivial for a man in the middle (MitM) attacker. Additionally, a nefarious actor may associate metadata such as IP addresses and transaction origins without explicitly having to connect directly to peers. BIP 324 - "V2" - transport forces nefarious observers to actively connect to peers as opposed to passively observing network traffic, and makes packet tampering detectable. Furthermore, V2 messages over TCP/IP look no different from random noise, making Bitcoin P2P packets indistinguishable from other network packets. -## Protocol Brief - -Alice and Bob initiate a connection by sending three messages to each other to derive a number of shared secrets. Alice begins the connection by deriving a public/private keypair over `secp256k1`, the typical Bitcoin curve. Alice is known as the initiator. She encodes the public key in the [Elligator Swift](https://eprint.iacr.org/2022/759.pdf) format (64-bytes), optionally pads it with some random garbage bytes, and sends the message to Bob. Bob, known as the responder, decodes the Elligator Swift public key, and derives an ephemeral public/private keypair himself. Using his public and private keys, as well as Alice's public key, Bob performs a variation of the Elliptic Curve Diffie Hellman algorithm to derive a shared key. From this shared key, Bob derives multiple keys and a session ID using the HKDF algorithm. Next, Bob creates garbage data, and sends his public key, garbage data, an encrypted packet using the garbage data, and a version negotiation to Alice. With Bob's public key, Alice derives the shared secret and ensures the decrypted packet is authenticated with the garbage Bob sent her. Finally, Alice sends a "garbage terminator" and an encrypted packet using her garbage data, so Bob may authenticate she derived the correct secret and he can decode her messages. Alice and Bob may now freely exchange encrypted messages over the Bitcoin P2P protocol. - -## Implementation - -The crate exposes 4 functions, of which each party need to call only two for a complete handshake. For encrypting and decrypting messages, a `PacketHandler` struct is exposed with two methods. All messages are expected to be a `Vec` arrays of bytes, as this structure works well with `TcpStream` from the standard library and Bitcoin P2P messages. To initiate a handshake Alice calls `initialize_v2_handshake` and `initiator_complete_v2_handshake`. Similarly, to respond to a V2 handshake, Bob calls `receive_v2_handshake` and `responder_complete_v2_handshake`. Each function creates the appropriate message as well as additional data or structures to complete the handshake. Errors thrown by each of these functions should result in disconnection from the peer. - -```rust -use bip324::{initialize_v2_handshake, initiator_complete_v2_handshake, receive_v2_handshake, responder_complete_v2_handshake}; -fn main() { - // Alice starts a connection with Bob by making a pub/priv keypair and sending a message to Bob. - let handshake_init = initialize_v2_handshake(None).unwrap(); - // Bob parses Alice's message, generates his pub/priv key, and sends a message back. - let mut bob_handshake = receive_v2_handshake(handshake_init.message.clone()).unwrap(); - // Alice finishes her handshake by using her keys from earlier, and sending a final message to Bob. - let alice_completion = initiator_complete_v2_handshake(bob_handshake.message.clone(), handshake_init).unwrap(); - // Bob checks Alice derived the correct keys for the session by authenticating her first message. - let _bob_completion = responder_complete_v2_handshake(alice_completion.message.clone(), &mut bob_handshake).unwrap(); - // Alice and Bob can freely exchange encrypted messages using the packet handler returned by each handshake. - let mut alice = alice_completion.packet_handler; - let mut bob = bob_handshake.packet_handler; - let message = b"Hello world".to_vec(); - let encrypted_message_to_alice = bob.prepare_v2_packet(message.clone(), None, false).unwrap(); - let messages = alice.receive_v2_packets(encrypted_message_to_alice, None).unwrap(); - let secret_message = messages.first().unwrap().message.clone().unwrap(); - assert_eq!(message, secret_message); - let message = b"Goodbye!".to_vec(); - let encrypted_message_to_bob = alice.prepare_v2_packet(message.clone(), None, false).unwrap(); - let messages = bob.receive_v2_packets(encrypted_message_to_bob, None).unwrap(); - let secret_message = messages.first().unwrap().message.clone().unwrap(); - assert_eq!(message, secret_message); -} -``` - -There are also `no_std` compliant versions of these functions which require an RNG to be initialized by the consumer. - -# ChaCha20Poly1305 - -BIP324 elects to use the ChaCha20Poly1305 Authenticated Encryption with Addition Data (AEAD) algorithm under the hood. This is a combination of the ChaCha20 stream cipher and the Poly1305 message authentication code (MAC). In this context, "authentication" refers to the encrypted message's integrity, not to the identity of either party communicating. - -Poly1305 is a purpose-built MAC, as opposed to something like an HMAC using SHA256 which leverages an existing hash scheme to build a message authentication code. Purpose-built introduces new complexity, but also allows for increased performance. - -ChaCha20 and Poly1305 are both implemented in this crate to keep dependencies to a minimum. - -# Development - -The implementation is tested against vectors from the BIP324 reference and a number of additional library tests. - +The `protocol` package export the `BIP324` library. The `proxy` package is a small application to enable V2 for V1-only clients. diff --git a/protocol/README.md b/protocol/README.md new file mode 100644 index 0000000..8b5d578 --- /dev/null +++ b/protocol/README.md @@ -0,0 +1,47 @@ +# Protocol + +Alice and Bob initiate a connection by sending three messages to each other to derive a number of shared secrets. Alice begins the connection by deriving a public/private keypair over `secp256k1`, the typical Bitcoin curve. Alice is known as the initiator. She encodes the public key in the [Elligator Swift](https://eprint.iacr.org/2022/759.pdf) format (64-bytes), optionally pads it with some random garbage bytes, and sends the message to Bob. Bob, known as the responder, decodes the Elligator Swift public key, and derives an ephemeral public/private keypair himself. Using his public and private keys, as well as Alice's public key, Bob performs a variation of the Elliptic Curve Diffie Hellman algorithm to derive a shared key. From this shared key, Bob derives multiple keys and a session ID using the HKDF algorithm. Next, Bob creates garbage data, and sends his public key, garbage data, an encrypted packet using the garbage data, and a version negotiation to Alice. With Bob's public key, Alice derives the shared secret and ensures the decrypted packet is authenticated with the garbage Bob sent her. Finally, Alice sends a "garbage terminator" and an encrypted packet using her garbage data, so Bob may authenticate she derived the correct secret and he can decode her messages. Alice and Bob may now freely exchange encrypted messages over the Bitcoin P2P protocol. + +The crate exposes 4 functions, of which each party need to call only two for a complete handshake. For encrypting and decrypting messages, a `PacketHandler` struct is exposed with two methods. All messages are expected to be a `Vec` arrays of bytes, as this structure works well with `TcpStream` from the standard library and Bitcoin P2P messages. To initiate a handshake Alice calls `initialize_v2_handshake` and `initiator_complete_v2_handshake`. Similarly, to respond to a V2 handshake, Bob calls `receive_v2_handshake` and `responder_complete_v2_handshake`. Each function creates the appropriate message as well as additional data or structures to complete the handshake. Errors thrown by each of these functions should result in disconnection from the peer. + +```rust +use bip324::{initialize_v2_handshake, initiator_complete_v2_handshake, receive_v2_handshake, responder_complete_v2_handshake}; +fn main() { + // Alice starts a connection with Bob by making a pub/priv keypair and sending a message to Bob. + let handshake_init = initialize_v2_handshake(None).unwrap(); + // Bob parses Alice's message, generates his pub/priv key, and sends a message back. + let mut bob_handshake = receive_v2_handshake(handshake_init.message.clone()).unwrap(); + // Alice finishes her handshake by using her keys from earlier, and sending a final message to Bob. + let alice_completion = initiator_complete_v2_handshake(bob_handshake.message.clone(), handshake_init).unwrap(); + // Bob checks Alice derived the correct keys for the session by authenticating her first message. + let _bob_completion = responder_complete_v2_handshake(alice_completion.message.clone(), &mut bob_handshake).unwrap(); + // Alice and Bob can freely exchange encrypted messages using the packet handler returned by each handshake. + let mut alice = alice_completion.packet_handler; + let mut bob = bob_handshake.packet_handler; + let message = b"Hello world".to_vec(); + let encrypted_message_to_alice = bob.prepare_v2_packet(message.clone(), None, false).unwrap(); + let messages = alice.receive_v2_packets(encrypted_message_to_alice, None).unwrap(); + let secret_message = messages.first().unwrap().message.clone().unwrap(); + assert_eq!(message, secret_message); + let message = b"Goodbye!".to_vec(); + let encrypted_message_to_bob = alice.prepare_v2_packet(message.clone(), None, false).unwrap(); + let messages = bob.receive_v2_packets(encrypted_message_to_bob, None).unwrap(); + let secret_message = messages.first().unwrap().message.clone().unwrap(); + assert_eq!(message, secret_message); +} +``` + +There are also `no_std` compliant versions of these functions which require an RNG to be initialized by the consumer. + +## ChaCha20Poly1305 + +BIP324 elects to use the ChaCha20Poly1305 Authenticated Encryption with Addition Data (AEAD) algorithm under the hood. This is a combination of the ChaCha20 stream cipher and the Poly1305 message authentication code (MAC). In this context, "authentication" refers to the encrypted message's integrity, not to the identity of either party communicating. + +Poly1305 is a purpose-built MAC, as opposed to something like an HMAC using SHA256 which leverages an existing hash scheme to build a message authentication code. Purpose-built introduces new complexity, but also allows for increased performance. + +ChaCha20 and Poly1305 are both implemented in this crate to keep dependencies to a minimum. + +## Development + +The implementation is tested against vectors from the BIP324 reference and a number of additional library tests. + diff --git a/proxy/README.md b/proxy/README.md new file mode 100644 index 0000000..319827e --- /dev/null +++ b/proxy/README.md @@ -0,0 +1,3 @@ +# Proxy + +A simple proxy process to show off the BIP324 protocol.