Skip to main content

tor_cert/
lib.rs

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
49mod err;
50pub mod rsa;
51
52#[cfg(feature = "x509")]
53pub use tor_cert_x509 as x509;
54
55use caret::caret_int;
56use tor_bytes::{Error as BytesError, Result as BytesResult};
57use tor_bytes::{Readable, Reader};
58use tor_llcrypto::pk::*;
59
60use std::time;
61
62pub use err::CertError;
63
64#[cfg(feature = "encode")]
65mod encode;
66#[cfg(feature = "encode")]
67pub use encode::EncodedEd25519Cert;
68#[cfg(feature = "encode")]
69pub use err::CertEncodeError;
70
71/// A Result defined to use CertError
72type CertResult<T> = std::result::Result<T, CertError>;
73
74caret_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
148caret_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
158caret_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)]
178pub struct Ed25519Cert {
179    /// How many _hours_ after the epoch will this certificate expire?
180    #[cfg_attr(feature = "encode", builder(setter(custom)))]
181    exp_hours: u32,
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]
202pub 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)]
215pub 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
222impl CertifiedKey {
223    /// Return the byte that identifies the type of this key.
224    pub fn key_type(&self) -> KeyType {
225        match self {
226            CertifiedKey::Ed25519(_) => KeyType::ED25519_KEY,
227            CertifiedKey::RsaSha256Digest(_) => KeyType::SHA256_OF_RSA,
228            CertifiedKey::X509Sha256Digest(_) => KeyType::SHA256_OF_X509,
229
230            CertifiedKey::Unrecognized(u) => u.key_type,
231        }
232    }
233    /// Return the bytes that are used for the body of this certified
234    /// key or object.
235    pub fn as_bytes(&self) -> &[u8] {
236        match self {
237            CertifiedKey::Ed25519(k) => k.as_bytes(),
238            CertifiedKey::RsaSha256Digest(k) => &k[..],
239            CertifiedKey::X509Sha256Digest(k) => &k[..],
240            CertifiedKey::Unrecognized(u) => &u.key_digest[..],
241        }
242    }
243    /// If this is an Ed25519 public key, return Some(key).
244    /// Otherwise, return None.
245    pub fn as_ed25519(&self) -> Option<&ed25519::Ed25519Identity> {
246        match self {
247            CertifiedKey::Ed25519(k) => Some(k),
248            _ => None,
249        }
250    }
251    /// Try to extract a CertifiedKey from a Reader, given that we have
252    /// already read its type as `key_type`.
253    fn from_reader(key_type: KeyType, r: &mut Reader<'_>) -> BytesResult<Self> {
254        Ok(match key_type {
255            KeyType::ED25519_KEY => CertifiedKey::Ed25519(r.extract()?),
256            KeyType::SHA256_OF_RSA => CertifiedKey::RsaSha256Digest(r.extract()?),
257            KeyType::SHA256_OF_X509 => CertifiedKey::X509Sha256Digest(r.extract()?),
258            _ => CertifiedKey::Unrecognized(UnrecognizedKey {
259                key_type,
260                key_digest: r.extract()?,
261            }),
262        })
263    }
264}
265
266/// An extension in a Tor certificate.
267#[derive(Debug, Clone)]
268enum 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)]
278struct 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
288impl CertExt {
289    /// Return the identifier code for this Extension.
290    fn ext_id(&self) -> ExtType {
291        match self {
292            CertExt::SignedWithEd25519(_) => ExtType::SIGNED_WITH_ED25519_KEY,
293            CertExt::Unrecognized(u) => u.ext_type,
294        }
295    }
296}
297
298/// Extension indicating that a key that signed a given certificate.
299#[derive(Debug, Clone)]
300struct SignedWithEd25519Ext {
301    /// The key that signed the certificate including this extension.
302    pk: ed25519::Ed25519Identity,
303}
304
305impl Readable for CertExt {
306    fn take_from(b: &mut Reader<'_>) -> BytesResult<Self> {
307        let len = b.take_u16()?;
308        let ext_type: ExtType = b.take_u8()?.into();
309        let flags = b.take_u8()?;
310        let body = b.take(len as usize)?;
311
312        Ok(match ext_type {
313            ExtType::SIGNED_WITH_ED25519_KEY => CertExt::SignedWithEd25519(SignedWithEd25519Ext {
314                pk: ed25519::Ed25519Identity::from_bytes(body).ok_or_else(|| {
315                    BytesError::InvalidMessage("wrong length on Ed25519 key".into())
316                })?,
317            }),
318            _ => {
319                if (flags & 1) != 0 {
320                    return Err(BytesError::InvalidMessage(
321                        "unrecognized certificate extension, with 'affects_validation' flag set."
322                            .into(),
323                    ));
324                }
325                CertExt::Unrecognized(UnrecognizedExt {
326                    affects_validation: false,
327                    ext_type,
328                    body: body.into(),
329                })
330            }
331        })
332    }
333}
334
335impl 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    pub fn decode(cert: &[u8]) -> BytesResult<KeyUnknownCert> {
345        let mut r = Reader::from_slice(cert);
346        let v = r.take_u8()?;
347        if v != 1 {
348            // This would be something other than a "v1" certificate. We don't
349            // understand those.
350            return Err(BytesError::InvalidMessage(
351                "Unrecognized certificate version".into(),
352            ));
353        }
354        let cert_type = r.take_u8()?.into();
355        let exp_hours = r.take_u32()?;
356        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        if cert_type == CertType::SIGNING_V_TLS_CERT && cert_key_type == KeyType::ED25519_KEY {
362            cert_key_type = KeyType::SHA256_OF_X509;
363        }
364
365        let cert_key = CertifiedKey::from_reader(cert_key_type, &mut r)?;
366        let n_exts = r.take_u8()?;
367        let mut extensions = Vec::new();
368        for _ in 0..n_exts {
369            let e: CertExt = r.extract()?;
370            extensions.push(e);
371        }
372
373        let sig_offset = r.consumed();
374        let signature: ed25519::Signature = r.extract()?;
375        r.should_be_exhausted()?;
376
377        let keyext = extensions
378            .iter()
379            .find(|e| e.ext_id() == ExtType::SIGNED_WITH_ED25519_KEY);
380
381        let included_pkey = match keyext {
382            Some(CertExt::SignedWithEd25519(s)) => Some(s.pk),
383            _ => None,
384        };
385
386        Ok(KeyUnknownCert {
387            cert: UncheckedCert {
388                cert: Ed25519Cert {
389                    exp_hours,
390                    cert_type,
391                    cert_key,
392                    extensions,
393
394                    signed_with: included_pkey,
395                },
396                text: cert[0..sig_offset].into(),
397                signature,
398            },
399        })
400    }
401
402    /// Return the time at which this certificate becomes expired
403    pub fn expiry(&self) -> std::time::SystemTime {
404        let d = std::time::Duration::new(u64::from(self.exp_hours) * 3600, 0);
405        std::time::SystemTime::UNIX_EPOCH + d
406    }
407
408    /// Return true iff this certificate will be expired at the time `when`.
409    pub fn is_expired_at(&self, when: std::time::SystemTime) -> bool {
410        when >= self.expiry()
411    }
412
413    /// Return the signed key or object that is authenticated by this
414    /// certificate.
415    pub fn subject_key(&self) -> &CertifiedKey {
416        &self.cert_key
417    }
418
419    /// Return the ed25519 key that signed this certificate.
420    pub fn signing_key(&self) -> Option<&ed25519::Ed25519Identity> {
421        self.signed_with.as_ref()
422    }
423
424    /// Return the type of this certificate.
425    pub fn cert_type(&self) -> CertType {
426        self.cert_type
427    }
428}
429
430/// A parsed Ed25519 certificate. Maybe it includes its signing key;
431/// maybe it doesn't.
432///
433/// To validate this cert, either it must contain its signing key,
434/// or the caller must know the signing key.  In the first case, call
435/// [`should_have_signing_key`](KeyUnknownCert::should_have_signing_key);
436/// in the latter, call
437/// [`should_be_signed_with`](KeyUnknownCert::should_be_signed_with).
438#[derive(Clone, Debug)]
439pub struct KeyUnknownCert {
440    /// The certificate whose signing key might not be known.
441    cert: UncheckedCert,
442}
443
444impl KeyUnknownCert {
445    /// Return the certificate type of the underling cert.
446    pub fn peek_cert_type(&self) -> CertType {
447        self.cert.cert.cert_type
448    }
449    /// Return subject key of the underlying cert.
450    pub fn peek_subject_key(&self) -> &CertifiedKey {
451        &self.cert.cert.cert_key
452    }
453
454    /// Check whether a given pkey is (or might be) a key that has correctly
455    /// signed this certificate.
456    ///
457    /// If pkey is None, this certificate must contain its signing key.
458    ///
459    /// On success, we can check whether the certificate is well-signed;
460    /// otherwise, we can't check the certificate.
461    #[deprecated(
462        since = "0.7.1",
463        note = "Use should_have_signing_key or should_be_signed_with instead."
464    )]
465    pub fn check_key(self, pkey: Option<&ed25519::Ed25519Identity>) -> CertResult<UncheckedCert> {
466        match pkey {
467            Some(wanted) => self.should_be_signed_with(wanted),
468            None => self.should_have_signing_key(),
469        }
470    }
471
472    /// Declare that this should be a self-contained certificate that contains its own
473    /// signing key.
474    ///
475    /// On success, this certificate did indeed turn out to be self-contained, and so
476    /// we can validate it.
477    /// On failure, this certificate was not self-contained.
478    pub fn should_have_signing_key(self) -> CertResult<UncheckedCert> {
479        let real_key = match &self.cert.cert.signed_with {
480            Some(a) => *a,
481            None => return Err(CertError::MissingPubKey),
482        };
483
484        Ok(UncheckedCert {
485            cert: Ed25519Cert {
486                signed_with: Some(real_key),
487                ..self.cert.cert
488            },
489            ..self.cert
490        })
491    }
492
493    /// Declare that this should be a certificate signed with a given key.
494    ///
495    /// On success, this certificate either listed the provided key, or did not
496    /// list any key: in either case, we can validate it.
497    /// On failure, this certificate claims to be signed with a different key.
498    pub fn should_be_signed_with(
499        self,
500        pkey: &ed25519::Ed25519Identity,
501    ) -> CertResult<UncheckedCert> {
502        let real_key = match &self.cert.cert.signed_with {
503            Some(a) if a == pkey => *pkey,
504            None => *pkey,
505            Some(_) => return Err(CertError::KeyMismatch),
506        };
507
508        Ok(UncheckedCert {
509            cert: Ed25519Cert {
510                signed_with: Some(real_key),
511                ..self.cert.cert
512            },
513            ..self.cert
514        })
515    }
516}
517
518/// A certificate that has been parsed, but whose signature and
519/// timeliness have not been checked.
520#[derive(Debug, Clone)]
521pub struct UncheckedCert {
522    /// The parsed certificate, possibly modified by inserting an externally
523    /// supplied key as its signing key.
524    cert: Ed25519Cert,
525
526    /// The signed text of the certificate. (Checking ed25519 signatures
527    /// forces us to store this.
528    // TODO(nickm)  It would be better to store a hash here, but we
529    // don't have the right Ed25519 API.
530    text: Vec<u8>,
531
532    /// The alleged signature
533    signature: ed25519::Signature,
534}
535
536/// A certificate that has been parsed and signature-checked, but whose
537/// timeliness has not been checked.
538pub struct SigCheckedCert {
539    /// The certificate that might or might not be timely
540    cert: Ed25519Cert,
541}
542
543impl UncheckedCert {
544    /// Split this unchecked cert into a component that assumes it has
545    /// been checked, and a signature to validate.
546    pub fn dangerously_split(
547        self,
548    ) -> CertResult<(SigCheckedCert, ed25519::ValidatableEd25519Signature)> {
549        use tor_checkable::SelfSigned;
550        let signing_key = self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
551        let signing_key = signing_key
552            .try_into()
553            .map_err(|_| CertError::BadSignature)?;
554        let signature =
555            ed25519::ValidatableEd25519Signature::new(signing_key, self.signature, &self.text[..]);
556        Ok((self.dangerously_assume_wellsigned(), signature))
557    }
558
559    /// Return subject key of the underlying cert.
560    pub fn peek_subject_key(&self) -> &CertifiedKey {
561        &self.cert.cert_key
562    }
563    /// Return signing key of the underlying cert.
564    pub fn peek_signing_key(&self) -> &ed25519::Ed25519Identity {
565        self.cert
566            .signed_with
567            .as_ref()
568            .expect("Made an UncheckedCert without a signing key")
569    }
570}
571
572impl tor_checkable::SelfSigned<SigCheckedCert> for UncheckedCert {
573    type Error = CertError;
574
575    fn is_well_signed(&self) -> CertResult<()> {
576        let pubkey = &self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
577        let pubkey: ed25519::PublicKey = pubkey.try_into().map_err(|_| CertError::BadSignature)?;
578
579        pubkey
580            .verify(&self.text[..], &self.signature)
581            .map_err(|_| CertError::BadSignature)?;
582
583        Ok(())
584    }
585
586    fn dangerously_assume_wellsigned(self) -> SigCheckedCert {
587        SigCheckedCert { cert: self.cert }
588    }
589}
590
591impl tor_checkable::Timebound<Ed25519Cert> for Ed25519Cert {
592    type Error = tor_checkable::TimeValidityError;
593
594    fn is_valid_at(&self, t: &time::SystemTime) -> Result<(), Self::Error> {
595        if self.is_expired_at(*t) {
596            let expiry = self.expiry();
597            Err(Self::Error::Expired(
598                t.duration_since(expiry)
599                    .expect("certificate expiry time inconsistent"),
600            ))
601        } else {
602            Ok(())
603        }
604    }
605
606    fn dangerously_assume_timely(self) -> Ed25519Cert {
607        self
608    }
609}
610
611impl tor_checkable::Timebound<Ed25519Cert> for SigCheckedCert {
612    type Error = tor_checkable::TimeValidityError;
613    fn is_valid_at(&self, t: &time::SystemTime) -> std::result::Result<(), Self::Error> {
614        self.cert.is_valid_at(t)
615    }
616
617    fn dangerously_assume_timely(self) -> Ed25519Cert {
618        self.cert.dangerously_assume_timely()
619    }
620}
621
622#[cfg(test)]
623mod test {
624    // @@ begin test lint list maintained by maint/add_warning @@
625    #![allow(clippy::bool_assert_comparison)]
626    #![allow(clippy::clone_on_copy)]
627    #![allow(clippy::dbg_macro)]
628    #![allow(clippy::mixed_attributes_style)]
629    #![allow(clippy::print_stderr)]
630    #![allow(clippy::print_stdout)]
631    #![allow(clippy::single_char_pattern)]
632    #![allow(clippy::unwrap_used)]
633    #![allow(clippy::unchecked_time_subtraction)]
634    #![allow(clippy::useless_vec)]
635    #![allow(clippy::needless_pass_by_value)]
636    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
637    use super::*;
638    use hex_literal::hex;
639
640    #[test]
641    fn parse_unrecognized_ext() -> BytesResult<()> {
642        // case one: a flag is set but we don't know it
643        let b = hex!("0009 99 10 657874656e73696f6e");
644        let mut r = Reader::from_slice(&b);
645        let e: CertExt = r.extract()?;
646        r.should_be_exhausted()?;
647
648        assert_eq!(e.ext_id(), 0x99.into());
649
650        // case two: we've been told to ignore the cert if we can't
651        // handle the extension.
652        let b = hex!("0009 99 11 657874656e73696f6e");
653        let mut r = Reader::from_slice(&b);
654        let e: Result<CertExt, BytesError> = r.extract();
655        assert!(e.is_err());
656        assert_eq!(
657            e.err().unwrap(),
658            BytesError::InvalidMessage(
659                "unrecognized certificate extension, with 'affects_validation' flag set.".into()
660            )
661        );
662
663        Ok(())
664    }
665
666    #[test]
667    fn certified_key() -> BytesResult<()> {
668        let b =
669            hex!("4c27616d6f757220756e6974206365757820717527656e636861c3ae6e616974206c6520666572");
670        let mut r = Reader::from_slice(&b);
671
672        let ck = CertifiedKey::from_reader(KeyType::SHA256_OF_RSA, &mut r)?;
673        assert_eq!(ck.as_bytes(), &b[..32]);
674        assert_eq!(ck.key_type(), KeyType::SHA256_OF_RSA);
675        assert_eq!(r.remaining(), 7);
676
677        let mut r = Reader::from_slice(&b);
678        let ck = CertifiedKey::from_reader(42.into(), &mut r)?;
679        assert_eq!(ck.as_bytes(), &b[..32]);
680        assert_eq!(ck.key_type(), 42.into());
681        assert_eq!(r.remaining(), 7);
682
683        Ok(())
684    }
685}