Skip to content

Commit

Permalink
starting to handle args/opts
Browse files Browse the repository at this point in the history
  • Loading branch information
jmwample committed Mar 24, 2024
1 parent cd01f66 commit 6103205
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 53 deletions.
1 change: 1 addition & 0 deletions crates/obfs4/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ group = "0.13.0"
hex = "0.4.3"
tracing = "0.1.40"
colored = "2.0.4"
base64 = "0.22.0"

## Networking tools
pin-project = "1.1.3"
Expand Down
17 changes: 11 additions & 6 deletions crates/obfs4/src/bin/fwd/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,9 @@ use std::{
/// Client Socks address to listen on.
const CLIENT_SOCKS_ADDR: &str = "127.0.0.1:0";


/// Pre-generated / shared key for use while running in debug mode.
#[cfg(debug)]
const DEV_PRIV_KEY: [u8;32] = b"0123456789abcdeffedcba9876543210";
const DEV_PRIV_KEY: [u8; 32] = b"0123456789abcdeffedcba9876543210";

const U4: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 9000);
const U6: SocketAddr = SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 9000);
Expand Down Expand Up @@ -301,7 +300,8 @@ where

// build the pluggable transport client and then dial, completing the
// connection and handshake when the `wrap(..)` is await-ed.
let mut b = builder.options(ptrs::args::Args::new())?;
let args = &ptrs::args::Args::new();
let mut b = builder.options(args)?;
let pt_client = builder.build();
let mut pt_conn = match ptrs::ClientTransport::<TcpStream, std::io::Error>::establish(
pt_client,
Expand Down Expand Up @@ -473,7 +473,10 @@ where
S: ptrs::ServerTransport<TcpStream>,
<S as ptrs::ServerTransport<In>>::OutErr: 'static,
{
let mut pt_conn = server.reveal(conn).await.context("server handshake failed {client_addr}")?;
let mut pt_conn = server
.reveal(conn)
.await
.context("server handshake failed {client_addr}")?;

let mut remote_conn = TcpStream::connect(remote_addr).await?;

Expand Down Expand Up @@ -506,7 +509,10 @@ where
S: ptrs::ServerTransport<In> + Clone + Send + Sync + 'static,
<S as ptrs::ServerTransport<In>>::OutErr: 'static,
{
let mut pt_conn = server.reveal(conn).await.context("server handshake failed {client_addr}")?;
let mut pt_conn = server
.reveal(conn)
.await
.context("server handshake failed {client_addr}")?;

let (mut r, mut w) = tokio::io::split(pt_conn);
match tokio::io::copy(&mut r, &mut w).await {
Expand All @@ -526,4 +532,3 @@ where

Ok(())
}

14 changes: 7 additions & 7 deletions crates/obfs4/src/obfs4/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,37 +72,37 @@ impl ClientBuilder {
})
}

pub fn with_node_pubkey(mut self, pubkey: [u8; KEY_LENGTH]) -> Self {
pub fn with_node_pubkey(&mut self, pubkey: [u8; KEY_LENGTH]) -> &mut Self {
self.station_pubkey = pubkey;
self
}

pub fn with_statefile_path(mut self, path: &str) -> Self {
pub fn with_statefile_path(&mut self, path: &str) -> &mut Self {
self.statefile_path = Some(path.into());
self
}

pub fn with_node_id(mut self, id: [u8; NODE_ID_LENGTH]) -> Self {
pub fn with_node_id(&mut self, id: [u8; NODE_ID_LENGTH]) -> &mut Self {
self.station_id = id;
self
}

pub fn with_iat_mode(mut self, iat: IAT) -> Self {
pub fn with_iat_mode(&mut self, iat: IAT) -> &mut Self {
self.iat_mode = iat;
self
}

pub fn with_handshake_timeout(mut self, d: Duration) -> Self {
pub fn with_handshake_timeout(&mut self, d: Duration) -> &mut Self {
self.handshake_timeout = MaybeTimeout::Length(d);
self
}

pub fn with_handshake_deadline(mut self, deadline: Instant) -> Self {
pub fn with_handshake_deadline(&mut self, deadline: Instant) -> &mut Self {
self.handshake_timeout = MaybeTimeout::Fixed(deadline);
self
}

pub fn fail_fast(mut self) -> Self {
pub fn fail_fast(&mut self) -> &mut Self {
self.handshake_timeout = MaybeTimeout::Unset;
self
}
Expand Down
15 changes: 5 additions & 10 deletions crates/obfs4/src/obfs4/handshake/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ fn test_obfs4_roundtrip() -> Result<()> {

let chs_materials = CHSMaterials::new(relay_private.pk, colorize(sid));

let mut server = Server::new_from_random(&mut rng);
server.identity_keys = relay_private;
let server = Server::new_from_key(relay_private);

let shs_materials = SHSMaterials {
identity_keys: server.identity_keys.clone(),
Expand All @@ -70,8 +69,8 @@ fn test_obfs4_roundtrip() -> Result<()> {
// Same as previous test, but use the higher-level APIs instead.
#[test]
fn test_obfs4_roundtrip_highlevel() -> Result<()> {
let mut rng = testing_rng();
let relay_secret = StaticSecret::random_from_rng(&mut rng);
let rng = testing_rng();
let relay_secret = StaticSecret::random_from_rng(rng);
let relay_public = PublicKey::from(&relay_secret);
let relay_identity = RsaIdentity::from_bytes(&[12; 20]).unwrap();
let relay_ntpk = Obfs4NtorPublicKey {
Expand All @@ -85,8 +84,7 @@ fn test_obfs4_roundtrip_highlevel() -> Result<()> {
pk: relay_ntpk,
sk: relay_secret,
};
let mut server = Server::new_from_random(&mut rng);
server.identity_keys = relay_ntsk.clone();
let server = Server::new_from_key(relay_ntsk.clone());
let shs_materials = [SHSMaterials::new(
&relay_ntsk,
"s-yyy".into(),
Expand All @@ -110,8 +108,6 @@ fn test_obfs4_roundtrip_highlevel() -> Result<()> {
#[test]
fn test_obfs4_testvec_compat() -> Result<()> {
init_subscriber();
let rng = rand::thread_rng();

let b_sk = hex!("a83fdd04eb9ed77a2b38d86092a09a1cecfb93a7bdec0da35e542775b2e7af6e");
let x_sk = hex!("308ff4f3a0ebe8c1a93bcd40d67e3eec6b856aa5c07ef6d5a3d3cedf13dcf150");
let y_sk = hex!("881f9ad60e0833a627f0c47f5aafbdcb0b5471800eaeaa1e678291b947e4295c");
Expand All @@ -125,8 +121,7 @@ fn test_obfs4_testvec_compat() -> Result<()> {
pk: (&sk).into(),
};
let relay_sk = Obfs4NtorSecretKey { pk, sk };
let mut server = Server::new_from_random(rng);
server.identity_keys = relay_sk;
let server = Server::new_from_key(relay_sk.clone());

let x: StaticSecret = x_sk.into();

Expand Down
29 changes: 29 additions & 0 deletions crates/obfs4/src/obfs4/handshake/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::{

use std::borrow::Borrow;

use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _};
use bytes::BytesMut;
use digest::Mac;
use hmac::Hmac;
Expand Down Expand Up @@ -124,6 +125,8 @@ pub(crate) struct Obfs4NtorPublicKey {
}

impl Obfs4NtorPublicKey {
const CERT_LENGTH: usize = NODE_ID_LENGTH + NODE_PUBKEY_LENGTH;
const CERT_SUFFIX: &'static str = "==";
/// Construct a new Obfs4NtorPublicKey from its components.
#[allow(unused)]
pub(crate) fn new(pk: [u8; NODE_PUBKEY_LENGTH], id: [u8; NODE_ID_LENGTH]) -> Self {
Expand All @@ -134,6 +137,32 @@ impl Obfs4NtorPublicKey {
}
}

impl std::str::FromStr for Obfs4NtorPublicKey {
type Err = Error;
fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
let mut cert = String::from(s);
cert.push_str(Self::CERT_SUFFIX);
let decoded = STANDARD_NO_PAD
.decode(cert.as_bytes())
.map_err(|e| format!("failed to decode cert: {e}"))?;
if decoded.len() != Self::CERT_LENGTH {
return Err(format!("cert length {} is invalid", decoded.len()).into());
}
let id: [u8; NODE_ID_LENGTH] = decoded[..NODE_ID_LENGTH].try_into().unwrap();
let pk: [u8; NODE_PUBKEY_LENGTH] = decoded[NODE_ID_LENGTH..].try_into().unwrap();
Ok(Obfs4NtorPublicKey::new(pk, id))
}
}

#[allow(clippy::to_string_trait_impl)]
impl std::string::ToString for Obfs4NtorPublicKey {
fn to_string(&self) -> String {
let mut s = Vec::from(self.id.as_bytes());
s.extend(self.pk.as_bytes());
STANDARD_NO_PAD.encode(s)
}
}

/// Secret key information used by a relay for the ntor v3 handshake.
#[derive(Clone)]
pub(crate) struct Obfs4NtorSecretKey {
Expand Down
17 changes: 13 additions & 4 deletions crates/obfs4/src/obfs4/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
probdist::{self, WeightedDist},
},
obfs4::{constants::*, framing, sessions::Session},
Result,
Error, Result,
};

use bytes::{Buf, BytesMut};
Expand All @@ -25,9 +25,6 @@ use std::{

use super::framing::{FrameError, Messages};

// mod handshake_client;
// mod handshake_server;

#[allow(dead_code, unused)]
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub enum IAT {
Expand All @@ -45,6 +42,18 @@ pub(crate) enum MaybeTimeout {
Unset,
}

impl std::str::FromStr for IAT {
type Err = Error;
fn from_str(s: &str) -> StdResult<Self, Self::Err> {
match s {
"0" => Ok(IAT::Off),
"1" => Ok(IAT::Enabled),
"2" => Ok(IAT::Paranoid),
_ => Err(format!("invalid iat-mode '{s}'").into()),
}
}
}

impl MaybeTimeout {
pub(crate) fn duration(&self) -> Option<Duration> {
match self {
Expand Down
55 changes: 34 additions & 21 deletions crates/obfs4/src/obfs4/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use super::*;
use crate::{
common::{
colorize,
curve25519::PublicKey,
curve25519::StaticSecret,
drbg,
replay_filter::{self, ReplayFilter},
Expand All @@ -21,7 +22,7 @@ use crate::{
Error, Result,
};

use std::{ops::Deref, sync::Arc};
use std::{borrow::BorrowMut, ops::Deref, sync::Arc};

use bytes::{Buf, BufMut, Bytes};
use hmac::{Hmac, Mac};
Expand Down Expand Up @@ -158,24 +159,20 @@ impl Deref for Server {
}
}


impl Server {
pub fn new_from_random<R: RngCore + CryptoRng>(mut rng: R) -> Self {
let mut id = [0_u8; 20];
// Random bytes will work for testing, but aren't necessarily actually a valid id.
rng.fill_bytes(&mut id);

// Generated identity secret key does not need to be elligator2 representable
// so we can use the regular dalek_x25519 key generation.
let sk = StaticSecret::random_from_rng(rng);

pub fn new(sec: [u8; KEY_LENGTH], id: [u8; NODE_ID_LENGTH]) -> Self {
let sk = StaticSecret::from(sec);
let pk = Obfs4NtorPublicKey {
pk: (&sk).into(),
pk: PublicKey::from(&sk),
id: id.into(),
};

let identity_keys = Obfs4NtorSecretKey { pk, sk };

Self::new_from_key(identity_keys)
}

pub(crate) fn new_from_key(identity_keys: Obfs4NtorSecretKey) -> Self {
Self(Arc::new(ServerInner {
handshake_timeout: Some(SERVER_HANDSHAKE_TIMEOUT),
identity_keys,
Expand All @@ -187,17 +184,28 @@ impl Server {
}))
}

pub fn new_from_random<R: RngCore + CryptoRng>(mut rng: R) -> Self {
let mut id = [0_u8; 20];
// Random bytes will work for testing, but aren't necessarily actually a valid id.
rng.fill_bytes(&mut id);

// Generated identity secret key does not need to be elligator2 representable
// so we can use the regular dalek_x25519 key generation.
let sk = StaticSecret::random_from_rng(rng);

let pk = Obfs4NtorPublicKey {
pk: PublicKey::from(&sk),
id: id.into(),
};

let identity_keys = Obfs4NtorSecretKey { pk, sk };

Self::new_from_key(identity_keys)
}

pub fn getrandom() -> Self {
let identity_keys = Obfs4NtorSecretKey::getrandom();
Self(Arc::new(ServerInner {
identity_keys,
handshake_timeout: Some(SERVER_HANDSHAKE_TIMEOUT),
iat_mode: IAT::Off,
biased: false,

// metrics: Arc::new(std::sync::Mutex::new(ServerMetrics {})),
replay_filter: ReplayFilter::new(REPLAY_TTL),
}))
Self::new_from_key(identity_keys)
}

pub async fn wrap<T>(self, stream: T) -> Result<Obfs4Stream<T>>
Expand All @@ -210,6 +218,11 @@ impl Server {
session.handshake(&self, stream, deadline).await
}

// pub fn set_iat_mode(&mut self, mode: IAT) -> &Self {
// self.iat_mode = mode;
// self
// }

pub fn set_args(&mut self, args: &dyn std::any::Any) -> Result<&Self> {
Ok(self)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/obfs4/src/obfs4/sessions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ impl Server {
let mut buf = [0_u8; MAX_HANDSHAKE_LENGTH];
loop {
let n = stream.read(&mut buf).await?;
if n==0 {
if n == 0 {
stream.shutdown().await?;
return Err(IoError::from(IoErrorKind::UnexpectedEof).into());
}
Expand Down
Loading

0 comments on commit 6103205

Please sign in to comment.