tor_key_forge/
traits.rs

1//! All the traits of this crate.
2
3use downcast_rs::{impl_downcast, Downcast};
4use rand::RngCore;
5use ssh_key::{
6    private::{Ed25519Keypair, Ed25519PrivateKey, KeypairData, OpaqueKeypair},
7    public::{Ed25519PublicKey, KeyData, OpaquePublicKey},
8    rand_core::CryptoRng,
9    Algorithm, AlgorithmName,
10};
11use tor_error::internal;
12use tor_llcrypto::pk::{curve25519, ed25519};
13
14use crate::certs::CertData;
15use crate::key_type::CertType;
16use crate::{
17    ssh::{SshKeyData, ED25519_EXPANDED_ALGORITHM_NAME, X25519_ALGORITHM_NAME},
18    ErasedKey, KeyType, KeystoreItemType, Result,
19};
20
21use std::result::Result as StdResult;
22
23/// A random number generator for generating [`EncodableItem`]s.
24pub trait KeygenRng: RngCore + CryptoRng {}
25
26impl<T> KeygenRng for T where T: RngCore + CryptoRng {}
27
28/// A trait for generating fresh keys.
29pub 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.
37pub trait ItemType: Downcast {
38    /// The type of the key.
39    fn item_type() -> KeystoreItemType
40    where
41        Self: Sized;
42}
43impl_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`).
51pub trait EncodableItem: ItemType + Downcast {
52    /// Return the key as a [`KeystoreItem`].
53    fn as_keystore_item(&self) -> Result<KeystoreItem>;
54}
55impl_downcast!(EncodableItem);
56
57/// A public key, keypair, or key certificate.
58#[derive(Debug, Clone, derive_more::From)]
59#[non_exhaustive]
60pub enum KeystoreItem {
61    /// A public key or a keypair.
62    Key(SshKeyData),
63    /// A certificate.
64    Cert(CertData),
65}
66
67impl 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//
103pub trait ToEncodableKey: From<Self::KeyPair>
104where
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.
129pub 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]
165pub 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
187impl 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
199impl ItemType for curve25519::StaticKeypair {
200    fn item_type() -> KeystoreItemType
201    where
202        Self: Sized,
203    {
204        KeyType::X25519StaticKeypair.into()
205    }
206}
207
208impl 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
223impl ItemType for curve25519::PublicKey {
224    fn item_type() -> KeystoreItemType
225    where
226        Self: Sized,
227    {
228        KeyType::X25519PublicKey.into()
229    }
230}
231
232impl 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
244impl 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
253impl ItemType for ed25519::Keypair {
254    fn item_type() -> KeystoreItemType
255    where
256        Self: Sized,
257    {
258        KeyType::Ed25519Keypair.into()
259    }
260}
261
262impl 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
273impl ItemType for ed25519::PublicKey {
274    fn item_type() -> KeystoreItemType
275    where
276        Self: Sized,
277    {
278        KeyType::Ed25519PublicKey.into()
279    }
280}
281
282impl 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
291impl 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
302impl ItemType for ed25519::ExpandedKeypair {
303    fn item_type() -> KeystoreItemType
304    where
305        Self: Sized,
306    {
307        KeyType::Ed25519ExpandedKeypair.into()
308    }
309}
310
311impl 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
327impl ItemType for crate::EncodedEd25519Cert {
328    fn item_type() -> KeystoreItemType
329    where
330        Self: Sized,
331    {
332        CertType::Ed25519TorCert.into()
333    }
334}
335
336impl ItemType for crate::ParsedEd25519Cert {
337    fn item_type() -> KeystoreItemType
338    where
339        Self: Sized,
340    {
341        CertType::Ed25519TorCert.into()
342    }
343}
344
345impl EncodableItem for crate::EncodedEd25519Cert {
346    fn as_keystore_item(&self) -> Result<KeystoreItem> {
347        Ok(CertData::TorEd25519Cert(self.clone()).into())
348    }
349}