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
use std::time::SystemTime;
6

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

            
11
use tor_error::Bug;
12
use tor_key_forge::define_ed25519_keypair;
13
use tor_keymgr::{
14
    derive_deftly_template_KeySpecifier, InvalidKeyPathComponentValue, KeySpecifier,
15
    KeySpecifierComponent,
16
};
17
use 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

            
22
define_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)
34
pub 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.
43
pub struct RelayIdentityPublicKeySpecifier;
44

            
45
define_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.
56
pub 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)]
81
pub struct Timestamp(Iso8601TimeSlug);
82

            
83
impl From<SystemTime> for Timestamp {
84
2
    fn from(t: SystemTime) -> Self {
85
2
        Self(t.into())
86
2
    }
87
}
88

            
89
impl KeySpecifierComponent for Timestamp {
90
4
    fn to_slug(&self) -> Result<Slug, Bug> {
91
4
        self.0.try_into()
92
4
    }
93

            
94
2
    fn from_slug(s: &Slug) -> Result<Self, InvalidKeyPathComponentValue>
95
2
    where
96
2
        Self: Sized,
97
2
    {
98
        use std::str::FromStr as _;
99

            
100
2
        let timestamp = Iso8601TimeSlug::from_str(s.as_ref())
101
2
            .map_err(|e| InvalidKeyPathComponentValue::Slug(e.to_string()))?;
102

            
103
2
        Ok(Self(timestamp))
104
2
    }
105

            
106
    fn fmt_pretty(&self, f: &mut fmt::Formatter) -> fmt::Result {
107
        fmt::Display::fmt(&self.0, f)
108
    }
109
}
110

            
111
define_ed25519_keypair!(
112
    /// [KP_link_ed] Short-term signing keypair for link authentication. Rotated frequently.
113
    pub RelayLinkSigning
114
);
115

            
116
#[cfg(test)]
117
mod 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
}