From 00349a9be9cfc8f694f7a7d667e9b1a16c6f5df4 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Fri, 19 Apr 2024 19:40:47 -0700 Subject: [PATCH 1/4] Make read no std --- protocol/src/lib.rs | 88 ++++++++++++++++++++++++++++++--------------- proxy/src/lib.rs | 12 ++++--- 2 files changed, 67 insertions(+), 33 deletions(-) diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index a1cf131..60771ee 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -30,7 +30,7 @@ use secp256k1::{ const DECOY_BYTES: usize = 1; /// Number of bytes for the authentication tag of a packet. const TAG_BYTES: usize = 16; -/// Number of bytes for the length encoding of a packet. +/// Number of bytes for the length encoding prefix of a packet. const LENGTH_BYTES: usize = 3; /// Value for decoy flag. const DECOY: u8 = 128; @@ -136,6 +136,19 @@ pub struct ReceivedMessage { pub message: Option>, } +impl ReceivedMessage { + pub fn new(msg_bytes: &[u8]) -> Result { + let header = msg_bytes.first().ok_or(Error::MessageLengthTooSmall)?; + if header.eq(&DECOY) { + Ok(ReceivedMessage { message: None }) + } else { + Ok(ReceivedMessage { + message: Some(msg_bytes[1..].to_vec()), + }) + } + } +} + #[derive(Clone, Debug)] pub struct PacketReader { length_decoding_cipher: FSChaCha20, @@ -158,7 +171,6 @@ impl PacketReader { /// The length to be read into the buffer next to receive the full message from the peer. pub fn decypt_len(&mut self, len_bytes: [u8; 3]) -> usize { let mut enc_content_len = [0u8; 3]; - // TODO: should we just make len_butes mutable? enc_content_len.copy_from_slice(&len_bytes); self.length_decoding_cipher .crypt(&mut enc_content_len) @@ -176,40 +188,51 @@ impl PacketReader { /// /// # Arguments /// - /// - `contents` - The message from the peer. + /// - `ciphertext` - The message from the peer. + /// - `contents` - Mutable buffer to write plaintext. /// - `aad` - Optional authentication for the peer, currently only used for the first round of messages. /// - /// # Returns - /// - /// The message from the peer. - /// /// # Errors /// /// Fails if the packet was not decrypted or authenticated properly. pub fn decrypt_contents( &mut self, - contents: Vec, - aad: Option>, - ) -> Result { + ciphertext: &[u8], + contents: &mut [u8], + aad: Option<&[u8]>, + ) -> Result<(), Error> { let auth = aad.unwrap_or_default(); - let mut contents = contents.clone(); - let contents_len = contents.len(); - let (ciphertext, tag) = contents.split_at_mut(contents_len - 16); + let (msg, tag) = ciphertext.split_at(ciphertext.len() - TAG_BYTES); + contents.copy_from_slice(msg); self.packet_decoding_cipher.decrypt( - &auth, - ciphertext, - tag.try_into().expect("16 bytes"), + auth, + contents, + tag.try_into().map_err(|_| Error::MessageLengthTooSmall)?, )?; - let header = *ciphertext - .first() - .expect("All contents should include a header."); - if header.eq(&DECOY) { - return Ok(ReceivedMessage { message: None }); - } - let message = ciphertext[1..].to_vec(); - Ok(ReceivedMessage { - message: Some(message), - }) + + Ok(()) + } + + /// Decrypt the rest of the message from the peer, excluding the 3 length bytes. This method should only be called after + /// calling `decrypt_len` on the first three bytes of the buffer. + /// + /// # Arguments + /// + /// - `ciphertext` - The message from the peer. + /// - `aad` - Optional authentication for the peer, currently only used for the first round of messages. + /// + /// # Errors + /// + /// Fails if the packet was not decrypted or authenticated properly. + #[cfg(feature = "std")] + pub fn decrypt_contents_with_alloc( + &mut self, + ciphertext: &[u8], + aad: Option<&[u8]>, + ) -> Result, Error> { + let mut contents = vec![0u8; ciphertext.len() - TAG_BYTES]; + self.decrypt_contents(ciphertext, &mut contents, aad)?; + Ok(contents) } } @@ -403,12 +426,19 @@ impl PacketHandler { /// # Errors /// /// Fails if the packet was not decrypted or authenticated properly. + #[cfg(feature = "std")] pub fn decrypt_contents( &mut self, contents: Vec, - aad: Option>, + aad: Option<&[u8]>, ) -> Result { - self.packet_reader.decrypt_contents(contents, aad) + let contents = self + .packet_reader + .decrypt_contents_with_alloc(&contents, aad)?; + + let message = ReceivedMessage::new(&contents)?; + + Ok(message) } /// Decrypt the one or more messages from bytes received by a V2 peer. @@ -776,7 +806,7 @@ impl<'a> Handshake<'a> { // moves along state in the ciphers. packet_handler.decrypt_contents( message[LENGTH_BYTES..packet_length + LENGTH_BYTES].to_vec(), - Some(garbage.to_vec()), + Some(garbage), )?; Ok(()) diff --git a/proxy/src/lib.rs b/proxy/src/lib.rs index 3b20a59..878e51f 100644 --- a/proxy/src/lib.rs +++ b/proxy/src/lib.rs @@ -6,6 +6,7 @@ use std::fmt; use std::net::SocketAddr; +use bip324::ReceivedMessage; use bip324::{PacketReader, PacketWriter}; use bitcoin::consensus::Decodable; use bitcoin::hashes::sha256d; @@ -165,12 +166,15 @@ pub async fn read_v2( let packet_bytes_len = decrypter.decypt_len(length_bytes); let mut packet_bytes = vec![0u8; packet_bytes_len]; input.read_exact(&mut packet_bytes).await?; - let contents = decrypter - .decrypt_contents(packet_bytes, None) - .expect("decrypt") - .message + let raw = decrypter + .decrypt_contents_with_alloc(&packet_bytes, None) .expect("decrypt"); + let contents = ReceivedMessage::new(&raw) + .expect("some bytes") + .message + .expect("not a decoy"); + // If packet is using short or full ID. let (cmd, cmd_index) = if contents.starts_with(&[0u8]) { (to_ascii(contents[1..13].try_into().expect("12 bytes")), 13) From 85b94617fdbca5c11d3334c33fe356398b011154 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Sat, 20 Apr 2024 07:20:25 -0700 Subject: [PATCH 2/4] Read to no std --- protocol/src/lib.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 60771ee..08fb4f4 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -10,6 +10,7 @@ mod fschacha20poly1305; mod hkdf; use core::fmt; +use std::println; pub use bitcoin::Network; use bitcoin_hashes::sha256; @@ -203,7 +204,7 @@ impl PacketReader { ) -> Result<(), Error> { let auth = aad.unwrap_or_default(); let (msg, tag) = ciphertext.split_at(ciphertext.len() - TAG_BYTES); - contents.copy_from_slice(msg); + contents[0..msg.len()].copy_from_slice(msg); self.packet_decoding_cipher.decrypt( auth, contents, @@ -804,8 +805,12 @@ impl<'a> Handshake<'a> { // Assuming no decoy packets so AAD is set on version packet. // The version packet is ignored in this version of the protocol, but // moves along state in the ciphers. - packet_handler.decrypt_contents( - message[LENGTH_BYTES..packet_length + LENGTH_BYTES].to_vec(), + + // Version packets have 0 contents. + let mut version_packet = [0u8; DECOY_BYTES + TAG_BYTES]; + packet_handler.packet_reader.decrypt_contents( + &message[LENGTH_BYTES..packet_length + LENGTH_BYTES], + &mut version_packet, Some(garbage), )?; From 2a61042accdc754d10b55e5d28b63d0dc188c8f6 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Sat, 20 Apr 2024 07:34:32 -0700 Subject: [PATCH 3/4] Only decrypt known size --- protocol/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 08fb4f4..7cefaae 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -190,8 +190,8 @@ impl PacketReader { /// # Arguments /// /// - `ciphertext` - The message from the peer. - /// - `contents` - Mutable buffer to write plaintext. - /// - `aad` - Optional authentication for the peer, currently only used for the first round of messages. + /// - `contents` - Mutable buffer to write plaintext. + /// - `aad` - Optional authentication for the peer, currently only used for the first round of messages. /// /// # Errors /// @@ -207,7 +207,7 @@ impl PacketReader { contents[0..msg.len()].copy_from_slice(msg); self.packet_decoding_cipher.decrypt( auth, - contents, + &mut contents[0..msg.len()], tag.try_into().map_err(|_| Error::MessageLengthTooSmall)?, )?; From 85e9720ad0415133f6c370178bef4da4c5cfdc46 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Sat, 20 Apr 2024 14:54:09 -0700 Subject: [PATCH 4/4] Drop unused import --- protocol/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 7cefaae..c21a758 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -10,7 +10,6 @@ mod fschacha20poly1305; mod hkdf; use core::fmt; -use std::println; pub use bitcoin::Network; use bitcoin_hashes::sha256;