Skip to content
Snippets Groups Projects
Commit 455d0106 authored by Nick Mathewson's avatar Nick Mathewson :game_die:
Browse files

Merge branch 'fix-1242' into 'main'

tor-hsservice: Do not store the subcredentials in RendRequestContext.

Closes #1242

See merge request !1901
parents 8d50447e 5f9d5cad
No related branches found
No related tags found
1 merge request!1901tor-hsservice: Do not store the subcredentials in RendRequestContext.
ADDED: `RunningOnionService`, `OnionServiceState`
BREAKING: `OnionService` API changes
BREAKING: renamed stop to pause, then removed it.
ADDED: `IntroRequestError::Subcredentials`
......@@ -5,22 +5,28 @@
use educe::Educe;
use futures::{Stream, StreamExt};
use itertools::Itertools;
use std::sync::Arc;
use tor_cell::relaycell::msg::{Connected, End, Introduce2};
use tor_hscrypto::{
pk::{HsIntroPtSessionIdKey, HsSvcNtorKeypair},
pk::{HsBlindIdKeypair, HsIdKey, HsIntroPtSessionIdKey, HsSvcNtorKeypair},
time::TimePeriod,
Subcredential,
};
use tor_keymgr::{
ArtiPathComponent, KeyMgr, KeyPath, KeyPathRange, KeySpecifierComponent, KeySpecifierPattern,
};
use tor_error::Bug;
use tor_error::{internal, Bug};
use tor_proto::{
circuit::ClientCirc,
stream::{DataStream, IncomingStream, IncomingStreamRequest},
};
use crate::{
keys::BlindIdKeypairSpecifierPattern,
svc::rend_handshake::{self, RendCircConnector},
ClientError, IptLocalId,
ClientError, FatalError, HsIdPublicKeySpecifier, HsNickname, IptLocalId,
};
/// Request to complete an introduction/rendezvous handshake.
......@@ -75,6 +81,12 @@ pub struct StreamRequest {
/// Keys and objects needed to answer a RendRequest.
pub(crate) struct RendRequestContext {
/// The nickname of the service receiving the request.
pub(crate) nickname: HsNickname,
/// The key manager, used for looking up subcredentials.
pub(crate) keymgr: Arc<KeyMgr>,
/// Key we'll use to decrypt the rendezvous request.
pub(crate) kp_hss_ntor: Arc<HsSvcNtorKeypair>,
......@@ -82,10 +94,6 @@ pub(crate) struct RendRequestContext {
/// and prevent replays across sessions.
pub(crate) kp_hs_ipt_sid: HsIntroPtSessionIdKey,
/// A set of subcredentials that we accept as identifying ourself on this
/// introduction point.
pub(crate) subcredentials: Vec<Subcredential>,
/// Provider we'll use to find a directory so that we can build a rendezvous
/// circuit.
pub(crate) netdir_provider: Arc<dyn tor_netdir::NetDirProvider>,
......@@ -94,6 +102,89 @@ pub(crate) struct RendRequestContext {
pub(crate) circ_pool: Arc<dyn RendCircConnector + Send + Sync>,
}
impl RendRequestContext {
/// Obtain the all current `Subcredential`s of `nickname`
/// from the `K_hs_blind_id` read from the keystore.
pub(crate) fn compute_subcredentials(&self) -> Result<Vec<Subcredential>, FatalError> {
let hsid_key_spec = HsIdPublicKeySpecifier::new(self.nickname.clone());
let hsid = self
.keymgr
.get::<HsIdKey>(&hsid_key_spec)?
.ok_or_else(|| FatalError::MissingHsIdKeypair(self.nickname.clone()))?;
let pattern = BlindIdKeypairSpecifierPattern {
nickname: Some(self.nickname.clone()),
period: None,
}
.arti_pattern()?;
let blind_id_kps: Vec<(HsBlindIdKeypair, TimePeriod)> = self
.keymgr
.list_matching(&pattern)?
.iter()
.map(|(path, key_type)| -> Result<Option<_>, FatalError> {
let matches = path
.matches(&pattern)
.ok_or_else(|| internal!("path matched but no longer does?!"))?;
let period = Self::parse_time_period(path, &matches)?;
// Try to retrieve the key.
self.keymgr
.get_with_type::<HsBlindIdKeypair>(path, key_type)
.map_err(FatalError::Keystore)
// If the key is not found, it means it has been garbage collected between the time
// we queried the keymgr for the list of keys matching the pattern and now.
// This is OK, because we only need the "current" keys
.map(|maybe_key| maybe_key.map(|key| (key, period)))
})
.flatten_ok()
.collect::<Result<Vec<_>, FatalError>>()?;
Ok(blind_id_kps
.iter()
.map(|(blind_id_key, period)| hsid.compute_subcredential(&blind_id_key.into(), *period))
.collect())
}
/// Try to parse the `captures` of `path` as a [`TimePeriod`].
fn parse_time_period(
path: &KeyPath,
captures: &[KeyPathRange],
) -> Result<TimePeriod, tor_keymgr::Error> {
use tor_keymgr::{KeyPathError, KeystoreCorruptionError as KCE};
let path = match path {
KeyPath::Arti(path) => path,
KeyPath::CTor(_) => todo!(),
_ => todo!(),
};
let [denotator] = captures else {
return Err(internal!(
"invalid number of denotator captures: expected 1, found {}",
captures.len()
)
.into());
};
let Some(denotator) = path.substring(denotator) else {
return Err(internal!("captured substring out of range?!").into());
};
let comp = ArtiPathComponent::new(denotator.to_string())
.map_err(|e| KCE::KeyPath(KeyPathError::InvalidArtiPath(e)))?;
let tp = TimePeriod::from_component(&comp).map_err(|error| {
KCE::KeyPath(KeyPathError::InvalidKeyPathComponentValue {
key: "time_period".to_owned(),
value: comp,
error,
})
})?;
Ok(tp)
}
}
impl RendRequest {
/// Construct a new RendRequest from its parts.
pub(crate) fn new(
......
......@@ -12,7 +12,6 @@ use std::sync::{Arc, Mutex};
use educe::Educe;
use futures::{channel::mpsc, task::SpawnExt as _, Future, FutureExt as _};
use itertools::Itertools;
use postage::watch;
use safelog::Redactable as _;
use tor_async_utils::oneshot;
......@@ -24,16 +23,8 @@ use tor_cell::relaycell::{
};
use tor_circmgr::hspool::HsCircPool;
use tor_error::{bad_api_usage, debug_report, internal, into_internal};
use tor_hscrypto::{
pk::{HsBlindIdKeypair, HsIdKey, HsIntroPtSessionIdKeypair, HsSvcNtorKeypair},
time::TimePeriod,
Subcredential,
};
use tor_keymgr::ArtiPathComponent;
use tor_keymgr::KeyPath;
use tor_keymgr::KeySpecifierComponent;
use tor_keymgr::KeySpecifierPattern as _;
use tor_keymgr::{KeyMgr, KeyPathRange};
use tor_hscrypto::pk::{HsIntroPtSessionIdKeypair, HsSvcNtorKeypair};
use tor_keymgr::KeyMgr;
use tor_linkspec::CircTarget;
use tor_linkspec::{HasRelayIds as _, RelayIds};
use tor_netdir::NetDirProvider;
......@@ -42,10 +33,8 @@ use tor_rtcompat::{Runtime, SleepProviderExt as _};
use tracing::debug;
use void::{ResultVoidErrExt as _, Void};
use crate::keys::BlindIdKeypairSpecifierPattern;
use crate::replay::ReplayError;
use crate::replay::ReplayLog;
use crate::HsIdPublicKeySpecifier;
use crate::OnionServiceConfig;
use crate::{
req::RendRequestContext,
......@@ -327,18 +316,11 @@ impl IptEstablisher {
let state = Arc::new(Mutex::new(EstablisherState { accepting_requests }));
// We need the subcredential for the *current time period* in order to do the hs_ntor
// handshake. But that can change over time. We will instead use KeyMgr::get_matching to
// find all current subcredentials.
//
// TODO HSS: perhaps the subcredentials should be retrieved in
// server_receive_intro_no_keygen instead? See also the TODO in HsNtorServiceInput
let subcredentials = compute_subcredentials(&nickname, keymgr)?;
let request_context = Arc::new(RendRequestContext {
nickname: nickname.clone(),
keymgr: Arc::clone(keymgr),
kp_hss_ntor: Arc::clone(&k_ntor),
kp_hs_ipt_sid: k_sid.as_ref().as_ref().verifying_key().into(),
subcredentials,
netdir_provider: netdir_provider.clone(),
circ_pool: pool.clone(),
});
......@@ -401,88 +383,6 @@ impl IptEstablisher {
}
}
/// Obtain the all current `Subcredential`s of `nickname`
/// from the `K_hs_blind_id` read from the keystore.
fn compute_subcredentials(
nickname: &HsNickname,
keymgr: &Arc<KeyMgr>,
) -> Result<Vec<Subcredential>, FatalError> {
let hsid_key_spec = HsIdPublicKeySpecifier::new(nickname.clone());
let hsid = keymgr
.get::<HsIdKey>(&hsid_key_spec)?
.ok_or_else(|| FatalError::MissingHsIdKeypair(nickname.clone()))?;
let pattern = BlindIdKeypairSpecifierPattern {
nickname: Some(nickname.clone()),
period: None,
}
.arti_pattern()?;
let blind_id_kps: Vec<(HsBlindIdKeypair, TimePeriod)> = keymgr
.list_matching(&pattern)?
.iter()
.map(|(path, key_type)| -> Result<Option<_>, FatalError> {
let matches = path
.matches(&pattern)
.ok_or_else(|| internal!("path matched but no longer does?!"))?;
let period = parse_time_period(path, &matches)?;
// Try to retrieve the key.
keymgr
.get_with_type::<HsBlindIdKeypair>(path, key_type)
.map_err(FatalError::Keystore)
// If the key is not found, it means it has been garbage collected between the time
// we queried the keymgr for the list of keys matching the pattern and now.
// This is OK, because we only need the "current" keys
.map(|maybe_key| maybe_key.map(|key| (key, period)))
})
.flatten_ok()
.collect::<Result<Vec<_>, FatalError>>()?;
Ok(blind_id_kps
.iter()
.map(|(blind_id_key, period)| hsid.compute_subcredential(&blind_id_key.into(), *period))
.collect())
}
/// Try to parse the `captures` of `path` as a [`TimePeriod`].
fn parse_time_period(
path: &KeyPath,
captures: &[KeyPathRange],
) -> Result<TimePeriod, tor_keymgr::Error> {
use tor_keymgr::{KeyPathError, KeystoreCorruptionError as KCE};
let path = match path {
KeyPath::Arti(path) => path,
KeyPath::CTor(_) => todo!(),
_ => todo!(),
};
let [denotator] = captures else {
return Err(internal!(
"invalid number of denotator captures: expected 1, found {}",
captures.len()
)
.into());
};
let Some(denotator) = path.substring(denotator) else {
return Err(internal!("captured substring out of range?!").into());
};
let comp = ArtiPathComponent::new(denotator.to_string())
.map_err(|e| KCE::KeyPath(KeyPathError::InvalidArtiPath(e)))?;
let tp = TimePeriod::from_component(&comp).map_err(|error| {
KCE::KeyPath(KeyPathError::InvalidKeyPathComponentValue {
key: "time_period".to_owned(),
value: comp,
error,
})
})?;
Ok(tp)
}
/// The current status of an introduction point, as defined in
/// `hssvc-ipt-algorithms.md`.
///
......
......@@ -49,6 +49,10 @@ pub enum IntroRequestError {
/// We weren't able to build a ChanTarget from the Introduce2 message.
#[error("Invalid link specifiers in INTRODUCE2 payload")]
InvalidLinkSpecs(#[source] tor_linkspec::decode::ChanTargetDecodeError),
/// We weren't able to obtain the subcredentials for decrypting the Introduce2 message.
#[error("Could not obtain subcredebtials")]
Subcredentials(#[source] crate::FatalError),
}
impl HasKind for IntroRequestError {
......@@ -59,6 +63,7 @@ impl HasKind for IntroRequestError {
E::InvalidHandshake(e) => e.kind(),
E::InvalidPayload(_) => EK::RemoteProtocolViolation,
E::InvalidLinkSpecs(_) => EK::RemoteProtocolViolation,
E::Subcredentials(e) => e.kind(),
}
}
}
......@@ -200,11 +205,18 @@ impl IntroRequest {
use IntroRequestError as E;
let mut rng = rand::thread_rng();
// We need the subcredential for the *current time period* in order to do the hs_ntor
// handshake. But that can change over time. We will instead use KeyMgr::get_matching to
// find all current subcredentials.
let subcredentials = context
.compute_subcredentials()
.map_err(IntroRequestError::Subcredentials)?;
let (key_gen, rend1_body, msg_body) = hs_ntor::server_receive_intro(
&mut rng,
&context.kp_hss_ntor,
&context.kp_hs_ipt_sid,
&context.subcredentials[..],
&subcredentials[..],
req.encoded_header(),
req.encrypted_body(),
)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment