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 ```