Lines
0 %
Functions
Branches
100 %
//! Implementations for the relay channel handshake
use futures::SinkExt;
use futures::io::{AsyncRead, AsyncWrite};
use futures::stream::{Stream, StreamExt};
use rand::Rng;
use safelog::Sensitive;
use std::net::IpAddr;
use std::{sync::Arc, time::SystemTime};
use tracing::trace;
use tor_cell::chancell::msg::AnyChanMsg;
use tor_cell::chancell::{AnyChanCell, ChanCmd, ChanMsg, msg};
use tor_cell::restricted_msg;
use tor_linkspec::{ChannelMethod, HasChanMethod, OwnedChanTarget};
use tor_rtcompat::{CertifiedConn, CoarseTimeProvider, SleepProvider, StreamOps};
use crate::channel::handshake::{
ChannelBaseHandshake, ChannelInitiatorHandshake, UnverifiedChannel, unauthenticated_clock_skew,
};
use crate::channel::{ChannelFrame, ChannelType, UniqId, new_frame};
use crate::memquota::ChannelAccount;
use crate::peer::PeerAddr;
use crate::relay::channel::initiator::UnverifiedInitiatorRelayChannel;
use crate::relay::channel::responder::{
MaybeVerifiableRelayResponderChannel, NonVerifiableResponderRelayChannel,
UnverifiedResponderRelayChannel,
use crate::relay::channel::{RelayIdentities, build_certs_cell, build_netinfo_cell};
use crate::{Error, Result};
/// The "Ed25519-SHA256-RFC5705" link authentication which is value "00 03".
pub(super) static AUTHTYPE_ED25519_SHA256_RFC5705: u16 = 3;
/// A relay channel handshake as the initiator.
pub struct RelayInitiatorHandshake<
T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
S: CoarseTimeProvider + SleepProvider,
> {
/// Runtime handle (insofar as we need it)
sleep_prov: S,
/// Memory quota account
memquota: ChannelAccount,
/// Underlying TLS stream in a channel frame.
///
/// (We don't enforce that this is actually TLS, but if it isn't, the
/// connection won't be secure.)
framed_tls: ChannelFrame<T>,
/// Logging identifier for this stream. (Used for logging only.)
unique_id: UniqId,
/// Our identity keys needed for authentication.
identities: Arc<RelayIdentities>,
/// The peer we are attempting to connect to.
target_method: ChannelMethod,
/// Our advertised addresses. Needed for the NETINFO.
my_addrs: Vec<IpAddr>,
}
/// Implement the base channel handshake trait.
impl<T, S> ChannelBaseHandshake<T> for RelayInitiatorHandshake<T, S>
where
{
fn framed_tls(&mut self) -> &mut ChannelFrame<T> {
&mut self.framed_tls
fn unique_id(&self) -> &UniqId {
&self.unique_id
/// Implement the initiator channel handshake trait.
impl<T, S> ChannelInitiatorHandshake<T> for RelayInitiatorHandshake<T, S>
impl<
> RelayInitiatorHandshake<T, S>
/// Constructor.
pub(crate) fn new(
tls: T,
peer: &OwnedChanTarget,
) -> Self {
Self {
framed_tls: new_frame(tls, ChannelType::RelayInitiator),
unique_id: UniqId::new(),
sleep_prov,
identities,
memquota,
my_addrs,
target_method: peer.chan_method(),
/// Connect to another relay as the relay Initiator.
/// Takes a function that reports the current time. In theory, this can just be
/// `SystemTime::now()`.
pub async fn connect<F>(mut self, now_fn: F) -> Result<UnverifiedInitiatorRelayChannel<T, S>>
F: FnOnce() -> SystemTime,
// Send the VERSIONS.
let (versions_flushed_at, versions_flushed_wallclock) =
self.send_versions_cell(now_fn).await?;
// Receive the VERSIONS.
let link_protocol = self.recv_versions_cell().await?;
// Read until we have all the remaining cells from the responder.
let (auth_challenge_cell, certs_cell, (netinfo_cell, netinfo_rcvd_at)) =
self.recv_cells_from_responder().await?;
trace!(stream_id = %self.unique_id,
"received handshake, ready to verify.",
);
// Calculate our clock skew from the timings we just got/calculated.
let clock_skew = unauthenticated_clock_skew(
&netinfo_cell,
netinfo_rcvd_at,
versions_flushed_at,
versions_flushed_wallclock,
Ok(UnverifiedInitiatorRelayChannel {
inner: UnverifiedChannel {
link_protocol,
framed_tls: self.framed_tls,
clock_skew,
memquota: self.memquota,
target_method: Some(self.target_method),
unique_id: self.unique_id,
sleep_prov: self.sleep_prov.clone(),
certs_cell: Some(certs_cell),
},
auth_challenge_cell,
netinfo_cell,
identities: self.identities,
my_addrs: self.my_addrs,
})
/// A relay channel handshake as the responder.
pub struct RelayResponderHandshake<
/// The peer IP address as in the address the initiator is connecting from. This can be a
/// client so keep it sensitive.
peer_addr: Sensitive<PeerAddr>,
impl<T, S> ChannelBaseHandshake<T> for RelayResponderHandshake<T, S>
> RelayResponderHandshake<T, S>
peer_addr,
framed_tls: new_frame(
tls,
ChannelType::RelayResponder {
authenticated: false,
),
/// Begin the handshake process.
pub async fn handshake<F>(
mut self,
now_fn: F,
) -> Result<MaybeVerifiableRelayResponderChannel<T, S>>
// Receive initiator VERSIONS.
// Send VERSION, CERTS, AUTH_CHALLENGE and NETINFO
self.send_cells_to_initiator(now_fn).await?;
// Receive NETINFO and possibly [CERTS, AUTHENTICATE]. The connection could be from a
// client/bridge and thus no authentication meaning no CERTS/AUTHENTICATE cells.
let (cells, (netinfo_cell, netinfo_rcvd_at)) = self.recv_cells_from_initiator().await?;
let (certs_cell, auth_cell) = cells.unzip();
let inner = UnverifiedChannel {
target_method: None,
sleep_prov: self.sleep_prov,
certs_cell,
// With an AUTHENTICATE cell, we can verify (relay). Else (client/bridge), we can't.
Ok(match auth_cell {
Some(auth_cell) => {
MaybeVerifiableRelayResponderChannel::Verifiable(UnverifiedResponderRelayChannel {
inner,
auth_cell,
peer_addr: self.peer_addr.into_inner(), // Relay address.
None => MaybeVerifiableRelayResponderChannel::NonVerifiable(
NonVerifiableResponderRelayChannel {
peer_addr: self.peer_addr,
/// Receive all the cells expected from the initiator of the connection. Keep in mind that it
/// can be either a relay or client or bridge.
async fn recv_cells_from_initiator(
&mut self,
) -> Result<(
Option<(msg::Certs, msg::Authenticate)>,
(msg::Netinfo, coarsetime::Instant),
)> {
// IMPORTANT: Protocol wise, we MUST only allow one single cell of each type for a valid
// handshake. Any duplicates lead to a failure.
// They must arrive in a specific order in order for the CLOG calculation to be valid.
/// Read a message from the stream.
/// The `expecting` parameter is used for logging purposes, not filtering.
async fn read_msg<T>(
stream_id: UniqId,
mut stream: impl Stream<Item = Result<AnyChanCell>> + Unpin,
expecting: &[ChanCmd],
) -> Result<T>
T: TryFrom<AnyChanMsg, Error = AnyChanMsg>,
let Some(cell) = stream.next().await.transpose()? else {
// The entire channel has ended, so nothing else to be done.
return Err(Error::HandshakeProto("Stream ended unexpectedly".into()));
let (id, m) = cell.into_circid_and_msg();
trace!(%stream_id, "received a {} cell", m.cmd());
// TODO: Maybe also check this in the channel handshake codec?
if let Some(id) = id {
return Err(Error::HandshakeProto(format!(
"Expected no circ ID for {} cell, but received circ ID of {id} instead",
m.cmd(),
)));
let m = m.try_into().map_err(|m: AnyChanMsg| {
Error::HandshakeProto(format!(
"Expected {expecting:?} cell, but received {} cell instead",
))
})?;
Ok(m)
// Note that the `ChannelFrame` already restricts the messages due to its handshake cell
// handler.
// This is kind of ugly, but I don't see a nicer way to write the authentication branch
// without a bunch of boilerplate for a state machine.
let (certs_and_auth, netinfo, netinfo_rcvd_at) = 'outer: {
// CERTS or NETINFO cell.
let certs = loop {
restricted_msg! {
enum CertsNetinfoMsg : ChanMsg {
// VPADDING cells (but not PADDING) can be sent during handshaking.
Vpadding,
Netinfo,
Certs,
let msg = read_msg(
*self.unique_id(),
self.framed_tls(),
&[ChanCmd::NETINFO, ChanCmd::CERTS],
)
.await?;
break match msg {
CertsNetinfoMsg::Vpadding(_) => continue,
// If a NETINFO cell, the initiator did not authenticate and we can stop early.
CertsNetinfoMsg::Netinfo(msg) => {
break 'outer (None, msg, coarsetime::Instant::now());
// If a CERTS cell, the initiator is authenticating.
CertsNetinfoMsg::Certs(msg) => msg,
// AUTHENTICATE cell.
let auth = loop {
enum AuthenticateMsg : ChanMsg {
Authenticate,
&[ChanCmd::AUTHENTICATE],
AuthenticateMsg::Vpadding(_) => continue,
AuthenticateMsg::Authenticate(msg) => msg,
// NETINFO cell (if we didn't receive it earlier).
let (netinfo, netinfo_rcvd_at) = loop {
enum NetinfoMsg : ChanMsg {
let msg =
read_msg(*self.unique_id(), self.framed_tls(), &[ChanCmd::NETINFO]).await?;
NetinfoMsg::Vpadding(_) => continue,
NetinfoMsg::Netinfo(msg) => (msg, coarsetime::Instant::now()),
(Some((certs, auth)), netinfo, netinfo_rcvd_at)
Ok((certs_and_auth, (netinfo, netinfo_rcvd_at)))
/// Send all expected cells to the initiator of the channel as the responder.
/// Return the sending times of the [`msg::Versions`] so it can be used for clock skew
/// validation.
async fn send_cells_to_initiator<F>(
) -> Result<(coarsetime::Instant, SystemTime)>
// Send the VERSIONS message.
// Send the CERTS message.
let certs = build_certs_cell(&self.identities, /* is_responder */ true);
trace!(channel_id = %self.unique_id, "Sending CERTS as responder cell.");
self.framed_tls.send(certs.into()).await?;
// Send the AUTH_CHALLENGE.
let challenge: [u8; 32] = rand::rng().random();
let auth_challenge = msg::AuthChallenge::new(challenge, [AUTHTYPE_ED25519_SHA256_RFC5705]);
trace!(channel_id = %self.unique_id, "Sending AUTH_CHALLENGE as responder cell.");
self.framed_tls.send(auth_challenge.into()).await?;
// Send the NETINFO message.
let peer_ip = self.peer_addr.netinfo_addr();
let netinfo = build_netinfo_cell(peer_ip, self.my_addrs.clone(), &self.sleep_prov)?;
trace!(channel_id = %self.unique_id, "Sending NETINFO as responder cell.");
self.framed_tls.send(netinfo.into()).await?;
Ok((versions_flushed_at, versions_flushed_wallclock))