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

            
4
use std::fmt;
5

            
6
use derive_deftly::Deftly;
7
use derive_more::Constructor;
8
use derive_more::derive::{From, Into};
9

            
10
use tor_error::Bug;
11
use tor_keymgr::{
12
    InvalidKeyPathComponentValue, KeySpecifier, KeySpecifierComponent,
13
    derive_deftly_template_CertSpecifier, derive_deftly_template_KeySpecifier,
14
};
15
use tor_persist::slug::{Slug, timestamp::Iso8601TimeSlug};
16
use web_time_compat::SystemTime;
17

            
18
/// The key specifier of the relay long-term identity key (RelayIdentityKeypair)
19
#[non_exhaustive]
20
#[derive(Deftly, PartialEq, Debug, Constructor, Copy, Clone)]
21
#[derive_deftly(KeySpecifier)]
22
#[deftly(prefix = "relay")]
23
#[deftly(role = "KS_relayid_ed")]
24
#[deftly(summary = "Relay long-term identity keypair")]
25
pub(crate) struct RelayIdentityKeypairSpecifier;
26

            
27
/// The public part of the long-term identity key of the relay.
28
#[non_exhaustive]
29
#[derive(Deftly, PartialEq, Debug, Constructor, Copy, Clone)]
30
#[derive_deftly(KeySpecifier)]
31
#[deftly(prefix = "relay")]
32
#[deftly(role = "KP_relayid_ed")]
33
#[deftly(summary = "Public part of the relay long-term identity keypair")]
34
pub(crate) struct RelayIdentityPublicKeySpecifier;
35

            
36
/// The key specifier of the legacy RSA relay long-term identity key (RelayIdentityRsaKeypair)
37
#[non_exhaustive]
38
#[derive(Deftly, PartialEq, Debug, Constructor, Copy, Clone)]
39
#[derive_deftly(KeySpecifier)]
40
#[deftly(prefix = "relay")]
41
#[deftly(role = "KS_relayid_rsa")]
42
#[deftly(summary = "Legacy RSA long-term relay identity keypair")]
43
pub(crate) struct RelayIdentityRsaKeypairSpecifier;
44

            
45
/// The public part of the long-term identity key of the relay.
46
#[non_exhaustive]
47
#[derive(Deftly, PartialEq, Debug, Constructor, Copy, Clone)]
48
#[derive_deftly(KeySpecifier)]
49
#[deftly(prefix = "relay")]
50
#[deftly(role = "KP_relayid_rsa")]
51
#[deftly(summary = "Public part of the relay long-term identity keypair")]
52
pub(crate) struct RelayIdentityRsaPublicKeySpecifier;
53

            
54
/// The key specifier of the relay medium-term signing key.
55
#[derive(Deftly, PartialEq, Debug, Constructor, Copy, Clone)]
56
#[derive_deftly(KeySpecifier)]
57
#[deftly(prefix = "relay")]
58
#[deftly(role = "KS_relaysign_ed")]
59
#[deftly(summary = "Relay medium-term signing keypair")]
60
pub(crate) struct RelaySigningKeypairSpecifier {
61
    /// The expiration time of this key.
62
    ///
63
    /// This **must** be the same as the expiration timestamp from the
64
    /// `K_relaysign_ed` certificate of this key.
65
    ///
66
    /// This serves as a unique identifier for this key instance,
67
    /// and is used for deciding which `K_relaysign_ed` key to use
68
    /// (we use the newest key that is not yet expired according to
69
    /// the `valid_until` timestamp from its specifier).
70
    ///
71
    /// **Important**: this timestamp should not be used for anything other than
72
    /// distinguishing between different signing keypair instances.
73
    /// In particular, it should **not** be used for validating the keypair,
74
    /// or for checking its timeliness.
75
    #[deftly(denotator)]
76
    pub(crate) valid_until: Timestamp,
77
}
78

            
79
/// The key specifier of the public part of the relay medium-term signing key.
80
#[derive(Deftly, PartialEq, Debug, Constructor, Copy, Clone)]
81
#[derive_deftly(KeySpecifier)]
82
#[deftly(prefix = "relay")]
83
#[deftly(role = "KP_relaysign_ed")]
84
#[deftly(summary = "Public part of the relay medium-term signing keypair")]
85
#[deftly(keypair_specifier = RelaySigningKeypairSpecifier)]
86
pub(crate) struct RelaySigningPublicKeySpecifier {
87
    /// The expiration time of this key.
88
    ///
89
    /// This **must** be the same as the expiration timestamp from the
90
    /// `K_relaysign_ed` certificate of this key.
91
    ///
92
    /// This serves as a unique identifier for this key instance,
93
    /// and is used for deciding which `K_relaysign_ed` key to use
94
    /// (we use the newest key that is not yet expired according to
95
    /// the `valid_until` timestamp from its specifier).
96
    ///
97
    /// **Important**: this timestamp should not be used for anything other than
98
    /// distinguishing between different signing keypair instances.
99
    /// In particular, it should **not** be used for validating the keypair,
100
    /// or for checking its timeliness.
101
    #[deftly(denotator)]
102
    pub(crate) valid_until: Timestamp,
103
}
104

            
105
impl From<&RelaySigningPublicKeySpecifier> for RelaySigningKeypairSpecifier {
106
68
    fn from(public_key_specifier: &RelaySigningPublicKeySpecifier) -> RelaySigningKeypairSpecifier {
107
68
        RelaySigningKeypairSpecifier::new(public_key_specifier.valid_until)
108
68
    }
109
}
110

            
111
/// The key specifier of the relay medium-term ntor circuit extension key.
112
#[derive(Deftly, PartialEq, Debug, Constructor, Copy, Clone)]
113
#[derive_deftly(KeySpecifier)]
114
#[deftly(prefix = "relay")]
115
#[deftly(role = "KS_ntor")]
116
#[deftly(summary = "Relay medium-term ntor keypair")]
117
pub(crate) struct RelayNtorKeypairSpecifier {
118
    /// The expiration time of this key.
119
    #[deftly(denotator)]
120
    pub(crate) valid_until: Timestamp,
121
}
122

            
123
/// The key specifier of the public part of the relay medium-term ntor circuit extension key.
124
#[derive(Deftly, PartialEq, Debug, Constructor, Copy, Clone)]
125
#[derive_deftly(KeySpecifier)]
126
#[deftly(prefix = "relay")]
127
#[deftly(role = "KP_ntor")]
128
#[deftly(summary = "Public part of the relay medium-term ntor keypair")]
129
#[deftly(keypair_specifier = RelayNtorKeypairSpecifier)]
130
pub(crate) struct RelayNtorPublicKeySpecifier {
131
    /// The expiration time of this key.
132
    #[deftly(denotator)]
133
    pub(crate) valid_until: Timestamp,
134
}
135

            
136
impl From<&RelayNtorPublicKeySpecifier> for RelayNtorKeypairSpecifier {
137
    fn from(public_key_specifier: &RelayNtorPublicKeySpecifier) -> RelayNtorKeypairSpecifier {
138
        RelayNtorKeypairSpecifier::new(public_key_specifier.valid_until)
139
    }
140
}
141

            
142
/// Certificate specifier for the [`RelaySigningPublicKeySpecifier`] certificate.
143
///
144
/// Represents `KP_relaysign_ed` signed by the `KS_relayid_ed` identity key.
145
#[derive(Deftly, Constructor)]
146
#[derive_deftly(CertSpecifier)]
147
pub(crate) struct RelaySigningKeyCertSpecifier {
148
    /// The subject key of this certificate.
149
    #[deftly(subject)]
150
    subject: RelaySigningPublicKeySpecifier,
151
}
152

            
153
/// The approximate time when a [`RelaySigningKeypairSpecifier`] was generated.
154
///
155
/// Used as a denotator to distinguish between the different signing keypair instances
156
/// that might be stored in the keystore.
157
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] //
158
#[derive(Into, From)]
159
pub(crate) struct Timestamp(Iso8601TimeSlug);
160

            
161
impl From<SystemTime> for Timestamp {
162
316
    fn from(t: SystemTime) -> Self {
163
316
        Self(t.into())
164
316
    }
165
}
166

            
167
impl From<Timestamp> for SystemTime {
168
130
    fn from(t: Timestamp) -> Self {
169
130
        t.0.into()
170
130
    }
171
}
172

            
173
impl KeySpecifierComponent for Timestamp {
174
428
    fn to_slug(&self) -> Result<Slug, Bug> {
175
428
        self.0.try_into()
176
428
    }
177

            
178
218
    fn from_slug(s: &Slug) -> Result<Self, InvalidKeyPathComponentValue>
179
218
    where
180
218
        Self: Sized,
181
    {
182
        use std::str::FromStr as _;
183

            
184
218
        let timestamp = Iso8601TimeSlug::from_str(s.as_ref())
185
218
            .map_err(|e| InvalidKeyPathComponentValue::Slug(e.to_string()))?;
186

            
187
218
        Ok(Self(timestamp))
188
218
    }
189

            
190
    fn fmt_pretty(&self, f: &mut fmt::Formatter) -> fmt::Result {
191
        fmt::Display::fmt(&self.0, f)
192
    }
193
}
194

            
195
/// The key specifier of the relay link authentication key.
196
#[derive(Deftly, PartialEq, Debug, Constructor)]
197
#[derive_deftly(KeySpecifier)]
198
#[deftly(prefix = "relay")]
199
#[deftly(role = "KS_link_ed")]
200
#[deftly(summary = "Relay short-term link authentication keypair")]
201
pub(crate) struct RelayLinkSigningKeypairSpecifier {
202
    /// The expiration time of this key.
203
    ///
204
    /// This **must** be the same as the expiration timestamp from the
205
    /// `KP_link_ed` certificate of this key.
206
    ///
207
    /// This serves as a unique identifier for this key instance,
208
    /// and is used for deciding which `KP_link_ed` key to use
209
    /// (we use the newest key that is not yet expired according to
210
    /// the `valid_until` timestamp from its specifier).
211
    ///
212
    /// **Important**: this timestamp should not be used for anything other than
213
    /// distinguishing between different signing keypair instances.
214
    /// In particular, it should **not** be used for validating the keypair,
215
    /// or for checking its timeliness.
216
    #[deftly(denotator)]
217
    pub(crate) valid_until: Timestamp,
218
}
219

            
220
#[cfg(test)]
221
mod test {
222
    // @@ begin test lint list maintained by maint/add_warning @@
223
    #![allow(clippy::bool_assert_comparison)]
224
    #![allow(clippy::clone_on_copy)]
225
    #![allow(clippy::dbg_macro)]
226
    #![allow(clippy::mixed_attributes_style)]
227
    #![allow(clippy::print_stderr)]
228
    #![allow(clippy::print_stdout)]
229
    #![allow(clippy::single_char_pattern)]
230
    #![allow(clippy::unwrap_used)]
231
    #![allow(clippy::unchecked_time_subtraction)]
232
    #![allow(clippy::useless_vec)]
233
    #![allow(clippy::needless_pass_by_value)]
234
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
235
    use super::*;
236

            
237
    use tor_keymgr::test_utils::check_key_specifier;
238
    use tor_keymgr::{CertSpecifierPattern, KeyCertificateSpecifier, KeyPathPattern};
239

            
240
    #[test]
241
    fn relay_signing_key_specifiers() {
242
        let ts = SystemTime::UNIX_EPOCH;
243
        let key_spec = RelaySigningKeypairSpecifier::new(ts.into());
244

            
245
        assert_eq!(
246
            key_spec.arti_path().unwrap().as_str(),
247
            "relay/ks_relaysign_ed+19700101000000"
248
        );
249

            
250
        check_key_specifier(&key_spec, "relay/ks_relaysign_ed+19700101000000");
251

            
252
        let pubkey_spec = RelaySigningPublicKeySpecifier::new(ts.into());
253

            
254
        assert_eq!(
255
            pubkey_spec.arti_path().unwrap().as_str(),
256
            "relay/kp_relaysign_ed+19700101000000"
257
        );
258

            
259
        check_key_specifier(&pubkey_spec, "relay/kp_relaysign_ed+19700101000000");
260
        let cert_spec = RelaySigningKeyCertSpecifier {
261
            subject: pubkey_spec,
262
        };
263

            
264
        assert_eq!(
265
            cert_spec.subject_key_specifier().arti_path().unwrap(),
266
            pubkey_spec.arti_path().unwrap()
267
        );
268

            
269
        assert_eq!(
270
            RelaySigningKeyCertSpecifierPattern::new_any()
271
                .arti_pattern()
272
                .unwrap(),
273
            KeyPathPattern::Arti("relay/kp_relaysign_ed+*".to_string())
274
        );
275
    }
276

            
277
    #[test]
278
    fn relay_identity_key_specifiers() {
279
        let key_spec = RelayIdentityKeypairSpecifier::new();
280

            
281
        assert_eq!(
282
            key_spec.arti_path().unwrap().as_str(),
283
            "relay/ks_relayid_ed"
284
        );
285

            
286
        check_key_specifier(&key_spec, "relay/ks_relayid_ed");
287

            
288
        let key_spec = RelayIdentityPublicKeySpecifier::new();
289

            
290
        assert_eq!(
291
            key_spec.arti_path().unwrap().as_str(),
292
            "relay/kp_relayid_ed"
293
        );
294

            
295
        check_key_specifier(&key_spec, "relay/kp_relayid_ed");
296
    }
297
}