tor_cert/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg, 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_duration_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//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
45
46mod err;
47pub mod rsa;
48
49use caret::caret_int;
50use tor_bytes::{Error as BytesError, Result as BytesResult};
51use tor_bytes::{Readable, Reader};
52use tor_llcrypto::pk::*;
53
54use std::time;
55
56pub use err::CertError;
57
58#[cfg(feature = "encode")]
59mod encode;
60#[cfg(feature = "encode")]
61pub use encode::EncodedEd25519Cert;
62#[cfg(feature = "encode")]
63pub use err::CertEncodeError;
64
65/// A Result defined to use CertError
66type CertResult<T> = std::result::Result<T, CertError>;
67
68caret_int! {
69    /// Recognized values for Tor's certificate type field.
70    ///
71    /// In the names used here, "X_V_Y" means "key X verifying key Y",
72    /// whereas "X_CC_Y" means "key X cross-certifying key Y".  In both
73    /// cases, X is the key that is doing the signing, and Y is the key
74    /// or object that is getting signed.
75    ///
76    /// Not every one of these types is valid for an Ed25519
77    /// certificate.  Some are for X.509 certs in a CERTS cell; some
78    /// are for RSA->Ed crosscerts in a CERTS cell.
79    pub struct CertType(u8) {
80        /// TLS link key, signed with RSA identity. X.509 format. (Obsolete)
81        TLS_LINK_X509 = 0x01,
82        /// Self-signed RSA identity certificate. X.509 format. (Legacy)
83        RSA_ID_X509 = 0x02,
84        /// RSA lnk authentication key signed with RSA identity
85        /// key. X.509 format. (Obsolete)
86        LINK_AUTH_X509 = 0x03,
87
88        /// Identity verifying a signing key, directly.
89        IDENTITY_V_SIGNING = 0x04,
90
91        /// Signing key verifying a TLS certificate by digest.
92        SIGNING_V_TLS_CERT = 0x05,
93
94        /// Signing key verifying a link authentication key.
95        SIGNING_V_LINK_AUTH = 0x06,
96
97        /// RSA identity key certifying an Ed25519 identity key. RSA
98        /// crosscert format. (Legacy)
99        RSA_ID_V_IDENTITY = 0x07,
100
101        /// For onion services: short-term descriptor signing key
102        /// (`KP_hs_desc_sign`), signed with blinded onion service identity
103        /// (`KP_hs_blind_id`).
104        HS_BLINDED_ID_V_SIGNING = 0x08,
105
106        /// For onion services: Introduction point authentication key
107        /// (`KP_hs_ipt_sid`), signed with short term descriptor signing key
108        /// (`KP_hs_desc_sign`).
109        ///
110        /// This one is, sadly, a bit complicated. In the original specification
111        /// it was meant to be a cross-certificate, where the signature would be
112        /// _on_ the descriptor signing key, _signed with_ the intro TID key.
113        /// But we got it backwards in the C Tor implementation, and now, for
114        /// compatibility, we are stuck doing it backwards in the future.
115        ///
116        /// If we find in the future that it is actually important to
117        /// cross-certify these keys (as originally intended), then we should
118        /// add a new certificate type, and put the new certificate in the onion
119        /// service descriptor.
120        HS_IP_V_SIGNING = 0x09,
121
122        /// An ntor key converted to a ed25519 key, cross-certifying an
123        /// identity key.
124        NTOR_CC_IDENTITY = 0x0A,
125
126        /// For onion services: Ntor encryption key (`KP_hss_ntor`),
127        /// converted to ed25519, signed with the descriptor signing key
128        /// (`KP_hs_desc_sign`).
129        ///
130        /// As with [`HS_IP_V_SIGNING`](CertType::HS_IP_V_SIGNING), this
131        /// certificate type is backwards.  In the original specification it was
132        /// meant to be a cross certificate, with the signing and signed keys
133        /// reversed.
134        HS_IP_CC_SIGNING = 0x0B,
135
136        /// For relays: family key certifying membership of a relay
137        /// by signing its identity.
138        FAMILY_V_IDENTITY = 0x0C,
139    }
140}
141
142caret_int! {
143    /// Extension identifiers for extensions in certificates.
144    pub struct ExtType(u8) {
145        /// Extension indicating an Ed25519 key that signed this certificate.
146        ///
147        /// Certificates do not always contain the key that signed them.
148        SIGNED_WITH_ED25519_KEY = 0x04,
149    }
150}
151
152caret_int! {
153    /// Identifiers for the type of key or object getting signed.
154    pub struct KeyType(u8) {
155        /// Identifier for an Ed25519 key.
156        ED25519_KEY = 0x01,
157        /// Identifier for the SHA256 of an DER-encoded RSA key.
158        SHA256_OF_RSA = 0x02,
159        /// Identifies the SHA256 of an X.509 certificate.
160        SHA256_OF_X509 = 0x03,
161    }
162}
163
164/// Structure for an Ed25519-signed certificate as described in Tor's
165/// cert-spec.txt.
166#[derive(Debug, Clone)]
167#[cfg_attr(feature = "encode", derive(derive_builder::Builder))]
168#[cfg_attr(
169    feature = "encode",
170    builder(name = "Ed25519CertConstructor", build_fn(skip))
171)]
172pub struct Ed25519Cert {
173    /// How many _hours_ after the epoch will this certificate expire?
174    #[cfg_attr(feature = "encode", builder(setter(custom)))]
175    exp_hours: u32,
176    /// Type of the certificate; recognized values are in certtype::*
177    cert_type: CertType,
178    /// The key or object being certified.
179    cert_key: CertifiedKey,
180    /// A list of extensions.
181    #[allow(unused)]
182    #[cfg_attr(feature = "encode", builder(setter(custom)))]
183    extensions: Vec<CertExt>,
184    /// The key that signed this cert.
185    ///
186    /// Once the cert has been unwrapped from an KeyUnknownCert, this field will
187    /// be set.  If there is a `SignedWithEd25519` extension in
188    /// `self.extensions`, this will match it.
189    #[cfg_attr(feature = "encode", builder(setter(custom)))]
190    signed_with: Option<ed25519::Ed25519Identity>,
191}
192
193/// One of the data types that can be certified by an Ed25519Cert.
194#[derive(Debug, Clone)]
195#[non_exhaustive]
196pub enum CertifiedKey {
197    /// An Ed25519 public key, signed directly.
198    Ed25519(ed25519::Ed25519Identity),
199    /// The SHA256 digest of a DER-encoded RsaPublicKey
200    RsaSha256Digest([u8; 32]),
201    /// The SHA256 digest of an X.509 certificate.
202    X509Sha256Digest([u8; 32]),
203    /// Some unrecognized key type.
204    Unrecognized(UnrecognizedKey),
205}
206
207/// A key whose type we didn't recognize.
208#[derive(Debug, Clone)]
209pub struct UnrecognizedKey {
210    /// Actual type of the key.
211    key_type: KeyType,
212    /// digest of the key, or the key itself.
213    key_digest: [u8; 32],
214}
215
216impl CertifiedKey {
217    /// Return the byte that identifies the type of this key.
218    pub fn key_type(&self) -> KeyType {
219        match self {
220            CertifiedKey::Ed25519(_) => KeyType::ED25519_KEY,
221            CertifiedKey::RsaSha256Digest(_) => KeyType::SHA256_OF_RSA,
222            CertifiedKey::X509Sha256Digest(_) => KeyType::SHA256_OF_X509,
223
224            CertifiedKey::Unrecognized(u) => u.key_type,
225        }
226    }
227    /// Return the bytes that are used for the body of this certified
228    /// key or object.
229    pub fn as_bytes(&self) -> &[u8] {
230        match self {
231            CertifiedKey::Ed25519(k) => k.as_bytes(),
232            CertifiedKey::RsaSha256Digest(k) => &k[..],
233            CertifiedKey::X509Sha256Digest(k) => &k[..],
234            CertifiedKey::Unrecognized(u) => &u.key_digest[..],
235        }
236    }
237    /// If this is an Ed25519 public key, return Some(key).
238    /// Otherwise, return None.
239    pub fn as_ed25519(&self) -> Option<&ed25519::Ed25519Identity> {
240        match self {
241            CertifiedKey::Ed25519(k) => Some(k),
242            _ => None,
243        }
244    }
245    /// Try to extract a CertifiedKey from a Reader, given that we have
246    /// already read its type as `key_type`.
247    fn from_reader(key_type: KeyType, r: &mut Reader<'_>) -> BytesResult<Self> {
248        Ok(match key_type {
249            KeyType::ED25519_KEY => CertifiedKey::Ed25519(r.extract()?),
250            KeyType::SHA256_OF_RSA => CertifiedKey::RsaSha256Digest(r.extract()?),
251            KeyType::SHA256_OF_X509 => CertifiedKey::X509Sha256Digest(r.extract()?),
252            _ => CertifiedKey::Unrecognized(UnrecognizedKey {
253                key_type,
254                key_digest: r.extract()?,
255            }),
256        })
257    }
258}
259
260/// An extension in a Tor certificate.
261#[derive(Debug, Clone)]
262enum CertExt {
263    /// Indicates which Ed25519 public key signed this cert.
264    SignedWithEd25519(SignedWithEd25519Ext),
265    /// An extension whose identity we don't recognize.
266    Unrecognized(UnrecognizedExt),
267}
268
269/// Any unrecognized extension on a Tor certificate.
270#[derive(Debug, Clone)]
271#[allow(unused)]
272struct UnrecognizedExt {
273    /// True iff this extension must be understand in order to validate the
274    /// certificate.
275    affects_validation: bool,
276    /// The type of the extension
277    ext_type: ExtType,
278    /// The body of the extension.
279    body: Vec<u8>,
280}
281
282impl CertExt {
283    /// Return the identifier code for this Extension.
284    fn ext_id(&self) -> ExtType {
285        match self {
286            CertExt::SignedWithEd25519(_) => ExtType::SIGNED_WITH_ED25519_KEY,
287            CertExt::Unrecognized(u) => u.ext_type,
288        }
289    }
290}
291
292/// Extension indicating that a key that signed a given certificate.
293#[derive(Debug, Clone)]
294struct SignedWithEd25519Ext {
295    /// The key that signed the certificate including this extension.
296    pk: ed25519::Ed25519Identity,
297}
298
299impl Readable for CertExt {
300    fn take_from(b: &mut Reader<'_>) -> BytesResult<Self> {
301        let len = b.take_u16()?;
302        let ext_type: ExtType = b.take_u8()?.into();
303        let flags = b.take_u8()?;
304        let body = b.take(len as usize)?;
305
306        Ok(match ext_type {
307            ExtType::SIGNED_WITH_ED25519_KEY => CertExt::SignedWithEd25519(SignedWithEd25519Ext {
308                pk: ed25519::Ed25519Identity::from_bytes(body).ok_or_else(|| {
309                    BytesError::InvalidMessage("wrong length on Ed25519 key".into())
310                })?,
311            }),
312            _ => {
313                if (flags & 1) != 0 {
314                    return Err(BytesError::InvalidMessage(
315                        "unrecognized certificate extension, with 'affects_validation' flag set."
316                            .into(),
317                    ));
318                }
319                CertExt::Unrecognized(UnrecognizedExt {
320                    affects_validation: false,
321                    ext_type,
322                    body: body.into(),
323                })
324            }
325        })
326    }
327}
328
329impl Ed25519Cert {
330    /// Try to decode a certificate from a byte slice.
331    ///
332    /// This function returns an error if the byte slice is not
333    /// completely exhausted.
334    ///
335    /// Note that the resulting KeyUnknownCertificate is not checked
336    /// for validity at all: you will need to provide it with an expected
337    /// signing key, then check it for timeliness and well-signedness.
338    pub fn decode(cert: &[u8]) -> BytesResult<KeyUnknownCert> {
339        let mut r = Reader::from_slice(cert);
340        let v = r.take_u8()?;
341        if v != 1 {
342            // This would be something other than a "v1" certificate. We don't
343            // understand those.
344            return Err(BytesError::InvalidMessage(
345                "Unrecognized certificate version".into(),
346            ));
347        }
348        let cert_type = r.take_u8()?.into();
349        let exp_hours = r.take_u32()?;
350        let mut cert_key_type = r.take_u8()?.into();
351
352        // This is a workaround for a tor bug: the key type is
353        // wrong. It was fixed in tor#40124, which got merged into Tor
354        // 0.4.5.x and later.
355        if cert_type == CertType::SIGNING_V_TLS_CERT && cert_key_type == KeyType::ED25519_KEY {
356            cert_key_type = KeyType::SHA256_OF_X509;
357        }
358
359        let cert_key = CertifiedKey::from_reader(cert_key_type, &mut r)?;
360        let n_exts = r.take_u8()?;
361        let mut extensions = Vec::new();
362        for _ in 0..n_exts {
363            let e: CertExt = r.extract()?;
364            extensions.push(e);
365        }
366
367        let sig_offset = r.consumed();
368        let signature: ed25519::Signature = r.extract()?;
369        r.should_be_exhausted()?;
370
371        let keyext = extensions
372            .iter()
373            .find(|e| e.ext_id() == ExtType::SIGNED_WITH_ED25519_KEY);
374
375        let included_pkey = match keyext {
376            Some(CertExt::SignedWithEd25519(s)) => Some(s.pk),
377            _ => None,
378        };
379
380        Ok(KeyUnknownCert {
381            cert: UncheckedCert {
382                cert: Ed25519Cert {
383                    exp_hours,
384                    cert_type,
385                    cert_key,
386                    extensions,
387
388                    signed_with: included_pkey,
389                },
390                text: cert[0..sig_offset].into(),
391                signature,
392            },
393        })
394    }
395
396    /// Return the time at which this certificate becomes expired
397    pub fn expiry(&self) -> std::time::SystemTime {
398        let d = std::time::Duration::new(u64::from(self.exp_hours) * 3600, 0);
399        std::time::SystemTime::UNIX_EPOCH + d
400    }
401
402    /// Return true iff this certificate will be expired at the time `when`.
403    pub fn is_expired_at(&self, when: std::time::SystemTime) -> bool {
404        when >= self.expiry()
405    }
406
407    /// Return the signed key or object that is authenticated by this
408    /// certificate.
409    pub fn subject_key(&self) -> &CertifiedKey {
410        &self.cert_key
411    }
412
413    /// Return the ed25519 key that signed this certificate.
414    pub fn signing_key(&self) -> Option<&ed25519::Ed25519Identity> {
415        self.signed_with.as_ref()
416    }
417
418    /// Return the type of this certificate.
419    pub fn cert_type(&self) -> CertType {
420        self.cert_type
421    }
422}
423
424/// A parsed Ed25519 certificate. Maybe it includes its signing key;
425/// maybe it doesn't.
426///
427/// To validate this cert, either it must contain its signing key,
428/// or the caller must know the signing key.  In the first case, call
429/// [`should_have_signing_key`](KeyUnknownCert::should_have_signing_key);
430/// in the latter, call
431/// [`should_be_signed_with`](KeyUnknownCert::should_be_signed_with).
432#[derive(Clone, Debug)]
433pub struct KeyUnknownCert {
434    /// The certificate whose signing key might not be known.
435    cert: UncheckedCert,
436}
437
438impl KeyUnknownCert {
439    /// Return the certificate type of the underling cert.
440    pub fn peek_cert_type(&self) -> CertType {
441        self.cert.cert.cert_type
442    }
443    /// Return subject key of the underlying cert.
444    pub fn peek_subject_key(&self) -> &CertifiedKey {
445        &self.cert.cert.cert_key
446    }
447
448    /// Check whether a given pkey is (or might be) a key that has correctly
449    /// signed this certificate.
450    ///
451    /// If pkey is None, this certificate must contain its signing key.
452    ///
453    /// On success, we can check whether the certificate is well-signed;
454    /// otherwise, we can't check the certificate.
455    #[deprecated(
456        since = "0.7.1",
457        note = "Use should_have_signing_key or should_be_signed_with instead."
458    )]
459    pub fn check_key(self, pkey: Option<&ed25519::Ed25519Identity>) -> CertResult<UncheckedCert> {
460        match pkey {
461            Some(wanted) => self.should_be_signed_with(wanted),
462            None => self.should_have_signing_key(),
463        }
464    }
465
466    /// Declare that this should be a self-contained certificate that contains its own
467    /// signing key.
468    ///
469    /// On success, this certificate did indeed turn out to be self-contained, and so
470    /// we can validate it.
471    /// On failure, this certificate was not self-contained.
472    pub fn should_have_signing_key(self) -> CertResult<UncheckedCert> {
473        let real_key = match &self.cert.cert.signed_with {
474            Some(a) => *a,
475            None => return Err(CertError::MissingPubKey),
476        };
477
478        Ok(UncheckedCert {
479            cert: Ed25519Cert {
480                signed_with: Some(real_key),
481                ..self.cert.cert
482            },
483            ..self.cert
484        })
485    }
486
487    /// Declare that this should be a certificate signed with a given key.
488    ///
489    /// On success, this certificate either listed the provided key, or did not
490    /// list any key: in either case, we can validate it.
491    /// On failure, this certificate claims to be signed with a different key.
492    pub fn should_be_signed_with(
493        self,
494        pkey: &ed25519::Ed25519Identity,
495    ) -> CertResult<UncheckedCert> {
496        let real_key = match &self.cert.cert.signed_with {
497            Some(a) if a == pkey => *pkey,
498            None => *pkey,
499            Some(_) => return Err(CertError::KeyMismatch),
500        };
501
502        Ok(UncheckedCert {
503            cert: Ed25519Cert {
504                signed_with: Some(real_key),
505                ..self.cert.cert
506            },
507            ..self.cert
508        })
509    }
510}
511
512/// A certificate that has been parsed, but whose signature and
513/// timeliness have not been checked.
514#[derive(Debug, Clone)]
515pub struct UncheckedCert {
516    /// The parsed certificate, possibly modified by inserting an externally
517    /// supplied key as its signing key.
518    cert: Ed25519Cert,
519
520    /// The signed text of the certificate. (Checking ed25519 signatures
521    /// forces us to store this.
522    // TODO(nickm)  It would be better to store a hash here, but we
523    // don't have the right Ed25519 API.
524    text: Vec<u8>,
525
526    /// The alleged signature
527    signature: ed25519::Signature,
528}
529
530/// A certificate that has been parsed and signature-checked, but whose
531/// timeliness has not been checked.
532pub struct SigCheckedCert {
533    /// The certificate that might or might not be timely
534    cert: Ed25519Cert,
535}
536
537impl UncheckedCert {
538    /// Split this unchecked cert into a component that assumes it has
539    /// been checked, and a signature to validate.
540    pub fn dangerously_split(
541        self,
542    ) -> CertResult<(SigCheckedCert, ed25519::ValidatableEd25519Signature)> {
543        use tor_checkable::SelfSigned;
544        let signing_key = self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
545        let signing_key = signing_key
546            .try_into()
547            .map_err(|_| CertError::BadSignature)?;
548        let signature =
549            ed25519::ValidatableEd25519Signature::new(signing_key, self.signature, &self.text[..]);
550        Ok((self.dangerously_assume_wellsigned(), signature))
551    }
552
553    /// Return subject key of the underlying cert.
554    pub fn peek_subject_key(&self) -> &CertifiedKey {
555        &self.cert.cert_key
556    }
557    /// Return signing key of the underlying cert.
558    pub fn peek_signing_key(&self) -> &ed25519::Ed25519Identity {
559        self.cert
560            .signed_with
561            .as_ref()
562            .expect("Made an UncheckedCert without a signing key")
563    }
564}
565
566impl tor_checkable::SelfSigned<SigCheckedCert> for UncheckedCert {
567    type Error = CertError;
568
569    fn is_well_signed(&self) -> CertResult<()> {
570        let pubkey = &self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
571        let pubkey: ed25519::PublicKey = pubkey.try_into().map_err(|_| CertError::BadSignature)?;
572
573        pubkey
574            .verify(&self.text[..], &self.signature)
575            .map_err(|_| CertError::BadSignature)?;
576
577        Ok(())
578    }
579
580    fn dangerously_assume_wellsigned(self) -> SigCheckedCert {
581        SigCheckedCert { cert: self.cert }
582    }
583}
584
585impl tor_checkable::Timebound<Ed25519Cert> for Ed25519Cert {
586    type Error = tor_checkable::TimeValidityError;
587
588    fn is_valid_at(&self, t: &time::SystemTime) -> Result<(), Self::Error> {
589        if self.is_expired_at(*t) {
590            let expiry = self.expiry();
591            Err(Self::Error::Expired(
592                t.duration_since(expiry)
593                    .expect("certificate expiry time inconsistent"),
594            ))
595        } else {
596            Ok(())
597        }
598    }
599
600    fn dangerously_assume_timely(self) -> Ed25519Cert {
601        self
602    }
603}
604
605impl tor_checkable::Timebound<Ed25519Cert> for SigCheckedCert {
606    type Error = tor_checkable::TimeValidityError;
607    fn is_valid_at(&self, t: &time::SystemTime) -> std::result::Result<(), Self::Error> {
608        self.cert.is_valid_at(t)
609    }
610
611    fn dangerously_assume_timely(self) -> Ed25519Cert {
612        self.cert.dangerously_assume_timely()
613    }
614}
615
616#[cfg(test)]
617mod test {
618    // @@ begin test lint list maintained by maint/add_warning @@
619    #![allow(clippy::bool_assert_comparison)]
620    #![allow(clippy::clone_on_copy)]
621    #![allow(clippy::dbg_macro)]
622    #![allow(clippy::mixed_attributes_style)]
623    #![allow(clippy::print_stderr)]
624    #![allow(clippy::print_stdout)]
625    #![allow(clippy::single_char_pattern)]
626    #![allow(clippy::unwrap_used)]
627    #![allow(clippy::unchecked_duration_subtraction)]
628    #![allow(clippy::useless_vec)]
629    #![allow(clippy::needless_pass_by_value)]
630    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
631    use super::*;
632    use hex_literal::hex;
633
634    #[test]
635    fn parse_unrecognized_ext() -> BytesResult<()> {
636        // case one: a flag is set but we don't know it
637        let b = hex!("0009 99 10 657874656e73696f6e");
638        let mut r = Reader::from_slice(&b);
639        let e: CertExt = r.extract()?;
640        r.should_be_exhausted()?;
641
642        assert_eq!(e.ext_id(), 0x99.into());
643
644        // case two: we've been told to ignore the cert if we can't
645        // handle the extension.
646        let b = hex!("0009 99 11 657874656e73696f6e");
647        let mut r = Reader::from_slice(&b);
648        let e: Result<CertExt, BytesError> = r.extract();
649        assert!(e.is_err());
650        assert_eq!(
651            e.err().unwrap(),
652            BytesError::InvalidMessage(
653                "unrecognized certificate extension, with 'affects_validation' flag set.".into()
654            )
655        );
656
657        Ok(())
658    }
659
660    #[test]
661    fn certified_key() -> BytesResult<()> {
662        let b =
663            hex!("4c27616d6f757220756e6974206365757820717527656e636861c3ae6e616974206c6520666572");
664        let mut r = Reader::from_slice(&b);
665
666        let ck = CertifiedKey::from_reader(KeyType::SHA256_OF_RSA, &mut r)?;
667        assert_eq!(ck.as_bytes(), &b[..32]);
668        assert_eq!(ck.key_type(), KeyType::SHA256_OF_RSA);
669        assert_eq!(r.remaining(), 7);
670
671        let mut r = Reader::from_slice(&b);
672        let ck = CertifiedKey::from_reader(42.into(), &mut r)?;
673        assert_eq!(ck.as_bytes(), &b[..32]);
674        assert_eq!(ck.key_type(), 42.into());
675        assert_eq!(r.remaining(), 7);
676
677        Ok(())
678    }
679}