Skip to content

Commit be8b4ed

Browse files
committed
lib: remove the dependency on bitcoin
1 parent eceb074 commit be8b4ed

12 files changed

Lines changed: 186 additions & 500 deletions

File tree

protocol/Cargo.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,24 @@ rust-version = "1.63.0"
1313
default = ["std"]
1414
# High-level wrappers using tokio traits - may affect MSRV requirements.
1515
tokio = ["std", "dep:tokio"]
16-
std = ["bitcoin/std", "bitcoin_hashes/std", "chacha20-poly1305/std", "rand/std", "rand/std_rng"]
16+
std = ["chacha20-poly1305/std", "bitcoin_hashes/std", "secp256k1/std", "rand/std", "rand/std_rng"]
1717

1818
[dependencies]
1919
# The tokio feature may increase the MSRV beyond 1.63.0
2020
# depending on which version of tokio is selected by the caller.
2121
tokio = { version = "1", default-features = false, optional = true, features = ["io-util"] }
22-
rand = { version = "0.8.0", default-features = false }
23-
bitcoin = { version = "0.32.4", default-features = false }
22+
secp256k1 = { version = "0.30.0", default-features = false, features = ["alloc"] }
23+
rand = { version = "0.8.5", default-features = false }
24+
bitcoin_hashes = { version ="0.15.0", default-features = false }
25+
p2p = { package = "bitcoin-p2p-messages", default-features = false, git = "https://github.com/rust-bitcoin/rust-bitcoin", rev = "16cc257c3695dea0e7301a5fa9cab44b8ed60598" }
26+
2427
# Depending on hashes directly for HKDF, can drop this and
2528
# use the re-exported version in bitcoin > 0.32.*.
26-
bitcoin_hashes = { version ="0.15.0", default-features = false }
2729
chacha20-poly1305 = { version = "0.1.1", default-features = false }
2830

2931
[dev-dependencies]
3032
# bitcoind version 26.0 includes support for BIP-324's V2 protocol, but it is disabled by default.
3133
bitcoind = { package = "corepc-node", version = "0.7.1", default-features = false, features = ["26_0","download"] }
3234
hex = { package = "hex-conservative", version = "0.2.0" }
35+
p2p = { package = "bitcoin-p2p-messages", default-features = true, git = "https://github.com/rust-bitcoin/rust-bitcoin", rev = "16cc257c3695dea0e7301a5fa9cab44b8ed60598" }
3336
tokio = { version = "1", features = ["io-util", "net", "rt-multi-thread", "macros"] }

protocol/src/futures.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
//!
3131
//! // Establish BIP-324 encrypted connection
3232
//! let mut protocol = Protocol::new(
33-
//! Network::Bitcoin,
33+
//! p2p::Magic::BITCOIN,
3434
//! Role::Initiator,
3535
//! None, // no garbage bytes
3636
//! None, // no decoy packets
@@ -51,7 +51,6 @@ use std::task::{Context, Poll};
5151
use std::vec;
5252
use std::vec::Vec;
5353

54-
use bitcoin::Network;
5554
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
5655

5756
use crate::{
@@ -146,7 +145,7 @@ impl<R: AsyncRead + Unpin> AsyncRead for ProtocolSessionReader<R> {
146145
///
147146
/// * `Io` - Includes a flag for if the remote probably only understands the V1 protocol.
148147
pub async fn handshake<R, W>(
149-
network: Network,
148+
magic: p2p::Magic,
150149
role: Role,
151150
garbage: Option<Vec<u8>>,
152151
decoys: Option<Vec<Vec<u8>>>,
@@ -166,7 +165,7 @@ where
166165
.map(|vecs| vecs.iter().map(Vec::as_slice).collect());
167166
let decoys_ref = decoy_refs.as_deref();
168167

169-
let handshake = Handshake::<handshake::Initialized>::new(network, role)?;
168+
let handshake = Handshake::<handshake::Initialized>::new(magic, role)?;
170169

171170
// Send local public key and optional garbage.
172171
let key_buffer_len = Handshake::<handshake::Initialized>::send_key_len(garbage_ref);
@@ -284,15 +283,15 @@ where
284283
///
285284
/// * `Io` - Includes a flag for if the remote probably only understands the V1 protocol.
286285
pub async fn new(
287-
network: Network,
286+
magic: p2p::Magic,
288287
role: Role,
289288
garbage: Option<Vec<u8>>,
290289
decoys: Option<Vec<Vec<u8>>>,
291290
reader: R,
292291
mut writer: W,
293292
) -> Result<Protocol<R, W>, ProtocolError> {
294293
let (inbound_cipher, outbound_cipher, session_reader) =
295-
handshake(network, role, garbage, decoys, reader, &mut writer).await?;
294+
handshake(magic, role, garbage, decoys, reader, &mut writer).await?;
296295

297296
Ok(Protocol {
298297
reader: ProtocolReader {
@@ -480,7 +479,6 @@ where
480479
#[cfg(test)]
481480
mod tests {
482481
use super::*;
483-
use bitcoin::Network;
484482

485483
#[tokio::test]
486484
async fn test_async_handshake_functions() {
@@ -493,7 +491,7 @@ mod tests {
493491

494492
let local_handshake = tokio::spawn(async move {
495493
handshake(
496-
Network::Bitcoin,
494+
p2p::Magic::BITCOIN,
497495
Role::Initiator,
498496
Some(b"local garbage".to_vec()),
499497
Some(vec![b"local decoy".to_vec()]),
@@ -505,7 +503,7 @@ mod tests {
505503

506504
let remote_handshake = tokio::spawn(async move {
507505
handshake(
508-
Network::Bitcoin,
506+
p2p::Magic::BITCOIN,
509507
Role::Responder,
510508
Some(b"remote garbage".to_vec()),
511509
Some(vec![b"remote decoy 1".to_vec(), b"remote decoy 2".to_vec()]),
@@ -532,7 +530,7 @@ mod tests {
532530

533531
let local_handshake = tokio::spawn(async move {
534532
handshake(
535-
Network::Bitcoin,
533+
p2p::Magic::BITCOIN,
536534
Role::Initiator,
537535
None,
538536
None,
@@ -545,7 +543,7 @@ mod tests {
545543
let remote_handshake = tokio::spawn(async move {
546544
let large_decoy = vec![0u8; MAX_PACKET_SIZE_FOR_ALLOCATION + 1];
547545
handshake(
548-
Network::Bitcoin,
546+
p2p::Magic::BITCOIN,
549547
Role::Responder,
550548
None,
551549
Some(vec![large_decoy]),

protocol/src/handshake.rs

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,16 @@
88
//! 4. **Version Authentication**: Version packets are exchanged to negotiate the protocol version for the channel.
99
//! 5. **Session Establishment**: The secure communication channel is ready for message exchange.
1010
11-
use bitcoin::{
12-
key::Secp256k1,
13-
secp256k1::{
14-
ellswift::{ElligatorSwift, ElligatorSwiftParty},
15-
PublicKey, SecretKey, Signing,
16-
},
17-
Network,
11+
use secp256k1::{
12+
ellswift::{ElligatorSwift, ElligatorSwiftParty},
13+
PublicKey, Secp256k1, SecretKey, Signing,
1814
};
19-
use rand::Rng;
2015

2116
use crate::{
2217
CipherSession, Error, OutboundCipher, PacketType, Role, SessionKeyMaterial,
2318
NUM_ELLIGATOR_SWIFT_BYTES, NUM_GARBAGE_TERMINTOR_BYTES, VERSION_CONTENT,
2419
};
20+
use rand::Rng;
2521

2622
// Maximum number of garbage bytes before the terminator.
2723
const MAX_NUM_GARBAGE_BYTES: usize = 4095;
@@ -93,7 +89,7 @@ pub enum VersionResult<'a> {
9389
/// 5. Complete - After receiving and authenticating remote's garbage, garbage terminator, decoy packets, and version packet.
9490
pub struct Handshake<State> {
9591
/// Bitcoin network both peers are operating on.
96-
network: Network,
92+
magic: p2p::Magic,
9793
/// Local role in the handshake, initiator or responder.
9894
role: Role,
9995
/// State-specific data.
@@ -103,8 +99,8 @@ pub struct Handshake<State> {
10399
// Methods available in all states.
104100
impl<State> Handshake<State> {
105101
/// Get the network this handshake is operating on.
106-
pub fn network(&self) -> Network {
107-
self.network
102+
pub fn magic(&self) -> p2p::Magic {
103+
self.magic
108104
}
109105

110106
/// Get the local role in the handshake.
@@ -116,22 +112,22 @@ impl<State> Handshake<State> {
116112
impl Handshake<Initialized> {
117113
/// Initialize a V2 transport handshake with a remote peer.
118114
#[cfg(feature = "std")]
119-
pub fn new(network: Network, role: Role) -> Result<Self, Error> {
115+
pub fn new(magic: p2p::Magic, role: Role) -> Result<Self, Error> {
120116
let mut rng = rand::thread_rng();
121117
let curve = Secp256k1::signing_only();
122-
Self::new_with_rng(network, role, &mut rng, &curve)
118+
Self::new_with_rng(magic, role, &mut rng, &curve)
123119
}
124120

125121
/// Initialize a V2 transport handshake with remote peer using supplied RNG and secp context.
126122
pub fn new_with_rng<C: Signing>(
127-
network: Network,
123+
magic: p2p::Magic,
128124
role: Role,
129125
rng: &mut impl Rng,
130126
curve: &Secp256k1<C>,
131127
) -> Result<Self, Error> {
132128
let mut secret_key_buffer = [0u8; 32];
133129
rng.fill(&mut secret_key_buffer[..]);
134-
let sk = SecretKey::from_slice(&secret_key_buffer)?;
130+
let sk = SecretKey::from_byte_array(&secret_key_buffer)?;
135131
let pk = PublicKey::from_secret_key(curve, &sk);
136132
let es = ElligatorSwift::from_pubkey(pk);
137133

@@ -141,7 +137,7 @@ impl Handshake<Initialized> {
141137
};
142138

143139
Ok(Handshake {
144-
network,
140+
magic,
145141
role,
146142
state: Initialized { point },
147143
})
@@ -198,7 +194,7 @@ impl Handshake<Initialized> {
198194
}
199195

200196
Ok(Handshake {
201-
network: self.network,
197+
magic: self.magic,
202198
role: self.role,
203199
state: SentKey {
204200
point: self.state.point,
@@ -235,8 +231,8 @@ impl<'a> Handshake<SentKey<'a>> {
235231
let their_ellswift = ElligatorSwift::from_array(their_key);
236232

237233
// Check for V1 protocol magic bytes
238-
if self.network.magic()
239-
== bitcoin::p2p::Magic::from_bytes(
234+
if self.magic
235+
== p2p::Magic::from_bytes(
240236
their_key[..4]
241237
.try_into()
242238
.expect("64 byte array to have 4 byte prefix"),
@@ -266,11 +262,11 @@ impl<'a> Handshake<SentKey<'a>> {
266262
responder_ellswift,
267263
secret,
268264
party,
269-
self.network,
265+
self.magic,
270266
)?;
271267

272268
Ok(Handshake {
273-
network: self.network,
269+
magic: self.magic,
274270
role: self.role,
275271
state: ReceivedKey {
276272
session_keys,
@@ -373,7 +369,7 @@ impl<'a> Handshake<ReceivedKey<'a>> {
373369
)?;
374370

375371
Ok(Handshake {
376-
network: self.network,
372+
magic: self.magic,
377373
role: self.role,
378374
state: SentVersion {
379375
cipher,
@@ -414,7 +410,7 @@ impl Handshake<SentVersion> {
414410
/// use bip324::{Handshake, GarbageResult, SentVersion};
415411
/// # use bip324::{Role, Network};
416412
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
417-
/// # let mut handshake = Handshake::new(Network::Bitcoin, Role::Initiator)?;
413+
/// # let mut handshake = Handshake::new(p2p::Magic::BITCOIN, Role::Initiator)?;
418414
/// # // ... complete handshake to SentVersion state ...
419415
/// # let handshake: Handshake<SentVersion> = todo!();
420416
///
@@ -445,7 +441,7 @@ impl Handshake<SentVersion> {
445441
Ok((garbage, _ciphertext)) => {
446442
let consumed_bytes = garbage.len() + NUM_GARBAGE_TERMINTOR_BYTES;
447443
let handshake = Handshake {
448-
network: self.network,
444+
magic: self.magic,
449445
role: self.role,
450446
state: ReceivedGarbage {
451447
cipher: self.state.cipher,
@@ -534,7 +530,7 @@ impl<'a> Handshake<ReceivedGarbage<'a>> {
534530
/// use bip324::{Handshake, VersionResult, ReceivedGarbage, NUM_LENGTH_BYTES};
535531
/// # use bip324::{Role, Network};
536532
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
537-
/// # let mut handshake = Handshake::new(Network::Bitcoin, Role::Initiator)?;
533+
/// # let mut handshake = Handshake::new(p2p::Magic::BITCOIN, Role::Initiator)?;
538534
/// # // ... complete handshake to ReceivedGarbage state ...
539535
/// # let mut handshake: Handshake<ReceivedGarbage> = todo!();
540536
/// # let encrypted_data: &[u8] = todo!();
@@ -602,7 +598,7 @@ mod tests {
602598
let responder_garbage = vec![4u8, 5u8];
603599

604600
let init_handshake =
605-
Handshake::<Initialized>::new(Network::Bitcoin, Role::Initiator).unwrap();
601+
Handshake::<Initialized>::new(p2p::Magic::BITCOIN, Role::Initiator).unwrap();
606602

607603
// Send initiator key + garbage.
608604
let mut init_buffer =
@@ -612,7 +608,7 @@ mod tests {
612608
.unwrap();
613609

614610
let resp_handshake =
615-
Handshake::<Initialized>::new(Network::Bitcoin, Role::Responder).unwrap();
611+
Handshake::<Initialized>::new(p2p::Magic::BITCOIN, Role::Responder).unwrap();
616612

617613
// Send responder key + garbage.
618614
let mut resp_buffer =
@@ -740,29 +736,33 @@ mod tests {
740736
fn test_handshake_send_key() {
741737
// Test with valid garbage length
742738
let valid_garbage = vec![0u8; MAX_NUM_GARBAGE_BYTES];
743-
let handshake = Handshake::<Initialized>::new(Network::Bitcoin, Role::Initiator).unwrap();
739+
let handshake =
740+
Handshake::<Initialized>::new(p2p::Magic::BITCOIN, Role::Initiator).unwrap();
744741
let mut buffer = vec![0u8; NUM_ELLIGATOR_SWIFT_BYTES + MAX_NUM_GARBAGE_BYTES];
745742
let result = handshake.send_key(Some(&valid_garbage), &mut buffer);
746743
assert!(result.is_ok());
747744

748745
// Test with garbage length exceeding MAX_NUM_GARBAGE_BYTES
749746
let too_much_garbage = vec![0u8; MAX_NUM_GARBAGE_BYTES + 1];
750-
let handshake = Handshake::<Initialized>::new(Network::Bitcoin, Role::Initiator).unwrap();
747+
let handshake =
748+
Handshake::<Initialized>::new(p2p::Magic::BITCOIN, Role::Initiator).unwrap();
751749
let result = handshake.send_key(Some(&too_much_garbage), &mut buffer);
752750
assert!(matches!(result, Err(Error::TooMuchGarbage)));
753751

754752
// Test too small of buffer
755753
let buffer_size = NUM_ELLIGATOR_SWIFT_BYTES + valid_garbage.len() - 1;
756754
let mut too_small_buffer = vec![0u8; buffer_size];
757-
let handshake = Handshake::<Initialized>::new(Network::Bitcoin, Role::Initiator).unwrap();
755+
let handshake =
756+
Handshake::<Initialized>::new(p2p::Magic::BITCOIN, Role::Initiator).unwrap();
758757
let result = handshake.send_key(Some(&valid_garbage), &mut too_small_buffer);
759758
assert!(
760759
matches!(result, Err(Error::BufferTooSmall { required_bytes }) if required_bytes == NUM_ELLIGATOR_SWIFT_BYTES + valid_garbage.len()),
761760
"Expected BufferTooSmall with correct size"
762761
);
763762

764763
// Test with no garbage
765-
let handshake = Handshake::<Initialized>::new(Network::Bitcoin, Role::Initiator).unwrap();
764+
let handshake =
765+
Handshake::<Initialized>::new(p2p::Magic::BITCOIN, Role::Initiator).unwrap();
766766
let result = handshake.send_key(None, &mut buffer);
767767
assert!(result.is_ok());
768768
}
@@ -773,9 +773,9 @@ mod tests {
773773
#[test]
774774
fn test_handshake_receive_garbage_buffer() {
775775
let init_handshake =
776-
Handshake::<Initialized>::new(Network::Bitcoin, Role::Initiator).unwrap();
776+
Handshake::<Initialized>::new(p2p::Magic::BITCOIN, Role::Initiator).unwrap();
777777
let resp_handshake =
778-
Handshake::<Initialized>::new(Network::Bitcoin, Role::Responder).unwrap();
778+
Handshake::<Initialized>::new(p2p::Magic::BITCOIN, Role::Responder).unwrap();
779779

780780
let mut init_buffer = vec![0u8; NUM_ELLIGATOR_SWIFT_BYTES];
781781
let init_handshake = init_handshake.send_key(None, &mut init_buffer).unwrap();
@@ -855,7 +855,8 @@ mod tests {
855855
#[test]
856856
fn test_handshake_split_garbage() {
857857
// Create a handshake and bring it to the SentVersion state to test split_garbage.
858-
let handshake = Handshake::<Initialized>::new(Network::Bitcoin, Role::Initiator).unwrap();
858+
let handshake =
859+
Handshake::<Initialized>::new(p2p::Magic::BITCOIN, Role::Initiator).unwrap();
859860
let mut buffer = vec![0u8; NUM_ELLIGATOR_SWIFT_BYTES];
860861
let handshake = handshake.send_key(None, &mut buffer).unwrap();
861862

@@ -890,23 +891,26 @@ mod tests {
890891
#[test]
891892
fn test_v1_protocol_detection() {
892893
// Test that receive_key properly detects V1 protocol magic bytes
893-
let handshake = Handshake::<Initialized>::new(Network::Bitcoin, Role::Initiator).unwrap();
894+
let handshake =
895+
Handshake::<Initialized>::new(p2p::Magic::BITCOIN, Role::Initiator).unwrap();
894896
let mut buffer = vec![0u8; NUM_ELLIGATOR_SWIFT_BYTES];
895897
let handshake = handshake.send_key(None, &mut buffer).unwrap();
896898

897899
// Create a key that starts with Bitcoin mainnet magic bytes
898900
let mut v1_key = [0u8; NUM_ELLIGATOR_SWIFT_BYTES];
899-
v1_key[..4].copy_from_slice(&Network::Bitcoin.magic().to_bytes());
901+
let magic = p2p::Magic::BITCOIN.to_bytes();
902+
v1_key[..4].copy_from_slice(&magic);
900903

901904
let result = handshake.receive_key(v1_key);
902905
assert!(matches!(result, Err(Error::V1Protocol)));
903906

904907
// Test with different networks
905-
let handshake = Handshake::<Initialized>::new(Network::Testnet, Role::Responder).unwrap();
908+
let handshake = Handshake::<Initialized>::new(p2p::Magic::SIGNET, Role::Responder).unwrap();
906909
let handshake = handshake.send_key(None, &mut buffer).unwrap();
907910

908911
let mut v1_testnet_key = [0u8; NUM_ELLIGATOR_SWIFT_BYTES];
909-
v1_testnet_key[..4].copy_from_slice(&Network::Testnet.magic().to_bytes());
912+
let foo_magic = p2p::Magic::SIGNET.to_bytes();
913+
v1_testnet_key[..4].copy_from_slice(&foo_magic);
910914

911915
let result = handshake.receive_key(v1_testnet_key);
912916
assert!(matches!(result, Err(Error::V1Protocol)));

0 commit comments

Comments
 (0)