diff --git a/crates/tor-cell/Cargo.toml b/crates/tor-cell/Cargo.toml index 3d534704c..310a6ab48 100644 --- a/crates/tor-cell/Cargo.toml +++ b/crates/tor-cell/Cargo.toml @@ -16,6 +16,7 @@ default = [] experimental = ["experimental-udp"] # Enable experimental UDP support. experimental-udp = [] +onion-service = [] # Enable testing only API testing = ["experimental-udp"] diff --git a/crates/tor-cell/src/relaycell.rs b/crates/tor-cell/src/relaycell.rs index c2ed951c0..17b0cf139 100644 --- a/crates/tor-cell/src/relaycell.rs +++ b/crates/tor-cell/src/relaycell.rs @@ -11,6 +11,8 @@ use rand::{CryptoRng, Rng}; pub mod extend; pub mod msg; +#[cfg(feature = "onion-service")] +pub mod onion_service; #[cfg(feature = "experimental-udp")] pub mod udp; diff --git a/crates/tor-cell/src/relaycell/msg.rs b/crates/tor-cell/src/relaycell/msg.rs index f9f54e8f6..5c9b9c0f3 100644 --- a/crates/tor-cell/src/relaycell/msg.rs +++ b/crates/tor-cell/src/relaycell/msg.rs @@ -17,6 +17,8 @@ use tor_llcrypto::pk::rsa::RsaIdentity; use bitflags::bitflags; +#[cfg(feature = "onion-service")] +use super::onion_service; #[cfg(feature = "experimental-udp")] use super::udp; @@ -63,10 +65,13 @@ pub enum RelayMsg { /// UDP stream data #[cfg(feature = "experimental-udp")] Datagram(udp::Datagram), + // No hs for now. + #[cfg(feature = "onion-service")] + /// Establish Introduction + EstablishIntro(onion_service::EstablishIntro), /// An unrecognized command. Unrecognized(Unrecognized), - // No hs for now. } /// Internal: traits in common different cell bodies. @@ -111,6 +116,8 @@ impl RelayMsg { ConnectedUdp(_) => RelayCmd::CONNECTED_UDP, #[cfg(feature = "experimental-udp")] Datagram(_) => RelayCmd::DATAGRAM, + #[cfg(feature = "onion-service")] + EstablishIntro(_) => RelayCmd::ESTABLISH_INTRO, Unrecognized(u) => u.cmd(), } } @@ -140,6 +147,10 @@ impl RelayMsg { } #[cfg(feature = "experimental-udp")] RelayCmd::DATAGRAM => RelayMsg::Datagram(udp::Datagram::decode_from_reader(r)?), + #[cfg(feature = "onion-service")] + RelayCmd::ESTABLISH_INTRO => { + RelayMsg::EstablishIntro(onion_service::EstablishIntro::decode_from_reader(r)?) + } _ => RelayMsg::Unrecognized(Unrecognized::decode_with_cmd(c, r)?), }) } @@ -168,6 +179,8 @@ impl RelayMsg { ConnectedUdp(b) => b.encode_onto(w), #[cfg(feature = "experimental-udp")] Datagram(b) => b.encode_onto(w), + #[cfg(feature = "onion-service")] + EstablishIntro(b) => b.encode_onto(w), Unrecognized(b) => b.encode_onto(w), } } diff --git a/crates/tor-cell/src/relaycell/onion_service.rs b/crates/tor-cell/src/relaycell/onion_service.rs new file mode 100644 index 000000000..650153992 --- /dev/null +++ b/crates/tor-cell/src/relaycell/onion_service.rs @@ -0,0 +1,72 @@ +//! Encoding and decoding for relay messages +//! +//! Relay messages are sent along circuits, inside RELAY or RELAY_EARLY +//! cells. + +use super::msg; +use caret::caret_int; +use tor_bytes::{Error, Result}; +use tor_bytes::{Reader, Writer}; + +caret_int! { + /// The type of the introduction point auth key + pub struct AuthKeyType(u8) { + /// Ed25519; SHA3-256 + ED25519_SHA3_256 = 2, + } +} + +/// A hidden services establishes a new introduction point, +/// by sending an EstablishIntro message. +#[derive(Debug, Clone)] +pub struct EstablishIntro { + /// Introduction point auth key type and the type of + /// the MAC used in `handshake_auth`. + auth_key_type: AuthKeyType, + /// The public introduction point auth key. + auth_key: Vec, + /// the MAC of all earlier fields in the cell. + handshake_auth: [u8; 32], + /// A signature using `auth_key` of all contents + /// of the cell. + sig: Vec, +} + +impl msg::Body for EstablishIntro { + fn into_message(self) -> msg::RelayMsg { + msg::RelayMsg::EstablishIntro(self) + } + fn decode_from_reader(r: &mut Reader<'_>) -> Result { + let auth_key_type = r.take_u8()?.into(); + let auth_key_len = r.take_u16()?; + let auth_key = r.take(auth_key_len as usize)?.into(); + if r.take_u8()? != 0 { + // TODO: Support ESTABLISH_INTRO extensions + return Err(Error::BadMessage( + "ESTABLISH_INTRO extension not supported.", + )); + } + let handshake_auth = r.extract()?; + let sig_len = r.take_u16()?; + let sig = r.take(sig_len as usize)?.into(); + Ok(EstablishIntro { + auth_key_type, + auth_key, + handshake_auth, + sig, + }) + } + fn encode_onto(self, w: &mut Vec) { + w.write_u8(self.auth_key_type.get()); + // TODO: This should fail when auth_key is too long, + // but `as` truncates the value silently. This depends on + // `tor_bytes::Writer::write` to return a `Result`. + w.write_u16(self.auth_key.len() as u16); + w.write_all(&self.auth_key[..]); + // N_EXTENTIONS is zero for now + w.write_u8(0_u8); + w.write_all(&self.handshake_auth[..]); + w.write_u16(self.sig.len() as u16); + w.write_all(&self.sig[..]); + } +}