tor_hsclient/
keys.rs

1//! Hidden service (onion service) client key management functionality
2
3// TODO HS what layer should be responsible for finding and dispatching keys?
4// I think it should be as high as possible, so keys should be passed into
5// the hs connector for each connection.  Otherwise there would have to be an
6// HsKeyProvider trait here, and error handling gets complicated.
7
8use std::fmt::{self, Debug};
9use std::hash::{Hash, Hasher};
10use std::sync::Arc;
11
12#[allow(deprecated)]
13use tor_hscrypto::pk::HsClientIntroAuthKeypair;
14use tor_hscrypto::pk::{HsClientDescEncKeypair, HsId};
15use tor_keymgr::{derive_deftly_template_KeySpecifier, CTorPath};
16
17use derive_deftly::Deftly;
18use derive_more::Constructor;
19use tor_keymgr::KeySpecifier;
20
21/// Service discovery keys (if any) to use when connecting to a specific onion service.
22///
23/// Represents a possibly empty subset of the following keys:
24///  * `KS_hsc_desc_enc`, [`HsClientDescEncKeypair`]
25///  * `KS_hsc_intro_auth`, [`HsClientIntroAuthKeypair`]
26///
27/// `HsClientSecretKeys` is constructed with a `Builder`:
28/// use `ClientSecretKeysBuilder::default()`,
29/// optionally call setters, and then call `build()`.
30///
31/// For client connections to share circuits and streams,
32/// call `build` only once.
33/// Different calls to `build` yield `HsClientSecretKeys` values
34/// which won't share HS circuits, streams, or authentication.
35///
36/// Conversely, `Clone`s of an `HsClientSecretKeys` *can* share circuits.
37//
38/// All [empty](HsClientSecretKeys::is_empty) `HsClientSecretKeys`
39/// (for example, from [`:none()`](HsClientSecretKeys::none))
40/// *can* share circuits.
41//
42// TODO HS some way to read these from files or something!
43//
44// TODO HS: some of our APIs take Option<HsClientSecretKeys>.
45// But HsClientSecretKeys is can be empty, so we should remove the `Option`.
46#[derive(Clone, Default)]
47pub struct HsClientSecretKeys {
48    /// The actual keys
49    ///
50    /// This is compared and hashed by the Arc pointer value.
51    /// We don't want to implement key comparison by comparing secret key values.
52    pub(crate) keys: Arc<ClientSecretKeyValues>,
53}
54
55impl Debug for HsClientSecretKeys {
56    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57        // TODO derive this?
58        let mut d = f.debug_tuple("HsClientSecretKeys");
59        d.field(&Arc::as_ptr(&self.keys));
60        self.keys
61            .ks_hsc_desc_enc
62            .as_ref()
63            .map(|_| d.field(&"<desc_enc>"));
64        self.keys
65            .ks_hsc_intro_auth
66            .as_ref()
67            .map(|_| d.field(&"<intro_uath>"));
68        d.finish()
69    }
70}
71
72impl PartialEq for HsClientSecretKeys {
73    fn eq(&self, other: &Self) -> bool {
74        self.is_empty() && other.is_empty() || Arc::ptr_eq(&self.keys, &other.keys)
75    }
76}
77impl Eq for HsClientSecretKeys {}
78impl Hash for HsClientSecretKeys {
79    fn hash<H: Hasher>(&self, state: &mut H) {
80        Arc::as_ptr(&self.keys).hash(state);
81    }
82}
83
84impl HsClientSecretKeys {
85    /// Create a new `HsClientSecretKeys`, for making connections to services
86    /// that are not running in restricted discovery mode.
87    ///
88    /// Creates a `HsClientSecretKeys` which has no actual keys,
89    /// so will not use a descriptor cookie when decrypting the second layer
90    /// of descriptor encryption.
91    pub fn none() -> Self {
92        Self::default()
93    }
94
95    /// Tests whether this `HsClientSecretKeys` actually contains any keys
96    pub fn is_empty(&self) -> bool {
97        // TODO derive this.  For now, we deconstruct it to prove we check all the fields.
98        let ClientSecretKeyValues {
99            ks_hsc_desc_enc,
100            ks_hsc_intro_auth,
101        } = &*self.keys;
102        ks_hsc_desc_enc.is_none() && ks_hsc_intro_auth.is_none()
103    }
104}
105
106/// Client secret key values
107///
108/// Skip the whole builder pattern derivation, etc. - the types are just the same
109type ClientSecretKeyValues = HsClientSecretKeysBuilder;
110
111/// Builder for `HsClientSecretKeys`
112#[derive(Default, Debug)]
113pub struct HsClientSecretKeysBuilder {
114    /// Possibly, a key that is used to decrypt a descriptor.
115    pub(crate) ks_hsc_desc_enc: Option<HsClientDescEncKeypair>,
116
117    /// Possibly, a key that is used to authenticate while introducing.
118    #[allow(deprecated)]
119    pub(crate) ks_hsc_intro_auth: Option<HsClientIntroAuthKeypair>,
120}
121
122// TODO derive these setters
123//
124// TODO HS is this what we want for an API?  We need *some* API.
125// This is a bit like config but we probably don't want to
126// feed secret key material through config-rs, etc.
127impl HsClientSecretKeysBuilder {
128    /// Provide a descriptor decryption key
129    pub fn ks_hsc_desc_enc(&mut self, ks: HsClientDescEncKeypair) -> &mut Self {
130        self.ks_hsc_desc_enc = Some(ks);
131        self
132    }
133    /// Provide an introduction authentication key
134    #[deprecated]
135    #[allow(deprecated)]
136    pub fn ks_hsc_intro_auth(&mut self, ks: HsClientIntroAuthKeypair) -> &mut Self {
137        self.ks_hsc_intro_auth = Some(ks);
138        self
139    }
140
141    /// Convert these
142    pub fn build(self) -> Result<HsClientSecretKeys, tor_config::ConfigBuildError> {
143        Ok(HsClientSecretKeys {
144            keys: Arc::new(self),
145        })
146    }
147}
148
149#[derive(Deftly, PartialEq, Debug, Constructor)]
150#[derive_deftly(KeySpecifier)]
151#[deftly(prefix = "client")]
152#[deftly(role = "KS_hsc_desc_enc")]
153#[deftly(summary = "Descriptor decryption key")]
154#[deftly(ctor_path = "client_desc_enc_keypair_key_specifier_ctor_path")]
155/// A key for deriving keys for decrypting HS descriptors (KS_hsc_desc_enc).
156pub struct HsClientDescEncKeypairSpecifier {
157    /// The hidden service this authorization key is for.
158    pub(crate) hs_id: HsId,
159}
160/// The `CTorPath` of HsClientDescEncKeypairSpecifier
161fn client_desc_enc_keypair_key_specifier_ctor_path(
162    spec: &HsClientDescEncKeypairSpecifier,
163) -> CTorPath {
164    CTorPath::ClientHsDescEncKey(spec.hs_id)
165}