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
3 changes: 0 additions & 3 deletions protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@ rust-version = "1.63.0"

[features]
default = ["std"]
# High-level wrappers using futures traits.
futures = ["std", "dep:futures"]
# 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"]

[dependencies]
futures = { version = "0.3.30", default-features = false, optional = true, features = ["std"] }
# 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"] }
Expand Down
5 changes: 2 additions & 3 deletions protocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ A BIP324 library to establish and communicate over an encrypted channel.

The library is designed with a bare `no_std` and "Sans I/O" interface to keep it as agnostic as possible to application runtimes, but higher level interfaces are exposed for ease of use.

The `futures` feature includes the high-level `AsyncProcotol` type which helps create and manage an encrypted channel.
The `tokio` feature includes the high-level `AsyncProcotol` type which helps create and manage an encrypted channel.

The lower-level `CipherSession` and `Handshake` types can be directly used by applications which require more control. The handshake performs the one-and-a-half round trip dance between the peers in order to generate secret materials and verify a channel. A successful handshake results in a cipher session which performs the encrypt and decrypt operations for the lifetime of the channel.

## Feature Flags

* `std` -- Standard library dependencies for I/O, memory allocation, and random number generators.
* `futures` -- High level wrappers for asynchronous read and write runtimes using agnostic futures-rs traits.
* `tokio` -- Same wrappers as `futures`, but using the popular tokio runtime's specific traits instead of futures-rs.
* `tokio` -- High level I/O wrappers for asynchronous tokio runtime.

## Minimum Supported Rust Version (MSRV)

Expand Down
46 changes: 19 additions & 27 deletions protocol/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,31 @@
//! connections over Read/Write transports.

use core::fmt;

#[cfg(feature = "std")]
#[cfg(feature = "tokio")]
use std::vec;
#[cfg(feature = "std")]
use std::vec::Vec;

use bitcoin::Network;
use crate::{Error, PacketType};

// Default to the futures-rs traits, but can overwrite with more specific
// tokio implementations for easier caller integration.
#[cfg(all(feature = "futures", not(feature = "tokio")))]
use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
#[cfg(feature = "tokio")]
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use bitcoin::Network;

#[cfg(feature = "tokio")]
use crate::{
handshake::{self, GarbageResult, VersionResult},
Error, Handshake, InboundCipher, OutboundCipher, PacketType, Role, NUM_ELLIGATOR_SWIFT_BYTES,
Handshake, InboundCipher, OutboundCipher, Role, NUM_ELLIGATOR_SWIFT_BYTES,
NUM_GARBAGE_TERMINTOR_BYTES,
};

#[cfg(feature = "tokio")]
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};

/// A decrypted BIP324 payload with its packet type.
#[cfg(feature = "std")]
pub struct Payload {
contents: Vec<u8>,
packet_type: PacketType,
}

#[cfg(feature = "std")]
impl Payload {
/// Create a new payload.
pub fn new(contents: Vec<u8>, packet_type: PacketType) -> Self {
Expand All @@ -54,7 +50,6 @@ impl Payload {
}

/// High level error type for the protocol interface.
#[cfg(feature = "std")]
#[derive(Debug)]
pub enum ProtocolError {
/// Wrap all IO errors with suggestion for next step on failure.
Expand All @@ -64,7 +59,6 @@ pub enum ProtocolError {
}

/// Suggest to caller next step on protocol failure.
#[cfg(feature = "std")]
#[derive(Debug)]
pub enum ProtocolFailureSuggestion {
/// Caller could attempt to retry the connection with protocol V1 if desired.
Expand All @@ -73,7 +67,6 @@ pub enum ProtocolFailureSuggestion {
Abort,
}

#[cfg(feature = "std")]
impl From<std::io::Error> for ProtocolError {
fn from(error: std::io::Error) -> Self {
// Detect IO errors which possibly mean the remote doesn't understand
Expand All @@ -94,19 +87,18 @@ impl From<std::io::Error> for ProtocolError {
}
}

#[cfg(feature = "std")]
impl From<Error> for ProtocolError {
fn from(error: Error) -> Self {
ProtocolError::Internal(error)
}
}

#[cfg(feature = "std")]
impl ProtocolError {
/// Create an EOF error that suggests retrying with V1 protocol.
///
/// This is used when the remote peer closes the connection during handshake,
/// which often indicates they don't support the V2 protocol.
#[cfg(feature = "tokio")]
fn eof() -> Self {
ProtocolError::Io(
std::io::Error::new(
Expand All @@ -118,7 +110,6 @@ impl ProtocolError {
}
}

#[cfg(feature = "std")]
impl std::error::Error for ProtocolError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Expand All @@ -128,7 +119,6 @@ impl std::error::Error for ProtocolError {
}
}

#[cfg(feature = "std")]
impl fmt::Display for ProtocolError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expand All @@ -149,13 +139,13 @@ impl fmt::Display for ProtocolError {
}

/// A protocol session with handshake and send/receive packet management.
#[cfg(any(feature = "futures", feature = "tokio"))]
#[cfg(feature = "tokio")]
pub struct AsyncProtocol {
reader: AsyncProtocolReader,
writer: AsyncProtocolWriter,
}

#[cfg(any(feature = "futures", feature = "tokio"))]
#[cfg(feature = "tokio")]
impl AsyncProtocol {
/// New protocol session which completes the initial handshake and returns a handler.
///
Expand Down Expand Up @@ -297,7 +287,9 @@ impl AsyncProtocol {
}

/// State machine of an asynchronous packet read.
#[cfg(any(feature = "futures", feature = "tokio"))]
///
/// This maintains state between await points to ensure cancellation safety.
#[cfg(feature = "tokio")]
#[derive(Debug)]
enum DecryptState {
ReadingLength {
Expand All @@ -310,7 +302,7 @@ enum DecryptState {
},
}

#[cfg(any(feature = "futures", feature = "tokio"))]
#[cfg(feature = "tokio")]
impl DecryptState {
/// Transition state to reading the length bytes.
fn init_reading_length() -> Self {
Expand All @@ -330,13 +322,13 @@ impl DecryptState {
}

/// Manages an async buffer to automatically decrypt contents of received packets.
#[cfg(any(feature = "futures", feature = "tokio"))]
#[cfg(feature = "tokio")]
pub struct AsyncProtocolReader {
inbound_cipher: InboundCipher,
state: DecryptState,
}

#[cfg(any(feature = "futures", feature = "tokio"))]
#[cfg(feature = "tokio")]
impl AsyncProtocolReader {
/// Decrypt contents of received packet from buffer.
///
Expand Down Expand Up @@ -397,12 +389,12 @@ impl AsyncProtocolReader {
}

/// Manages an async buffer to automatically encrypt and send contents in packets.
#[cfg(any(feature = "futures", feature = "tokio"))]
#[cfg(feature = "tokio")]
pub struct AsyncProtocolWriter {
outbound_cipher: OutboundCipher,
}

#[cfg(any(feature = "futures", feature = "tokio"))]
#[cfg(feature = "tokio")]
impl AsyncProtocolWriter {
/// Encrypt contents and write packet buffer.
///
Expand Down
9 changes: 1 addition & 8 deletions protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extern crate std;

mod fschacha20poly1305;
mod handshake;
#[cfg(any(feature = "futures", feature = "tokio"))]
#[cfg(feature = "std")]
pub mod io;
#[cfg(feature = "std")]
pub mod serde;
Expand All @@ -43,14 +43,7 @@ pub use handshake::{
GarbageResult, Handshake, Initialized, ReceivedGarbage, ReceivedKey, SentKey, SentVersion,
VersionResult,
};
// Re-exports from io module (async I/O types for backwards compatibility)
#[cfg(any(feature = "futures", feature = "tokio"))]
pub use io::{
AsyncProtocol, AsyncProtocolReader, AsyncProtocolWriter, Payload, ProtocolError,
ProtocolFailureSuggestion,
};

// Internal imports
use fschacha20poly1305::{FSChaCha20, FSChaCha20Poly1305};

/// Value for header byte with the decoy flag flipped to true.
Expand Down
7 changes: 5 additions & 2 deletions proxy/src/bin/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
use std::str::FromStr;

use bip324::{
io::{AsyncProtocol, ProtocolFailureSuggestion},
serde::{deserialize, serialize},
AsyncProtocol, PacketType, ProtocolFailureSuggestion, Role,
PacketType, Role,
};
use bip324_proxy::{V1ProtocolReader, V1ProtocolWriter};
use bitcoin::Network;
Expand Down Expand Up @@ -87,7 +88,9 @@ async fn v2_proxy(
.await
{
Ok(p) => p,
Err(bip324::ProtocolError::Io(_, ProtocolFailureSuggestion::RetryV1)) if v1_fallback => {
Err(bip324::io::ProtocolError::Io(_, ProtocolFailureSuggestion::RetryV1))
if v1_fallback =>
{
info!("V2 protocol failed, falling back to V1...");
return v1_proxy(client, network).await;
}
Expand Down
6 changes: 3 additions & 3 deletions proxy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub enum Error {
WrongCommand,
Serde,
Io(std::io::Error),
Protocol(bip324::ProtocolError),
Protocol(bip324::io::ProtocolError),
}

impl fmt::Display for Error {
Expand All @@ -61,8 +61,8 @@ impl std::error::Error for Error {
}
}

impl From<bip324::ProtocolError> for Error {
fn from(e: bip324::ProtocolError) -> Self {
impl From<bip324::io::ProtocolError> for Error {
fn from(e: bip324::io::ProtocolError) -> Self {
Error::Protocol(e)
}
}
Expand Down