1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! Features for manual invocation of Tor's cryptographic circuit handshakes.
//!
//! These features are used to implement onion services, by giving the onion
//! service code more direct control over the lower-level pieces of the protocol.
// Here we re-export some key types from our cryptographic code, for use when we
// implement our onion handshake.
//
// TODO: it might be neat, someday, to clean this all up so that the types
// and functions in hs_ntor are all methods on a set of related traits. But
// that can wait IMO until we have a second circuit creation mechanism for use
// with onion services.
use tor_cell::relaycell::{RelayCellFormat, RelayCellFormatV0};
use tor_error::internal;
use crate::crypto::binding::CircuitBinding;
#[cfg(feature = "hs-common")]
use crate::crypto::cell::Tor1Hsv3RelayCrypto;
use crate::crypto::cell::{
ClientLayer, CryptInit, InboundClientLayer, OutboundClientLayer, Tor1RelayCrypto,
};
use crate::Result;
#[cfg(feature = "hs-common")]
#[cfg_attr(docsrs, doc(cfg(feature = "hs-common")))]
pub use crate::crypto::handshake::hs_ntor;
pub use crate::crypto::handshake::KeyGenerator;
/// The relay protocol to use when extending a circuit manually with
/// [`Circuit::extend_virtual`](crate::circuit::ClientCirc::extend_virtual).
//
// NOTE: These correspond internally to implementations of
// crate::crypto::cell::ClientLayer.
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[cfg(feature = "hs-common")]
pub enum RelayProtocol {
/// A variation of Tor's original protocol, using AES-256 and SHA-3.
HsV3,
}
/// Internal counterpart of RelayProtocol; includes variants that can't be
/// negotiated from [`extend_virtual`](crate::circuit::ClientCirc::extend_virtual).
#[derive(Copy, Clone, Debug)]
pub(crate) enum RelayCryptLayerProtocol {
/// The original Tor cell encryption protocol, using AES-128 and SHA-1.
///
/// References:
/// - <https://spec.torproject.org/tor-spec/relay-cells.html>
/// - <https://spec.torproject.org/tor-spec/routing-relay-cells.html>
Tor1(RelayCellFormat),
/// A variation of Tor's original cell encryption protocol, using AES-256
/// and SHA3-256.
///
/// Reference:
/// - <https://spec.torproject.org/rend-spec/encrypting-user-data.html>
/// - <https://spec.torproject.org/rend-spec/introduction-protocol.html#INTRO-HANDSHAKE-REQS>
#[cfg(feature = "hs-common")]
HsV3(RelayCellFormat),
}
#[cfg(feature = "hs-common")]
impl From<RelayProtocol> for RelayCryptLayerProtocol {
fn from(value: RelayProtocol) -> Self {
match value {
RelayProtocol::HsV3 => RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0),
}
}
}
/// What role we are playing in a handshake.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum HandshakeRole {
/// We are the party initiating the handshake.
Initiator,
/// We are the party responding to the handshake.
Responder,
}
/// A set of type-erased cryptographic layers to use for a single hop at a
/// client.
pub(crate) struct BoxedClientLayer {
/// The outbound cryptographic layer to use for this hop
pub(crate) fwd: Box<dyn OutboundClientLayer + Send>,
/// The inbound cryptogarphic layer to use for this hop
pub(crate) back: Box<dyn InboundClientLayer + Send>,
/// A circuit binding key for this hop.
pub(crate) binding: Option<CircuitBinding>,
}
impl RelayCryptLayerProtocol {
/// Construct the cell-crypto layers that are needed for a given set of
/// circuit hop parameters.
pub(crate) fn construct_layers(
self,
role: HandshakeRole,
keygen: impl KeyGenerator,
) -> Result<BoxedClientLayer> {
use RelayCellFormat::*;
use RelayCryptLayerProtocol::*;
match self {
Tor1(V0) => construct::<Tor1RelayCrypto<RelayCellFormatV0>, _>(keygen, role),
Tor1(_) => Err(internal!("protocol not implemented").into()),
#[cfg(feature = "hs-common")]
HsV3(V0) => construct::<Tor1Hsv3RelayCrypto<RelayCellFormatV0>, _>(keygen, role),
#[cfg(feature = "hs-common")]
HsV3(_) => Err(internal!("protocol not implemented").into()),
}
}
/// Return the cell format used by this protocol.
pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
match self {
RelayCryptLayerProtocol::Tor1(v) => *v,
#[cfg(feature = "hs-common")]
RelayCryptLayerProtocol::HsV3(v) => *v,
}
}
}
/// Helper: Construct a BoxedClientLayer for a layer type L whose inbound and outbound
/// cryptographic states are the same type.
fn construct<L, F>(keygen: impl KeyGenerator, role: HandshakeRole) -> Result<BoxedClientLayer>
where
L: CryptInit + ClientLayer<F, F>,
F: OutboundClientLayer + InboundClientLayer + Send + 'static,
{
let layer = L::construct(keygen)?;
let (mut fwd, mut back, binding) = layer.split();
if role == HandshakeRole::Responder {
std::mem::swap(&mut fwd, &mut back);
}
Ok(BoxedClientLayer {
fwd: Box::new(fwd),
back: Box::new(back),
binding: Some(binding),
})
}