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
#![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
#![deny(clippy::string_slice)] // See arti#2571
48
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
49
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
50

            
51
mod err;
52
pub mod rsa;
53

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

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

            
63
use web_time_compat as time;
64

            
65
pub use err::CertError;
66

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
390
69724
        let keyext = extensions
391
69724
            .iter()
392
70352
            .find(|e| e.ext_id() == ExtType::SIGNED_WITH_ED25519_KEY);
393

            
394
69724
        let included_pkey = match keyext {
395
44588
            Some(CertExt::SignedWithEd25519(s)) => Some(s.pk),
396
25136
            _ => None,
397
        };
398

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

            
407
69724
                    signed_with: included_pkey,
408
69724
                },
409
69724
                text: cert[0..sig_offset].into(),
410
69724
                signature,
411
69724
            },
412
69724
        })
413
70079
    }
414

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

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

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

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

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

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

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

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

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

            
498
40186
        Ok(UncheckedCert {
499
40186
            cert: Ed25519Cert {
500
40186
                signed_with: Some(real_key),
501
40186
                ..self.cert.cert
502
40186
            },
503
40186
            ..self.cert
504
40186
        })
505
40470
    }
506

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

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

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

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

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

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

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

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

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

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

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

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

            
617
19385
        Ok(())
618
19385
    }
619

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

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

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

            
632
61488
    fn dangerously_assume_timely(self) -> Ed25519Cert {
633
61488
        self
634
61488
    }
635
}
636

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
740
        Ok(())
741
    }
742

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

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

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

            
760
        Ok(())
761
    }
762

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

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

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