1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6
#![warn(missing_docs)]
7
#![warn(noop_method_call)]
8
#![warn(unreachable_pub)]
9
#![warn(clippy::all)]
10
#![deny(clippy::await_holding_lock)]
11
#![deny(clippy::cargo_common_metadata)]
12
#![deny(clippy::cast_lossless)]
13
#![deny(clippy::checked_conversions)]
14
#![allow(clippy::cognitive_complexity)] // See arti#2556
15
#![deny(clippy::debug_assert_with_mut_call)]
16
#![deny(clippy::exhaustive_enums)]
17
#![deny(clippy::exhaustive_structs)]
18
#![deny(clippy::expl_impl_clone_on_copy)]
19
#![deny(clippy::fallible_impl_from)]
20
#![deny(clippy::implicit_clone)]
21
#![deny(clippy::large_stack_arrays)]
22
#![warn(clippy::manual_ok_or)]
23
#![deny(clippy::missing_docs_in_private_items)]
24
#![warn(clippy::needless_borrow)]
25
#![warn(clippy::needless_pass_by_value)]
26
#![warn(clippy::option_option)]
27
#![warn(clippy::or_fun_call)]
28
#![deny(clippy::print_stderr)]
29
#![deny(clippy::print_stdout)]
30
#![warn(clippy::rc_buffer)]
31
#![deny(clippy::ref_option_ref)]
32
#![warn(clippy::semicolon_if_nothing_returned)]
33
#![warn(clippy::trait_duplication_in_bounds)]
34
#![deny(clippy::unchecked_time_subtraction)]
35
#![deny(clippy::unnecessary_wraps)]
36
#![warn(clippy::unseparated_literal_suffix)]
37
#![deny(clippy::unwrap_used)]
38
#![deny(clippy::mod_module_files)]
39
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
40
#![allow(clippy::uninlined_format_args)]
41
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
42
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
43
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
44
#![allow(clippy::needless_lifetimes)] // See arti#1765
45
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
46
#![allow(clippy::collapsible_if)] // See arti#2342
47
#![deny(clippy::unused_async)]
48
#![deny(clippy::string_slice)] // See arti#2571
49
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
50
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
51

            
52
mod err;
53
pub mod rsa;
54

            
55
#[cfg(feature = "x509")]
56
pub use tor_cert_x509 as x509;
57

            
58
use caret::caret_int;
59
use tor_bytes::{Error as BytesError, Result as BytesResult};
60
use tor_bytes::{Readable, Reader, Writeable, Writer};
61
use tor_checkable::{TimeRange, TimeRangeBound};
62
use tor_llcrypto::pk::*;
63

            
64
use web_time_compat as time;
65

            
66
pub use err::CertError;
67

            
68
mod encode;
69
pub use encode::{EncodedCert, EncodedEd25519Cert};
70
pub use err::CertEncodeError;
71

            
72
/// A Result defined to use CertError
73
type CertResult<T> = std::result::Result<T, CertError>;
74

            
75
caret_int! {
76
    /// Recognized values for Tor's certificate type field.
77
    ///
78
    /// In the names used here, "X_V_Y" means "key X verifying key Y",
79
    /// whereas "X_CC_Y" means "key X cross-certifying key Y".  In both
80
    /// cases, X is the key that is doing the signing, and Y is the key
81
    /// or object that is getting signed.
82
    ///
83
    /// Not every one of these types is valid for an Ed25519
84
    /// certificate.  Some are for X.509 certs in a CERTS cell; some
85
    /// are for RSA->Ed crosscerts in a CERTS cell.
86
    pub struct CertType(u8) {
87
        /// TLS link key, signed with RSA identity. X.509 format. (Obsolete)
88
        TLS_LINK_X509 = 0x01,
89
        /// Self-signed RSA identity certificate. X.509 format. (Legacy)
90
        RSA_ID_X509 = 0x02,
91
        /// RSA lnk authentication key signed with RSA identity
92
        /// key. X.509 format. (Obsolete)
93
        LINK_AUTH_X509 = 0x03,
94

            
95
        /// Identity verifying a signing key, directly.
96
        IDENTITY_V_SIGNING = 0x04,
97

            
98
        /// Signing key verifying a TLS certificate by digest.
99
        SIGNING_V_TLS_CERT = 0x05,
100

            
101
        /// Signing key verifying a link authentication key.
102
        SIGNING_V_LINK_AUTH = 0x06,
103

            
104
        /// RSA identity key certifying an Ed25519 identity key. RSA
105
        /// crosscert format. (Legacy)
106
        RSA_ID_V_IDENTITY = 0x07,
107

            
108
        /// For onion services: short-term descriptor signing key
109
        /// (`KP_hs_desc_sign`), signed with blinded onion service identity
110
        /// (`KP_hs_blind_id`).
111
        HS_BLINDED_ID_V_SIGNING = 0x08,
112

            
113
        /// For onion services: Introduction point authentication key
114
        /// (`KP_hs_ipt_sid`), signed with short term descriptor signing key
115
        /// (`KP_hs_desc_sign`).
116
        ///
117
        /// This one is, sadly, a bit complicated. In the original specification
118
        /// it was meant to be a cross-certificate, where the signature would be
119
        /// _on_ the descriptor signing key, _signed with_ the intro TID key.
120
        /// But we got it backwards in the C Tor implementation, and now, for
121
        /// compatibility, we are stuck doing it backwards in the future.
122
        ///
123
        /// If we find in the future that it is actually important to
124
        /// cross-certify these keys (as originally intended), then we should
125
        /// add a new certificate type, and put the new certificate in the onion
126
        /// service descriptor.
127
        HS_IP_V_SIGNING = 0x09,
128

            
129
        /// An ntor key converted to a ed25519 key, cross-certifying an
130
        /// identity key.
131
        NTOR_CC_IDENTITY = 0x0A,
132

            
133
        /// For onion services: Ntor encryption key (`KP_hss_ntor`),
134
        /// converted to ed25519, signed with the descriptor signing key
135
        /// (`KP_hs_desc_sign`).
136
        ///
137
        /// As with [`HS_IP_V_SIGNING`](CertType::HS_IP_V_SIGNING), this
138
        /// certificate type is backwards.  In the original specification it was
139
        /// meant to be a cross certificate, with the signing and signed keys
140
        /// reversed.
141
        HS_IP_CC_SIGNING = 0x0B,
142

            
143
        /// For relays: family key certifying membership of a relay
144
        /// by signing its identity.
145
        FAMILY_V_IDENTITY = 0x0C,
146
    }
147
}
148

            
149
caret_int! {
150
    /// Extension identifiers for extensions in certificates.
151
    pub struct ExtType(u8) {
152
        /// Extension indicating an Ed25519 key that signed this certificate.
153
        ///
154
        /// Certificates do not always contain the key that signed them.
155
        SIGNED_WITH_ED25519_KEY = 0x04,
156
    }
157
}
158

            
159
caret_int! {
160
    /// Identifiers for the type of key or object getting signed.
161
    pub struct KeyType(u8) {
162
        /// Identifier for an Ed25519 key.
163
        ED25519_KEY = 0x01,
164
        /// Identifier for the SHA256 of an DER-encoded RSA key.
165
        SHA256_OF_RSA = 0x02,
166
        /// Identifies the SHA256 of an X.509 certificate.
167
        SHA256_OF_X509 = 0x03,
168
    }
169
}
170

            
171
/// Structure for an Ed25519-signed certificate as described in Tor's
172
/// cert-spec.txt.
173
#[derive(Debug, Clone, PartialEq, Eq, derive_builder::Builder)]
174
#[builder(build_fn(skip))]
175
pub struct Ed25519Cert {
176
    /// How many _hours_ after the epoch will this certificate expire?
177
    #[builder(setter(custom))]
178
    exp_hours: ExpiryHours,
179
    /// Type of the certificate; recognized values are in certtype::*
180
    cert_type: CertType,
181
    /// The key or object being certified.
182
    cert_key: CertifiedKey,
183
    /// A list of extensions.
184
    #[allow(unused)] // TODO review CertExt and make it pub, and add a getter
185
    #[builder(setter(custom))]
186
    extensions: Vec<CertExt>,
187
    /// The key that signed this cert.
188
    ///
189
    /// Once the cert has been unwrapped from an KeyUnknownCert, this field will
190
    /// be set.  If there is a `SignedWithEd25519` extension in
191
    /// `self.extensions`, this will match it.
192
    #[builder(setter(custom))]
193
    signed_with: Option<ed25519::Ed25519Identity>,
194
}
195

            
196
/// One of the data types that can be certified by an Ed25519Cert.
197
#[derive(Debug, Clone, PartialEq, Eq, derive_more::From)]
198
#[non_exhaustive]
199
pub enum CertifiedKey {
200
    /// An Ed25519 public key, signed directly.
201
    Ed25519(ed25519::Ed25519Identity),
202
    /// The SHA256 digest of a DER-encoded RsaPublicKey
203
    #[from(skip)]
204
    RsaSha256Digest([u8; 32]),
205
    /// The SHA256 digest of an X.509 certificate.
206
    #[from(skip)]
207
    X509Sha256Digest([u8; 32]),
208
    /// Some unrecognized key type.
209
    #[from(skip)]
210
    Unrecognized(UnrecognizedKey),
211
}
212

            
213
/// A key whose type we didn't recognize.
214
#[derive(Debug, Clone, PartialEq, Eq)]
215
pub struct UnrecognizedKey {
216
    /// Actual type of the key.
217
    key_type: KeyType,
218
    /// digest of the key, or the key itself.
219
    key_digest: [u8; 32],
220
}
221

            
222
impl CertifiedKey {
223
    /// Return the byte that identifies the type of this key.
224
40883
    pub fn key_type(&self) -> KeyType {
225
40883
        match self {
226
40391
            CertifiedKey::Ed25519(_) => KeyType::ED25519_KEY,
227
211
            CertifiedKey::RsaSha256Digest(_) => KeyType::SHA256_OF_RSA,
228
280
            CertifiedKey::X509Sha256Digest(_) => KeyType::SHA256_OF_X509,
229

            
230
1
            CertifiedKey::Unrecognized(u) => u.key_type,
231
        }
232
40883
    }
233
    /// Return the bytes that are used for the body of this certified
234
    /// key or object.
235
41933
    pub fn as_bytes(&self) -> &[u8] {
236
41933
        match self {
237
40881
            CertifiedKey::Ed25519(k) => k.as_bytes(),
238
421
            CertifiedKey::RsaSha256Digest(k) => &k[..],
239
630
            CertifiedKey::X509Sha256Digest(k) => &k[..],
240
1
            CertifiedKey::Unrecognized(u) => &u.key_digest[..],
241
        }
242
41933
    }
243
    /// If this is an Ed25519 public key, return Some(key).
244
    /// Otherwise, return None.
245
43680
    pub fn as_ed25519(&self) -> Option<&ed25519::Ed25519Identity> {
246
43680
        match self {
247
43400
            CertifiedKey::Ed25519(k) => Some(k),
248
280
            _ => None,
249
        }
250
43680
    }
251
    /// Try to extract a CertifiedKey from a Reader, given that we have
252
    /// already read its type as `key_type`.
253
74063
    fn from_reader(key_type: KeyType, r: &mut Reader<'_>) -> BytesResult<Self> {
254
74063
        Ok(match key_type {
255
73361
            KeyType::ED25519_KEY => CertifiedKey::Ed25519(r.extract()?),
256
211
            KeyType::SHA256_OF_RSA => CertifiedKey::RsaSha256Digest(r.extract()?),
257
490
            KeyType::SHA256_OF_X509 => CertifiedKey::X509Sha256Digest(r.extract()?),
258
            _ => CertifiedKey::Unrecognized(UnrecognizedKey {
259
1
                key_type,
260
1
                key_digest: r.extract()?,
261
            }),
262
        })
263
74063
    }
264
}
265

            
266
/// An extension in a Tor certificate.
267
#[derive(Debug, Clone, PartialEq, Eq)]
268
enum CertExt {
269
    /// Indicates which Ed25519 public key signed this cert.
270
    SignedWithEd25519(SignedWithEd25519Ext),
271
    /// An extension whose identity we don't recognize.
272
    Unrecognized(UnrecognizedExt),
273
}
274

            
275
/// Any unrecognized extension on a Tor certificate.
276
#[derive(Debug, Clone, PartialEq, Eq)]
277
#[allow(unused)]
278
struct UnrecognizedExt {
279
    /// True iff this extension must be understand in order to validate the
280
    /// certificate.
281
    affects_validation: bool,
282
    /// The type of the extension
283
    ext_type: ExtType,
284
    /// The body of the extension.
285
    body: Vec<u8>,
286
}
287

            
288
impl CertExt {
289
    /// Return the identifier code for this Extension.
290
46551
    fn ext_id(&self) -> ExtType {
291
46551
        match self {
292
46550
            CertExt::SignedWithEd25519(_) => ExtType::SIGNED_WITH_ED25519_KEY,
293
1
            CertExt::Unrecognized(u) => u.ext_type,
294
        }
295
46551
    }
296
}
297

            
298
/// Extension indicating that a key that signed a given certificate.
299
#[derive(Debug, Clone, PartialEq, Eq)]
300
struct SignedWithEd25519Ext {
301
    /// The key that signed the certificate including this extension.
302
    pk: ed25519::Ed25519Identity,
303
}
304

            
305
impl Readable for CertExt {
306
46693
    fn take_from(b: &mut Reader<'_>) -> BytesResult<Self> {
307
46693
        let len = b.take_u16()?;
308
46693
        let ext_type: ExtType = b.take_u8()?.into();
309
46693
        let flags = b.take_u8()?;
310
46693
        let body = b.take(len as usize)?;
311

            
312
46693
        Ok(match ext_type {
313
            ExtType::SIGNED_WITH_ED25519_KEY => CertExt::SignedWithEd25519(SignedWithEd25519Ext {
314
46620
                pk: ed25519::Ed25519Identity::from_bytes(body).ok_or_else(|| {
315
70
                    BytesError::InvalidMessage("wrong length on Ed25519 key".into())
316
70
                })?,
317
            }),
318
            _ => {
319
73
                if (flags & 1) != 0 {
320
71
                    return Err(BytesError::InvalidMessage(
321
71
                        "unrecognized certificate extension, with 'affects_validation' flag set."
322
71
                            .into(),
323
71
                    ));
324
2
                }
325
2
                CertExt::Unrecognized(UnrecognizedExt {
326
2
                    affects_validation: false,
327
2
                    ext_type,
328
2
                    body: body.into(),
329
2
                })
330
            }
331
        })
332
46693
    }
333
}
334

            
335
impl Writeable for KeyUnknownCert {
336
62
    fn write_onto<B: Writer + ?Sized>(&self, b: &mut B) -> Result<(), tor_bytes::EncodeError> {
337
62
        self.cert.write_onto(b)
338
62
    }
339
}
340

            
341
impl Readable for KeyUnknownCert {
342
47320
    fn take_from(r: &mut Reader<'_>) -> BytesResult<KeyUnknownCert> {
343
47320
        let b = r.take_rest();
344
47320
        Ed25519Cert::decode(b)
345
47320
    }
346
}
347

            
348
impl Ed25519Cert {
349
    /// Try to decode a certificate from a byte slice.
350
    ///
351
    /// This function returns an error if the byte slice is not
352
    /// completely exhausted.
353
    ///
354
    /// Note that the resulting KeyUnknownCertificate is not checked
355
    /// for validity at all: you will need to provide it with an expected
356
    /// signing key, then check it for timeliness and well-signedness.
357
74271
    pub fn decode(cert: &[u8]) -> BytesResult<KeyUnknownCert> {
358
74271
        let mut r = Reader::from_slice(cert);
359
74271
        let v = r.take_u8()?;
360
74271
        if v != 1 {
361
            // This would be something other than a "v1" certificate. We don't
362
            // understand those.
363
140
            return Err(BytesError::InvalidMessage(
364
140
                "Unrecognized certificate version".into(),
365
140
            ));
366
74131
        }
367
74131
        let cert_type = r.take_u8()?.into();
368
74131
        let exp_hours = r.extract()?;
369
74061
        let mut cert_key_type = r.take_u8()?.into();
370

            
371
        // This is a workaround for a tor bug: the key type is
372
        // wrong. It was fixed in tor#40124, which got merged into Tor
373
        // 0.4.5.x and later.
374
74061
        if cert_type == CertType::SIGNING_V_TLS_CERT && cert_key_type == KeyType::ED25519_KEY {
375
            cert_key_type = KeyType::SHA256_OF_X509;
376
74061
        }
377

            
378
74061
        let cert_key = CertifiedKey::from_reader(cert_key_type, &mut r)?;
379
74061
        let n_exts = r.take_u8()?;
380
74061
        let mut extensions = Vec::new();
381
74061
        for _ in 0..n_exts {
382
46690
            let e: CertExt = r.extract()?;
383
46550
            extensions.push(e);
384
        }
385

            
386
73921
        let sig_offset = r.consumed();
387
73921
        let signature: ed25519::Signature = r.extract()?;
388
73921
        r.should_be_exhausted()?;
389
        // See comment in `impl Writeable for UncheckedCert`.
390

            
391
73921
        let keyext = extensions
392
73921
            .iter()
393
73921
            .find(|e| e.ext_id() == ExtType::SIGNED_WITH_ED25519_KEY);
394

            
395
73921
        let included_pkey = match keyext {
396
46550
            Some(CertExt::SignedWithEd25519(s)) => Some(s.pk),
397
27371
            _ => None,
398
        };
399

            
400
73921
        Ok(KeyUnknownCert {
401
73921
            cert: UncheckedCert {
402
73921
                cert: Ed25519Cert {
403
73921
                    exp_hours,
404
73921
                    cert_type,
405
73921
                    cert_key,
406
73921
                    extensions,
407
73921

            
408
73921
                    signed_with: included_pkey,
409
73921
                },
410
73921
                text: cert[0..sig_offset].into(),
411
73921
                signature,
412
73921
            },
413
73921
        })
414
74271
    }
415

            
416
    /// Return the time at which this certificate becomes expired
417
66991
    pub fn expiry(&self) -> std::time::SystemTime {
418
66991
        self.exp_hours.into()
419
66991
    }
420

            
421
    /// Return true iff this certificate will be expired at the time `when`.
422
    ///
423
    /// This is inclusive, meaning that `when == self.expiry()` is still valid.
424
    pub fn is_expired_at(&self, when: std::time::SystemTime) -> bool {
425
        when > self.expiry()
426
    }
427

            
428
    /// Return the signed key or object that is authenticated by this
429
    /// certificate.
430
38571
    pub fn subject_key(&self) -> &CertifiedKey {
431
38571
        &self.cert_key
432
38571
    }
433

            
434
    /// Return the ed25519 key that signed this certificate.
435
40671
    pub fn signing_key(&self) -> Option<&ed25519::Ed25519Identity> {
436
40671
        self.signed_with.as_ref()
437
40671
    }
438

            
439
    /// Return the type of this certificate.
440
21631
    pub fn cert_type(&self) -> CertType {
441
21631
        self.cert_type
442
21631
    }
443
}
444

            
445
/// A parsed Ed25519 certificate. Maybe it includes its signing key;
446
/// maybe it doesn't.
447
///
448
/// To validate this cert, either it must contain its signing key,
449
/// or the caller must know the signing key.  In the first case, call
450
/// [`should_have_signing_key`](KeyUnknownCert::should_have_signing_key);
451
/// in the latter, call
452
/// [`should_be_signed_with`](KeyUnknownCert::should_be_signed_with).
453
#[derive(Clone, Debug, PartialEq, Eq)]
454
pub struct KeyUnknownCert {
455
    /// The certificate whose signing key might not be known.
456
    cert: UncheckedCert,
457
}
458

            
459
impl KeyUnknownCert {
460
    /// Return the certificate type of the underling cert.
461
45570
    pub fn peek_cert_type(&self) -> CertType {
462
45570
        self.cert.cert.cert_type
463
45570
    }
464
    /// Return subject key of the underlying cert.
465
24290
    pub fn peek_subject_key(&self) -> &CertifiedKey {
466
24290
        &self.cert.cert.cert_key
467
24290
    }
468

            
469
    /// Check whether a given pkey is (or might be) a key that has correctly
470
    /// signed this certificate.
471
    ///
472
    /// If pkey is None, this certificate must contain its signing key.
473
    ///
474
    /// On success, we can check whether the certificate is well-signed;
475
    /// otherwise, we can't check the certificate.
476
    #[deprecated(
477
        since = "0.7.1",
478
        note = "Use should_have_signing_key or should_be_signed_with instead."
479
    )]
480
    pub fn check_key(self, pkey: Option<&ed25519::Ed25519Identity>) -> CertResult<UncheckedCert> {
481
        match pkey {
482
            Some(wanted) => self.should_be_signed_with(wanted),
483
            None => self.should_have_signing_key(),
484
        }
485
    }
486

            
487
    /// Declare that this should be a self-contained certificate that contains its own
488
    /// signing key.
489
    ///
490
    /// On success, this certificate did indeed turn out to be self-contained, and so
491
    /// we can validate it.
492
    /// On failure, this certificate was not self-contained.
493
42490
    pub fn should_have_signing_key(self) -> CertResult<UncheckedCert> {
494
42490
        let real_key = match &self.cert.cert.signed_with {
495
42210
            Some(a) => *a,
496
280
            None => return Err(CertError::MissingPubKey),
497
        };
498

            
499
42210
        Ok(UncheckedCert {
500
42210
            cert: Ed25519Cert {
501
42210
                signed_with: Some(real_key),
502
42210
                ..self.cert.cert
503
42210
            },
504
42210
            ..self.cert
505
42210
        })
506
42490
    }
507

            
508
    /// Declare that this should be a certificate signed with a given key.
509
    ///
510
    /// On success, this certificate either listed the provided key, or did not
511
    /// list any key: in either case, we can validate it.
512
    /// On failure, this certificate claims to be signed with a different key.
513
24361
    pub fn should_be_signed_with(
514
24361
        self,
515
24361
        pkey: &ed25519::Ed25519Identity,
516
24361
    ) -> CertResult<UncheckedCert> {
517
24291
        let real_key = match &self.cert.cert.signed_with {
518
490
            Some(a) if a == pkey => *pkey,
519
23871
            None => *pkey,
520
70
            Some(_) => return Err(CertError::KeyMismatch),
521
        };
522

            
523
24291
        Ok(UncheckedCert {
524
24291
            cert: Ed25519Cert {
525
24291
                signed_with: Some(real_key),
526
24291
                ..self.cert.cert
527
24291
            },
528
24291
            ..self.cert
529
24291
        })
530
24361
    }
531
}
532

            
533
/// A certificate that has been parsed, but whose signature and
534
/// timeliness have not been checked.
535
#[derive(Debug, Clone, PartialEq, Eq)]
536
pub struct UncheckedCert {
537
    /// The parsed certificate, possibly modified by inserting an externally
538
    /// supplied key as its signing key.
539
    cert: Ed25519Cert,
540

            
541
    /// The signed text of the certificate. (Checking ed25519 signatures
542
    /// forces us to store this.
543
    // TODO(nickm)  It would be better to store a hash here, but we
544
    // don't have the right Ed25519 API.
545
    text: Vec<u8>,
546

            
547
    /// The alleged signature
548
    signature: ed25519::Signature,
549
}
550

            
551
/// A certificate that has been parsed and signature-checked, but whose
552
/// timeliness has not been checked.
553
#[derive(Debug, Clone, PartialEq, Eq)]
554
pub struct SigCheckedCert {
555
    /// The certificate that might or might not be timely
556
    cert: Ed25519Cert,
557
}
558

            
559
impl UncheckedCert {
560
    /// Split this unchecked cert into a component that assumes it has
561
    /// been checked, and a signature to validate.
562
44100
    pub fn dangerously_split(
563
44100
        self,
564
44100
    ) -> CertResult<(SigCheckedCert, ed25519::ValidatableEd25519Signature)> {
565
        use tor_checkable::SelfSigned;
566
44100
        let signing_key = self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
567
44100
        let signing_key = signing_key
568
44100
            .try_into()
569
44100
            .map_err(|_| CertError::BadSignature)?;
570
44100
        let signature =
571
44100
            ed25519::ValidatableEd25519Signature::new(signing_key, self.signature, &self.text[..]);
572
44100
        Ok((self.dangerously_assume_wellsigned(), signature))
573
44100
    }
574

            
575
    /// Return subject key of the underlying cert.
576
5460
    pub fn peek_subject_key(&self) -> &CertifiedKey {
577
5460
        &self.cert.cert_key
578
5460
    }
579
    /// Return signing key of the underlying cert.
580
3990
    pub fn peek_signing_key(&self) -> &ed25519::Ed25519Identity {
581
3990
        self.cert
582
3990
            .signed_with
583
3990
            .as_ref()
584
3990
            .expect("Made an UncheckedCert without a signing key")
585
3990
    }
586
}
587

            
588
impl Writeable for UncheckedCert {
589
    // TODO in some sense this duplicates things in encode.rs.
590
    // However, encode.rs is not useable in type-driven (derive-based) situations,
591
    // because it uses entirely different types for encoding to those for decoding.
592
    //
593
    // Therefore, here we implement tor_bytes's encoding trait for the type which can
594
    // also be decoded.  Perhaps the encode module could be abolished.
595
62
    fn write_onto<B: Writer + ?Sized>(&self, b: &mut B) -> Result<(), tor_bytes::EncodeError> {
596
        // Ed25519Cert::decode does a lot of work, which finds a lot of fields,
597
        // but also `sig_offset`.  It then splits the incoming byte buffer at `sig_offset`
598
        // into `text` and `signature`, insisting that there is nothing else.
599
        //
600
        // Therefore this is guaranteed to write precisely the input to `decode`.
601
62
        self.text.write_onto(b)?;
602
62
        self.signature.write_onto(b)?;
603
62
        Ok(())
604
62
    }
605
}
606

            
607
impl tor_checkable::SelfSigned<SigCheckedCert> for UncheckedCert {
608
    type Error = CertError;
609

            
610
21701
    fn is_well_signed(&self) -> CertResult<()> {
611
21701
        let pubkey = &self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
612
21701
        let pubkey: ed25519::PublicKey = pubkey.try_into().map_err(|_| CertError::BadSignature)?;
613

            
614
21701
        pubkey
615
21701
            .verify(&self.text[..], &self.signature)
616
21701
            .map_err(|_| CertError::BadSignature)?;
617

            
618
21701
        Ok(())
619
21701
    }
620

            
621
65871
    fn dangerously_assume_wellsigned(self) -> SigCheckedCert {
622
65871
        SigCheckedCert { cert: self.cert }
623
65871
    }
624
}
625

            
626
impl tor_checkable::TimeBound for Ed25519Cert {
627
    type Inner = Ed25519Cert;
628

            
629
1681
    fn bounds(&self) -> TimeRange {
630
1681
        TimeRangeBound::new((), ..=self.expiry())
631
1681
    }
632

            
633
65801
    fn dangerously_assume_timely(self) -> Ed25519Cert {
634
65801
        self
635
65801
    }
636
}
637

            
638
impl tor_checkable::TimeBound for SigCheckedCert {
639
    type Inner = Ed25519Cert;
640

            
641
1681
    fn bounds(&self) -> TimeRange {
642
1681
        self.cert.bounds()
643
1681
    }
644

            
645
65801
    fn dangerously_assume_timely(self) -> Ed25519Cert {
646
65801
        self.cert.dangerously_assume_timely()
647
65801
    }
648
}
649

            
650
/// A certificate expiration time, represented in _hours_ since the unix epoch.
651
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
652
struct ExpiryHours(u32);
653

            
654
/// The number of seconds in an hour.
655
const SEC_PER_HOUR: u64 = 3600;
656

            
657
impl From<ExpiryHours> for time::SystemTime {
658
74755
    fn from(value: ExpiryHours) -> Self {
659
        // TODO MSRV 1.91; use from_hours.
660
74755
        let d = std::time::Duration::from_secs(u64::from(value.0) * SEC_PER_HOUR);
661
74755
        std::time::SystemTime::UNIX_EPOCH + d
662
74755
    }
663
}
664

            
665
impl ExpiryHours {
666
    /// Return the earliest possible `ExpiryHours` that is no earlier than `expiry`.
667
44553
    fn try_from_systemtime_ceil(expiry: time::SystemTime) -> Result<Self, CertEncodeError> {
668
44553
        let d = expiry
669
44553
            .duration_since(time::SystemTime::UNIX_EPOCH)
670
44553
            .map_err(|_| CertEncodeError::InvalidExpiration)?;
671
44553
        let sec_ceil = d.as_secs() + if d.subsec_nanos() > 0 { 1 } else { 0 };
672
44553
        let hours = sec_ceil
673
44553
            .div_ceil(SEC_PER_HOUR)
674
44553
            .try_into()
675
44553
            .map_err(|_| CertEncodeError::InvalidExpiration)?;
676
44553
        Ok(ExpiryHours(hours))
677
44553
    }
678

            
679
    /// Return the latest possible ExpiryHours
680
    const fn max() -> Self {
681
        ExpiryHours(u32::MAX)
682
    }
683
}
684

            
685
impl Readable for ExpiryHours {
686
74762
    fn take_from(b: &mut Reader<'_>) -> BytesResult<Self> {
687
74762
        Ok(ExpiryHours(b.take_u32()?))
688
74762
    }
689
}
690

            
691
impl tor_bytes::Writeable for ExpiryHours {
692
40952
    fn write_onto<B: tor_bytes::Writer + ?Sized>(&self, b: &mut B) -> tor_bytes::EncodeResult<()> {
693
40952
        b.write_u32(self.0);
694
40952
        Ok(())
695
40952
    }
696
}
697

            
698
#[cfg(test)]
699
mod test {
700
    // @@ begin test lint list maintained by maint/add_warning @@
701
    #![allow(clippy::bool_assert_comparison)]
702
    #![allow(clippy::clone_on_copy)]
703
    #![allow(clippy::dbg_macro)]
704
    #![allow(clippy::mixed_attributes_style)]
705
    #![allow(clippy::print_stderr)]
706
    #![allow(clippy::print_stdout)]
707
    #![allow(clippy::single_char_pattern)]
708
    #![allow(clippy::unwrap_used)]
709
    #![allow(clippy::unchecked_time_subtraction)]
710
    #![allow(clippy::useless_vec)]
711
    #![allow(clippy::needless_pass_by_value)]
712
    #![allow(clippy::string_slice)] // See arti#2571
713
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
714
    use super::*;
715
    use hex_literal::hex;
716
    use web_time_compat::SystemTimeExt;
717

            
718
    #[test]
719
    fn parse_unrecognized_ext() -> BytesResult<()> {
720
        // case one: a flag is set but we don't know it
721
        let b = hex!("0009 99 10 657874656e73696f6e");
722
        let mut r = Reader::from_slice(&b);
723
        let e: CertExt = r.extract()?;
724
        r.should_be_exhausted()?;
725

            
726
        assert_eq!(e.ext_id(), 0x99.into());
727

            
728
        // case two: we've been told to ignore the cert if we can't
729
        // handle the extension.
730
        let b = hex!("0009 99 11 657874656e73696f6e");
731
        let mut r = Reader::from_slice(&b);
732
        let e: Result<CertExt, BytesError> = r.extract();
733
        assert!(e.is_err());
734
        assert_eq!(
735
            e.err().unwrap(),
736
            BytesError::InvalidMessage(
737
                "unrecognized certificate extension, with 'affects_validation' flag set.".into()
738
            )
739
        );
740

            
741
        Ok(())
742
    }
743

            
744
    #[test]
745
    fn certified_key() -> BytesResult<()> {
746
        let b =
747
            hex!("4c27616d6f757220756e6974206365757820717527656e636861c3ae6e616974206c6520666572");
748
        let mut r = Reader::from_slice(&b);
749

            
750
        let ck = CertifiedKey::from_reader(KeyType::SHA256_OF_RSA, &mut r)?;
751
        assert_eq!(ck.as_bytes(), &b[..32]);
752
        assert_eq!(ck.key_type(), KeyType::SHA256_OF_RSA);
753
        assert_eq!(r.remaining(), 7);
754

            
755
        let mut r = Reader::from_slice(&b);
756
        let ck = CertifiedKey::from_reader(42.into(), &mut r)?;
757
        assert_eq!(ck.as_bytes(), &b[..32]);
758
        assert_eq!(ck.key_type(), 42.into());
759
        assert_eq!(r.remaining(), 7);
760

            
761
        Ok(())
762
    }
763

            
764
    #[test]
765
    fn expiry_hours_ceil() {
766
        use std::time::{Duration, SystemTime};
767

            
768
        let now = SystemTime::get();
769
        let mut exp = now + Duration::from_secs(24 * 60 * 60);
770
        for _ in 0..=3600 {
771
            let eh = ExpiryHours::try_from_systemtime_ceil(exp).unwrap();
772
            assert!(SystemTime::from(eh) >= exp);
773
            assert!(SystemTime::from(eh) < exp + Duration::from_secs(SEC_PER_HOUR));
774

            
775
            exp += Duration::from_secs(1);
776
        }
777
    }
778
}