tor_relay_crypto/
pk.rs

1//! This module is where all relay related keys are declared along their key specifier for the
2//! KeyMgr so some of them can be stored on disk.
3
4use std::fmt;
5use std::time::SystemTime;
6
7use derive_deftly::Deftly;
8use derive_more::derive::{From, Into};
9use derive_more::Constructor;
10
11use tor_error::Bug;
12use tor_key_forge::define_ed25519_keypair;
13use tor_keymgr::{
14    derive_deftly_template_KeySpecifier, InvalidKeyPathComponentValue, KeySpecifier,
15    KeySpecifierComponent,
16};
17use tor_persist::slug::{timestamp::Iso8601TimeSlug, Slug};
18
19// TODO: The legacy RSA key is needed. Require support in tor-key-forge and keystore.
20// See https://gitlab.torproject.org/tpo/core/arti/-/work_items/1598
21
22define_ed25519_keypair!(
23    /// [KP_relayid_ed] Long-term identity keypair. Never rotates.
24    pub RelayIdentity
25);
26
27#[non_exhaustive]
28#[derive(Deftly, PartialEq, Debug, Constructor)]
29#[derive_deftly(KeySpecifier)]
30#[deftly(prefix = "relay")]
31#[deftly(role = "KS_relayid_ed")]
32#[deftly(summary = "Relay long-term identity keypair")]
33/// The key specifier of the relay long-term identity key (RelayIdentityKeypair)
34pub struct RelayIdentityKeypairSpecifier;
35
36#[non_exhaustive]
37#[derive(Deftly, PartialEq, Debug, Constructor)]
38#[derive_deftly(KeySpecifier)]
39#[deftly(prefix = "relay")]
40#[deftly(role = "KP_relayid_ed")]
41#[deftly(summary = "Public part of the relay long-term identity keypair")]
42/// The public part of the long-term identity key of the relay.
43pub struct RelayIdentityPublicKeySpecifier;
44
45define_ed25519_keypair!(
46    /// [KP_relaysign_ed] Medium-term signing keypair. Rotated periodically.
47    pub RelaySigning
48);
49
50#[derive(Deftly, PartialEq, Debug, Constructor)]
51#[derive_deftly(KeySpecifier)]
52#[deftly(prefix = "relay")]
53#[deftly(role = "KS_relaysign_ed")]
54#[deftly(summary = "Relay medium-term signing keypair")]
55/// The key specifier of the relay medium-term signing key.
56pub struct RelaySigningKeypairSpecifier {
57    /// The expiration time of this key.
58    ///
59    /// This **must** be the same as the expiration timestamp from the
60    /// `K_relaysign_ed` certificate of this key.
61    ///
62    /// This serves as a unique identifier for this key instance,
63    /// and is used for deciding which `K_relaysign_ed` key to use
64    /// (we use the newest key that is not yet expired according to
65    /// the `valid_until` timestamp from its specifier).
66    ///
67    /// **Important**: this timestamp should not be used for anything other than
68    /// distinguishing between different signing keypair instances.
69    /// In particular, it should **not** be used for validating the keypair,
70    /// or for checking its timeliness.
71    #[deftly(denotator)]
72    pub(crate) valid_until: Timestamp,
73}
74
75/// The approximate time when a [`RelaySigningKeypairSpecifier`] was generated.
76///
77/// Used as a denotator to distinguish between the different signing keypair instances
78/// that might be stored in the keystore.
79#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] //
80#[derive(Into, From)]
81pub struct Timestamp(Iso8601TimeSlug);
82
83impl From<SystemTime> for Timestamp {
84    fn from(t: SystemTime) -> Self {
85        Self(t.into())
86    }
87}
88
89impl KeySpecifierComponent for Timestamp {
90    fn to_slug(&self) -> Result<Slug, Bug> {
91        self.0.try_into()
92    }
93
94    fn from_slug(s: &Slug) -> Result<Self, InvalidKeyPathComponentValue>
95    where
96        Self: Sized,
97    {
98        use std::str::FromStr as _;
99
100        let timestamp = Iso8601TimeSlug::from_str(s.as_ref())
101            .map_err(|e| InvalidKeyPathComponentValue::Slug(e.to_string()))?;
102
103        Ok(Self(timestamp))
104    }
105
106    fn fmt_pretty(&self, f: &mut fmt::Formatter) -> fmt::Result {
107        fmt::Display::fmt(&self.0, f)
108    }
109}
110
111define_ed25519_keypair!(
112    /// [KP_link_ed] Short-term signing keypair for link authentication. Rotated frequently.
113    pub RelayLinkSigning
114);
115
116#[cfg(test)]
117mod test {
118    // @@ begin test lint list maintained by maint/add_warning @@
119    #![allow(clippy::bool_assert_comparison)]
120    #![allow(clippy::clone_on_copy)]
121    #![allow(clippy::dbg_macro)]
122    #![allow(clippy::mixed_attributes_style)]
123    #![allow(clippy::print_stderr)]
124    #![allow(clippy::print_stdout)]
125    #![allow(clippy::single_char_pattern)]
126    #![allow(clippy::unwrap_used)]
127    #![allow(clippy::unchecked_duration_subtraction)]
128    #![allow(clippy::useless_vec)]
129    #![allow(clippy::needless_pass_by_value)]
130    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
131    use super::*;
132
133    use tor_keymgr::test_utils::check_key_specifier;
134
135    #[test]
136    fn relay_signing_key_specifiers() {
137        let ts = SystemTime::UNIX_EPOCH;
138        let key_spec = RelaySigningKeypairSpecifier::new(ts.into());
139
140        assert_eq!(
141            key_spec.arti_path().unwrap().as_str(),
142            "relay/ks_relaysign_ed+19700101000000"
143        );
144
145        check_key_specifier(&key_spec, "relay/ks_relaysign_ed+19700101000000");
146    }
147
148    #[test]
149    fn relay_identity_key_specifiers() {
150        let key_spec = RelayIdentityKeypairSpecifier::new();
151
152        assert_eq!(
153            key_spec.arti_path().unwrap().as_str(),
154            "relay/ks_relayid_ed"
155        );
156
157        check_key_specifier(&key_spec, "relay/ks_relayid_ed");
158
159        let key_spec = RelayIdentityPublicKeySpecifier::new();
160
161        assert_eq!(
162            key_spec.arti_path().unwrap().as_str(),
163            "relay/kp_relayid_ed"
164        );
165
166        check_key_specifier(&key_spec, "relay/kp_relayid_ed");
167    }
168}