/ crates / tor-key-forge / src / traits.rs
traits.rs
  1  //! All the traits of this crate.
  2  
  3  use downcast_rs::{impl_downcast, Downcast};
  4  use rand::RngCore;
  5  use ssh_key::{
  6      private::{Ed25519Keypair, Ed25519PrivateKey, KeypairData, OpaqueKeypair},
  7      public::{Ed25519PublicKey, KeyData, OpaquePublicKey},
  8      rand_core::CryptoRng,
  9      Algorithm, AlgorithmName,
 10  };
 11  use tor_error::internal;
 12  use tor_llcrypto::pk::{curve25519, ed25519};
 13  
 14  use crate::certs::CertData;
 15  use crate::key_type::CertType;
 16  use crate::{
 17      ssh::{SshKeyData, ED25519_EXPANDED_ALGORITHM_NAME, X25519_ALGORITHM_NAME},
 18      ErasedKey, KeyType, KeystoreItemType, Result,
 19  };
 20  
 21  use std::result::Result as StdResult;
 22  
 23  /// A random number generator for generating [`EncodableItem`]s.
 24  pub trait KeygenRng: RngCore + CryptoRng {}
 25  
 26  impl<T> KeygenRng for T where T: RngCore + CryptoRng {}
 27  
 28  /// A trait for generating fresh keys.
 29  pub trait Keygen {
 30      /// Generate a new key of this type.
 31      fn generate(rng: &mut dyn KeygenRng) -> Result<Self>
 32      where
 33          Self: Sized;
 34  }
 35  
 36  /// A trait for getting the type of an item.
 37  pub trait ItemType: Downcast {
 38      /// The type of the key.
 39      fn item_type() -> KeystoreItemType
 40      where
 41          Self: Sized;
 42  }
 43  impl_downcast!(ItemType);
 44  
 45  /// A key that can be serialized to, and deserialized from.
 46  //
 47  // When adding a new `EncodableItem` impl, you must also update
 48  // [`SshKeyData::into_erased`](crate::SshKeyData::into_erased) to
 49  // return the corresponding concrete type implementing `EncodableItem`
 50  // (as a `dyn EncodableItem`).
 51  pub trait EncodableItem: ItemType + Downcast {
 52      /// Return the key as a [`KeystoreItem`].
 53      fn as_keystore_item(&self) -> Result<KeystoreItem>;
 54  }
 55  impl_downcast!(EncodableItem);
 56  
 57  /// A public key, keypair, or key certificate.
 58  #[derive(Debug, Clone, derive_more::From)]
 59  #[non_exhaustive]
 60  pub enum KeystoreItem {
 61      /// A public key or a keypair.
 62      Key(SshKeyData),
 63      /// A certificate.
 64      Cert(CertData),
 65  }
 66  
 67  impl KeystoreItem {
 68      /// Return the [`KeystoreItemType`] of this item.
 69      pub fn item_type(&self) -> Result<KeystoreItemType> {
 70          match self {
 71              KeystoreItem::Key(ssh_key_data) => ssh_key_data.key_type().map(KeystoreItemType::Key),
 72              KeystoreItem::Cert(cert) => Ok(KeystoreItemType::Cert(cert.cert_type())),
 73          }
 74      }
 75  
 76      /// Convert the key/cert material into a known type,
 77      /// and return the type-erased value.
 78      ///
 79      /// The caller is expected to downcast the value returned to the correct concrete type.
 80      pub fn into_erased(self) -> Result<ErasedKey> {
 81          match self {
 82              KeystoreItem::Key(ssh_key_data) => ssh_key_data.into_erased(),
 83              KeystoreItem::Cert(cert_data) => cert_data.into_erased(),
 84          }
 85      }
 86  }
 87  
 88  /// A key that can be converted to an [`EncodableItem`].
 89  //
 90  // NOTE: Conceptually, the `ToEncodableKey` and `EncodableItem` traits serve the same purpose (they
 91  // provide information about how to encode/decode a key).
 92  //
 93  // The reason we have two traits instead of just one is because `EncodableItem` cannot have an
 94  // associated type: for instance, if it did, we'd need to either give
 95  // `tor-keymgr::Keystore::insert` a generic parameter (which would make `Keystore` object-unsafe),
 96  // or specify a concrete type for the associated type of the `EncodableItem` (which would defeat the
 97  // whole purpose of the trait, i.e. to enable users to store their own "encodable key" types).
 98  //
 99  // `ToEncodableKey` is used in the `KeyMgr` impl, where the associated type isn't an issue because
100  // the `KeyMgr` implementation is generic over `K: ToEncodableKey`. The `Keystore`s themselves only
101  // receive `&dyn EncodableItem`s.
102  //
103  pub trait ToEncodableKey: From<Self::KeyPair>
104  where
105      Self::Key: From<<Self::KeyPair as ToEncodableKey>::Key>,
106  {
107      /// The key type this can be converted to/from.
108      type Key: EncodableItem + 'static;
109  
110      /// The KeyPair (secret+public) of which this key is a subset.  For secret
111      /// keys, this type is Self.  For public keys, this type is the
112      /// corresponding (secret) keypair.
113      ///
114      /// The associated type constraint (`where`) expresses the fact that a
115      /// public key is always derivable from its corresponding secret key.
116      ///
117      type KeyPair: ToEncodableKey;
118  
119      /// Convert this key to a type that implements [`EncodableItem`].
120      fn to_encodable_key(self) -> Self::Key;
121  
122      /// Convert an [`EncodableItem`] to another key type.
123      fn from_encodable_key(key: Self::Key) -> Self;
124  }
125  
126  /// A trait representing an encodable certificate.
127  ///
128  /// `K` represents the (Rust) type of the subject key.
129  pub trait ToEncodableCert<K: ToEncodableKey>: Clone {
130      /// The low-level type this can be converted from.
131      type ParsedCert: ItemType + 'static;
132  
133      /// The low-level type this can be converted to.
134      type EncodableCert: EncodableItem + 'static;
135  
136      /// The (Rust) type of the signing key.
137      type SigningKey: ToEncodableKey;
138  
139      /// Validate this certificate.
140      //
141      // This function will be called from functions such as KeyMgr::get_key_and_cert()
142      // to validate the cert using the provided subject key
143      // (the concrete type of which is given by the `K` in KeyMgr::get_key_and_cert())
144      // and ToEncodableCert::SigningKey.
145      //
146      /// This function should return an error if
147      ///   * the certificate is not timely
148      ///     (i.e. it is expired, or not yet valid), or
149      ///   * the certificate is not well-signed, or
150      ///   * the subject key or signing key in the certificate do not match
151      ///      the subject and signing keys specified in `cert_spec`
152      fn validate(
153          cert: Self::ParsedCert,
154          subject: &K,
155          signed_with: &Self::SigningKey,
156      ) -> StdResult<Self, InvalidCertError>;
157  
158      /// Convert this cert to a type that implements [`EncodableItem`].
159      fn to_encodable_cert(self) -> Self::EncodableCert;
160  }
161  
162  /// The error type returned by [`ToEncodableCert::validate`].
163  #[derive(thiserror::Error, Debug, Clone)]
164  #[non_exhaustive]
165  pub enum InvalidCertError {
166      /// An error caused by a key certificate with an invalid signature.
167      #[error("Invalid signature")]
168      CertSignature(#[from] tor_cert::CertError),
169  
170      /// An error caused by an untimely key certificate.
171      #[error("Certificate is expired or not yet valid")]
172      TimeValidity(#[from] tor_checkable::TimeValidityError),
173  
174      /// A key certificate with an unexpected subject key algorithm.
175      #[error("Unexpected subject key algorithm")]
176      InvalidSubjectKeyAlgorithm,
177  
178      /// An error caused by a key certificate with an unexpected subject key.
179      #[error("Certificate certifies the wrong key")]
180      SubjectKeyMismatch,
181  
182      /// An error caused by a key certificate with an unexpected `CertType`.
183      #[error("Unexpected cert type")]
184      CertType(tor_cert::CertType),
185  }
186  
187  impl Keygen for curve25519::StaticKeypair {
188      fn generate(rng: &mut dyn KeygenRng) -> Result<Self>
189      where
190          Self: Sized,
191      {
192          let secret = curve25519::StaticSecret::random_from_rng(rng);
193          let public = curve25519::PublicKey::from(&secret);
194  
195          Ok(curve25519::StaticKeypair { secret, public })
196      }
197  }
198  
199  impl ItemType for curve25519::StaticKeypair {
200      fn item_type() -> KeystoreItemType
201      where
202          Self: Sized,
203      {
204          KeyType::X25519StaticKeypair.into()
205      }
206  }
207  
208  impl EncodableItem for curve25519::StaticKeypair {
209      fn as_keystore_item(&self) -> Result<KeystoreItem> {
210          let algorithm_name = AlgorithmName::new(X25519_ALGORITHM_NAME)
211              .map_err(|_| internal!("invalid algorithm name"))?;
212  
213          let ssh_public = OpaquePublicKey::new(
214              self.public.to_bytes().to_vec(),
215              Algorithm::Other(algorithm_name),
216          );
217          let keypair = OpaqueKeypair::new(self.secret.to_bytes().to_vec(), ssh_public);
218  
219          SshKeyData::try_from_keypair_data(KeypairData::Other(keypair)).map(KeystoreItem::from)
220      }
221  }
222  
223  impl ItemType for curve25519::PublicKey {
224      fn item_type() -> KeystoreItemType
225      where
226          Self: Sized,
227      {
228          KeyType::X25519PublicKey.into()
229      }
230  }
231  
232  impl EncodableItem for curve25519::PublicKey {
233      fn as_keystore_item(&self) -> Result<KeystoreItem> {
234          let algorithm_name = AlgorithmName::new(X25519_ALGORITHM_NAME)
235              .map_err(|_| internal!("invalid algorithm name"))?;
236  
237          let ssh_public =
238              OpaquePublicKey::new(self.to_bytes().to_vec(), Algorithm::Other(algorithm_name));
239  
240          SshKeyData::try_from_key_data(KeyData::Other(ssh_public)).map(KeystoreItem::from)
241      }
242  }
243  
244  impl Keygen for ed25519::Keypair {
245      fn generate(mut rng: &mut dyn KeygenRng) -> Result<Self>
246      where
247          Self: Sized,
248      {
249          Ok(ed25519::Keypair::generate(&mut rng))
250      }
251  }
252  
253  impl ItemType for ed25519::Keypair {
254      fn item_type() -> KeystoreItemType
255      where
256          Self: Sized,
257      {
258          KeyType::Ed25519Keypair.into()
259      }
260  }
261  
262  impl EncodableItem for ed25519::Keypair {
263      fn as_keystore_item(&self) -> Result<KeystoreItem> {
264          let keypair = Ed25519Keypair {
265              public: Ed25519PublicKey(self.verifying_key().to_bytes()),
266              private: Ed25519PrivateKey::from_bytes(self.as_bytes()),
267          };
268  
269          SshKeyData::try_from_keypair_data(KeypairData::Ed25519(keypair)).map(KeystoreItem::from)
270      }
271  }
272  
273  impl ItemType for ed25519::PublicKey {
274      fn item_type() -> KeystoreItemType
275      where
276          Self: Sized,
277      {
278          KeyType::Ed25519PublicKey.into()
279      }
280  }
281  
282  impl EncodableItem for ed25519::PublicKey {
283      fn as_keystore_item(&self) -> Result<KeystoreItem> {
284          let key_data = Ed25519PublicKey(self.to_bytes());
285  
286          SshKeyData::try_from_key_data(ssh_key::public::KeyData::Ed25519(key_data))
287              .map(KeystoreItem::from)
288      }
289  }
290  
291  impl Keygen for ed25519::ExpandedKeypair {
292      fn generate(rng: &mut dyn KeygenRng) -> Result<Self>
293      where
294          Self: Sized,
295      {
296          let keypair = <ed25519::Keypair as Keygen>::generate(rng)?;
297  
298          Ok((&keypair).into())
299      }
300  }
301  
302  impl ItemType for ed25519::ExpandedKeypair {
303      fn item_type() -> KeystoreItemType
304      where
305          Self: Sized,
306      {
307          KeyType::Ed25519ExpandedKeypair.into()
308      }
309  }
310  
311  impl EncodableItem for ed25519::ExpandedKeypair {
312      fn as_keystore_item(&self) -> Result<KeystoreItem> {
313          let algorithm_name = AlgorithmName::new(ED25519_EXPANDED_ALGORITHM_NAME)
314              .map_err(|_| internal!("invalid algorithm name"))?;
315  
316          let ssh_public = OpaquePublicKey::new(
317              self.public().to_bytes().to_vec(),
318              Algorithm::Other(algorithm_name),
319          );
320  
321          let keypair = OpaqueKeypair::new(self.to_secret_key_bytes().to_vec(), ssh_public);
322  
323          SshKeyData::try_from_keypair_data(KeypairData::Other(keypair)).map(KeystoreItem::from)
324      }
325  }
326  
327  impl ItemType for crate::EncodedEd25519Cert {
328      fn item_type() -> KeystoreItemType
329      where
330          Self: Sized,
331      {
332          CertType::Ed25519TorCert.into()
333      }
334  }
335  
336  impl ItemType for crate::ParsedEd25519Cert {
337      fn item_type() -> KeystoreItemType
338      where
339          Self: Sized,
340      {
341          CertType::Ed25519TorCert.into()
342      }
343  }
344  
345  impl EncodableItem for crate::EncodedEd25519Cert {
346      fn as_keystore_item(&self) -> Result<KeystoreItem> {
347          Ok(CertData::TorEd25519Cert(self.clone()).into())
348      }
349  }