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
#![warn(clippy::cognitive_complexity)]
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
#![deny(clippy::print_stderr)]
28
#![deny(clippy::print_stdout)]
29
#![warn(clippy::rc_buffer)]
30
#![deny(clippy::ref_option_ref)]
31
#![warn(clippy::semicolon_if_nothing_returned)]
32
#![warn(clippy::trait_duplication_in_bounds)]
33
#![deny(clippy::unchecked_time_subtraction)]
34
#![deny(clippy::unnecessary_wraps)]
35
#![warn(clippy::unseparated_literal_suffix)]
36
#![deny(clippy::unwrap_used)]
37
#![deny(clippy::mod_module_files)]
38
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
39
#![allow(clippy::uninlined_format_args)]
40
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
41
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
42
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
43
#![allow(clippy::needless_lifetimes)] // See arti#1765
44
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
45
#![allow(clippy::collapsible_if)] // See arti#2342
46
#![deny(clippy::unused_async)]
47
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
48

            
49
mod err;
50
pub mod rsa;
51

            
52
#[cfg(feature = "x509")]
53
pub use tor_cert_x509 as x509;
54

            
55
use caret::caret_int;
56
use tor_bytes::{Error as BytesError, Result as BytesResult};
57
use tor_bytes::{Readable, Reader};
58
use tor_llcrypto::pk::*;
59

            
60
use web_time_compat as time;
61

            
62
pub use err::CertError;
63

            
64
#[cfg(feature = "encode")]
65
mod encode;
66
#[cfg(feature = "encode")]
67
pub use encode::{EncodedCert, EncodedEd25519Cert};
68
#[cfg(feature = "encode")]
69
pub use err::CertEncodeError;
70

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
213
/// A key whose type we didn't recognize.
214
#[derive(Debug, Clone)]
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
34713
    pub fn key_type(&self) -> KeyType {
225
34713
        match self {
226
34502
            CertifiedKey::Ed25519(_) => KeyType::ED25519_KEY,
227
2
            CertifiedKey::RsaSha256Digest(_) => KeyType::SHA256_OF_RSA,
228
207
            CertifiedKey::X509Sha256Digest(_) => KeyType::SHA256_OF_X509,
229

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

            
266
/// An extension in a Tor certificate.
267
#[derive(Debug, Clone)]
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)]
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
17804
    fn ext_id(&self) -> ExtType {
291
17804
        match self {
292
17802
            CertExt::SignedWithEd25519(_) => ExtType::SIGNED_WITH_ED25519_KEY,
293
2
            CertExt::Unrecognized(u) => u.ext_type,
294
        }
295
17804
    }
296
}
297

            
298
/// Extension indicating that a key that signed a given certificate.
299
#[derive(Debug, Clone)]
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
17946
    fn take_from(b: &mut Reader<'_>) -> BytesResult<Self> {
307
17946
        let len = b.take_u16()?;
308
17946
        let ext_type: ExtType = b.take_u8()?.into();
309
17946
        let flags = b.take_u8()?;
310
17946
        let body = b.take(len as usize)?;
311

            
312
17946
        Ok(match ext_type {
313
            ExtType::SIGNED_WITH_ED25519_KEY => CertExt::SignedWithEd25519(SignedWithEd25519Ext {
314
17872
                pk: ed25519::Ed25519Identity::from_bytes(body).ok_or_else(|| {
315
69
                    BytesError::InvalidMessage("wrong length on Ed25519 key".into())
316
70
                })?,
317
            }),
318
            _ => {
319
75
                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
4
                }
325
4
                CertExt::Unrecognized(UnrecognizedExt {
326
4
                    affects_validation: false,
327
4
                    ext_type,
328
4
                    body: body.into(),
329
4
                })
330
            }
331
        })
332
17946
    }
333
}
334

            
335
impl Ed25519Cert {
336
    /// Try to decode a certificate from a byte slice.
337
    ///
338
    /// This function returns an error if the byte slice is not
339
    /// completely exhausted.
340
    ///
341
    /// Note that the resulting KeyUnknownCertificate is not checked
342
    /// for validity at all: you will need to provide it with an expected
343
    /// signing key, then check it for timeliness and well-signedness.
344
22013
    pub fn decode(cert: &[u8]) -> BytesResult<KeyUnknownCert> {
345
22013
        let mut r = Reader::from_slice(cert);
346
22013
        let v = r.take_u8()?;
347
22013
        if v != 1 {
348
            // This would be something other than a "v1" certificate. We don't
349
            // understand those.
350
138
            return Err(BytesError::InvalidMessage(
351
138
                "Unrecognized certificate version".into(),
352
138
            ));
353
21875
        }
354
21875
        let cert_type = r.take_u8()?.into();
355
21875
        let exp_hours = r.extract()?;
356
21806
        let mut cert_key_type = r.take_u8()?.into();
357

            
358
        // This is a workaround for a tor bug: the key type is
359
        // wrong. It was fixed in tor#40124, which got merged into Tor
360
        // 0.4.5.x and later.
361
21806
        if cert_type == CertType::SIGNING_V_TLS_CERT && cert_key_type == KeyType::ED25519_KEY {
362
            cert_key_type = KeyType::SHA256_OF_X509;
363
21806
        }
364

            
365
21806
        let cert_key = CertifiedKey::from_reader(cert_key_type, &mut r)?;
366
21806
        let n_exts = r.take_u8()?;
367
21806
        let mut extensions = Vec::new();
368
21806
        for _ in 0..n_exts {
369
17940
            let e: CertExt = r.extract()?;
370
17802
            extensions.push(e);
371
        }
372

            
373
21668
        let sig_offset = r.consumed();
374
21668
        let signature: ed25519::Signature = r.extract()?;
375
21668
        r.should_be_exhausted()?;
376

            
377
21668
        let keyext = extensions
378
21668
            .iter()
379
21926
            .find(|e| e.ext_id() == ExtType::SIGNED_WITH_ED25519_KEY);
380

            
381
21668
        let included_pkey = match keyext {
382
17802
            Some(CertExt::SignedWithEd25519(s)) => Some(s.pk),
383
3866
            _ => None,
384
        };
385

            
386
21668
        Ok(KeyUnknownCert {
387
21668
            cert: UncheckedCert {
388
21668
                cert: Ed25519Cert {
389
21668
                    exp_hours,
390
21668
                    cert_type,
391
21668
                    cert_key,
392
21668
                    extensions,
393
21668

            
394
21668
                    signed_with: included_pkey,
395
21668
                },
396
21668
                text: cert[0..sig_offset].into(),
397
21668
                signature,
398
21668
            },
399
21668
        })
400
22013
    }
401

            
402
    /// Return the time at which this certificate becomes expired
403
21530
    pub fn expiry(&self) -> std::time::SystemTime {
404
21530
        self.exp_hours.into()
405
21530
    }
406

            
407
    /// Return true iff this certificate will be expired at the time `when`.
408
1175
    pub fn is_expired_at(&self, when: std::time::SystemTime) -> bool {
409
1175
        when >= self.expiry()
410
1175
    }
411

            
412
    /// Return the signed key or object that is authenticated by this
413
    /// certificate.
414
13940
    pub fn subject_key(&self) -> &CertifiedKey {
415
13940
        &self.cert_key
416
13940
    }
417

            
418
    /// Return the ed25519 key that signed this certificate.
419
15527
    pub fn signing_key(&self) -> Option<&ed25519::Ed25519Identity> {
420
15527
        self.signed_with.as_ref()
421
15527
    }
422

            
423
    /// Return the type of this certificate.
424
209
    pub fn cert_type(&self) -> CertType {
425
209
        self.cert_type
426
209
    }
427
}
428

            
429
/// A parsed Ed25519 certificate. Maybe it includes its signing key;
430
/// maybe it doesn't.
431
///
432
/// To validate this cert, either it must contain its signing key,
433
/// or the caller must know the signing key.  In the first case, call
434
/// [`should_have_signing_key`](KeyUnknownCert::should_have_signing_key);
435
/// in the latter, call
436
/// [`should_be_signed_with`](KeyUnknownCert::should_be_signed_with).
437
#[derive(Clone, Debug)]
438
pub struct KeyUnknownCert {
439
    /// The certificate whose signing key might not be known.
440
    cert: UncheckedCert,
441
}
442

            
443
impl KeyUnknownCert {
444
    /// Return the certificate type of the underling cert.
445
21528
    pub fn peek_cert_type(&self) -> CertType {
446
21528
        self.cert.cert.cert_type
447
21528
    }
448
    /// Return subject key of the underlying cert.
449
3795
    pub fn peek_subject_key(&self) -> &CertifiedKey {
450
3795
        &self.cert.cert.cert_key
451
3795
    }
452

            
453
    /// Check whether a given pkey is (or might be) a key that has correctly
454
    /// signed this certificate.
455
    ///
456
    /// If pkey is None, this certificate must contain its signing key.
457
    ///
458
    /// On success, we can check whether the certificate is well-signed;
459
    /// otherwise, we can't check the certificate.
460
    #[deprecated(
461
        since = "0.7.1",
462
        note = "Use should_have_signing_key or should_be_signed_with instead."
463
    )]
464
    pub fn check_key(self, pkey: Option<&ed25519::Ed25519Identity>) -> CertResult<UncheckedCert> {
465
        match pkey {
466
            Some(wanted) => self.should_be_signed_with(wanted),
467
            None => self.should_have_signing_key(),
468
        }
469
    }
470

            
471
    /// Declare that this should be a self-contained certificate that contains its own
472
    /// signing key.
473
    ///
474
    /// On success, this certificate did indeed turn out to be self-contained, and so
475
    /// we can validate it.
476
    /// On failure, this certificate was not self-contained.
477
17664
    pub fn should_have_signing_key(self) -> CertResult<UncheckedCert> {
478
17664
        let real_key = match &self.cert.cert.signed_with {
479
17526
            Some(a) => *a,
480
138
            None => return Err(CertError::MissingPubKey),
481
        };
482

            
483
17526
        Ok(UncheckedCert {
484
17526
            cert: Ed25519Cert {
485
17526
                signed_with: Some(real_key),
486
17526
                ..self.cert.cert
487
17526
            },
488
17526
            ..self.cert
489
17526
        })
490
17664
    }
491

            
492
    /// Declare that this should be a certificate signed with a given key.
493
    ///
494
    /// On success, this certificate either listed the provided key, or did not
495
    /// list any key: in either case, we can validate it.
496
    /// On failure, this certificate claims to be signed with a different key.
497
3866
    pub fn should_be_signed_with(
498
3866
        self,
499
3866
        pkey: &ed25519::Ed25519Identity,
500
3866
    ) -> CertResult<UncheckedCert> {
501
3797
        let real_key = match &self.cert.cert.signed_with {
502
138
            Some(a) if a == pkey => *pkey,
503
3728
            None => *pkey,
504
69
            Some(_) => return Err(CertError::KeyMismatch),
505
        };
506

            
507
3797
        Ok(UncheckedCert {
508
3797
            cert: Ed25519Cert {
509
3797
                signed_with: Some(real_key),
510
3797
                ..self.cert.cert
511
3797
            },
512
3797
            ..self.cert
513
3797
        })
514
3866
    }
515
}
516

            
517
/// A certificate that has been parsed, but whose signature and
518
/// timeliness have not been checked.
519
#[derive(Debug, Clone)]
520
pub struct UncheckedCert {
521
    /// The parsed certificate, possibly modified by inserting an externally
522
    /// supplied key as its signing key.
523
    cert: Ed25519Cert,
524

            
525
    /// The signed text of the certificate. (Checking ed25519 signatures
526
    /// forces us to store this.
527
    // TODO(nickm)  It would be better to store a hash here, but we
528
    // don't have the right Ed25519 API.
529
    text: Vec<u8>,
530

            
531
    /// The alleged signature
532
    signature: ed25519::Signature,
533
}
534

            
535
/// A certificate that has been parsed and signature-checked, but whose
536
/// timeliness has not been checked.
537
pub struct SigCheckedCert {
538
    /// The certificate that might or might not be timely
539
    cert: Ed25519Cert,
540
}
541

            
542
impl UncheckedCert {
543
    /// Split this unchecked cert into a component that assumes it has
544
    /// been checked, and a signature to validate.
545
20217
    pub fn dangerously_split(
546
20217
        self,
547
20217
    ) -> CertResult<(SigCheckedCert, ed25519::ValidatableEd25519Signature)> {
548
        use tor_checkable::SelfSigned;
549
20217
        let signing_key = self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
550
20217
        let signing_key = signing_key
551
20217
            .try_into()
552
20217
            .map_err(|_| CertError::BadSignature)?;
553
20217
        let signature =
554
20217
            ed25519::ValidatableEd25519Signature::new(signing_key, self.signature, &self.text[..]);
555
20217
        Ok((self.dangerously_assume_wellsigned(), signature))
556
20217
    }
557

            
558
    /// Return subject key of the underlying cert.
559
4968
    pub fn peek_subject_key(&self) -> &CertifiedKey {
560
4968
        &self.cert.cert_key
561
4968
    }
562
    /// Return signing key of the underlying cert.
563
10626
    pub fn peek_signing_key(&self) -> &ed25519::Ed25519Identity {
564
10626
        self.cert
565
10626
            .signed_with
566
10626
            .as_ref()
567
10626
            .expect("Made an UncheckedCert without a signing key")
568
10626
    }
569
}
570

            
571
impl tor_checkable::SelfSigned<SigCheckedCert> for UncheckedCert {
572
    type Error = CertError;
573

            
574
209
    fn is_well_signed(&self) -> CertResult<()> {
575
209
        let pubkey = &self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
576
209
        let pubkey: ed25519::PublicKey = pubkey.try_into().map_err(|_| CertError::BadSignature)?;
577

            
578
209
        pubkey
579
209
            .verify(&self.text[..], &self.signature)
580
209
            .map_err(|_| CertError::BadSignature)?;
581

            
582
209
        Ok(())
583
209
    }
584

            
585
20495
    fn dangerously_assume_wellsigned(self) -> SigCheckedCert {
586
20495
        SigCheckedCert { cert: self.cert }
587
20495
    }
588
}
589

            
590
impl tor_checkable::Timebound<Ed25519Cert> for Ed25519Cert {
591
    type Error = tor_checkable::TimeValidityError;
592

            
593
1175
    fn is_valid_at(&self, t: &time::SystemTime) -> Result<(), Self::Error> {
594
1175
        if self.is_expired_at(*t) {
595
69
            let expiry = self.expiry();
596
69
            Err(Self::Error::Expired(
597
69
                t.duration_since(expiry)
598
69
                    .expect("certificate expiry time inconsistent"),
599
69
            ))
600
        } else {
601
1106
            Ok(())
602
        }
603
1175
    }
604

            
605
20426
    fn dangerously_assume_timely(self) -> Ed25519Cert {
606
20426
        self
607
20426
    }
608
}
609

            
610
impl tor_checkable::Timebound<Ed25519Cert> for SigCheckedCert {
611
    type Error = tor_checkable::TimeValidityError;
612
1175
    fn is_valid_at(&self, t: &time::SystemTime) -> std::result::Result<(), Self::Error> {
613
1175
        self.cert.is_valid_at(t)
614
1175
    }
615

            
616
20426
    fn dangerously_assume_timely(self) -> Ed25519Cert {
617
20426
        self.cert.dangerously_assume_timely()
618
20426
    }
619
}
620

            
621
/// A certificate expiration time, represented in _hours_ since the unix epoch.
622
#[derive(Debug, Clone, Copy)]
623
struct ExpiryHours(u32);
624

            
625
/// The number of seconds in an hour.
626
const SEC_PER_HOUR: u64 = 3600;
627

            
628
impl From<ExpiryHours> for time::SystemTime {
629
36490
    fn from(value: ExpiryHours) -> Self {
630
        // TODO MSRV 1.91; use from_hours.
631
36490
        let d = std::time::Duration::from_secs(u64::from(value.0) * SEC_PER_HOUR);
632
36490
        std::time::SystemTime::UNIX_EPOCH + d
633
36490
    }
634
}
635

            
636
#[cfg(feature = "encode")]
637
impl ExpiryHours {
638
    /// Return the earliest possible `ExpiryHours` that is no earlier than `expiry`.
639
41913
    fn try_from_systemtime_ceil(expiry: time::SystemTime) -> Result<Self, CertEncodeError> {
640
41913
        let d = expiry
641
41913
            .duration_since(time::SystemTime::UNIX_EPOCH)
642
41913
            .map_err(|_| CertEncodeError::InvalidExpiration)?;
643
41913
        let sec_ceil = d.as_secs() + if d.subsec_nanos() > 0 { 1 } else { 0 };
644
41913
        let hours = sec_ceil
645
41913
            .div_ceil(SEC_PER_HOUR)
646
41913
            .try_into()
647
41913
            .map_err(|_| CertEncodeError::InvalidExpiration)?;
648
41913
        Ok(ExpiryHours(hours))
649
41913
    }
650

            
651
    /// Return the latest possible ExpiryHours
652
    const fn max() -> Self {
653
        ExpiryHours(u32::MAX)
654
    }
655
}
656

            
657
impl Readable for ExpiryHours {
658
22498
    fn take_from(b: &mut Reader<'_>) -> BytesResult<Self> {
659
22498
        Ok(ExpiryHours(b.take_u32()?))
660
22498
    }
661
}
662

            
663
impl tor_bytes::Writeable for ExpiryHours {
664
34711
    fn write_onto<B: tor_bytes::Writer + ?Sized>(&self, b: &mut B) -> tor_bytes::EncodeResult<()> {
665
34711
        b.write_u32(self.0);
666
34711
        Ok(())
667
34711
    }
668
}
669

            
670
#[cfg(test)]
671
mod test {
672
    // @@ begin test lint list maintained by maint/add_warning @@
673
    #![allow(clippy::bool_assert_comparison)]
674
    #![allow(clippy::clone_on_copy)]
675
    #![allow(clippy::dbg_macro)]
676
    #![allow(clippy::mixed_attributes_style)]
677
    #![allow(clippy::print_stderr)]
678
    #![allow(clippy::print_stdout)]
679
    #![allow(clippy::single_char_pattern)]
680
    #![allow(clippy::unwrap_used)]
681
    #![allow(clippy::unchecked_time_subtraction)]
682
    #![allow(clippy::useless_vec)]
683
    #![allow(clippy::needless_pass_by_value)]
684
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
685
    use super::*;
686
    use hex_literal::hex;
687
    use web_time_compat::SystemTimeExt;
688

            
689
    #[test]
690
    fn parse_unrecognized_ext() -> BytesResult<()> {
691
        // case one: a flag is set but we don't know it
692
        let b = hex!("0009 99 10 657874656e73696f6e");
693
        let mut r = Reader::from_slice(&b);
694
        let e: CertExt = r.extract()?;
695
        r.should_be_exhausted()?;
696

            
697
        assert_eq!(e.ext_id(), 0x99.into());
698

            
699
        // case two: we've been told to ignore the cert if we can't
700
        // handle the extension.
701
        let b = hex!("0009 99 11 657874656e73696f6e");
702
        let mut r = Reader::from_slice(&b);
703
        let e: Result<CertExt, BytesError> = r.extract();
704
        assert!(e.is_err());
705
        assert_eq!(
706
            e.err().unwrap(),
707
            BytesError::InvalidMessage(
708
                "unrecognized certificate extension, with 'affects_validation' flag set.".into()
709
            )
710
        );
711

            
712
        Ok(())
713
    }
714

            
715
    #[test]
716
    fn certified_key() -> BytesResult<()> {
717
        let b =
718
            hex!("4c27616d6f757220756e6974206365757820717527656e636861c3ae6e616974206c6520666572");
719
        let mut r = Reader::from_slice(&b);
720

            
721
        let ck = CertifiedKey::from_reader(KeyType::SHA256_OF_RSA, &mut r)?;
722
        assert_eq!(ck.as_bytes(), &b[..32]);
723
        assert_eq!(ck.key_type(), KeyType::SHA256_OF_RSA);
724
        assert_eq!(r.remaining(), 7);
725

            
726
        let mut r = Reader::from_slice(&b);
727
        let ck = CertifiedKey::from_reader(42.into(), &mut r)?;
728
        assert_eq!(ck.as_bytes(), &b[..32]);
729
        assert_eq!(ck.key_type(), 42.into());
730
        assert_eq!(r.remaining(), 7);
731

            
732
        Ok(())
733
    }
734

            
735
    #[cfg(feature = "encode")]
736
    #[test]
737
    fn expiry_hours_ceil() {
738
        use std::time::{Duration, SystemTime};
739

            
740
        let now = SystemTime::get();
741
        let mut exp = now + Duration::from_secs(24 * 60 * 60);
742
        for _ in 0..=3600 {
743
            let eh = ExpiryHours::try_from_systemtime_ceil(exp).unwrap();
744
            assert!(SystemTime::from(eh) >= exp);
745
            assert!(SystemTime::from(eh) < exp + Duration::from_secs(SEC_PER_HOUR));
746

            
747
            exp += Duration::from_secs(1);
748
        }
749
    }
750
}