/ doc / dev / notes / keymgr-certificates.md
keymgr-certificates.md
  1  # Storing certificates in the Arti key store
  2  
  3  ## Motivation
  4  
  5  We have decided to add support for storing certificates in the Arti keystore
  6  (see #1617).
  7  
  8  This is needed, for example, to enable relays to run with offline identity keys.
  9  
 10  ## Assumptions
 11  
 12  Here are the assumptions that motivate the design proposed here:
 13  
 14    * keys may have multiple certificates for different purposes.
 15    While we don't have such keys right now,
 16    we might conceivably need to support this in the future.
 17    For this reason, the new APIs described here support certificates
 18    that have a many:1 relationship with their subject keys.
 19    * the keys that are expected to have
 20    an associated certificate[^1] stored in the keystore
 21    will be accessed through the new
 22    `KeyMgr::{get_key_and_cert, get_or_generate_key_and_cert}` APIs
 23    instead of the `KeyMgr::{get, get_or_generate}` ones.
 24    In addition to retrieving the key/certificate,
 25    the new `*_and_cert` APIs will also validate the key certificate
 26    (by checking if it is well-signed and timely).
 27    * the `ArtiPath` of a certificate is derived from the `ArtiPath`
 28    of the key it certifies. More specifically, it is formed by
 29    concatenating the `ArtiPath` of the subject key with the
 30    denotators provided by
 31    `KeyCertificateSpecifier::cert_denotators()`, if any
 32    (if there are no cert denotators, the `ArtiPath` of the certificate
 33    is the same as the `ArtiPath` of the subject key).
 34    The reason we are not giving certificates their own
 35    `KeyCertificateSpecifier`-defined `ArtiPath`
 36    is because we want to ensure the certificates stored
 37    in the keystore are always for subject keys that we *own*.
 38    That is, the subject key **must** be a key
 39    whose private part is stored in one of our keystores.
 40    We will not provide functionality for retrieving certificates
 41    whose subject key we do not own, because the keymgr is not meant
 42    to be used as a general-purpose trusted certificate store.
 43    See the `tor_key_forge::key_type` module-level docs
 44    for more about the reasoning behind this.
 45  
 46  [^1]: The key is said to have an "associated certificate" if
 47  it is the subject key of a certificate used in the Tor protocol
 48  
 49  ### Key certificate representation
 50  
 51  The purpose and meaning of the certificate, as well as the algorithms
 52  of the subject and signing keys, will be given by the `KeyType`
 53  of the certificate (i.e. the file extension).
 54  
 55  More specifically, the `KeyType` of a certificate encodes the following:
 56  
 57  1. The cryptographic algorithms of the subject key and the signing key
 58  2. How the subject key value and its properties are encoded before
 59     the signing key key makes its signature
 60  3. How the signature and the other information is encoded for storage.
 61  
 62  > Note: the name of the `KeyType` enum now makes little sense,
 63  > because it no longer represents a key type, but rather a type of object
 64  > we're able to serialize and write to the keystore.
 65  
 66  > Recall that the `KeyType` of an object is given by its `EncodableKey` impl:
 67  > ```rust
 68  > pub trait EncodableKey: Downcast {
 69  >     /// The type of the key.
 70  >     fn key_type() -> KeyType
 71  >     where
 72  >         Self: Sized;
 73  >
 74  >     /// Return the [`SshKeyData`] of this key.
 75  >     fn as_ssh_key_data(&self) -> Result<SshKeyData>;
 76  > }
 77  > ```
 78  >
 79  > Like keys, certificates are encodable in the keystore,
 80  > so like keys, they must implement `EncodableKey`
 81  > (and therefore have an associated  `KeyType`, and `SshKeyData`).
 82  >
 83  > As a result, `EncodableKey`, `KeyType`, and `SshKeyData`
 84  > will need to be renamed (see the "Proposed renamings" section below).
 85  
 86  For example, the keystore entries for the `K_relaysign_ed` key are
 87  
 88  | Key                            | Description                                                  |
 89  |--------------------------------|--------------------------------------------------------------|
 90  | `KS_relaysign_ed`              | medium-term signing keypair                                  |
 91  | `KP_relaysign_ed`              | public part of `KS_relaysign_ed`                             |
 92  | `KP_relaysign_ed` certificate  | `KP_relaysign_ed` signed by the `KS_relayid_ed` identity key |
 93  
 94  In the on-disk Arti key store, their paths will be
 95  
 96  | Key                            | `ArtiPath`                      | Path                                              |
 97  |--------------------------------|---------------------------------|---------------------------------------------------|
 98  | `KS_relaysign_ed`              | `relay/relaysign_ed+<valid_until>` | `relay/relaysign_ed+<valid_until>.ed25519_private`   |
 99  | `KP_relaysign_ed`              | `relay/relaysign_ed+<valid_until>` | `relay/relaysign_ed+<valid_until>.ed25519_public`    |
100  | `KP_relaysign_ed` certificate  | `relay/relaysign_ed+<valid_until>` | `relay/relaysign_ed+<valid_until>.tor_ed25519_cert`  |
101  
102  Where `<valid_until>` is the expiry timestamp of the key.
103  The exact format of this identifier is outside the scope of this document
104  (see !2577).
105  
106  > Note that in the general case, a key certificate has a path of the form
107  > `<KEY_PATH>[+<D1>+<D2>+..+<Dn>]`, where `<KEY_PATH>` is the `ArtiPath`
108  > of the subject key, and `D1`, `D2`, ..., `Dn` are the certificate denotators
109  > (obtained from `KeyCertificateSpecifier::cert_denotators()`).
110  
111  We will introduce a new `tor_ed25519_cert` extension for Tor Ed25519 certificates
112  (represented in the Rust API by a new `KeystoreItemType::Ed25519TorCert` variant -- see below).
113  
114  ### Storage format
115  
116  The key certificates for `K_relaysign_ed` keys will be stored in the keystore
117  in Tor's [certificate format](https://spec.torproject.org/cert-spec.html#ed-certs).
118  
119  Note: the [Tor ed25519 key format](https://spec.torproject.org/cert-spec.html#ed-certs)
120  can only represent signatures *by* ed25519 keys
121  but it can represent signatures *on* a variety of algorithms.
122  The `.tor_ed25519` extension and the `KeystoreItemType::Ed25519TorCert` enum variant
123  are used only when the `CERT_KEY_TYPE` is `01`;
124  other subject key algorithms (types) will have different `KeystoreItemType` and extension.
125  
126  If at some point we choose to support other kinds of certificates
127  (i.e. with a different purpose and meaning),
128  we will likely use a different certificate format (not specified here).
129  
130  If we ever decide to change the format of the `K_relaysign_ed` certificate,
131  we will deprecate the existing `.tor_ed25519_cert` key type,
132  and introduce a new one for certificates using the new format.
133  
134  #### Implementation details
135  
136  ```rust
137  /// The "specifier" of a key certificate, which identifies an instance of a cert,
138  /// as well as its signing and subject keys.
139  ///
140  /// Certificates can only be fetched from Arti key stores
141  /// (we will not support loading certs from C Tor's key directory)
142  pub trait KeyCertificateSpecifier {
143      /// The denotators of the certificate.
144      ///
145      /// Used by `KeyMgr` to derive the `ArtiPath` of the certificate.
146      /// The `ArtiPath` of a certificate is obtained
147      /// by concatenating the `ArtiPath` of the subject key with the
148      /// denotators provided by this function,
149      /// with a `+` between the `ArtiPath` of the subject key and
150      /// the denotators (the `+` is omitted if there are no denotators).
151      ///
152      //
153      // TODO: perhaps we should invent (or find an existing) NonEmptyVec type
154      // to use here instead of Vec
155      fn cert_denotators(&self) -> Vec<dyn KeySpecifierComponent>;
156      /// The key specifier of the signing key.
157      ///
158      /// Returns `None` if the signing key should not be retrieved from the keystore.
159      ///
160      /// Note: a return value of `None` means the signing key will be provided
161      /// as an argument to the `KeyMgr` accessor this `KeyCertificateSpecifier`
162      /// will be used with.
163      fn signing_key_specifier(&self) -> Option<&dyn KeySpecifier>;
164      /// The key specifier of the subject key.
165      fn subject_key_specifier(&self) -> &dyn KeySpecifier;
166  
167      // other functions TBD
168  }
169  ```
170  
171  `KeyMgr` will be extended with some new functions
172  for cert retrieval and validation:
173  
174  ```rust
175  impl KeyMgr {
176      /// Read the specified key and certificate from one of the key stores,
177      /// deserializing the subject key as `K::Key`, the cert as `C::Cert`,
178      /// and the signing key (if the provided `signing_key` is `None`)
179      /// as `C::SigningKey`.
180      ///
181      /// Returns `Ok(None)` if none of the key stores have the requested key.
182      ///
183      /// This function validates the certificate,
184      /// returning an error if it is invalid or missing.
185      /// More specifically, it returns an error if
186      ///    * the certificate is not timely
187      ///      (i.e. it is expired, or not yet valid), or
188      ///    * the certificate is not well-signed, or
189      ///    * the subject key or signing key in the certificate do not match
190      ///      the subject and signing keys specified in `cert_spec`
191      ///
192      /// Exactly one of `signing_key` and `cert_spec.signing_key_specifier()` can be `Some`.
193      /// If both are missing, or both are present, an error is returned.
194      //
195      // TODO: this function takes a lot of args.
196      // When we implement it, we should rethink its args.
197      // Alternatively, we might choose to make the signing_key specifier
198      // from KeyCertificateSpecifier non-optional,
199      // and provide a *different* certificate specifier type for key certificates
200      // where the signing key is not present in the keystore.
201      // We would also provide a different set of `KeyMgr::get*` functions
202      // for retrieving such key certificates.
203      // These new `KeyMgr::get* functions would take a non-optional
204      // signing key argument.
205      fn get_key_and_cert<K: ToEncodableKey, C: ToEncodableCert<K>>(
206          &self,
207          cert_spec: &dyn KeyCertificateSpecifier,
208          signing_key: Option<<C as ToEncodableCert<K>>::SigningKey>,
209        ) -> Result<Option<(K, C)>> {
210          ...
211      }
212  
213      /// Read the specified key and certificate from one of the key stores,
214      /// deserializing the subject key as `K::Key`, the cert as `C::Cert`,
215      /// and the signing key (if the provided `signing_key` is `None`)
216      /// as `C::SigningKey`,
217      /// generating the key and its corresponding certificate
218      /// if either does not exist.
219      /// The certificate will be generated from the subject key and signing key
220      /// using the provided `make_certificate` callback.
221      ///
222      /// See [`KeyMgr::get_key_and_cert`] for possible errors.
223      ///
224      /// Generates the missing key and/or certificate as follows:
225      ///
226      /// | Subject Key exists | Signing Key exists | Cert exists | Action                                 |
227      /// |--------------------|--------------------|-------------|----------------------------------------|
228      /// | Y                  | Y/N                | Y           | Validate cert,                         |
229      /// |                    |                    |             | return key and cert if valid,          |
230      /// |                    |                    |             | error otherwise                        |
231      /// |--------------------|--------------------|-------------|----------------------------------------|
232      /// | N                  | Y                  | N           | Generate subject key and               |
233      /// |                    |                    |             | a new cert signed with signing key     |
234      /// |--------------------|--------------------|-------------|----------------------------------------|
235      /// | Y                  | Y                  | N           | Generate cert signed with signing key  |
236      /// |--------------------|--------------------|-------------|----------------------------------------|
237      /// | Y                  | N                  | N           | Error - cannot generate cert           |
238      /// |                    |                    |             | if signing key is not available        |
239      /// |--------------------|--------------------|-------------|----------------------------------------|
240      /// | N                  | N                  | N           | Error - cannot generate cert           |
241      /// |                    |                    |             | if signing key is not available        |
242      /// |--------------------|--------------------|-------------|----------------------------------------|
243      /// | N                  | Y/N                | Y           | Error - subject key was removed?       |
244      /// |                    |                    |             | (we already have a cert for it,        |
245      /// |                    |                    |             | but it's unavailable)                  |
246      //
247      ///
248      /// Exactly one of `signing_key` and `cert_spec.signing_key_specifier()` can be `Some`.
249      /// If both are missing, or both are present, an error is returned.
250      //
251      // TODO: this function takes a lot of args.
252      // When we implement it, we should rethink its args.
253      // Alternatively, we might choose to make the signing_key specifier
254      // from KeyCertificateSpecifier non-optional,
255      // and provide a *different* certificate specifier type for key certificates
256      // where the signing key is not present in the keystore.
257      // We would also provide a different set of `KeyMgr::get*` functions
258      // for retrieving such key certificates.
259      // These new `KeyMgr::get* functions would take a non-optional
260      // signing key argument.
261      fn get_or_generate_key_and_cert<K: ToEncodableKey, C: ToEncodableCert>(
262          &self,
263          cert_spec: &dyn KeyCertificateSpecifier,
264          signing_key: Option<<C as ToEncodableCert<K>>::SigningKey>,
265          make_certificate: impl FnOnce(K, <C as ToEncodableCert<K>>::SigningKey) -> C,
266        ) -> Result<(K, C)> {
267          ...
268      }
269  
270      // Note: this list of proposed additions is non-exhaustive.
271      // In the future, we might decide to also add
272      // e.g. a get_cert() function for retrieving a certificate
273  }
274  ```
275  
276  where `ToEncodableCert` is a new trait of the form:
277  
278  ```rust
279  /// A trait representing an encodable certificate.
280  ///
281  /// `K` represents the (Rust) type of the subject key.
282  pub trait ToEncodableCert<K: ToEncodableKey> {
283      /// The low-level type this can be converted to/from.
284      type Cert: EncodableKey + 'static;
285  
286      /// The (Rust) type of the signing key.
287      type SigningKey: ToEncodableKey;
288  
289      /// Validate this certificate.
290      //
291      // This function will be called from functions such as KeyMgr::get_key_and_cert()
292      // to validate the cert using the provided subject key
293      // (the concrete type of which is given by the `K` in KeyMgr::get_key_and_cert())
294      // and ToEncodableCert::SigningKey.
295      //
296      /// TODO: explain how
297      /// TODO: perhaps fold this into from_encodable_cert
298      /// (i.e. change the signature of from_encodable_cert to also take a subject
299      /// key and signing key, and have it handle validation internally).
300      fn validate(&self, subject: &K, signed_with: &Self::SigningKey) -> Result<()>;
301  
302      /// Convert this cert to a type that implements [`EncodableKey`].
303      fn to_encodable_cert(self) -> Self::Cert;
304  
305      /// Convert an [`EncodableKey`] to another cert type.
306      fn from_encodable_cert(cert: Self::Cert) -> Self
307      where
308          Self: Sized;
309  }
310  ```
311  
312  Note: the `ToEncodableCert::Cert` type is an `EncodableKey`:
313  despite its now-misleading name, `EncodableKey` is still our trait representing objects
314  that can be encoded and stored in the keystore.
315  
316  As mentioned before, once we add support for storing certificates,
317  the Arti keystore storage format will no longer be just OpenSSH
318  (it'll be OpenSSH for keys + the C Tor custom cert format for certificates),
319  so we ought to rename `SshKeyData` (the serializable storage type)
320  to `KeystoreItem`.
321  
322  ##### Proposed renamings
323  
324  * preemptively rename `KeystoreEntry` to `KeystoreItemEntry`
325  * rename `EncodableKey::as_ssh_key_data()` to `EncodableKey::as_keystore_item()`
326  * replace `SshKeyData` with a new `KeystoreItem` type
327  * Rename the `EncodableKey` trait to `KeystoreEncodable`
328    (other possible names: `EncodableEntry`, `EncodableKeystoreEntry`)
329  * Rename `KeyType` to `KeystoreItemType`
330  
331  IOW, I propose we rewrite the `EncodableKey` trait like so
332  
333  ```rust
334  /// An object that can be converted to and from `KeystoreItem`.
335  ///
336  /// Types implementing this trait can be written to the keystore.
337  //
338  // When adding a new `KeystoreEncodable` impl, you must also update
339  // [`KeystoreItem::into_erased`](crate::KeystoreItem::into_erased) to
340  // return the corresponding concrete type implementing `KeystoreEncodable`
341  // (as a `dyn KeystoreEncodable`).
342  pub trait KeystoreEncodable: Downcast {
343      /// The kind of keystore item this is.
344      fn item_type() -> KeystoreItemType
345      where
346          Self: Sized;
347  
348      /// Return the [`KeystoreItem`] representation of this object.
349      fn as_keystore_item(&self) -> Result<KeystoreItem>;
350  }
351  
352  /// A type of [`KeystoreEncodable`] entry
353  #[non_exhaustive]
354  enum KeystoreItemType {
355      /// A key
356      // KeyType is the same as before,
357      // except its Unknown variant is moved to KeystoreItemType
358      Key(KeyType),
359      /// A key certificate
360      Cert(CertType),
361      /// An unrecognized entry type.
362      Unknown {
363          /// The extension used for entries of this type in an Arti keystore.
364          arti_extension: String,
365      },
366  }
367  
368  #[non_exhaustive]
369  enum CertType {
370      /// A Tor Ed25519 certificate.
371      /// See https://spec.torproject.org/cert-spec.html#ed-certs
372      Ed25519TorCert,
373  }
374  
375  /// A public key, keypair, or key certificate.
376  #[derive(Clone, Debug)]
377  #[non_exhaustive]
378  pub struct KeystoreItem(KeystoreItemInner);
379  
380  /// The inner representation of a KeystoreItem.
381  #[derive(Clone, Debug)]
382  #[non_exhaustive]
383  enum KeystoreItemInner {
384      /// A public key or a keypair.
385      Key(SshKeyData),
386      /// A certificate.
387      Cert(CertData),
388  }
389  
390  enum CertData {
391      /// A tor-specific ed25519 cert.
392      TorEd25519Cert(Vec<u8>),
393  }
394  
395  // ======= unchanged =========
396  
397  /// A public key or a keypair.
398  #[derive(Clone, Debug)]
399  #[non_exhaustive]
400  pub struct SshKeyData(SshKeyDataInner);
401  
402  /// The inner representation of a public key or a keypair.
403  #[derive(Clone, Debug)]
404  #[non_exhaustive]
405  enum SshKeyDataInner {
406      /// The [`KeyData`] of a public key.
407      Public(KeyData),
408      /// The [`KeypairData`] of a private key.
409      Private(KeypairData),
410  }
411  // ======= unchanged end =========
412  ```