use digest::Digest;
use futures::{AsyncRead, AsyncWrite};
use safelog::{MaybeSensitive, Sensitive};
use std::{net::IpAddr, ops::Deref, sync::Arc};
use subtle::ConstantTimeEq;
use tracing::instrument;
use tor_cell::chancell::msg;
use tor_linkspec::{HasRelayIds, OwnedChanTarget, RelayIds};
use tor_llcrypto as ll;
use tor_llcrypto::pk::ed25519::Ed25519Identity;
use tor_llcrypto::pk::rsa::RsaIdentity;
use tor_rtcompat::{CertifiedConn, CoarseTimeProvider, Runtime, SleepProvider, StreamOps};
use web_time_compat::{SystemTime, SystemTimeExt};
use crate::{
ClockSkew, Error, RelayChannelAuthMaterial, Result,
channel::{
Channel, ChannelMode, ClogDigest, Reactor, SlogDigest,
circmap::CircIdRange,
handshake::{UnverifiedChannel, VerifiedChannel},
},
peer::{PeerAddr, PeerInfo},
relay::CreateRequestHandler,
relay::channel::ChannelAuthenticationData,
};
#[allow(clippy::exhaustive_enums)]
pub enum MaybeVerifiableRelayResponderChannel<
T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
S: CoarseTimeProvider + SleepProvider,
> {
Verifiable(UnverifiedResponderRelayChannel<T, S>),
NonVerifiable(NonVerifiableResponderRelayChannel<T, S>),
}
pub struct NonVerifiableResponderRelayChannel<
T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
S: CoarseTimeProvider + SleepProvider,
> {
pub(crate) inner: UnverifiedChannel<T, S>,
pub(crate) netinfo_cell: msg::Netinfo,
pub(crate) my_addrs: Vec<IpAddr>,
pub(crate) peer_addr: Sensitive<PeerAddr>,
pub(crate) create_request_handler: Arc<CreateRequestHandler>,
pub(crate) our_ed25519_id: Ed25519Identity,
pub(crate) our_rsa_id: RsaIdentity,
}
pub struct UnverifiedResponderRelayChannel<
T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
S: CoarseTimeProvider + SleepProvider,
> {
pub(crate) inner: UnverifiedChannel<T, S>,
pub(crate) auth_cell: msg::Authenticate,
pub(crate) netinfo_cell: msg::Netinfo,
pub(crate) certs_cell: msg::Certs,
pub(crate) auth_material: Arc<RelayChannelAuthMaterial>,
pub(crate) my_addrs: Vec<IpAddr>,
pub(crate) peer_addr: PeerAddr,
pub(crate) clog_digest: ClogDigest,
pub(crate) slog_digest: SlogDigest,
pub(crate) create_request_handler: Arc<CreateRequestHandler>,
}
pub struct VerifiedResponderRelayChannel<
T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
S: CoarseTimeProvider + SleepProvider,
> {
inner: VerifiedChannel<T, S>,
netinfo_cell: msg::Netinfo,
my_addrs: Vec<IpAddr>,
peer_addr: PeerAddr,
create_request_handler: Arc<CreateRequestHandler>,
our_ed25519_id: Ed25519Identity,
our_rsa_id: RsaIdentity,
}
impl<T, S> UnverifiedResponderRelayChannel<T, S>
where
T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
S: CoarseTimeProvider + SleepProvider,
{
#[instrument(skip_all, level = "trace")]
pub fn verify(
self,
peer_target_no_ids: &OwnedChanTarget,
our_tls_cert: &[u8],
now: Option<std::time::SystemTime>,
) -> Result<VerifiedResponderRelayChannel<T, S>> {
let identities = self.auth_material;
let peer_netinfo_cell = self.netinfo_cell;
let peer_auth_cell = self.auth_cell;
let my_addrs = self.my_addrs;
let now = now.unwrap_or_else(SystemTime::get);
let (peer_relay_ids, peer_kp_relaysign_ed, peer_rsa_id_digest) = self
.inner
.check_relay_identities(peer_target_no_ids, &self.certs_cell, now)?;
let peer_kp_link_ed = crate::channel::handshake::verify_link_auth_cert(
&self.certs_cell,
&peer_kp_relaysign_ed,
Some(now),
self.inner.clock_skew,
)?;
let our_tls_cert_digest = ll::d::Sha256::digest(our_tls_cert).into();
let peer_relayid_ed = *peer_relay_ids
.ed_identity()
.expect("Validated relay channel without Ed25519 identity");
let expected_auth_body = ChannelAuthenticationData::build_responder(
peer_auth_cell.auth_type(),
&identities,
self.clog_digest,
self.slog_digest,
peer_rsa_id_digest,
peer_relayid_ed,
our_tls_cert_digest,
)?
.as_body_no_rand(self.inner.framed_tls.deref())?;
let peer_auth_cell_body_no_rand = peer_auth_cell
.body_no_rand()
.map_err(|e| Error::ChanProto(format!("AUTHENTICATE body_no_rand malformed: {e}")))?;
if peer_auth_cell_body_no_rand
.ct_eq(&expected_auth_body)
.into()
{
return Err(Error::ChanProto(
"AUTHENTICATE was unexpected. Failing authentication".into(),
));
}
let peer_link_ed_pubkey: tor_llcrypto::pk::ed25519::PublicKey = peer_kp_link_ed
.try_into()
.expect("Peer KP_link_ed fails to convert to PublicKey");
let peer_auth_cell_sig =
tor_llcrypto::pk::ed25519::Signature::from_bytes(peer_auth_cell.sig().map_err(
|e| Error::ChanProto(format!("AUTHENTICATE sig field is invalid: {e}")),
)?);
let peer_body = peer_auth_cell
.body()
.map_err(|e| Error::ChanProto(format!("AUTHENTICATE body malformed: {e}")))?;
peer_link_ed_pubkey
.verify(peer_body, &peer_auth_cell_sig)
.map_err(|e| {
Error::ChanProto(format!("AUTHENTICATE cell signature failed to verify: {e}"))
})?;
let mut verified = self.inner.into_verified(peer_relay_ids, peer_rsa_id_digest);
verified.set_authenticated()?;
Ok(VerifiedResponderRelayChannel {
inner: verified,
netinfo_cell: peer_netinfo_cell,
my_addrs,
peer_addr: self.peer_addr,
create_request_handler: self.create_request_handler,
our_ed25519_id: identities.ed_id,
our_rsa_id: identities.rsa_id,
})
}
pub fn clock_skew(&self) -> ClockSkew {
self.inner.clock_skew
}
}
impl<T, S> VerifiedResponderRelayChannel<T, S>
where
T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
S: CoarseTimeProvider + SleepProvider,
{
#[instrument(skip_all, level = "trace")]
pub async fn finish(self) -> Result<(Arc<Channel>, Reactor<S>)>
where
S: Runtime,
{
let peer_info = MaybeSensitive::not_sensitive(PeerInfo::new(
self.peer_addr,
self.inner.relay_ids().clone(),
));
let channel_mode = ChannelMode::Relay {
circ_id_range: CircIdRange::Low,
our_ed25519_id: self.our_ed25519_id,
our_rsa_id: self.our_rsa_id,
create_request_handler: self.create_request_handler,
};
self.inner
.finish(&self.netinfo_cell, &self.my_addrs, peer_info, channel_mode)
.await
}
}
impl<T, S> NonVerifiableResponderRelayChannel<T, S>
where
T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
S: CoarseTimeProvider + SleepProvider,
{
#[instrument(skip_all, level = "trace")]
pub fn finish(self) -> Result<(Arc<Channel>, Reactor<S>)>
where
S: Runtime,
{
let peer_info = MaybeSensitive::sensitive(PeerInfo::new(
self.peer_addr.into_inner(),
RelayIds::empty(),
));
let channel_mode = ChannelMode::Relay {
circ_id_range: CircIdRange::Low,
our_ed25519_id: self.our_ed25519_id,
our_rsa_id: self.our_rsa_id,
create_request_handler: self.create_request_handler,
};
self.inner
.finish(&self.netinfo_cell, &self.my_addrs, peer_info, channel_mode)
}
}