1#![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#[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_PKCS1_SHA256,
105 RSA_PKCS1_SHA384,
107 RSA_PKCS1_SHA512,
109 ECDSA_NISTP256_SHA256,
111 ECDSA_NISTP384_SHA384,
113 ED25519,
115 RSA_PSS_SHA256,
117 RSA_PSS_SHA384,
119 RSA_PSS_SHA512,
121 ED448,
123}
124
125#[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 UnsupportedCertVersion,
138 UnsupportedSignatureAlgorithm,
140 UnsupportedSignatureAlgorithmForPublicKey,
142 InvalidSignatureForPublicKey,
144 SignatureAlgorithmMismatch,
146 BadDER,
152 BadDERTime,
154 CertNotValidYet,
156 CertExpired,
158 InvalidCertValidity,
160 UnknownIssuer,
162}
163
164#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
166#[repr(u8)]
167pub enum Version {
168 V1 = 0,
170 V2 = 1,
172 V3 = 2,
174}
175
176#[cfg(feature = "webpki")]
177pub use w::Error;
178
179#[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 pub fn das(&self) -> DataAlgorithmSignature<'a> { self.das }
199
200 pub fn serial(&self) -> &'a [u8] { self.serial }
203
204 pub fn issuer(&self) -> &'a [u8] { self.issuer }
207
208 pub fn not_before(&self) -> ASN1Time { self.not_before }
214
215 pub fn not_after(&self) -> ASN1Time { self.not_after }
221
222 pub fn subject(&self) -> &'a [u8] { self.subject }
225
226 pub fn subject_public_key_info(&self) -> SubjectPublicKeyInfo<'a> {
230 self.subject_public_key_info
231 }
232
233 pub fn extensions(&self) -> ExtensionIterator<'a> { self.extensions }
235
236 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 #[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 #[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 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 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 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 #[cfg(feature = "std")]
318 pub fn valid(&self) -> Result<(), Error> { self.valid_at_timestamp(ASN1Time::now()?.into()) }
319
320 pub fn tbs_certificate(&self) -> &[u8] { self.das.data() }
322
323 pub fn signature_algorithm_id(&self) -> &[u8] { self.das.algorithm() }
325
326 pub fn signature(&self) -> &[u8] { self.das.signature() }
328
329 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 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 #[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 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 let serial = der::positive_integer(input)
390 .map_err(|_| Error::BadDER)?
391 .big_endian_without_leading_zero();
392 if das::read_sequence(input)?.as_slice_less_safe() != das.algorithm() {
394 return Err(Error::SignatureAlgorithmMismatch);
396 }
397 let issuer = das::read_sequence(input)?.as_slice_less_safe();
399 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
467pub 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}