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 143 144 145 146 147 148 149 150 151 152 153 154 155 156
//! Hidden service (onion service) client key management functionality
// TODO HS what layer should be responsible for finding and dispatching keys?
// I think it should be as high as possible, so keys should be passed into
// the hs connector for each connection. Otherwise there would have to be an
// HsKeyProvider trait here, and error handling gets complicated.
use std::fmt::{self, Debug};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
#[allow(deprecated)]
use tor_hscrypto::pk::HsClientIntroAuthKeypair;
use tor_hscrypto::pk::{HsClientDescEncKeypair, HsId};
use tor_keymgr::derive_deftly_template_KeySpecifier;
use derive_deftly::Deftly;
use derive_more::Constructor;
/// Keys (if any) to use when connecting to a specific onion service.
///
/// Represents a possibly empty subset of the following keys:
/// * `KS_hsc_desc_enc`, [`HsClientDescEncKeypair`]
/// * `KS_hsc_intro_auth`, [`HsClientIntroAuthKeypair`]
///
/// `HsClientSecretKeys` is constructed with a `Builder`:
/// use `ClientSecretKeysBuilder::default()`,
/// optionally call setters, and then call `build()`.
///
/// For client connections to share circuits and streams,
/// call `build` only once.
/// Different calls to `build` yield `HsClientSecretKeys` values
/// which won't share HS circuits, streams, or authentication.
///
/// Conversely, `Clone`s of an `HsClientSecretKeys` *can* share circuits.
//
/// All [empty](HsClientSecretKeys::is_empty) `HsClientSecretKeys`
/// (for example, from [`:none()`](HsClientSecretKeys::none))
/// *can* share circuits.
//
// TODO HS some way to read these from files or something!
//
// TODO HS: some of our APIs take Option<HsClientSecretKeys>.
// But HsClientSecretKeys is can be empty, so we should remove the `Option`.
#[derive(Clone, Default)]
pub struct HsClientSecretKeys {
/// The actual keys
///
/// This is compared and hashed by the Arc pointer value.
/// We don't want to implement key comparison by comparing secret key values.
pub(crate) keys: Arc<ClientSecretKeyValues>,
}
impl Debug for HsClientSecretKeys {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// TODO derive this?
let mut d = f.debug_tuple("HsClientSecretKeys");
d.field(&Arc::as_ptr(&self.keys));
self.keys
.ks_hsc_desc_enc
.as_ref()
.map(|_| d.field(&"<desc_enc>"));
self.keys
.ks_hsc_intro_auth
.as_ref()
.map(|_| d.field(&"<intro_uath>"));
d.finish()
}
}
impl PartialEq for HsClientSecretKeys {
fn eq(&self, other: &Self) -> bool {
self.is_empty() && other.is_empty() || Arc::ptr_eq(&self.keys, &other.keys)
}
}
impl Eq for HsClientSecretKeys {}
impl Hash for HsClientSecretKeys {
fn hash<H: Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.keys).hash(state);
}
}
impl HsClientSecretKeys {
/// Create a new `HsClientSecretKeys`, for making unauthenticated connections
///
/// Creates a `HsClientSecretKeys` which has no actual keys,
/// so will make connections to hidden services
/// without any Tor-protocol-level client authentication.
pub fn none() -> Self {
Self::default()
}
/// Tests whether this `HsClientSecretKeys` actually contains any keys
pub fn is_empty(&self) -> bool {
// TODO derive this. For now, we deconstruct it to prove we check all the fields.
let ClientSecretKeyValues {
ks_hsc_desc_enc,
ks_hsc_intro_auth,
} = &*self.keys;
ks_hsc_desc_enc.is_none() && ks_hsc_intro_auth.is_none()
}
}
/// Client secret key values
///
/// Skip the whole builder pattern derivation, etc. - the types are just the same
type ClientSecretKeyValues = HsClientSecretKeysBuilder;
/// Builder for `HsClientSecretKeys`
#[derive(Default, Debug)]
pub struct HsClientSecretKeysBuilder {
/// Possibly, a key that is used to decrypt a descriptor.
pub(crate) ks_hsc_desc_enc: Option<HsClientDescEncKeypair>,
/// Possibly, a key that is used to authenticate while introducing.
#[allow(deprecated)]
pub(crate) ks_hsc_intro_auth: Option<HsClientIntroAuthKeypair>,
}
// TODO derive these setters
//
// TODO HS is this what we want for an API? We need *some* API.
// This is a bit like config but we probably don't want to
// feed secret key material through config-rs, etc.
impl HsClientSecretKeysBuilder {
/// Provide a descriptor decryption key
pub fn ks_hsc_desc_enc(&mut self, ks: HsClientDescEncKeypair) -> &mut Self {
self.ks_hsc_desc_enc = Some(ks);
self
}
/// Provide an introduction authentication key
#[deprecated]
#[allow(deprecated)]
pub fn ks_hsc_intro_auth(&mut self, ks: HsClientIntroAuthKeypair) -> &mut Self {
self.ks_hsc_intro_auth = Some(ks);
self
}
/// Convert these
pub fn build(self) -> Result<HsClientSecretKeys, tor_config::ConfigBuildError> {
Ok(HsClientSecretKeys {
keys: Arc::new(self),
})
}
}
#[derive(Deftly, PartialEq, Debug, Constructor)]
#[derive_deftly(KeySpecifier)]
#[deftly(prefix = "client")]
#[deftly(role = "KS_hsc_desc_enc")]
#[deftly(summary = "Descriptor decryption key")]
/// A key for deriving keys for decrypting HS descriptors (KS_hsc_desc_enc).
pub struct HsClientDescEncKeypairSpecifier {
/// The hidden service this authorization key is for.
pub(crate) hs_id: HsId,
}