barebones_x509/
lib.rs

1//! # A low-level X.509 parsing and certificate signature verification library.
2//!
3//! barebones-x509 can verify the signatures of X.509 certificates, as well as
4//! certificates made by their private keys.  It can also verify that a
5//! certificate is valid for the given time. However, it is (by design) very
6//! low-level: it does not know about *any* X.509 extensions, and does not parse
7//! distinguished names at all.  It also provides no path-building facilities.
8//! As such, it is not intended for use with the web PKI; use webpki for that.
9//!
10//! barebones-x509’s flexibiity is a double-edged sword: it allows it to be used
11//! in situations where webpki cannot be used, but it also makes it
12//! significantly more dangerous.  As a general rule, barebones-x509 will accept
13//! any certificate that webpki will, but it will also accept certificates that
14//! webpki will reject.  If you find a certificate that barebones-x509 rejects
15//! and webpki rejects, please report it as a bug.
16//!
17//! barebones-x509 was developed for use with
18//! [libp2p](https://github.com/libp2p), which uses certificates that webpki
19//! cannot handle.  Its bare-bones design ensures that it can handle almost any
20//! conforming X.509 certificate, but it also means that the application is
21//! responsible for ensuring that the certificate has valid X.509 extensions.
22//! barebones-x509 cannot distinguish between a certificate valid for
23//! `mozilla.org` and one for `evilmalware.com`!  However, barebones-x509
24//! does provide the hooks needed for higher-level libraries to be built on top
25//! of it.
26//!
27//! Like webpki, barebones-x509 is zero-copy and `#![no_std]` friendly.  If
28//! built without the `alloc` feature, barebones-x509 will not rely on features
29//! of *ring* that require heap allocation, specifically RSA.
30//!
31//! barebones-x509 should never panic on any input, regardless of its
32//! configuration options.  If it does panic, it is considered a security
33//! vulnerability and will be fixed with the highest priority.
34//!
35//! ## Features
36//!
37//! `barebones-x509` is highly configurable by means of compile-time options.
38//! Code that is not used by most users is off by default and must be enabled by
39//! means of a cargo feature.  This reduces the attack surface of normal builds.
40//!
41//! The following features are available:
42//!
43//! - `legacy-certificates`: Allows parsing legacy v1 and v2 certificates. This
44//!   is off by default.
45//! - `obsolete-unique-ids`: Allows parsing certificates containing the obsolete
46//!   `subjectUniqueId` and `issuerUniqueId` fields.  This is off by default.
47//!   The `subjectUniqueId` and `issuerUniqueId` fields available as the
48//!   `unique_id` field on the [`X509Certificate`] struct.  This feature is made
49//!   available so that `barebones-x509` can claim to be able to parse any valid
50//!   X.509 certificate.  If you do need to enable it, please e-mail me at
51//!   <demiobenour@gmail.com> explaining the reason.
52
53#![cfg_attr(not(any(test, feature = "std")), no_std)]
54#![deny(
55    const_err,
56    deprecated,
57    improper_ctypes,
58    non_shorthand_field_patterns,
59    nonstandard_style,
60    no_mangle_generic_items,
61    unknown_lints,
62    type_alias_bounds,
63    missing_copy_implementations,
64    missing_debug_implementations,
65    missing_docs,
66    single_use_lifetimes,
67    trivial_casts,
68    trivial_numeric_casts,
69    rust_2018_idioms,
70    unused,
71    future_incompatible,
72    clippy::all
73)]
74#![forbid(
75    unconditional_recursion,
76    unsafe_code,
77    broken_intra_doc_links,
78    while_true,
79    elided_lifetimes_in_paths
80)]
81#![cfg_attr(docsrs, feature(doc_cfg))]
82
83mod das;
84mod sequence;
85mod time;
86use ring::io::der;
87mod spki;
88pub use das::DataAlgorithmSignature;
89pub use sequence::{ExtensionIterator, SequenceIterator};
90pub use spki::{parse_algorithmid, Restrictions, SubjectPublicKeyInfo};
91
92pub use time::{days_from_ymd, seconds_from_hms, ASN1Time, MAX_ASN1_TIMESTAMP, MIN_ASN1_TIMESTAMP};
93
94#[cfg(feature = "rustls")]
95pub use r::SignatureScheme;
96
97/// A signature scheme supported by this library
98#[cfg(not(feature = "rustls"))]
99#[non_exhaustive]
100#[allow(non_camel_case_types)]
101#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
102pub enum SignatureScheme {
103    /// RSA PKCS#1 signatures with SHA256
104    RSA_PKCS1_SHA256,
105    /// RSA PKCS#1 signatures with SHA384
106    RSA_PKCS1_SHA384,
107    /// RSA PKCS#1 signatures with SHA512
108    RSA_PKCS1_SHA512,
109    /// ECDSA signatures with SHA256
110    ECDSA_NISTP256_SHA256,
111    /// ECDSA signatures with SHA384
112    ECDSA_NISTP384_SHA384,
113    /// ed25519 signatures
114    ED25519,
115    /// RSA-PSS signatures with SHA256
116    RSA_PSS_SHA256,
117    /// RSA-PSS signatures with SHA384
118    RSA_PSS_SHA384,
119    /// RSA-PSS signatures with SHA512
120    RSA_PSS_SHA512,
121    /// ed448 signatures
122    ED448,
123}
124
125/// Errors that can be produced when parsing a certificate or validating a
126/// signature.
127///
128/// More errors may be added in the future.
129#[cfg(not(feature = "webpki"))]
130#[cfg_attr(docsrs, doc(cfg(not(feature = "webpki"))))]
131#[non_exhaustive]
132#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
133pub enum Error {
134    /// Version is not valid.  Without the `legacy-certificates` feature, only
135    /// X.509 v3 certificates are supported.  If the `legacy-certificates`
136    /// feature is enabled, v1 and v2 certificates are also supported.
137    UnsupportedCertVersion,
138    /// Signature algorithm unsupported
139    UnsupportedSignatureAlgorithm,
140    /// Signature algorithm isn’t valid for the public key
141    UnsupportedSignatureAlgorithmForPublicKey,
142    /// Signature forged!
143    InvalidSignatureForPublicKey,
144    /// Signature algorithms don’t match
145    SignatureAlgorithmMismatch,
146    /// Invalid DER.  This will also result if the `legacy-certificates` feature
147    /// is disabled, but one of the (obsolete and virtually unused)
148    /// subjectUniqueId and issuerUniqueId fields are present.  Even if the
149    /// `legacy-certificates` feature is enabled, these fields will not
150    /// appear in the parsed certificate.
151    BadDER,
152    /// Invalid DER time
153    BadDERTime,
154    /// Certificate isn’t valid yet
155    CertNotValidYet,
156    /// Certificate has expired
157    CertExpired,
158    /// Certificate expired before beginning to be valid
159    InvalidCertValidity,
160    /// The issuer is not known.
161    UnknownIssuer,
162}
163
164/// X509 certificate version
165#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
166#[repr(u8)]
167pub enum Version {
168    /// Version 1
169    V1 = 0,
170    /// Version 2
171    V2 = 1,
172    /// Version 3
173    V3 = 2,
174}
175
176#[cfg(feature = "webpki")]
177pub use w::Error;
178
179/// A parsed (but not validated) X.509 version 3 certificate.
180#[derive(Debug)]
181pub struct X509Certificate<'a> {
182    das: DataAlgorithmSignature<'a>,
183    serial: &'a [u8],
184    issuer: &'a [u8],
185    not_before: ASN1Time,
186    not_after: ASN1Time,
187    subject: &'a [u8],
188    subject_public_key_info: SubjectPublicKeyInfo<'a>,
189    #[cfg(feature = "obsolete-unique-ids")]
190    issuer_unique_id: Option<untrusted::Input<'a>>,
191    #[cfg(feature = "obsolete-unique-ids")]
192    subject_unique_id: Option<untrusted::Input<'a>>,
193    extensions: ExtensionIterator<'a>,
194}
195
196impl<'a> X509Certificate<'a> {
197    /// The tbsCertificate, signatureAlgorithm, and signature
198    pub fn das(&self) -> DataAlgorithmSignature<'a> { self.das }
199
200    /// The serial number. Big-endian and non-empty. The first byte is
201    /// guaranteed to be non-zero.
202    pub fn serial(&self) -> &'a [u8] { self.serial }
203
204    /// The X.509 issuer. This has not been validated and is not trusted. In
205    /// particular, it is not guaranteed to be valid ASN.1 DER.
206    pub fn issuer(&self) -> &'a [u8] { self.issuer }
207
208    /// The earliest time, in seconds since the Unix epoch, that the certificate
209    /// is valid.
210    ///
211    /// Will always be between [`MIN_ASN1_TIMESTAMP`] and
212    /// [`MAX_ASN1_TIMESTAMP`], inclusive.
213    pub fn not_before(&self) -> ASN1Time { self.not_before }
214
215    /// The latest time, in seconds since the Unix epoch, that the certificate
216    /// is valid.
217    ///
218    /// Will always be between [`MIN_ASN1_TIMESTAMP`] and
219    /// [`MAX_ASN1_TIMESTAMP`], inclusive.
220    pub fn not_after(&self) -> ASN1Time { self.not_after }
221
222    /// X.509 subject. This has not been validated and is not trusted. In
223    /// particular, it is not guaranteed to be valid ASN.1 DER.
224    pub fn subject(&self) -> &'a [u8] { self.subject }
225
226    /// The subjectPublicKeyInfo, encoded as ASN.1 DER. There is no guarantee
227    /// that the OID or public key are valid ASN.1 DER, but if they are not,
228    /// all methods that check signatures will fail.
229    pub fn subject_public_key_info(&self) -> SubjectPublicKeyInfo<'a> {
230        self.subject_public_key_info
231    }
232
233    /// An iterator over the certificate’s extensions.
234    pub fn extensions(&self) -> ExtensionIterator<'a> { self.extensions }
235
236    /// Verify a signature made by the certificate.
237    pub fn check_signature(
238        &self, algorithm: SignatureScheme, message: &[u8], signature: &[u8],
239    ) -> Result<(), Error> {
240        self.subject_public_key_info.check_signature(
241            algorithm,
242            message,
243            signature,
244            Restrictions::None,
245        )
246    }
247
248    /// Retrieve the `issuerUniqueId` field of the certificate.
249    ///
250    /// This field is obsolete, so it will virtually always be `None`.  The data
251    /// is returned as an ASN.1 encoded BIT STRING.
252    #[cfg(feature = "obsolete-unique-ids")]
253    #[cfg_attr(docsrs, doc(cfg(feature = "obsolete-unique-ids")))]
254    pub fn issuer_unique_id(&self) -> Option<untrusted::Input<'a>> { self.issuer_unique_id }
255
256    /// Retrieve the `subjectUniqueId` field of the certificate.
257    ///
258    /// This field is obsolete, so it will virtually always be `None`.  The data
259    /// is returned as an ASN.1 encoded BIT STRING.
260    #[cfg(feature = "obsolete-unique-ids")]
261    #[cfg_attr(docsrs, doc(cfg(feature = "obsolete-unique-ids")))]
262    pub fn subject_unique_id(&self) -> Option<untrusted::Input<'a>> { self.subject_unique_id }
263
264    /// Verify a signature made by the certificate, applying the restrictions of
265    /// TLSv1.3:
266    ///
267    /// * ECDSA algorithms where the hash has a different size than the curve
268    ///   are not allowed.
269    /// * RSA PKCS1.5 signatures are not allowed.
270    ///
271    /// This is a good choice for new protocols and applications. Note that
272    /// extensions are not checked, so applications must process extensions
273    /// themselves.
274    pub fn check_tls13_signature(
275        &self, algorithm: SignatureScheme, message: &[u8], signature: &[u8],
276    ) -> Result<(), Error> {
277        self.subject_public_key_info.check_signature(
278            algorithm,
279            message,
280            signature,
281            Restrictions::TLSv13,
282        )
283    }
284
285    /// Verify a signature made by the certificate, applying the restrictions of
286    /// TLSv1.2:
287    ///
288    /// * RSA-PSS signatures are not allowed.
289    ///
290    /// This should not be used outside of a TLSv1.2 implementation. Note that
291    /// extensions are not checked, so applications must process extensions
292    /// themselves.
293    pub fn check_tls12_signature(
294        &self, algorithm: SignatureScheme, message: &[u8], signature: &[u8],
295    ) -> Result<(), Error> {
296        self.subject_public_key_info.check_signature(
297            algorithm,
298            message,
299            signature,
300            Restrictions::TLSv12,
301        )
302    }
303
304    /// Check that the certificate is valid at time `now`, in seconds since the
305    /// Epoch.
306    pub fn valid_at_timestamp(&self, now: i64) -> Result<(), Error> {
307        if now < self.not_before.into() {
308            Err(Error::CertNotValidYet)
309        } else if now > self.not_after.into() {
310            Err(Error::CertExpired)
311        } else {
312            Ok(())
313        }
314    }
315
316    /// Check if a certificate is currently valid.
317    #[cfg(feature = "std")]
318    pub fn valid(&self) -> Result<(), Error> { self.valid_at_timestamp(ASN1Time::now()?.into()) }
319
320    /// The tbsCertficate
321    pub fn tbs_certificate(&self) -> &[u8] { self.das.data() }
322
323    /// The `AlgorithmId` of the algorithm used to sign this certificate
324    pub fn signature_algorithm_id(&self) -> &[u8] { self.das.algorithm() }
325
326    /// The signature of the certificate
327    pub fn signature(&self) -> &[u8] { self.das.signature() }
328
329    /// Verify that this certificate was signed by `cert`’s secret key.
330    ///
331    /// This does not check that `cert` is a certificate authority.
332    pub fn check_signature_from(&self, cert: &X509Certificate<'_>) -> Result<(), Error> {
333        cert.check_signature(
334            parse_algorithmid(self.signature_algorithm_id())?,
335            self.tbs_certificate(),
336            self.signature(),
337        )
338    }
339
340    /// As above, but also check that `self`’s issuer is `cert`’s subject.
341    pub fn check_issued_by(&self, cert: &X509Certificate<'_>) -> Result<(), Error> {
342        if self.issuer != cert.subject {
343            return Err(Error::UnknownIssuer);
344        }
345        self.check_signature_from(cert)
346    }
347
348    /// Check that this certificate is self-signed. This does not check that the
349    /// subject and issuer are equal.
350    #[deprecated(since = "0.3.3", note = "Use check_self_issued instead")]
351    pub fn check_self_signature(&self) -> Result<(), Error> { self.check_signature_from(self) }
352
353    /// Check that this certificate is self-signed, and that the subject and
354    /// issuer are equal.
355    pub fn check_self_issued(&self) -> Result<(), Error> { self.check_issued_by(self) }
356}
357
358fn parse_input<'a>(
359    input: &mut untrusted::Reader<'a>, das: DataAlgorithmSignature<'a>,
360) -> Result<X509Certificate<'a>, Error> {
361    #[cfg(feature = "obsolete-unique-ids")]
362    const CONTEXT_SPECIFIC_PRIMITIVE_1: u8 = der::CONTEXT_SPECIFIC | 1;
363    #[cfg(feature = "obsolete-unique-ids")]
364    const CONTEXT_SPECIFIC_PRIMITIVE_2: u8 = der::CONTEXT_SPECIFIC | 2;
365    const CONTEXT_SPECIFIC_CONSTRUCTED_3: u8 = der::Tag::ContextSpecificConstructed3 as _;
366    #[cfg(not(feature = "legacy-certificates"))]
367    if input.read_bytes(5).map_err(|_| Error::BadDER)? != untrusted::Input::from(&[160, 3, 2, 1, 2])
368    {
369        return Err(Error::UnsupportedCertVersion);
370    }
371    #[cfg(not(feature = "legacy-certificates"))]
372    let version = Version::V3;
373    #[cfg(feature = "legacy-certificates")]
374    let version = if input.peek(160) {
375        match *input
376            .read_bytes(5)
377            .map_err(|_| Error::BadDER)?
378            .as_slice_less_safe()
379        {
380            [160, 3, 2, 1, 2] => Version::V3,
381            [160, 3, 2, 1, 1] => Version::V2,
382            [160, 3, 2, _, _] => return Err(Error::UnsupportedCertVersion),
383            _ => return Err(Error::BadDER),
384        }
385    } else {
386        Version::V1
387    };
388    // serialNumber
389    let serial = der::positive_integer(input)
390        .map_err(|_| Error::BadDER)?
391        .big_endian_without_leading_zero();
392    // signature
393    if das::read_sequence(input)?.as_slice_less_safe() != das.algorithm() {
394        // signature algorithms don’t match
395        return Err(Error::SignatureAlgorithmMismatch);
396    }
397    // issuer
398    let issuer = das::read_sequence(input)?.as_slice_less_safe();
399    // validity
400    let (not_before, not_after) = der::nested(input, der::Tag::Sequence, Error::BadDER, |input| {
401        Ok((time::read_time(input)?, time::read_time(input)?))
402    })?;
403    if not_before > not_after {
404        return Err(Error::InvalidCertValidity);
405    }
406    let subject = das::read_sequence(input)?.as_slice_less_safe();
407    let subject_public_key_info = SubjectPublicKeyInfo::read(input)?;
408    let mut extensions = None;
409    #[cfg(feature = "obsolete-unique-ids")]
410    let mut last_tag = 0;
411    #[cfg(feature = "obsolete-unique-ids")]
412    let mut unique_ids = [None; 2];
413    #[cfg_attr(not(feature = "obsolete-unique-ids"), allow(clippy::never_loop))]
414    while !input.at_end() {
415        let (tag, value) = der::read_tag_and_get_value(input).map_err(|_| Error::BadDER)?;
416        #[cfg(feature = "obsolete-unique-ids")]
417        if tag <= last_tag {
418            return Err(Error::BadDER);
419        } else {
420            last_tag = tag;
421        }
422        match tag {
423            #[cfg(feature = "obsolete-unique-ids")]
424            CONTEXT_SPECIFIC_PRIMITIVE_1 | CONTEXT_SPECIFIC_PRIMITIVE_2
425                if version >= Version::V2 =>
426            {
427                match *value.as_slice_less_safe() {
428                    [0, ..] => {},
429                    [unused_bits, .., last]
430                        if unused_bits < 8 && last.trailing_zeros() >= unused_bits.into() => {},
431                    _ => return Err(Error::BadDER),
432                }
433                unique_ids[usize::from(tag - CONTEXT_SPECIFIC_PRIMITIVE_1)] = Some(value)
434            }
435            CONTEXT_SPECIFIC_CONSTRUCTED_3 if version >= Version::V3 => {
436                let extension_data = value.read_all(Error::BadDER, das::read_sequence)?;
437                if extension_data.as_slice_less_safe().is_empty() {
438                    return Err(Error::BadDER);
439                }
440                extensions = Some(extension_data);
441                break;
442            },
443            _ => return Err(Error::BadDER),
444        }
445    }
446
447    let extensions = ExtensionIterator(SequenceIterator::read(&mut untrusted::Reader::new(
448        extensions.unwrap_or_else(|| untrusted::Input::from(b"")),
449    )));
450
451    Ok(X509Certificate {
452        das,
453        serial,
454        subject,
455        not_before,
456        not_after,
457        issuer,
458        subject_public_key_info,
459        #[cfg(feature = "obsolete-unique-ids")]
460        issuer_unique_id: unique_ids[0],
461        #[cfg(feature = "obsolete-unique-ids")]
462        subject_unique_id: unique_ids[1],
463        extensions,
464    })
465}
466
467/// Extracts the algorithm id and public key from a certificate
468pub fn parse_certificate(certificate: &[u8]) -> Result<X509Certificate<'_>, Error> {
469    use core::convert::TryFrom as _;
470    let das = DataAlgorithmSignature::try_from(certificate)?;
471    untrusted::Input::from(&*das.inner()).read_all(Error::BadDER, |i| parse_input(i, das))
472}
473
474#[cfg(test)]
475mod tests {
476    use super::*;
477    #[test]
478    fn parses_openssl_generated_cert() {
479        let signature = include_bytes!("../testing.sig");
480        let invalid_signature = include_bytes!("../testing.bad-sig");
481        let forged_message = include_bytes!("../forged-message.txt");
482        let message = include_bytes!("../gen-bad-cert.sh");
483        let certificate = include_bytes!("../testing.crt");
484        #[cfg(feature = "rsa")]
485        let ca_certificate = include_bytes!("../ca.crt");
486
487        let cert = parse_certificate(certificate).unwrap();
488        #[cfg(feature = "rsa")]
489        let ca_cert = parse_certificate(ca_certificate).unwrap();
490        assert_eq!(
491            cert.subject_public_key_info.algorithm(),
492            include_bytes!("data/alg-ecdsa-p256.der")
493        );
494        assert_eq!(cert.subject_public_key_info.key().len(), 65);
495        cert.valid_at_timestamp(1587492766).unwrap();
496        assert_eq!(cert.valid_at_timestamp(0), Err(Error::CertNotValidYet));
497        assert_eq!(
498            cert.valid_at_timestamp(i64::max_value()),
499            Err(Error::CertExpired)
500        );
501
502        cert.check_signature(SignatureScheme::ECDSA_NISTP256_SHA256, message, signature)
503            .expect("OpenSSL generates syntactically valid certificates");
504        assert_eq!(
505            cert.check_signature(
506                SignatureScheme::ECDSA_NISTP256_SHA256,
507                message,
508                invalid_signature,
509            )
510            .expect_err("corrupting a signature invalidates it"),
511            Error::InvalidSignatureForPublicKey
512        );
513        assert_eq!(
514            cert.check_signature(
515                SignatureScheme::ECDSA_NISTP256_SHA256,
516                message,
517                invalid_signature,
518            )
519            .expect_err("corrupting a message invalidates it"),
520            Error::InvalidSignatureForPublicKey
521        );
522        assert_eq!(
523            cert.check_signature(
524                SignatureScheme::ECDSA_NISTP256_SHA256,
525                forged_message,
526                signature,
527            )
528            .expect_err("forgery undetected?"),
529            Error::InvalidSignatureForPublicKey
530        );
531        #[cfg(feature = "rsa")]
532        ca_cert.check_self_issued().unwrap();
533        #[cfg(feature = "rsa")]
534        cert.check_issued_by(&ca_cert).unwrap();
535        let mut extensions = vec![];
536        cert.extensions()
537            .iterate(&mut |oid, critical, e| {
538                Ok(extensions.push((oid, critical, e.as_slice_less_safe())))
539            })
540            .unwrap();
541        assert_eq!(extensions.len(), 6, "wrong number of extensions");
542        assert_eq!(extensions[0], (&[85, 29, 19][..], true, &b"\x30\0"[..]));
543        assert_eq!(extensions[1].0, &[85, 29, 14][..]);
544        assert!(!extensions[1].1);
545        assert_eq!(extensions[1].2.len(), 22);
546        assert_eq!(extensions[1].2[..2], b"\x04\x14"[..]);
547    }
548}