tor_key_forge/
traits.rs

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