diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml index 7ec4989..dbead92 100644 --- a/protocol/Cargo.toml +++ b/protocol/Cargo.toml @@ -13,13 +13,12 @@ rust-version = "1.63.0" default = ["std"] # High-level wrappers using tokio traits - may affect MSRV requirements. tokio = ["std", "dep:tokio"] -std = ["bitcoin/std", "bitcoin_hashes/std", "chacha20-poly1305/std", "rand/std", "rand/std_rng"] +std = ["bitcoin/rand-std", "bitcoin_hashes/std", "chacha20-poly1305/std"] [dependencies] # The tokio feature may increase the MSRV beyond 1.63.0 # depending on which version of tokio is selected by the caller. tokio = { version = "1", default-features = false, optional = true, features = ["io-util"] } -rand = { version = "0.8.0", default-features = false } bitcoin = { version = "0.32.4", default-features = false } # Depending on hashes directly for HKDF, can drop this and # use the re-exported version in bitcoin > 0.32.*. diff --git a/protocol/src/handshake.rs b/protocol/src/handshake.rs index 6c60440..1591d44 100644 --- a/protocol/src/handshake.rs +++ b/protocol/src/handshake.rs @@ -16,10 +16,9 @@ use bitcoin::{ }, Network, }; -use rand::Rng; use crate::{ - CipherSession, Error, OutboundCipher, PacketType, Role, SessionKeyMaterial, + CipherSession, Error, FillBytes, OutboundCipher, PacketType, Role, SessionKeyMaterial, NUM_ELLIGATOR_SWIFT_BYTES, NUM_GARBAGE_TERMINTOR_BYTES, VERSION_CONTENT, }; @@ -117,7 +116,7 @@ impl Handshake { /// Initialize a V2 transport handshake with a remote peer. #[cfg(feature = "std")] pub fn new(network: Network, role: Role) -> Result { - let mut rng = rand::thread_rng(); + let mut rng = bitcoin::secp256k1::rand::thread_rng(); let curve = Secp256k1::signing_only(); Self::new_with_rng(network, role, &mut rng, &curve) } @@ -126,11 +125,12 @@ impl Handshake { pub fn new_with_rng( network: Network, role: Role, - rng: &mut impl Rng, + rng: &mut impl FillBytes, curve: &Secp256k1, ) -> Result { let mut secret_key_buffer = [0u8; 32]; - rng.fill(&mut secret_key_buffer[..]); + rng.fill_bytes(&mut secret_key_buffer); + debug_assert_ne!([0u8; 32], secret_key_buffer); let sk = SecretKey::from_slice(&secret_key_buffer)?; let pk = PublicKey::from_secret_key(curve, &sk); let es = ElligatorSwift::from_pubkey(pk); diff --git a/protocol/src/io.rs b/protocol/src/io.rs index 845796e..2a3ff4d 100644 --- a/protocol/src/io.rs +++ b/protocol/src/io.rs @@ -582,7 +582,7 @@ where #[cfg(test)] mod tests { use super::*; - use rand::{rngs::StdRng, SeedableRng}; + use bitcoin::secp256k1::rand::{rngs::StdRng, SeedableRng}; use std::io::Cursor; /// Generate deterministic handshake messages for testing. diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 7a626c3..e61c5ab 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -234,10 +234,10 @@ impl fmt::Display for Error { "Packet size exceeds maximum 4MiB size for automatic allocation." ), Error::NoGarbageTerminator => { - write!(f, "More than 4095 bytes of garbage recieved in the handshake before a terminator was sent.") + write!(f, "More than 4095 bytes of garbage received in the handshake before a terminator was sent.") } Error::SecretGeneration(e) => write!(f, "Cannot generate secrets: {e:?}."), - Error::Decryption(e) => write!(f, "Decrytion error: {e:?}."), + Error::Decryption(e) => write!(f, "Decryption error: {e:?}."), Error::V1Protocol => write!(f, "The remote peer is communicating on the V1 protocol."), Error::TooMuchGarbage => write!( f, @@ -376,7 +376,7 @@ impl SessionKeyMaterial { hk.expand(garbage_info, &mut garbage)?; let initiator_garbage_terminator: [u8; 16] = garbage[..16] .try_into() - .expect("first 16 btyes of expanded garbage"); + .expect("first 16 bytes of expanded garbage"); let responder_garbage_terminator: [u8; 16] = garbage[16..] .try_into() .expect("last 16 bytes of expanded garbage"); @@ -783,15 +783,41 @@ impl CipherSession { } } +/// Fill a slice with random bytes. This trait _should_ be cryptographically secure; however, a +/// psuedo-random number generator may be sufficient depending on your security model. +pub trait FillBytes { + /// Fill a 32 byte slice with random data. + fn fill_bytes(&mut self, dest: &mut [u8; 32]); +} + +#[cfg(feature = "std")] +macro_rules! impl_fill_bytes { + ($rng:ident) => { + impl FillBytes for $rng { + fn fill_bytes(&mut self, dest: &mut [u8; 32]) { + use bitcoin::secp256k1::rand::RngCore; + RngCore::fill_bytes(self, dest); + } + } + }; +} + +#[cfg(feature = "std")] +use bitcoin::secp256k1::rand::rngs::{StdRng, ThreadRng}; +#[cfg(feature = "std")] +impl_fill_bytes!(StdRng); +#[cfg(feature = "std")] +impl_fill_bytes!(ThreadRng); + #[cfg(all(test, feature = "std"))] mod tests { use super::*; use bitcoin::secp256k1::ellswift::{ElligatorSwift, ElligatorSwiftParty}; + use bitcoin::secp256k1::rand::Rng; use bitcoin::secp256k1::SecretKey; use core::str::FromStr; use hex::prelude::*; - use rand::Rng; use std::vec; use std::vec::Vec; @@ -972,7 +998,7 @@ mod tests { #[test] fn test_fuzz_packets() { - let mut rng = rand::thread_rng(); + let mut rng = bitcoin::secp256k1::rand::thread_rng(); let alice = SecretKey::from_str("61062ea5071d800bbfd59e2e8b53d47d194b095ae5a4df04936b49772ef0d4d7") .unwrap(); @@ -1039,7 +1065,7 @@ mod tests { #[test] fn test_additional_authenticated_data() { - let mut rng = rand::thread_rng(); + let mut rng = bitcoin::secp256k1::rand::thread_rng(); let alice = SecretKey::from_str("61062ea5071d800bbfd59e2e8b53d47d194b095ae5a4df04936b49772ef0d4d7") .unwrap(); @@ -1088,7 +1114,7 @@ mod tests { #[test] fn test_vector_1() { - let mut rng = rand::thread_rng(); + let mut rng = bitcoin::secp256k1::rand::thread_rng(); let alice = SecretKey::from_str("61062ea5071d800bbfd59e2e8b53d47d194b095ae5a4df04936b49772ef0d4d7") .unwrap();