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::private::KeypairData;
40
use ssh_key::public::KeyData;
41
use ssh_key::Algorithm;
42
use tor_error::{bad_api_usage, internal, Bug};
43

            
44
use crate::ssh::{ED25519_EXPANDED_ALGORITHM_NAME, X25519_ALGORITHM_NAME};
45
use crate::Result;
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
30926
            pub fn arti_extension(&self) -> String {
126
                use KeyType::*;
127
                use CertType::*;
128

            
129
30826
                match self {
130
                    $(
131
7730
                        Self::Key($variant) => $str_repr.into(),
132
                    )*
133
                    $(
134
98
                        Self::Cert($cert_variant) => $cert_str_repr.into(),
135
                    )*
136
2
                        Self::Unknown { arti_extension } => arti_extension.into(),
137
                }
138
30926
            }
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
4478
            fn from(key_type: &str) -> Self {
153
                use KeyType::*;
154
                use CertType::*;
155

            
156
4478
                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
4478
            }
168
        }
169

            
170
        impl fmt::Debug for KeystoreItemType {
171
96
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172
96
                match self {
173
96
                    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
96
            }
180
        }
181

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

            
188
        impl From<CertType> for KeystoreItemType {
189
96
            fn from(key: CertType) -> Self {
190
96
                Self::Cert(key)
191
96
            }
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_duration_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
2
                    unknown_key_ty,
218
2
                    KeystoreItemType::Unknown {
219
2
                        arti_extension: UNKNOWN_KEY_TYPE.into()
220
2
                    }
221
2
                );
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
2
                        key_ty,
231
2
                        KeystoreItemType::Key(KeyType::$variant)
232
2
                    );
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
2
                        cert_ty,
239
2
                        KeystoreItemType::Cert(CertType::$cert_variant)
240
2
                    );
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
    pub(crate) fn try_from_key_data(key: &KeyData) -> Result<KeyType> {
253
        match key.algorithm() {
254
            Algorithm::Ed25519 => Ok(KeyType::Ed25519PublicKey),
255
            Algorithm::Other(algo) if algo.as_str() == X25519_ALGORITHM_NAME => {
256
                Ok(KeyType::X25519PublicKey)
257
            }
258
            _ => Err(internal!("invalid key data").into()),
259
        }
260
    }
261

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

            
280
declare_item_type! {
281
    /// A type of key stored in the key store.
282
    #[derive(Clone, Debug, PartialEq, Eq, Hash)]
283
    #[non_exhaustive]
284
    pub enum KeyType {
285
        /// An Ed25519 keypair.
286
        Ed25519Keypair => "ed25519_private",
287
        /// An Ed25519 public key.
288
        Ed25519PublicKey => "ed25519_public",
289
        /// A Curve25519 keypair.
290
        X25519StaticKeypair => "x25519_private",
291
        /// A Curve25519 public key.
292
        X25519PublicKey => "x25519_public",
293
        /// An expanded Ed25519 keypair.
294
        Ed25519ExpandedKeypair => "ed25519_expanded_private",
295
    }
296

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