Skip to main content

tor_key_forge/
traits.rs

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