//! Relay initiator channel.
//!
//! Code only related to a relay channel opened as an initiator. The handshake code is responsible
//! for creating an [`UnverifiedInitiatorRelayChannel`] when connecting to another relay in order
//! to build a tor channel.
//!
//! The [`UnverifiedInitiatorRelayChannel::verify`] function needs to be called to verify the
//! underlying channel and build a [`VerifiedInitiatorRelayChannel`] channel which needs to be
//! finished with [`VerifiedInitiatorRelayChannel::finish`] to get a Channel/Reactor.
//!
//! Note that channel cells are sent in the handshake upon connection. And then in the finish()
//! process. The verify can be CPU intensive and thus in its own function.

use digest::Digest;
use futures::{AsyncRead, AsyncWrite, SinkExt};
use safelog::MaybeSensitive;
use std::{net::IpAddr, ops::Deref, sync::Arc};
use tracing::trace;

use tor_cell::chancell::msg;
use tor_linkspec::OwnedChanTarget;
use tor_rtcompat::{CertifiedConn, CoarseTimeProvider, Runtime, SleepProvider, StreamOps};

use crate::{
    ClockSkew, RelayChannelAuthMaterial, Result,
    channel::{
        Channel, ChannelMode, ClogDigest, Reactor, SlogDigest,
        circmap::CircIdRange,
        handshake::{UnverifiedInitiatorChannel, VerifiedChannel},
    },
    peer::{PeerAddr, PeerInfo},
    relay::CreateRequestHandler,
    relay::channel::ChannelAuthenticationData,
};

/// An unverified relay initiator channel.
///
/// This is built by the [`crate::relay::channel::handshake::RelayInitiatorHandshake`] upon a
/// connect. It has everything needed to verify in order to get a verified channel.
pub struct UnverifiedInitiatorRelayChannel<
    T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
    S: CoarseTimeProvider + SleepProvider,
> {
    /// The common unverified channel that both client and relays use.
    pub(crate) inner: UnverifiedInitiatorChannel<T, S>,
    /// AUTH_CHALLENGE cell received from the responder.
    pub(crate) auth_challenge_cell: msg::AuthChallenge,
    /// The SLOG digest.
    pub(crate) slog_digest: SlogDigest,
    /// The netinfo cell received from the responder.
    pub(crate) netinfo_cell: msg::Netinfo,
    /// Our channel key material needed for authentication.
    pub(crate) auth_material: Arc<RelayChannelAuthMaterial>,
    /// Our advertised IP addresses for the final NETINFO
    pub(crate) my_addrs: Vec<IpAddr>,
    /// Provided to each new channel so that they can handle CREATE* requests.
    pub(crate) create_request_handler: Arc<CreateRequestHandler>,
}

impl<T, S> UnverifiedInitiatorRelayChannel<T, S>
where
    T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
    S: CoarseTimeProvider + SleepProvider,
{
    /// Validate the certificates and keys in the relay's handshake. As an initiator, we always
    /// authenticate no matter what.
    ///
    /// 'peer_target' is the peer that we want to make sure we're connecting to.
    ///
    /// 'peer_tls_cert' is the x.509 certificate that the peer presented during its TLS handshake
    /// (ServerHello).
    ///
    /// 'now' is the time at which to check that certificates are valid.  `None` means to use the
    /// current time. It can be used for testing to override the current view of the time.
    ///
    /// This is a separate function because it's likely to be somewhat CPU-intensive.
    pub fn verify(
        self,
        peer_target: &OwnedChanTarget,
        peer_tls_cert: &[u8],
        now: Option<std::time::SystemTime>,
    ) -> Result<VerifiedInitiatorRelayChannel<T, S>> {
        // Get these object out as we consume "self" in the inner check().
        let auth_challenge_cell = self.auth_challenge_cell;
        let identities = self.auth_material;
        let my_addrs = self.my_addrs;
        let netinfo_cell = self.netinfo_cell;

        let peer_tls_cert_digest = tor_llcrypto::d::Sha256::digest(peer_tls_cert).into();

        // Verify our inner channel and then proceed to handle the authentication challenge if any.
        let verified = self.inner.verify(peer_target, peer_tls_cert_digest, now)?;

        Ok(VerifiedInitiatorRelayChannel {
            inner: verified,
            auth_material: identities,
            netinfo_cell,
            auth_challenge_cell,
            peer_tls_cert_digest,
            slog_digest: self.slog_digest,
            my_addrs,
            create_request_handler: self.create_request_handler,
        })
    }

    /// Return the clock skew of this channel.
    pub fn clock_skew(&self) -> ClockSkew {
        self.inner.inner.clock_skew
    }

    /// Return the link protocol version of this channel.
    #[cfg(test)]
    pub(crate) fn link_protocol(&self) -> u16 {
        self.inner.inner.link_protocol
    }
}

/// A verified relay initiator channel.
///
/// Holding this object means the channel TLS layer has been verified against the received CERTS
/// cell and we now believe that we are talking to the right relay end point.
///
/// The finish() function needs to be called in order to finalize this channel into a generic
/// Channel/Reactor.
pub struct VerifiedInitiatorRelayChannel<
    T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
    S: CoarseTimeProvider + SleepProvider,
> {
    /// The common unverified channel that both client and relays use.
    inner: VerifiedChannel<T, S>,
    /// Relay channel authentication material.
    auth_material: Arc<RelayChannelAuthMaterial>,
    /// The netinfo cell that we got from the relay.
    netinfo_cell: msg::Netinfo,
    /// The AUTH_CHALLENGE cell that we got from the relay.
    auth_challenge_cell: msg::AuthChallenge,
    /// The peer TLS certificate digest.
    peer_tls_cert_digest: [u8; 32],
    /// The SLOG digest.
    slog_digest: SlogDigest,
    /// Our advertised IP addresses.
    my_addrs: Vec<IpAddr>,
    /// Provided to each new channel so that they can handle CREATE* requests.
    create_request_handler: Arc<CreateRequestHandler>,
}

impl<T, S> VerifiedInitiatorRelayChannel<T, S>
where
    T: AsyncRead + AsyncWrite + CertifiedConn + StreamOps + Send + Unpin + 'static,
    S: CoarseTimeProvider + SleepProvider,
{
    /// Send our [`msg::Certs`], [`msg::Authenticate`] and [`msg::Netinfo`] to the relay to finish
    /// the handshake, which will create an open channel and reactor.
    ///
    /// The resulting channel is considered, by Tor protocol standard, an authenticated relay
    /// channel on which circuits can be opened.
    pub async fn finish(mut self, peer_addr: PeerAddr) -> Result<(Arc<Channel>, Reactor<S>)>
    where
        S: Runtime,
    {
        // Send the CERTS cell.
        let certs = super::build_certs_cell(&self.auth_material, /* is_responder */ false);
        trace!(channel_id = %self.inner.unique_id, "Sending CERTS as initiator cell.");
        self.inner.framed_tls.send(certs.into()).await?;

        // We're the initiator, which means that the send log is the CLOG.
        //
        // We can finalize the CLOG now that we're about to send the AUTHENTICATE cell.
        //
        // > The CLOG field is computed as the SHA-256 digest of all bytes sent within
        // > the TLS channel up to but not including the AUTHENTICATE cell.
        let clog_digest =
            ClogDigest::new(self.inner.framed_tls.codec_mut().take_send_log_digest()?);

        // Build the AUTHENTICATE cell.
        //
        // By building the ChannelAuthenticationData, we are certain that the authentication
        // type requested by the responder is supported by us.
        let auth_cell = ChannelAuthenticationData::build_initiator(
            &self.auth_challenge_cell,
            &self.auth_material,
            clog_digest,
            self.slog_digest,
            &mut self.inner,
            self.peer_tls_cert_digest,
        )?
        .into_authenticate(
            self.inner.framed_tls.deref(),
            &self.auth_material.link_sign_kp,
        )?;

        // Send the AUTHENTICATE cell.
        trace!(channel_id = %self.inner.unique_id, "Sending AUTHENTICATE as initiator cell.");
        self.inner.framed_tls.send(auth_cell.into()).await?;

        // Send our NETINFO cell. This will indicate the end of the handshake.
        let netinfo = super::build_netinfo_cell(
            peer_addr.netinfo_addr(),
            self.my_addrs.clone(),
            &self.inner.sleep_prov,
        )?;
        trace!(channel_id = %self.inner.unique_id, "Sending NETINFO as initiator cell.");
        self.inner.framed_tls.send(netinfo.into()).await?;

        // Relay only initiate to another relay so NOT sensitive.
        let peer_info =
            MaybeSensitive::not_sensitive(PeerInfo::new(peer_addr, self.inner.relay_ids().clone()));

        let channel_mode = ChannelMode::Relay {
            circ_id_range: CircIdRange::High,
            our_ed25519_id: self.auth_material.ed_id,
            our_rsa_id: self.auth_material.rsa_id,
            create_request_handler: self.create_request_handler,
        };

        // Get a Channel and a Reactor.
        self.inner
            .finish(&self.netinfo_cell, &self.my_addrs, peer_info, channel_mode)
            .await
    }
}
