hsclient: fill in a lot more introduce/rendezvous logic.
This code tries to fill in some TODO HS code, replacing it with a lot more code with a bunch more TODO HS comments. Hopefully the expansions of the new TODO HS comments should be simpler.
This commit is contained in:
parent
2c8e3b22b5
commit
255d74ac3c
|
@ -14,13 +14,18 @@ use futures::channel::oneshot;
|
||||||
use futures::{AsyncRead, AsyncWrite};
|
use futures::{AsyncRead, AsyncWrite};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use tor_cell::relaycell::msg::{AnyRelayMsg, Rendezvous2};
|
use tor_bytes::Writeable;
|
||||||
|
use tor_cell::relaycell::hs::intro_payload::{self, IntroduceHandshakePayload};
|
||||||
|
use tor_cell::relaycell::msg::{AnyRelayMsg, Introduce1, Rendezvous2};
|
||||||
use tor_hscrypto::Subcredential;
|
use tor_hscrypto::Subcredential;
|
||||||
|
use tor_proto::circuit::handshake::{self, hs_ntor};
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
use retry_error::RetryError;
|
use retry_error::RetryError;
|
||||||
use safelog::Redacted;
|
use safelog::Redacted;
|
||||||
use tor_cell::relaycell::hs::{EstablishRendezvous, RendezvousEstablished};
|
use tor_cell::relaycell::hs::{
|
||||||
|
AuthKeyType, EstablishRendezvous, IntroduceHeader, RendezvousEstablished,
|
||||||
|
};
|
||||||
use tor_cell::relaycell::{AnyRelayCell, RelayMsg, UnparsedRelayCell};
|
use tor_cell::relaycell::{AnyRelayCell, RelayMsg, UnparsedRelayCell};
|
||||||
use tor_checkable::{timed::TimerangeBound, Timebound};
|
use tor_checkable::{timed::TimerangeBound, Timebound};
|
||||||
use tor_circmgr::hspool::{HsCircKind, HsCircPool};
|
use tor_circmgr::hspool::{HsCircKind, HsCircPool};
|
||||||
|
@ -33,7 +38,7 @@ use tor_linkspec::{CircTarget, OwnedCircTarget, RelayId};
|
||||||
use tor_llcrypto::pk::ed25519::Ed25519Identity;
|
use tor_llcrypto::pk::ed25519::Ed25519Identity;
|
||||||
use tor_netdir::{HsDirOp, NetDir, Relay};
|
use tor_netdir::{HsDirOp, NetDir, Relay};
|
||||||
use tor_netdoc::doc::hsdesc::{HsDesc, IntroPointDesc};
|
use tor_netdoc::doc::hsdesc::{HsDesc, IntroPointDesc};
|
||||||
use tor_proto::circuit::{ClientCirc, MetaCellDisposition, MsgHandler};
|
use tor_proto::circuit::{CircParameters, ClientCirc, MetaCellDisposition, MsgHandler};
|
||||||
use tor_rtcompat::{Runtime, SleepProviderExt as _, TimeoutError};
|
use tor_rtcompat::{Runtime, SleepProviderExt as _, TimeoutError};
|
||||||
|
|
||||||
use crate::relay_info::ipt_to_circtarget;
|
use crate::relay_info::ipt_to_circtarget;
|
||||||
|
@ -812,6 +817,72 @@ impl<'c, R: Runtime, M: MocksForConnect<R>> Context<'c, R, M> {
|
||||||
intro_index,
|
intro_index,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Now we construct an introduce1 message and perform the first part of the
|
||||||
|
// rendezvous handshake.
|
||||||
|
//
|
||||||
|
// This process is tricky because the header of the INTRODUCE1 message
|
||||||
|
// -- which depends on the IntroPt configuration -- is authenticated as
|
||||||
|
// part of the HsDesc handshake.
|
||||||
|
|
||||||
|
// Construct the header, since we need it as input to our encryption.
|
||||||
|
let intro_header = {
|
||||||
|
let ipt_sid_key = ipt.intro_desc.ipt_sid_key();
|
||||||
|
let intro1 = Introduce1::new(
|
||||||
|
AuthKeyType::ED25519_SHA3_256,
|
||||||
|
ipt_sid_key.as_bytes().to_vec(),
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
let mut header = vec![];
|
||||||
|
intro1
|
||||||
|
.encode_onto(&mut header)
|
||||||
|
.map_err(into_internal!("couldn't encode intro1 header"))?;
|
||||||
|
header
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct the introduce payload, which tells the onion service how to find
|
||||||
|
// our rendezvous point. (We could do this earlier if we wanted.)
|
||||||
|
let intro_payload = {
|
||||||
|
let onion_key =
|
||||||
|
intro_payload::OnionKey::NtorOnionKey(*rendezvous.rend_relay.ntor_onion_key());
|
||||||
|
let linkspecs = rendezvous
|
||||||
|
.rend_relay
|
||||||
|
.linkspecs()
|
||||||
|
.map_err(into_internal!("Couldn't encode link specifiers"))?;
|
||||||
|
let payload =
|
||||||
|
IntroduceHandshakePayload::new(rendezvous.rend_cookie, onion_key, linkspecs);
|
||||||
|
let mut encoded = vec![];
|
||||||
|
payload
|
||||||
|
.write_onto(&mut encoded)
|
||||||
|
.map_err(into_internal!("Couldn't encode introduce1 payload"))?;
|
||||||
|
encoded
|
||||||
|
};
|
||||||
|
|
||||||
|
// Perform the cryptographic handshake with the onion service.
|
||||||
|
let service_info = hs_ntor::HsNtorServiceInfo::new(
|
||||||
|
ipt.intro_desc.svc_ntor_key().clone(),
|
||||||
|
ipt.intro_desc.ipt_sid_key().clone(),
|
||||||
|
self.subcredential,
|
||||||
|
);
|
||||||
|
let handshake_state =
|
||||||
|
hs_ntor::HsNtorClientState::new(&mut self.mocks.thread_rng(), service_info);
|
||||||
|
let encrypted_body = handshake_state
|
||||||
|
.client_send_intro(&intro_header, &intro_payload)
|
||||||
|
.map_err(into_internal!("can't begin hs-ntor handshake"))?;
|
||||||
|
|
||||||
|
// Build our actual INTRODUCE1 message.
|
||||||
|
let intro1_real = Introduce1::new(
|
||||||
|
AuthKeyType::ED25519_SHA3_256,
|
||||||
|
ipt.intro_desc.ipt_sid_key().as_bytes().to_vec(),
|
||||||
|
encrypted_body,
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO HS: Send intro1_real on the introduce circuit and wait for
|
||||||
|
// either an error or an INTRO_ACK.
|
||||||
|
// intro_circ.send_control_message(intro1_real)...
|
||||||
|
|
||||||
|
// TODO HS: We need to remember handshake_state so we can later handle a
|
||||||
|
// RENDEZVOUS2 message!
|
||||||
|
|
||||||
Err(internal!("sending INTRODUCE1 is not yet implemented!").into()) // TODO HS
|
Err(internal!("sending INTRODUCE1 is not yet implemented!").into()) // TODO HS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,6 +901,48 @@ impl<'c, R: Runtime, M: MocksForConnect<R>> Context<'c, R, M> {
|
||||||
ipt: &UsableIntroPt<'_>,
|
ipt: &UsableIntroPt<'_>,
|
||||||
rendezvous: Rendezvous<'c, R, M>,
|
rendezvous: Rendezvous<'c, R, M>,
|
||||||
) -> Result<Arc<ClientCirc!(R, M)>, FAE> {
|
) -> Result<Arc<ClientCirc!(R, M)>, FAE> {
|
||||||
|
#![allow(unreachable_code, clippy::diverging_sub_expression)] // TODO HS remove.
|
||||||
|
use tor_proto::circuit::handshake;
|
||||||
|
|
||||||
|
// TODO HS: Wait for our Rendezvous2 message on rend_tx
|
||||||
|
let rend2_msg: Rendezvous2 = todo!(); // TODO HS
|
||||||
|
|
||||||
|
// TODO HS: get handshake_state form wherever we stored it above.
|
||||||
|
//
|
||||||
|
// TODO: It would be great if we could have multiple of these existing
|
||||||
|
// in parallel with similar x,X values but different ipts. I believe C
|
||||||
|
// tor manages it somehow.
|
||||||
|
let handshake_state: &hs_ntor::HsNtorClientState = todo!(); // TODO HS
|
||||||
|
|
||||||
|
// Try to complete the cryptographic handshake.
|
||||||
|
let keygen = handshake_state
|
||||||
|
.client_receive_rend(rend2_msg.handshake_info())
|
||||||
|
.map_err(into_internal!(
|
||||||
|
"ACTUALLY this is a protocol violation, make a better error" // TODO HS
|
||||||
|
))?;
|
||||||
|
// TODO HS: make sure that we do the correct error recovery from the
|
||||||
|
// above error. Either the onion service has failed, or the rendezvous
|
||||||
|
// point has misbehaved, or we have used the wrong handshake_state.
|
||||||
|
|
||||||
|
// TODO HS: Generate this more sensibly!
|
||||||
|
let params = CircParameters::default();
|
||||||
|
|
||||||
|
rendezvous
|
||||||
|
.rend_circ
|
||||||
|
.extend_virtual(
|
||||||
|
handshake::RelayProtocol::HsV3,
|
||||||
|
handshake::HandshakeRole::Initiator,
|
||||||
|
keygen,
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(into_internal!(
|
||||||
|
"actually this is probably a 'circuit closed' error" // TODO HS
|
||||||
|
))?;
|
||||||
|
|
||||||
|
// TODO HS: Now we can return the rend_circ circuit to the calling code,
|
||||||
|
// which can start using it! Isn't that great? we're done!
|
||||||
|
|
||||||
todo!() // HS implement
|
todo!() // HS implement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -890,7 +1003,7 @@ trait MocksForConnect<R>: Clone {
|
||||||
type HsCircPool: MockableCircPool<R>;
|
type HsCircPool: MockableCircPool<R>;
|
||||||
|
|
||||||
/// A random number generator
|
/// A random number generator
|
||||||
type Rng: rand::Rng;
|
type Rng: rand::Rng + rand::CryptoRng;
|
||||||
|
|
||||||
/// Tell tests we got this descriptor text
|
/// Tell tests we got this descriptor text
|
||||||
fn test_got_desc(&self, desc: &HsDesc) {
|
fn test_got_desc(&self, desc: &HsDesc) {
|
||||||
|
@ -935,6 +1048,15 @@ trait MockableClientCirc: Debug {
|
||||||
msg: AnyRelayMsg,
|
msg: AnyRelayMsg,
|
||||||
reply_handler: impl MsgHandler + Send + 'static,
|
reply_handler: impl MsgHandler + Send + 'static,
|
||||||
) -> tor_proto::Result<()>;
|
) -> tor_proto::Result<()>;
|
||||||
|
|
||||||
|
/// Add a virtual hop to the circuit.
|
||||||
|
async fn extend_virtual(
|
||||||
|
&self,
|
||||||
|
protocol: tor_proto::circuit::handshake::RelayProtocol,
|
||||||
|
protocol: tor_proto::circuit::handshake::HandshakeRole,
|
||||||
|
handshake: impl tor_proto::circuit::handshake::KeyGenerator + Send,
|
||||||
|
params: CircParameters,
|
||||||
|
) -> tor_proto::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Runtime> MocksForConnect<R> for () {
|
impl<R: Runtime> MocksForConnect<R> for () {
|
||||||
|
@ -977,6 +1099,16 @@ impl MockableClientCirc for ClientCirc {
|
||||||
) -> tor_proto::Result<()> {
|
) -> tor_proto::Result<()> {
|
||||||
ClientCirc::send_control_message(self, msg, reply_handler).await
|
ClientCirc::send_control_message(self, msg, reply_handler).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn extend_virtual(
|
||||||
|
&self,
|
||||||
|
protocol: tor_proto::circuit::handshake::RelayProtocol,
|
||||||
|
role: tor_proto::circuit::handshake::HandshakeRole,
|
||||||
|
handshake: impl tor_proto::circuit::handshake::KeyGenerator + Send,
|
||||||
|
params: CircParameters,
|
||||||
|
) -> tor_proto::Result<()> {
|
||||||
|
ClientCirc::extend_virtual(self, protocol, role, handshake, params).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -1107,6 +1239,16 @@ mod test {
|
||||||
) -> tor_proto::Result<()> {
|
) -> tor_proto::Result<()> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn extend_virtual(
|
||||||
|
&self,
|
||||||
|
protocol: tor_proto::circuit::handshake::RelayProtocol,
|
||||||
|
role: tor_proto::circuit::handshake::HandshakeRole,
|
||||||
|
handshake: impl tor_proto::circuit::handshake::KeyGenerator + Send,
|
||||||
|
params: CircParameters,
|
||||||
|
) -> tor_proto::Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[traced_test]
|
#[traced_test]
|
||||||
|
|
Loading…
Reference in New Issue