Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ jobs:
- uses: extractions/setup-just@v3
- uses: actions-rust-lang/setup-rust-toolchain@v1
- run: just test no-std
bench:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v3
- uses: actions-rust-lang/setup-rust-toolchain@v1
- run: just bench
# Light smoke test fuzz.
fuzz:
runs-on: ubuntu-latest
Expand Down
9 changes: 7 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
NIGHTLY_TOOLCHAIN := "nightly-2025-06-19"
STABLE_TOOLCHAIN := "1.87.0"

@_default:
_default:
@just --list

# Quick check of the code including lints and formatting.
Expand All @@ -26,7 +26,8 @@ STABLE_TOOLCHAIN := "1.87.0"

# Unit test suite.
@_test-unit:
cargo +{{STABLE_TOOLCHAIN}} test --all-targets
# Run everything except benches which need the nightly toolchain.
cargo +{{STABLE_TOOLCHAIN}} test --lib --bins --tests --examples
cargo +{{STABLE_TOOLCHAIN}} test --doc

# Test feature flag matrix compatability.
Expand Down Expand Up @@ -62,6 +63,10 @@ STABLE_TOOLCHAIN := "1.87.0"
cargo install [email protected]
$HOME/.cargo/bin/cross build --package bip324 --target thumbv7m-none-eabi --no-default-features --features alloc

# Run benchmarks.
bench:
cargo +{{NIGHTLY_TOOLCHAIN}} bench --package bip324 --bench packet_handler

# Run fuzz test: handshake.
@fuzz target="handshake" time="60":
cargo install [email protected]
Expand Down
5 changes: 0 additions & 5 deletions protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,3 @@ chacha20-poly1305 = { version = "0.1.1", default-features = false }
# bitcoind version 26.0 includes support for BIP324's V2 protocol, but it is disabled by default.
bitcoind = { package = "corepc-node", version = "0.7.1", default-features = false, features = ["26_0","download"] }
hex = { package = "hex-conservative", version = "0.2.0" }

[lib]
name = "bip324"
path = "src/lib.rs"

115 changes: 115 additions & 0 deletions protocol/benches/packet_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#![feature(test)]

extern crate test;

use bip324::{Handshake, Network, PacketHandler, PacketType, Role};
use test::{black_box, Bencher};

fn create_packet_handler_pair() -> (PacketHandler, PacketHandler) {
// Create a proper handshake between Alice and Bob.
let mut alice_init_buffer = vec![0u8; 64];
let mut alice_handshake = Handshake::new(
Network::Bitcoin,
Role::Initiator,
None,
&mut alice_init_buffer,
)
.unwrap();

let mut bob_init_buffer = vec![0u8; 100];
let mut bob_handshake = Handshake::new(
Network::Bitcoin,
Role::Responder,
None,
&mut bob_init_buffer,
)
.unwrap();

// Bob completes materials with Alice's key.
bob_handshake
.complete_materials(
alice_init_buffer[..64].try_into().unwrap(),
&mut bob_init_buffer[64..],
None,
)
.unwrap();

// Alice completes materials with Bob's key.
let mut alice_response_buffer = vec![0u8; 36];
alice_handshake
.complete_materials(
bob_init_buffer[..64].try_into().unwrap(),
&mut alice_response_buffer,
None,
)
.unwrap();

// Authenticate.
alice_handshake
.authenticate_garbage_and_version(&bob_init_buffer[64..])
.unwrap();
bob_handshake
.authenticate_garbage_and_version(&alice_response_buffer)
.unwrap();

let alice = alice_handshake.finalize().unwrap();
let bob = bob_handshake.finalize().unwrap();

(alice, bob)
}

#[bench]
fn bench_round_trip_small_packet(b: &mut Bencher) {
let plaintext = b"Hello, World!"; // ~13 bytes.
let (mut alice, mut bob) = create_packet_handler_pair();

b.iter(|| {
// Encrypt the packet.
let encrypted = alice
.writer()
.encrypt_packet(black_box(plaintext), None, PacketType::Genuine)
.unwrap();

// Decrypt the length from first 3 bytes (real-world step).
let packet_length = bob
.reader()
.decypt_len(black_box(encrypted[0..3].try_into().unwrap()));

// Decrypt the payload using the decrypted length.
let decrypted = bob
.reader()
.decrypt_payload(black_box(&encrypted[3..3 + packet_length]), None)
.unwrap();

// Ensure the final result isn't optimized away.
black_box(decrypted)
});
}

#[bench]
fn bench_round_trip_large_packet(b: &mut Bencher) {
let plaintext = vec![0u8; 4096]; // 4KB packet.
let (mut alice, mut bob) = create_packet_handler_pair();

b.iter(|| {
// Encrypt the packet.
let encrypted = alice
.writer()
.encrypt_packet(black_box(&plaintext), None, PacketType::Genuine)
.unwrap();

// Decrypt the length from first 3 bytes (real-world step).
let packet_length = bob
.reader()
.decypt_len(black_box(encrypted[0..3].try_into().unwrap()));

// Decrypt the payload using the decrypted length.
let decrypted = bob
.reader()
.decrypt_payload(black_box(&encrypted[3..3 + packet_length]), None)
.unwrap();

// Ensure the final result isn't optimized away.
black_box(decrypted)
});
}
12 changes: 12 additions & 0 deletions protocol/src/fschacha20poly1305.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

//! Wrap ciphers with automatic re-keying in order to provide [forward secrecy](https://eprint.iacr.org/2001/035.pdf) within a session.
//! Logic is covered by the BIP324 test vectors.
//!
//! ## Performance Considerations
//!
//! This module uses small stack-allocated arrays (12-byte nonces, 32-byte keys) for temporary
//! cryptographic operations. These allocations are intentionally kept as local variables rather
//! than pre-allocated struct fields for optimal performance.
//!
//! Tests comparing the current implementation against a version with
//! pre-allocated buffers to reduce stack allocations showed decreased performance.
//!
//! * **FSChaCha20 operations** +3.7% overhead with pre-allocated buffers.
//! * **FSChaCha20Poly1305 operations** +1.0% overhead with pre-allocated buffers.

use chacha20_poly1305::{chacha20::ChaCha20, ChaCha20Poly1305, Key, Nonce};
use core::fmt;
Expand Down