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 itertools::Itertools;
|
||||
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_proto::circuit::handshake::{self, hs_ntor};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use retry_error::RetryError;
|
||||
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_checkable::{timed::TimerangeBound, Timebound};
|
||||
use tor_circmgr::hspool::{HsCircKind, HsCircPool};
|
||||
|
@ -33,7 +38,7 @@ use tor_linkspec::{CircTarget, OwnedCircTarget, RelayId};
|
|||
use tor_llcrypto::pk::ed25519::Ed25519Identity;
|
||||
use tor_netdir::{HsDirOp, NetDir, Relay};
|
||||
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 crate::relay_info::ipt_to_circtarget;
|
||||
|
@ -812,6 +817,72 @@ impl<'c, R: Runtime, M: MocksForConnect<R>> Context<'c, R, M> {
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -830,6 +901,48 @@ impl<'c, R: Runtime, M: MocksForConnect<R>> Context<'c, R, M> {
|
|||
ipt: &UsableIntroPt<'_>,
|
||||
rendezvous: Rendezvous<'c, R, M>,
|
||||
) -> 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
|
||||
}
|
||||
}
|
||||
|
@ -890,7 +1003,7 @@ trait MocksForConnect<R>: Clone {
|
|||
type HsCircPool: MockableCircPool<R>;
|
||||
|
||||
/// A random number generator
|
||||
type Rng: rand::Rng;
|
||||
type Rng: rand::Rng + rand::CryptoRng;
|
||||
|
||||
/// Tell tests we got this descriptor text
|
||||
fn test_got_desc(&self, desc: &HsDesc) {
|
||||
|
@ -935,6 +1048,15 @@ trait MockableClientCirc: Debug {
|
|||
msg: AnyRelayMsg,
|
||||
reply_handler: impl MsgHandler + Send + 'static,
|
||||
) -> 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 () {
|
||||
|
@ -977,6 +1099,16 @@ impl MockableClientCirc for ClientCirc {
|
|||
) -> tor_proto::Result<()> {
|
||||
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]
|
||||
|
@ -1107,6 +1239,16 @@ mod test {
|
|||
) -> tor_proto::Result<()> {
|
||||
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]
|
||||
|
|
Loading…
Reference in New Issue