diff --git a/TODO b/TODO index 8b35fd6a2..b0cc2475e 100644 --- a/TODO +++ b/TODO @@ -10,16 +10,17 @@ MILESTONE 0: Build a circuit and use it -- minimum to share inside tor o Pick random relays o Construct a one-hop circuit with CREATE_FAST o Construct a one-hop circuit with ntor - - Build relay cells + o Build relay cells - Construct a multihop circuit. - Open a stream - Make a request, get a response (no sendmes yet) MILESTONE 1: Refactoring on above -- minimum to share outside tor - Stop parameterizing all APIs on TLS type. + - Less copying, esp around Box. Consider bytes crate. - Is this "reactor" business a sensible design? Is there a better one? - Combine "create circuit" and "first hop"? - - Improve testing, + - Improve testing - Improve documentation - More types of circent for clarity. - Make sure readme is right diff --git a/tor-proto/src/chancell/msg.rs b/tor-proto/src/chancell/msg.rs index a25897066..dc3975e14 100644 --- a/tor-proto/src/chancell/msg.rs +++ b/tor-proto/src/chancell/msg.rs @@ -357,8 +357,22 @@ impl Readable for Created2 { /// XXXX. #[derive(Clone)] pub struct Relay { + // XXXX either this shouldn't be boxed, or RelayCellBody should be boxed! body: Box, } +impl Relay { + /// Construct a Relay message from its body. + pub fn from_raw(body: RawCellBody) -> Self { + Relay { + body: Box::new(body), + } + } + /// Consume this Relay message and return a RelayCellBody for + /// encryption/decryption. + pub fn into_relay_cell(self) -> crate::crypto::cell::RelayCellBody { + (*self.body).into() + } +} impl std::fmt::Debug for Relay { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Relay").finish() diff --git a/tor-proto/src/circuit.rs b/tor-proto/src/circuit.rs index 2e2496a3a..d9143861a 100644 --- a/tor-proto/src/circuit.rs +++ b/tor-proto/src/circuit.rs @@ -1,19 +1,21 @@ //! Multi-hop paths over the Tor network. use crate::chancell::{ + self, msg::{self, ChanMsg}, ChanCell, CircID, }; use crate::channel::Channel; use crate::crypto::cell::{ClientLayer, CryptInit}; use crate::crypto::handshake::{ClientHandshake, KeyGenerator}; +use crate::relaycell::msg::RelayCell; use crate::{Error, Result}; use futures::channel::mpsc; use futures::io::{AsyncRead, AsyncWrite}; use futures::stream::StreamExt; -use rand::{CryptoRng, Rng}; +use rand::{thread_rng, CryptoRng, Rng}; use crate::crypto::cell::ClientCrypt; @@ -65,6 +67,53 @@ where self.input.next().await.ok_or(Error::CircuitClosed) } + /// Encode the message `msg`, encrypt it, and send it to the 'hop'th hop. + /// + /// TODO: This is not a good long-term API. It should become private + /// if we keep it. + /// + /// TODO: use HopNum + pub async fn send_relay_cell(&mut self, hop: u8, early: bool, cell: RelayCell) -> Result<()> { + assert!((hop as usize) < self.crypto.n_layers()); + let mut body = cell.encode(&mut thread_rng())?; + self.crypto.encrypt(&mut body, hop)?; + let msg = chancell::msg::Relay::from_raw(body.into()); + let msg = if early { + ChanMsg::RelayEarly(msg) + } else { + ChanMsg::Relay(msg) + }; + self.send_msg(msg).await?; + Ok(()) + } + + /// Receive a message from the circuit, decrypt it, and return it as a + /// RelayCell. + /// + /// TODO: This is not a good long-term API. It should become private + /// if we keep it. + /// + /// TODO: use HopNum + pub async fn recv_relay_cell(&mut self) -> Result<(u8, RelayCell)> { + let chanmsg = self.read_msg().await?; + let body = match chanmsg { + ChanMsg::Relay(r) => r, + _ => { + return Err(Error::ChanProto(format!( + "{} cell received on circuit", + chanmsg.get_cmd() + ))) + } + }; + + // Decrypt, if possible. + let mut cell = body.into_relay_cell(); + let hopnum = self.crypto.decrypt(&mut cell)?; + let msg = RelayCell::decode(cell)?; + + Ok((hopnum, msg)) + } + /// Helper: create the first hop of a circuit. /// /// This is parameterized not just on the RNG, but a wrapper object to diff --git a/tor-proto/src/crypto/cell.rs b/tor-proto/src/crypto/cell.rs index 56393d755..cef68ee2c 100644 --- a/tor-proto/src/crypto/cell.rs +++ b/tor-proto/src/crypto/cell.rs @@ -124,6 +124,8 @@ impl ClientCrypt { } /// Return the number of layers configured on this ClientCrypt. + /// + /// TODO: use HopNum pub fn n_layers(&self) -> usize { self.layers.len() } diff --git a/tor-proto/src/relaycell/msg.rs b/tor-proto/src/relaycell/msg.rs index b02d9f507..b6f23f745 100644 --- a/tor-proto/src/relaycell/msg.rs +++ b/tor-proto/src/relaycell/msg.rs @@ -73,6 +73,14 @@ impl RelayCell { /// /// Requires that the cryptographic checks on the message have already been /// performed + pub fn decode(body: RelayCellBody) -> Result { + let mut reader = Reader::from_slice(body.as_ref()); + RelayCell::decode_from_reader(&mut reader) + } + /// Parse a RELAY or RELAY_EARLY cell body into a RelayCell from a reader. + /// + /// Requires that the cryptographic checks on the message have already been + /// performed fn decode_from_reader(r: &mut Reader<'_>) -> Result { let cmd = r.take_u8()?.into(); r.advance(2)?; // "recognized"