Skip to content

Commit

Permalink
Removed v2 onion service support
Browse files Browse the repository at this point in the history
  • Loading branch information
teawithsand committed Aug 29, 2021
1 parent 37e7f78 commit bbc11a7
Show file tree
Hide file tree
Showing 12 changed files with 16 additions and 535 deletions.
7 changes: 1 addition & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ readme = "README.MD"
keywords = ["tor", "controller", "tokio", "onion"]

[features]
default = ["serialize", "v2", "v3", "control"]
default = ["serialize", "v3", "control"]
serialize = ["serde", "serde_derive", "base32", "base64"]
control = ["tokio", "rand", "hex", "sha2", "hmac"]
v2 = ["openssl", "base32", "base32", "base64", "sha1"]
v3 = ["rand", "ed25519-dalek", "base32", "base64", "sha3"]
vendored_openssl = ["openssl/vendored"] # Thanks to this syntax we can allow user to choose whatever or not vendored openssl should be used

[badges]
travis-ci = { repository = "teawithsand/torut", branch = "master" }
Expand All @@ -28,11 +26,8 @@ serde_derive = { version = "1.0", optional = true }

derive_more = "0.99.16"

openssl = { version = "0.10", optional = true }

sha3 = { version = "0.9", optional = true } # for onion service v3 signature
sha2 = { version = "0.9", optional = true } # for ed25519-dalek key
sha1 = { version = "0.6", optional = true } # for onion service v2 signature
hmac = { version = "0.11.0", optional = true } # for authentication with tor controller

ed25519-dalek = { version = "1.0", optional = true }
Expand Down
14 changes: 4 additions & 10 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,21 @@ Torut is tor controller written in rust similar to
It tries to reasonably implement [specification of tor control port proto](https://gitweb.torproject.org/torspec.git/tree/control-spec.txt)
It works asynchronously with tokio and async/await.

It implements onion service key and address generation and serialization on it's own without tor process.
It implements onion service key and address generation and serialization on its own without tor process.

Right now logic is quite tightly coupled with tokio so there is no way to
remove tokio from dependencies and make all functions synchronous.

## Status of onion service V2
For now, code handling them is still in this library, but it will be removed soon.
Code handling V2 services has been removed in 0.2 release, since tor project removed(should have?) v2 handling code
from their codebase as well.
See [This page](https://blog.torproject.org/v2-deprecation-timeline)

## Usage considerations
In case there is no OpenSSL installed on your target machine you can embbed it into rust binary.
In order to do that use `vendored_openssl` feature.

# Testing
Tests in torut are split into two parts:
these which do use tor and these which do not use tor.
In order to enable tests which use tor use `RUSTFLAGS="--cfg=testtor"`
and provide `TORUT_TESTING_TOR_BINARY` environment variable containing path to tor binary.
Testing tor binary MUST be run with `--test-threads=1` for instance like:

`$ RUSTFLAGS="--cfg testtor" cargo test -- --test-threads=1`

Please also note that some of tests may fail against older
versions of tor(for instance with apt-get installed one on default ubuntu installation)
`$ RUSTFLAGS="--cfg testtor" cargo test -- --test-threads=1`
2 changes: 0 additions & 2 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ Note: there are TODOs in code as well
Figure out how to make tests utilizing tor process run with CI(is this possible?)
At least create a docker container so testing with tor is reasonably predictable

Migrate away from openssl for v2 key (de)serialization. Something more lightweight would be better.

Cleanup pub(crate) for fuzzing functions. Create modules exporting fuzzing stuff and then reexport them in src/fuzz.rs

Add tests for conditional compilation features on travis and some bash file
Expand Down
88 changes: 0 additions & 88 deletions src/control/conn/authenticated_conn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,58 +546,6 @@ impl<S, F, H> AuthenticatedConn<S, H>
Ok(res)
}

#[deprecated(
since = "0.1.10",
note = "V2 onion services are deprecated by tor and soon will stop working; It will be removed in next release"
)]
#[cfg(any(feature = "v2"))]
/// add_onion sends `ADD_ONION` command which spins up new onion service.
/// Using given tor secret key and some configuration values.
///
/// For onion service v3 take a look at `add_onion_v3`
///
/// # Parameters
/// `key` - key to use to start onion service
/// `detach` - if set to `false` it makes onion service disappear once control connection is closed
/// `non_anonymous` - if set to `true` it runs special single hop onion service. It can't be done on default compilation of tor.
/// `max_streams_close_circuit` - if set to `true` closes circuit if max streams is reached
/// `max_num_streams` - maximum amount of streams which may be attached to RP point. Zero is unlimited.
/// `None` is default and may vary depending on tor version being used.
/// `listeners` - set of pairs of ports and addresses to which connections should be redirected to.
/// Must contain at least one entry. Otherwise error is returned.
///
/// It does not support basic auth yet.
/// It does not support tor-side generated keys yet.
pub async fn add_onion_v2(
&mut self,
key: &crate::onion::TorSecretKeyV2,
detach: bool,
non_anonymous: bool,
max_streams_close_circuit: bool,
max_num_streams: Option<u16>,
listeners: &mut impl Iterator<Item=&(u16, SocketAddr)>,
) -> Result<(), ConnError> {
let mut res = Self::setup_onion_service_call(
true,
&key.as_tor_proto_encoded(),
detach,
non_anonymous,
max_streams_close_circuit,
max_num_streams,
listeners,
)?;
res.push_str("\r\n");
self.conn.write_data(res.as_bytes()).await?;

// we do not really care about contents of response
// we can derive all the data from tor's objects at the torut level
let (code, _) = self.recv_response().await?;
if code != 250 {
return Err(ConnError::InvalidResponseCode(code));
}
Ok(())
}

#[cfg(any(feature = "v3"))]
/// add_onion sends `ADD_ONION` command which spins up new onion service
/// using given tor secret key and some configuration values.
Expand Down Expand Up @@ -1098,42 +1046,6 @@ mod test_with_tor {
});
}

#[test]
fn test_can_create_onion_service_v2() {
let _c = run_testing_tor_instance(
&[
"--DisableNetwork", "1",
"--ControlPort", &TOR_TESTING_PORT.to_string(),
]);

block_on_with_env(async move {
let s = TcpStream::connect(&format!("127.0.0.1:{}", TOR_TESTING_PORT)).await.unwrap();
let mut utc = UnauthenticatedConn::new(s);
let proto_info = utc.load_protocol_info().await.unwrap();

assert!(proto_info.auth_methods.contains(&TorAuthMethod::Null));
utc.authenticate(&TorAuthData::Null).await.unwrap();
let mut ac = utc.into_authenticated().await;
ac.set_async_event_handler(Some(|_| {
async move { Ok(()) }
}));

let key = crate::onion::TorSecretKeyV2::generate();

ac.add_onion_v2(&key, false, false, false, None, &mut [
(15787, SocketAddr::new(IpAddr::from(Ipv4Addr::new(127,0,0,1)), 15787)),
].iter()).await.unwrap();

// additional actions to check if connection is in corrupted state
ac.take_ownership().await.unwrap();
ac.drop_ownership().await.unwrap();

// delete onion service so it works no more
// TOOD(teawithsand): implement get_onion_address for TorPublicKeyV2
// ac.del_onion(&key.public().get_onion_address().get_address_without_dot_onion()).await.unwrap();
});
}

#[test]
fn test_can_issue_getinfo_unquote() {
let _c = run_testing_tor_instance(
Expand Down
22 changes: 1 addition & 21 deletions src/fuzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use std::io::Cursor;
use std::str::FromStr;

use crate::control::conn::{Conn, UnauthenticatedConn};
#[cfg(feature = "v2")]
use crate::onion::OnionAddressV2;

#[cfg(feature = "v3")]
use crate::onion::OnionAddressV3;
use crate::utils::{BASE32_ALPHA, block_on, parse_single_key_value, unquote_string};
Expand Down Expand Up @@ -53,32 +52,13 @@ pub fn fuzz_unauthenticated_conn_parse_protocol_info(data: &[u8]) {
});
}

#[cfg(feature = "v2")]
pub fn fuzz_deserialize_onion_address_v2_from_text(data: &[u8]) {
if let Ok(data) = std::str::from_utf8(data) {
let _ = OnionAddressV2::from_str(data);
}
}

#[cfg(feature = "v3")]
pub fn fuzz_deserialize_onion_address_v3_from_text(data: &[u8]) {
if let Ok(data) = std::str::from_utf8(data) {
let _ = OnionAddressV3::from_str(data);
}
}

#[cfg(feature = "v2")]
pub fn fuzz_base32_decode(data: &[u8]) {
if let Ok(data) = std::str::from_utf8(data) {
let _ = base32::decode(BASE32_ALPHA, data);
}
}

#[cfg(feature = "v2")]
pub fn fuzz_base64_decode(data: &[u8]) {
let _ = base64::decode(data);
}

// TODO(teawithsand): get some deserialization crate which is easy for fuzzer(bincode?) and fuzz deserialization of onion services
// from serde
/*
Expand Down
4 changes: 0 additions & 4 deletions src/onion/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use std::fmt;
use std::fmt::Display;
use std::net::IpAddr;

#[cfg(feature = "v2")]
use crate::onion::{TorPublicKeyV2, TorSecretKeyV2};
#[cfg(feature = "v3")]
use crate::onion::{TorPublicKeyV3, TorSecretKeyV3};
use crate::onion::common::TorSecretKey;
Expand Down Expand Up @@ -74,8 +72,6 @@ impl OnionServiceBuilder {
}

pub enum RunningOnionServiceKeyPair {
#[cfg(feature = "v2")]
V2(TorPublicKeyV2, TorSecretKeyV2),
#[cfg(feature = "v3")]
V3(TorPublicKeyV3, TorSecretKeyV3),
}
Expand Down
19 changes: 7 additions & 12 deletions src/onion/common.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
use std::fmt::Display;

#[cfg(feature = "v2")]
use crate::onion::{OnionAddressV2, TorPublicKeyV2, TorSecretKeyV2};

#[cfg(feature = "v3")]
use crate::onion::{OnionAddressV3, TorPublicKeyV3, TorSecretKeyV3};

#[derive(Debug, Clone, PartialEq, Eq, From, TryInto)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum TorSecretKey {
#[cfg(feature = "v2")]
V2(TorSecretKeyV2),
// leave this type, since some day tor may introduce v4 addresses
// for instance quantum secure one

#[cfg(feature = "v3")]
V3(TorSecretKeyV3),
}

#[derive(Debug, Clone, PartialEq, Eq, From, TryInto)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum OnionAddress {
#[cfg(feature = "v2")]
V2(OnionAddressV2),
// leave this type, since some day tor may introduce v4 addresses
// for instance quantum secure one

#[cfg(feature = "v3")]
V3(OnionAddressV3),
}

impl Display for OnionAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
#[cfg(feature = "v2")]
OnionAddress::V2(a) => a.fmt(f),
#[cfg(feature = "v3")]
OnionAddress::V3(a) => a.fmt(f),
}
Expand All @@ -37,8 +36,6 @@ impl Display for OnionAddress {
impl OnionAddress {
pub fn get_address_without_dot_onion(&self) -> String {
match self {
#[cfg(feature = "v2")]
OnionAddress::V2(a) => a.get_address_without_dot_onion(),
#[cfg(feature = "v3")]
OnionAddress::V3(a) => a.get_address_without_dot_onion(),
}
Expand All @@ -48,8 +45,6 @@ impl OnionAddress {
#[derive(Debug, Clone, PartialEq, Eq, From, TryInto)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum TorPublicKey {
#[cfg(feature = "v2")]
V2(TorPublicKeyV2),
#[cfg(feature = "v3")]
V3(TorPublicKeyV3),
}
28 changes: 3 additions & 25 deletions src/onion/mod.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,13 @@
//! Onion module implements all utilities required to work with onion addresses both version two and three
//! Onion module implements all utilities required to work with onion addresses version three
//! Support for these may be enabled using cargo features.
//!
//! Note: right now it uses openssl for `v2` key generation and serialization
//! If there would be a library mature capable of both RSA 1024 key generation and (de)serialization.

// #[cfg(all(feature = "v2", feature = "v3"))]
// pub use builder::*;

#[cfg(any(feature = "v2", feature = "v3"))]
#[cfg(any(feature = "v3"))]
pub use common::*;
#[cfg(feature = "v2")]
pub use v2::*;
#[cfg(feature = "v3")]
pub use v3::*;

#[cfg(feature = "v2")]
#[deprecated(
since = "0.1.10",
note = "V2 onion services are deprecated by tor and soon will stop working; It will be removed in next release"
)]
mod v2;

#[cfg(feature = "v3")]
mod v3;


/*
#[cfg(all(feature = "v2", feature = "v3"))]
mod builder;
*/

#[cfg(any(feature = "v2", feature = "v3"))]
#[cfg(feature = "v3")]
mod common;

Loading

0 comments on commit bbc11a7

Please sign in to comment.