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  
  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      fn from(t: SystemTime) -> Self {
 85          Self(t.into())
 86      }
 87  }
 88  
 89  impl 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  
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  }