1
//! This module defines the key types that can be written to a [`Keystore`](tor-keymgr::Keystore).
2

            
3
// @Diziet's notes regarding why we shouldn't be storing public keys in the key store:
4
//
5
// "Let me talk through, a bit, why we have public keys here.
6
//
7
// ISTM that primarily, a keystore is a store of secrets (ie, things we use to demonstrate to other
8
// people).  Ie it corresponds to our identities.  It's not a registry of public data (including
9
// instructions about who to trust for what).
10
//
11
// But we do need to store some ancillary data with some of our identities.  Where this data is
12
// small or convenient, we can put it into the keystore.  And when we do that we can use the same
13
// storage format as we use for private keys?  (That's not actually true: openssh private keys and
14
// openssh public keys are different formats.)
15
//
16
// Which public keys are you anticipating storing here?
17
//
18
// I would like to rule out, at this stage, using the Arti keystore to store the public keys of
19
// clients for our HS.  That is, the HS client discovery keys for a particular HS should be
20
// stored, for that HS, outside the keystore.  (IIRC C Tor does keep the client discovery public keys
21
// for an HS in its keystore, so we need to be compatible with that, but that doesn't necessary
22
// have to be done in Arti via the keystore API.  Perhaps the "C Tor keystore" object could
23
// implement both the keystore trait and an "HS client public keys" trait.)
24
//
25
// I should explain why I have this opinion:
26
// Basically, (private) keystores are awkward.  They have to handle private key material, deal with
27
// privsep (possibly including offline hosts); they have to be transparent and manipulable, but
28
// also secure.  They might need to be implemented by or associated with HSMs.  All of these things
29
// make the keystore's APIs (both the caller API and the visible filesystem interface) compromises
30
// with nontrivial downsides.
31
//
32
// Whereas data about who we should trust is totally different.  It can live in normal
33
// configuration land; it doesn't need to be associated with HSMs.  It doesn't want or need (the
34
// possibility of) privsep.  And the user might want to override/supplement it in totally different
35
// ways.  For example, it should be possible for an HS server to look up client discovery keys
36
// in a database.  But we don't need or want to write a generic "look up stuff in a database" API;
37
// that can be (at least for now) a bespoke API just for HS restricted discovery."
38

            
39
use ssh_key::Algorithm;
40
use ssh_key::private::KeypairData;
41
use ssh_key::public::KeyData;
42
use tor_error::{Bug, bad_api_usage, internal};
43

            
44
use crate::Result;
45
use crate::ssh::{ED25519_EXPANDED_ALGORITHM_NAME, X25519_ALGORITHM_NAME};
46

            
47
use std::fmt;
48
use std::result::Result as StdResult;
49

            
50
/// Declare and implement the `KeyType` enum.
51
///
52
/// Each of the `variant`s is mapped to the specified `str_repr`.
53
///
54
/// `str_repr` is returned from [`KeyType::arti_extension`].
55
///
56
/// The `str_repr` is also used for implementing `From<&str>` for `KeyType`.
57
/// Note `KeyType` implements `From<&str>` rather than `FromStr`,
58
/// because the conversion from string is infallible
59
/// (unrecognized strings are mapped to `KeyType::Unknown`)
60
macro_rules! declare_item_type {
61
    {
62
        $(#[$enum_meta:meta])*
63
        $vis:vis enum KeyType {
64
            $(
65
                $(#[$meta:meta])*
66
                $variant:ident => $str_repr:expr,
67
            )*
68
        }
69

            
70
        $(#[$cert_enum_meta:meta])*
71
        $cert_vis:vis enum CertType {
72
            $(
73
                $(#[$cert_meta:meta])*
74
                $cert_variant:ident => $cert_str_repr:expr,
75
            )*
76
        }
77
    } => {
78

            
79
        $(#[$enum_meta])*
80
        $vis enum KeyType {
81
            $(
82
                $(#[$meta])*
83
                $variant,
84
            )*
85
        }
86

            
87
        $(#[$cert_enum_meta])*
88
        $cert_vis enum CertType {
89
            $(
90
                $(#[$cert_meta])*
91
                $cert_variant,
92
            )*
93
        }
94

            
95
        /// A type of item stored in a keystore.
96
        #[derive(Clone, PartialEq, Eq, Hash)]
97
        #[non_exhaustive]
98
        pub enum KeystoreItemType {
99
            /// A key
100
            Key(KeyType),
101
            /// A key certificate
102
            Cert(CertType),
103
            /// An unrecognized entry type
104
            Unknown {
105
                /// The extension used for entries of this type in an Arti keystore.
106
                arti_extension: String,
107
            },
108
        }
109

            
110
        impl KeyType {
111
            /// The file extension for a key of this type.
112
            pub fn arti_extension(&self) -> String {
113
                use KeyType::*;
114

            
115
                match self {
116
                    $(
117
                        $variant => $str_repr.into(),
118
                    )*
119
                }
120
            }
121
        }
122

            
123
        impl KeystoreItemType {
124
            /// The file extension for an item of this type.
125
40908
            pub fn arti_extension(&self) -> String {
126
                use KeyType::*;
127
                use CertType::*;
128

            
129
40788
                match self {
130
                    $(
131
10500
                        Self::Key($variant) => $str_repr.into(),
132
                    )*
133
                    $(
134
118
                        Self::Cert($cert_variant) => $cert_str_repr.into(),
135
                    )*
136
2
                        Self::Unknown { arti_extension } => arti_extension.into(),
137
                }
138
40908
            }
139

            
140
            /// Try to get the inner [`KeyType`], if this is a [`KeystoreItemType::Key`].
141
            ///
142
            /// Returns an error if this is not a key type.
143
            pub fn key_type(&self) -> StdResult<&KeyType, Bug> {
144
                match self {
145
                    KeystoreItemType::Key(key_type) => Ok(key_type),
146
                    _ => Err(bad_api_usage!("{:?} is not a key type", self)),
147
                }
148
            }
149
        }
150

            
151
        impl From<&str> for KeystoreItemType {
152
7674
            fn from(key_type: &str) -> Self {
153
                use KeyType::*;
154
                use CertType::*;
155

            
156
7674
                match key_type {
157
                    $(
158
6
                        $str_repr => Self::Key($variant),
159
                    )*
160
                    $(
161
4
                        $cert_str_repr => Self::Cert($cert_variant),
162
                    )*
163
2
                    _ => Self::Unknown {
164
2
                        arti_extension: key_type.into(),
165
2
                    },
166
                }
167
7674
            }
168
        }
169

            
170
        impl fmt::Debug for KeystoreItemType {
171
696
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172
696
                match self {
173
696
                    KeystoreItemType::Key(key_type) => write!(f, "{:?}", key_type),
174
                    KeystoreItemType::Cert(cert_type) => write!(f, "{:?}", cert_type),
175
                    KeystoreItemType::Unknown { arti_extension } => {
176
                        write!(f, "unknown item type (extension={arti_extension})")
177
                    }
178
                }
179
696
            }
180
        }
181

            
182
        impl From<KeyType> for KeystoreItemType {
183
113390
            fn from(key: KeyType) -> Self {
184
113390
                Self::Key(key)
185
113390
            }
186
        }
187

            
188
        impl From<CertType> for KeystoreItemType {
189
58
            fn from(key: CertType) -> Self {
190
58
                Self::Cert(key)
191
58
            }
192
        }
193

            
194
        #[cfg(test)]
195
        mod tests {
196
            // @@ begin test lint list maintained by maint/add_warning @@
197
            #![allow(clippy::bool_assert_comparison)]
198
            #![allow(clippy::clone_on_copy)]
199
            #![allow(clippy::dbg_macro)]
200
            #![allow(clippy::mixed_attributes_style)]
201
            #![allow(clippy::print_stderr)]
202
            #![allow(clippy::print_stdout)]
203
            #![allow(clippy::single_char_pattern)]
204
            #![allow(clippy::unwrap_used)]
205
            #![allow(clippy::unchecked_time_subtraction)]
206
            #![allow(clippy::useless_vec)]
207
            #![allow(clippy::needless_pass_by_value)]
208
            //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
209
            use super::*;
210

            
211
            #[test]
212
2
            fn unknown_item_types() {
213
                const UNKNOWN_KEY_TYPE: &str = "rsa";
214

            
215
2
                let unknown_key_ty = KeystoreItemType::from(UNKNOWN_KEY_TYPE);
216
2
                assert_eq!(
217
                    unknown_key_ty,
218
2
                    KeystoreItemType::Unknown {
219
2
                        arti_extension: UNKNOWN_KEY_TYPE.into()
220
2
                    }
221
                );
222
2
                assert_eq!(unknown_key_ty.arti_extension(), UNKNOWN_KEY_TYPE);
223
2
            }
224

            
225
            #[test]
226
2
            fn recognized_item_types() {
227
                $(
228
2
                    let key_ty = KeystoreItemType::from($str_repr);
229
2
                    assert_eq!(
230
                        key_ty,
231
                        KeystoreItemType::Key(KeyType::$variant)
232
                    );
233
2
                    assert_eq!(key_ty.arti_extension(), $str_repr);
234
                )*
235
                $(
236
2
                    let cert_ty = KeystoreItemType::from($cert_str_repr);
237
2
                    assert_eq!(
238
                        cert_ty,
239
                        KeystoreItemType::Cert(CertType::$cert_variant)
240
                    );
241
2
                    assert_eq!(cert_ty.arti_extension(), $cert_str_repr);
242
                )*
243
2
            }
244
        }
245
    }
246
}
247

            
248
impl KeyType {
249
    /// Return the `KeyType` of the specified [`KeyData`].
250
    ///
251
    /// Returns an error if the [`KeyData`] is of an unsupported type.
252
464
    pub(crate) fn try_from_key_data(key: &KeyData) -> Result<KeyType> {
253
464
        match key.algorithm() {
254
464
            Algorithm::Ed25519 => Ok(KeyType::Ed25519PublicKey),
255
            Algorithm::Rsa { hash: _ } => Ok(KeyType::RsaPublicKey),
256
            Algorithm::Other(algo) if algo.as_str() == X25519_ALGORITHM_NAME => {
257
                Ok(KeyType::X25519PublicKey)
258
            }
259
            _ => Err(internal!("invalid key data").into()),
260
        }
261
464
    }
262

            
263
    /// Return the `KeyType` of the specified [`KeypairData`].
264
    ///
265
    /// Returns an error if the [`KeypairData`] is of an unsupported type.
266
5278
    pub(crate) fn try_from_keypair_data(key: &KeypairData) -> Result<KeyType> {
267
5278
        let algo = key.algorithm().map_err(|e| internal!("invalid algr {e}"))?;
268
812
        match algo {
269
3306
            Algorithm::Ed25519 => Ok(KeyType::Ed25519Keypair),
270
            Algorithm::Rsa { hash: _ } => Ok(KeyType::RsaKeypair),
271
1972
            Algorithm::Other(algo) if algo.as_str() == X25519_ALGORITHM_NAME => {
272
1160
                Ok(KeyType::X25519StaticKeypair)
273
            }
274
812
            Algorithm::Other(algo) if algo.as_str() == ED25519_EXPANDED_ALGORITHM_NAME => {
275
812
                Ok(KeyType::Ed25519ExpandedKeypair)
276
            }
277
            _ => Err(internal!("invalid keypair data").into()),
278
        }
279
5278
    }
280
}
281

            
282
declare_item_type! {
283
    /// A type of key stored in the key store.
284
    #[derive(Clone, Debug, PartialEq, Eq, Hash)]
285
    #[non_exhaustive]
286
    pub enum KeyType {
287
        /// An Ed25519 keypair.
288
        Ed25519Keypair => "ed25519_private",
289
        /// An Ed25519 public key.
290
        Ed25519PublicKey => "ed25519_public",
291
        /// A Curve25519 keypair.
292
        X25519StaticKeypair => "x25519_private",
293
        /// A Curve25519 public key.
294
        X25519PublicKey => "x25519_public",
295
        /// An expanded Ed25519 keypair.
296
        Ed25519ExpandedKeypair => "ed25519_expanded_private",
297
        /// A RSA keypair. 1024 bits long, exponent 65537.
298
        RsaKeypair => "rsa_private",
299
        /// A RSA public key. 1024 bits long, exponent 65537.
300
        RsaPublicKey => "rsa_public",
301
    }
302

            
303
    /// A type of certificate stored in the keystore.
304
    ///
305
    /// The purpose and meaning of a certificate, as well as the algorithms
306
    /// of the subject and signing keys, are specified by its `CertType`
307
    ///
308
    /// More specifically, the `CertType` of a certificate determines
309
    ///  * The cryptographic algorithms of the subject key and the signing key
310
    ///  * How the subject key value and its properties are encoded before
311
    ///    the signing key key makes its signature
312
    ///  * How the signature and the other information is encoded for storage.
313
    #[derive(Clone, Debug, PartialEq, Eq, Hash)]
314
    #[non_exhaustive]
315
    pub enum CertType {
316
        /// A Tor Ed25519 certificate.
317
        ///
318
        /// See <https://spec.torproject.org/cert-spec.html#ed-certs>
319
        Ed25519TorCert => "tor_ed25519_cert",
320
    }
321
}