rcgen/
lib.rs

1/*!
2Rust X.509 certificate generation utility
3
4This crate provides a way to generate self signed X.509 certificates.
5
6The most simple way of using this crate is by calling the
7[`generate_simple_self_signed`] function.
8For more customization abilities, construct a [`CertificateParams`] and
9a key pair to call [`CertificateParams::signed_by()`] or [`CertificateParams::self_signed()`].
10*/
11#![cfg_attr(
12	feature = "pem",
13	doc = r##"
14## Example
15
16```
17use rcgen::{generate_simple_self_signed, CertifiedKey};
18# fn main () {
19// Generate a certificate that's valid for "localhost" and "hello.world.example"
20let subject_alt_names = vec!["hello.world.example".to_string(),
21	"localhost".to_string()];
22
23let CertifiedKey { cert, key_pair } = generate_simple_self_signed(subject_alt_names).unwrap();
24println!("{}", cert.pem());
25println!("{}", key_pair.serialize_pem());
26# }
27```"##
28)]
29#![forbid(unsafe_code)]
30#![forbid(non_ascii_idents)]
31#![deny(missing_docs)]
32#![allow(clippy::complexity, clippy::style, clippy::pedantic)]
33#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
34#![warn(unreachable_pub)]
35
36use std::collections::HashMap;
37use std::fmt;
38use std::hash::Hash;
39use std::net::IpAddr;
40#[cfg(feature = "x509-parser")]
41use std::net::{Ipv4Addr, Ipv6Addr};
42
43use time::{OffsetDateTime, Time};
44use yasna::models::ObjectIdentifier;
45use yasna::models::{GeneralizedTime, UTCTime};
46use yasna::tags::{TAG_BMPSTRING, TAG_TELETEXSTRING, TAG_UNIVERSALSTRING};
47use yasna::DERWriter;
48use yasna::Tag;
49
50pub use certificate::{
51	date_time_ymd, Attribute, BasicConstraints, Certificate, CertificateParams, CidrSubnet,
52	CustomExtension, DnType, ExtendedKeyUsagePurpose, GeneralSubtree, IsCa, NameConstraints,
53};
54pub use crl::{
55	CertificateRevocationList, CertificateRevocationListParams, CrlDistributionPoint,
56	CrlIssuingDistributionPoint, CrlScope, RevocationReason, RevokedCertParams,
57};
58pub use csr::{CertificateSigningRequest, CertificateSigningRequestParams, PublicKey};
59pub use error::{Error, InvalidAsn1String};
60pub use key_pair::PublicKeyData;
61#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))]
62pub use key_pair::RsaKeySize;
63pub use key_pair::{KeyPair, RemoteKeyPair, SubjectPublicKeyInfo};
64#[cfg(feature = "crypto")]
65use ring_like::digest;
66pub use sign_algo::algo::*;
67pub use sign_algo::SignatureAlgorithm;
68pub use string_types::*;
69
70mod certificate;
71mod crl;
72mod csr;
73mod error;
74mod key_pair;
75mod oid;
76mod ring_like;
77mod sign_algo;
78mod string_types;
79
80/// Type-alias for the old name of [`Error`].
81#[deprecated(
82	note = "Renamed to `Error`. We recommend to refer to it by fully-qualifying the crate: `rcgen::Error`."
83)]
84pub type RcgenError = Error;
85
86/// An issued certificate, together with the subject keypair.
87pub struct CertifiedKey {
88	/// An issued certificate.
89	pub cert: Certificate,
90	/// The certificate's subject key pair.
91	pub key_pair: KeyPair,
92}
93
94/**
95KISS function to generate a self signed certificate
96
97Given a set of domain names you want your certificate to be valid for,
98this function fills in the other generation parameters with
99reasonable defaults and generates a self signed certificate
100and key pair as output.
101*/
102#[cfg(feature = "crypto")]
103#[cfg_attr(
104	feature = "pem",
105	doc = r##"
106## Example
107
108```
109use rcgen::{generate_simple_self_signed, CertifiedKey};
110# fn main () {
111// Generate a certificate that's valid for "localhost" and "hello.world.example"
112let subject_alt_names = vec!["hello.world.example".to_string(),
113	"localhost".to_string()];
114
115let CertifiedKey { cert, key_pair } = generate_simple_self_signed(subject_alt_names).unwrap();
116
117// The certificate is now valid for localhost and the domain "hello.world.example"
118println!("{}", cert.pem());
119println!("{}", key_pair.serialize_pem());
120# }
121```
122"##
123)]
124pub fn generate_simple_self_signed(
125	subject_alt_names: impl Into<Vec<String>>,
126) -> Result<CertifiedKey, Error> {
127	let key_pair = KeyPair::generate()?;
128	let cert = CertificateParams::new(subject_alt_names)?.self_signed(&key_pair)?;
129	Ok(CertifiedKey { cert, key_pair })
130}
131
132struct Issuer<'a> {
133	distinguished_name: &'a DistinguishedName,
134	key_identifier_method: &'a KeyIdMethod,
135	key_usages: &'a [KeyUsagePurpose],
136	key_pair: &'a KeyPair,
137}
138
139// https://tools.ietf.org/html/rfc5280#section-4.1.1
140
141// Example certs usable as reference:
142// Uses ECDSA: https://crt.sh/?asn1=607203242
143
144#[cfg(feature = "pem")]
145const ENCODE_CONFIG: pem::EncodeConfig = {
146	let line_ending = match cfg!(target_family = "windows") {
147		true => pem::LineEnding::CRLF,
148		false => pem::LineEnding::LF,
149	};
150	pem::EncodeConfig::new().set_line_ending(line_ending)
151};
152
153#[derive(Debug, PartialEq, Eq, Hash, Clone)]
154#[allow(missing_docs)]
155#[non_exhaustive]
156/// The type of subject alt name
157pub enum SanType {
158	/// Also known as E-Mail address
159	Rfc822Name(Ia5String),
160	DnsName(Ia5String),
161	URI(Ia5String),
162	IpAddress(IpAddr),
163	OtherName((Vec<u64>, OtherNameValue)),
164}
165
166/// An `OtherName` value, defined in [RFC 5280§4.1.2.4].
167///
168/// While the standard specifies this could be any ASN.1 type rcgen limits
169/// the value to a UTF-8 encoded string as this will cover the most common
170/// use cases, for instance smart card user principal names (UPN).
171///
172/// [RFC 5280§4.1.2.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4
173#[derive(Debug, PartialEq, Eq, Hash, Clone)]
174#[non_exhaustive]
175pub enum OtherNameValue {
176	/// A string encoded using UTF-8
177	Utf8String(String),
178}
179
180impl OtherNameValue {
181	fn write_der(&self, writer: DERWriter) {
182		writer.write_tagged(Tag::context(0), |writer| match self {
183			OtherNameValue::Utf8String(s) => writer.write_utf8_string(s),
184		});
185	}
186}
187
188impl<T> From<T> for OtherNameValue
189where
190	T: Into<String>,
191{
192	fn from(t: T) -> Self {
193		OtherNameValue::Utf8String(t.into())
194	}
195}
196
197#[cfg(feature = "x509-parser")]
198fn ip_addr_from_octets(octets: &[u8]) -> Result<IpAddr, Error> {
199	if let Ok(ipv6_octets) = <&[u8; 16]>::try_from(octets) {
200		Ok(Ipv6Addr::from(*ipv6_octets).into())
201	} else if let Ok(ipv4_octets) = <&[u8; 4]>::try_from(octets) {
202		Ok(Ipv4Addr::from(*ipv4_octets).into())
203	} else {
204		Err(Error::InvalidIpAddressOctetLength(octets.len()))
205	}
206}
207
208impl SanType {
209	#[cfg(feature = "x509-parser")]
210	fn try_from_general(name: &x509_parser::extensions::GeneralName<'_>) -> Result<Self, Error> {
211		use x509_parser::der_parser::asn1_rs::{self, FromDer, Tag, TaggedExplicit};
212		Ok(match name {
213			x509_parser::extensions::GeneralName::RFC822Name(name) => {
214				SanType::Rfc822Name((*name).try_into()?)
215			},
216			x509_parser::extensions::GeneralName::DNSName(name) => {
217				SanType::DnsName((*name).try_into()?)
218			},
219			x509_parser::extensions::GeneralName::URI(name) => SanType::URI((*name).try_into()?),
220			x509_parser::extensions::GeneralName::IPAddress(octets) => {
221				SanType::IpAddress(ip_addr_from_octets(octets)?)
222			},
223			x509_parser::extensions::GeneralName::OtherName(oid, value) => {
224				let oid = oid.iter().ok_or(Error::CouldNotParseCertificate)?;
225				// We first remove the explicit tag ([0] EXPLICIT)
226				let (_, other_name) = TaggedExplicit::<asn1_rs::Any, _, 0>::from_der(value)
227					.map_err(|_| Error::CouldNotParseCertificate)?;
228				let other_name = other_name.into_inner();
229
230				let other_name_value = match other_name.tag() {
231					Tag::Utf8String => OtherNameValue::Utf8String(
232						std::str::from_utf8(other_name.data)
233							.map_err(|_| Error::CouldNotParseCertificate)?
234							.to_owned(),
235					),
236					_ => return Err(Error::CouldNotParseCertificate),
237				};
238				SanType::OtherName((oid.collect(), other_name_value))
239			},
240			_ => return Err(Error::InvalidNameType),
241		})
242	}
243
244	fn tag(&self) -> u64 {
245		// Defined in the GeneralName list in
246		// https://tools.ietf.org/html/rfc5280#page-38
247		const TAG_OTHER_NAME: u64 = 0;
248		const TAG_RFC822_NAME: u64 = 1;
249		const TAG_DNS_NAME: u64 = 2;
250		const TAG_URI: u64 = 6;
251		const TAG_IP_ADDRESS: u64 = 7;
252
253		match self {
254			SanType::Rfc822Name(_name) => TAG_RFC822_NAME,
255			SanType::DnsName(_name) => TAG_DNS_NAME,
256			SanType::URI(_name) => TAG_URI,
257			SanType::IpAddress(_addr) => TAG_IP_ADDRESS,
258			Self::OtherName(_oid) => TAG_OTHER_NAME,
259		}
260	}
261}
262
263/// A distinguished name entry
264#[derive(Debug, PartialEq, Eq, Hash, Clone)]
265#[non_exhaustive]
266pub enum DnValue {
267	/// A string encoded using UCS-2
268	BmpString(BmpString),
269	/// An ASCII string.
270	Ia5String(Ia5String),
271	/// An ASCII string containing only A-Z, a-z, 0-9, '()+,-./:=? and `<SPACE>`
272	PrintableString(PrintableString),
273	/// A string of characters from the T.61 character set
274	TeletexString(TeletexString),
275	/// A string encoded using UTF-32
276	UniversalString(UniversalString),
277	/// A string encoded using UTF-8
278	Utf8String(String),
279}
280
281impl<T> From<T> for DnValue
282where
283	T: Into<String>,
284{
285	fn from(t: T) -> Self {
286		DnValue::Utf8String(t.into())
287	}
288}
289
290#[derive(Debug, Default, PartialEq, Eq, Clone)]
291/**
292Distinguished name used e.g. for the issuer and subject fields of a certificate
293
294A distinguished name is a set of (attribute type, attribute value) tuples.
295
296This datastructure keeps them ordered by insertion order.
297
298See also the RFC 5280 sections on the [issuer](https://tools.ietf.org/html/rfc5280#section-4.1.2.4)
299and [subject](https://tools.ietf.org/html/rfc5280#section-4.1.2.6) fields.
300*/
301pub struct DistinguishedName {
302	entries: HashMap<DnType, DnValue>,
303	order: Vec<DnType>,
304}
305
306impl DistinguishedName {
307	/// Creates a new, empty distinguished name
308	pub fn new() -> Self {
309		Self::default()
310	}
311	/// Obtains the attribute value for the given attribute type
312	pub fn get(&self, ty: &DnType) -> Option<&DnValue> {
313		self.entries.get(ty)
314	}
315	/// Removes the attribute with the specified DnType
316	///
317	/// Returns true when an actual removal happened, false
318	/// when no attribute with the specified DnType was
319	/// found.
320	pub fn remove(&mut self, ty: DnType) -> bool {
321		let removed = self.entries.remove(&ty).is_some();
322		if removed {
323			self.order.retain(|ty_o| &ty != ty_o);
324		}
325		removed
326	}
327	/// Inserts or updates an attribute that consists of type and name
328	///
329	/// ```
330	/// # use rcgen::{DistinguishedName, DnType, DnValue};
331	/// let mut dn = DistinguishedName::new();
332	/// dn.push(DnType::OrganizationName, "Crab widgits SE");
333	/// dn.push(DnType::CommonName, DnValue::PrintableString("Master Cert".try_into().unwrap()));
334	/// assert_eq!(dn.get(&DnType::OrganizationName), Some(&DnValue::Utf8String("Crab widgits SE".to_string())));
335	/// assert_eq!(dn.get(&DnType::CommonName), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())));
336	/// ```
337	pub fn push(&mut self, ty: DnType, s: impl Into<DnValue>) {
338		if !self.entries.contains_key(&ty) {
339			self.order.push(ty.clone());
340		}
341		self.entries.insert(ty, s.into());
342	}
343	/// Iterate over the entries
344	pub fn iter(&self) -> DistinguishedNameIterator<'_> {
345		DistinguishedNameIterator {
346			distinguished_name: self,
347			iter: self.order.iter(),
348		}
349	}
350
351	#[cfg(feature = "x509-parser")]
352	fn from_name(name: &x509_parser::x509::X509Name) -> Result<Self, Error> {
353		use x509_parser::der_parser::asn1_rs::Tag;
354
355		let mut dn = DistinguishedName::new();
356		for rdn in name.iter() {
357			let mut rdn_iter = rdn.iter();
358			let dn_opt = rdn_iter.next();
359			let attr = if let Some(dn) = dn_opt {
360				if rdn_iter.next().is_some() {
361					// no support for distinguished names with more than one attribute
362					return Err(Error::CouldNotParseCertificate);
363				} else {
364					dn
365				}
366			} else {
367				panic!("x509-parser distinguished name set is empty");
368			};
369
370			let attr_type_oid = attr
371				.attr_type()
372				.iter()
373				.ok_or(Error::CouldNotParseCertificate)?;
374			let dn_type = DnType::from_oid(&attr_type_oid.collect::<Vec<_>>());
375			let data = attr.attr_value().data;
376			let try_str =
377				|data| std::str::from_utf8(data).map_err(|_| Error::CouldNotParseCertificate);
378			let dn_value = match attr.attr_value().header.tag() {
379				Tag::BmpString => DnValue::BmpString(BmpString::from_utf16be(data.to_vec())?),
380				Tag::Ia5String => DnValue::Ia5String(try_str(data)?.try_into()?),
381				Tag::PrintableString => DnValue::PrintableString(try_str(data)?.try_into()?),
382				Tag::T61String => DnValue::TeletexString(try_str(data)?.try_into()?),
383				Tag::UniversalString => {
384					DnValue::UniversalString(UniversalString::from_utf32be(data.to_vec())?)
385				},
386				Tag::Utf8String => DnValue::Utf8String(try_str(data)?.to_owned()),
387				_ => return Err(Error::CouldNotParseCertificate),
388			};
389
390			dn.push(dn_type, dn_value);
391		}
392		Ok(dn)
393	}
394}
395
396/**
397Iterator over [`DistinguishedName`] entries
398*/
399pub struct DistinguishedNameIterator<'a> {
400	distinguished_name: &'a DistinguishedName,
401	iter: std::slice::Iter<'a, DnType>,
402}
403
404impl<'a> Iterator for DistinguishedNameIterator<'a> {
405	type Item = (&'a DnType, &'a DnValue);
406
407	fn next(&mut self) -> Option<Self::Item> {
408		self.iter
409			.next()
410			.and_then(|ty| self.distinguished_name.entries.get(ty).map(|v| (ty, v)))
411	}
412}
413
414/// One of the purposes contained in the [key usage](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3) extension
415#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
416pub enum KeyUsagePurpose {
417	/// digitalSignature
418	DigitalSignature,
419	/// contentCommitment / nonRepudiation
420	ContentCommitment,
421	/// keyEncipherment
422	KeyEncipherment,
423	/// dataEncipherment
424	DataEncipherment,
425	/// keyAgreement
426	KeyAgreement,
427	/// keyCertSign
428	KeyCertSign,
429	/// cRLSign
430	CrlSign,
431	/// encipherOnly
432	EncipherOnly,
433	/// decipherOnly
434	DecipherOnly,
435}
436
437impl KeyUsagePurpose {
438	/// Encode a key usage as the value of a BIT STRING as defined by RFC 5280.
439	/// [`u16`] is sufficient to encode the largest possible key usage value (two bytes).
440	fn to_u16(&self) -> u16 {
441		const FLAG: u16 = 0b1000_0000_0000_0000;
442		FLAG >> match self {
443			KeyUsagePurpose::DigitalSignature => 0,
444			KeyUsagePurpose::ContentCommitment => 1,
445			KeyUsagePurpose::KeyEncipherment => 2,
446			KeyUsagePurpose::DataEncipherment => 3,
447			KeyUsagePurpose::KeyAgreement => 4,
448			KeyUsagePurpose::KeyCertSign => 5,
449			KeyUsagePurpose::CrlSign => 6,
450			KeyUsagePurpose::EncipherOnly => 7,
451			KeyUsagePurpose::DecipherOnly => 8,
452		}
453	}
454
455	/// Parse a collection of key usages from a [`u16`] representing the value
456	/// of a KeyUsage BIT STRING as defined by RFC 5280.
457	#[cfg(feature = "x509-parser")]
458	fn from_u16(value: u16) -> Vec<Self> {
459		[
460			KeyUsagePurpose::DigitalSignature,
461			KeyUsagePurpose::ContentCommitment,
462			KeyUsagePurpose::KeyEncipherment,
463			KeyUsagePurpose::DataEncipherment,
464			KeyUsagePurpose::KeyAgreement,
465			KeyUsagePurpose::KeyCertSign,
466			KeyUsagePurpose::CrlSign,
467			KeyUsagePurpose::EncipherOnly,
468			KeyUsagePurpose::DecipherOnly,
469		]
470		.iter()
471		.filter_map(|key_usage| {
472			let present = key_usage.to_u16() & value != 0;
473			present.then_some(*key_usage)
474		})
475		.collect()
476	}
477}
478
479/// Method to generate key identifiers from public keys.
480///
481/// Key identifiers should be derived from the public key data. [RFC 7093] defines
482/// three methods to do so using a choice of SHA256 (method 1), SHA384 (method 2), or SHA512
483/// (method 3). In each case the first 160 bits of the hash are used as the key identifier
484/// to match the output length that would be produced were SHA1 used (a legacy option defined
485/// in RFC 5280).
486///
487/// In addition to the RFC 7093 mechanisms, rcgen supports using a pre-specified key identifier.
488/// This can be helpful when working with an existing `Certificate`.
489///
490/// [RFC 7093]: https://www.rfc-editor.org/rfc/rfc7093
491#[derive(Debug, PartialEq, Eq, Hash, Clone)]
492#[non_exhaustive]
493pub enum KeyIdMethod {
494	/// RFC 7093 method 1 - a truncated SHA256 digest.
495	#[cfg(feature = "crypto")]
496	Sha256,
497	/// RFC 7093 method 2 - a truncated SHA384 digest.
498	#[cfg(feature = "crypto")]
499	Sha384,
500	/// RFC 7093 method 3 - a truncated SHA512 digest.
501	#[cfg(feature = "crypto")]
502	Sha512,
503	/// Pre-specified identifier. The exact given value is used as the key identifier.
504	PreSpecified(Vec<u8>),
505}
506
507impl KeyIdMethod {
508	/// Derive a key identifier for the provided subject public key info using the key ID method.
509	///
510	/// Typically this is a truncated hash over the raw subject public key info, but may
511	/// be a pre-specified value.
512	///
513	/// This key identifier is used in the SubjectKeyIdentifier and AuthorityKeyIdentifier
514	/// X.509v3 extensions.
515	#[allow(unused_variables)]
516	pub(crate) fn derive(&self, subject_public_key_info: impl AsRef<[u8]>) -> Vec<u8> {
517		let digest_method = match &self {
518			#[cfg(feature = "crypto")]
519			Self::Sha256 => &digest::SHA256,
520			#[cfg(feature = "crypto")]
521			Self::Sha384 => &digest::SHA384,
522			#[cfg(feature = "crypto")]
523			Self::Sha512 => &digest::SHA512,
524			Self::PreSpecified(b) => {
525				return b.to_vec();
526			},
527		};
528		#[cfg(feature = "crypto")]
529		{
530			let digest = digest::digest(digest_method, subject_public_key_info.as_ref());
531			digest.as_ref()[0..20].to_vec()
532		}
533	}
534}
535
536fn dt_strip_nanos(dt: OffsetDateTime) -> OffsetDateTime {
537	// Set nanoseconds to zero
538	// This is needed because the GeneralizedTime serializer would otherwise
539	// output fractional values which RFC 5280 explicitly forbode [1].
540	// UTCTime cannot express fractional seconds or leap seconds
541	// therefore, it needs to be stripped of nanoseconds fully.
542	// [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
543	// TODO: handle leap seconds if dt becomes leap second aware
544	let time =
545		Time::from_hms(dt.hour(), dt.minute(), dt.second()).expect("invalid or out-of-range time");
546	dt.replace_time(time)
547}
548
549fn dt_to_generalized(dt: OffsetDateTime) -> GeneralizedTime {
550	let date_time = dt_strip_nanos(dt);
551	GeneralizedTime::from_datetime(date_time)
552}
553
554fn write_dt_utc_or_generalized(writer: DERWriter, dt: OffsetDateTime) {
555	// RFC 5280 requires CAs to write certificate validity dates
556	// below 2050 as UTCTime, and anything starting from 2050
557	// as GeneralizedTime [1]. The RFC doesn't say anything
558	// about dates before 1950, but as UTCTime can't represent
559	// them, we have to use GeneralizedTime if we want to or not.
560	// [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5
561	if (1950..2050).contains(&dt.year()) {
562		let date_time = dt_strip_nanos(dt);
563		let ut = UTCTime::from_datetime(date_time);
564		writer.write_utctime(&ut);
565	} else {
566		let gt = dt_to_generalized(dt);
567		writer.write_generalized_time(&gt);
568	}
569}
570
571fn write_distinguished_name(writer: DERWriter, dn: &DistinguishedName) {
572	writer.write_sequence(|writer| {
573		for (ty, content) in dn.iter() {
574			writer.next().write_set(|writer| {
575				writer.next().write_sequence(|writer| {
576					writer.next().write_oid(&ty.to_oid());
577					match content {
578						DnValue::BmpString(s) => writer
579							.next()
580							.write_tagged_implicit(TAG_BMPSTRING, |writer| {
581								writer.write_bytes(s.as_bytes())
582							}),
583
584						DnValue::Ia5String(s) => writer.next().write_ia5_string(s.as_str()),
585
586						DnValue::PrintableString(s) => {
587							writer.next().write_printable_string(s.as_str())
588						},
589						DnValue::TeletexString(s) => writer
590							.next()
591							.write_tagged_implicit(TAG_TELETEXSTRING, |writer| {
592								writer.write_bytes(s.as_bytes())
593							}),
594						DnValue::UniversalString(s) => writer
595							.next()
596							.write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| {
597								writer.write_bytes(s.as_bytes())
598							}),
599						DnValue::Utf8String(s) => writer.next().write_utf8_string(s),
600					}
601				});
602			});
603		}
604	});
605}
606
607/// Serializes an X.509v3 extension according to RFC 5280
608fn write_x509_extension(
609	writer: DERWriter,
610	extension_oid: &[u64],
611	is_critical: bool,
612	value_serializer: impl FnOnce(DERWriter),
613) {
614	// Extension specification:
615	//    Extension  ::=  SEQUENCE  {
616	//         extnID      OBJECT IDENTIFIER,
617	//         critical    BOOLEAN DEFAULT FALSE,
618	//         extnValue   OCTET STRING
619	//                     -- contains the DER encoding of an ASN.1 value
620	//                     -- corresponding to the extension type identified
621	//                     -- by extnID
622	//         }
623
624	writer.write_sequence(|writer| {
625		let oid = ObjectIdentifier::from_slice(extension_oid);
626		writer.next().write_oid(&oid);
627		if is_critical {
628			writer.next().write_bool(true);
629		}
630		let bytes = yasna::construct_der(value_serializer);
631		writer.next().write_bytes(&bytes);
632	})
633}
634
635/// Serializes an X.509v3 authority key identifier extension according to RFC 5280.
636fn write_x509_authority_key_identifier(writer: DERWriter, aki: Vec<u8>) {
637	// Write Authority Key Identifier
638	// RFC 5280 states:
639	//   'The keyIdentifier field of the authorityKeyIdentifier extension MUST
640	//    be included in all certificates generated by conforming CAs to
641	//    facilitate certification path construction.  There is one exception;
642	//    where a CA distributes its public key in the form of a "self-signed"
643	//    certificate, the authority key identifier MAY be omitted.'
644	// In addition, for CRLs:
645	//    'Conforming CRL issuers MUST use the key identifier method, and MUST
646	//     include this extension in all CRLs issued.'
647	write_x509_extension(writer, oid::AUTHORITY_KEY_IDENTIFIER, false, |writer| {
648		writer.write_sequence(|writer| {
649			writer
650				.next()
651				.write_tagged_implicit(Tag::context(0), |writer| writer.write_bytes(&aki))
652		});
653	});
654}
655
656#[cfg(feature = "zeroize")]
657impl zeroize::Zeroize for KeyPair {
658	fn zeroize(&mut self) {
659		self.serialized_der.zeroize();
660	}
661}
662
663/// A certificate serial number.
664#[derive(Debug, PartialEq, Eq, Hash, Clone)]
665pub struct SerialNumber {
666	inner: Vec<u8>,
667}
668
669impl SerialNumber {
670	/// Create a serial number from the given byte slice.
671	pub fn from_slice(bytes: &[u8]) -> SerialNumber {
672		let inner = bytes.to_vec();
673		SerialNumber { inner }
674	}
675
676	/// Return the byte representation of the serial number.
677	pub fn to_bytes(&self) -> Vec<u8> {
678		self.inner.clone()
679	}
680
681	/// Return the length of the serial number in bytes.
682	pub fn len(&self) -> usize {
683		self.inner.len()
684	}
685}
686
687impl fmt::Display for SerialNumber {
688	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
689		let hex: Vec<_> = self.inner.iter().map(|b| format!("{:02x}", b)).collect();
690		write!(f, "{}", hex.join(":"))
691	}
692}
693
694impl From<Vec<u8>> for SerialNumber {
695	fn from(inner: Vec<u8>) -> SerialNumber {
696		SerialNumber { inner }
697	}
698}
699
700impl From<u64> for SerialNumber {
701	fn from(u: u64) -> SerialNumber {
702		let inner = u.to_be_bytes().into();
703		SerialNumber { inner }
704	}
705}
706
707impl AsRef<[u8]> for SerialNumber {
708	fn as_ref(&self) -> &[u8] {
709		&self.inner
710	}
711}
712
713#[cfg(test)]
714mod tests {
715	use std::panic::catch_unwind;
716
717	use time::{Date, Month, PrimitiveDateTime};
718
719	use super::*;
720
721	fn times() -> [OffsetDateTime; 2] {
722		let dt_nanos = {
723			let date = Date::from_calendar_date(2020, Month::December, 3).unwrap();
724			let time = Time::from_hms_nano(0, 0, 1, 444).unwrap();
725			PrimitiveDateTime::new(date, time).assume_utc()
726		};
727		let dt_zero = {
728			let date = Date::from_calendar_date(2020, Month::December, 3).unwrap();
729			let time = Time::from_hms_nano(0, 0, 1, 0).unwrap();
730			PrimitiveDateTime::new(date, time).assume_utc()
731		};
732		// TODO: include leap seconds if time becomes leap second aware
733		[dt_nanos, dt_zero]
734	}
735
736	#[test]
737	fn test_dt_utc_strip_nanos() {
738		let times = times();
739
740		// No stripping - OffsetDateTime with nanos
741		let res = catch_unwind(|| UTCTime::from_datetime(times[0]));
742		assert!(res.is_err());
743
744		// Stripping
745		for dt in times {
746			let date_time = dt_strip_nanos(dt);
747			assert_eq!(date_time.time().nanosecond(), 0);
748			let _ut = UTCTime::from_datetime(date_time);
749		}
750	}
751
752	#[test]
753	fn test_dt_to_generalized() {
754		let times = times();
755
756		for dt in times {
757			let _gt = dt_to_generalized(dt);
758		}
759	}
760
761	#[test]
762	fn signature_algos_different() {
763		// TODO unify this with test_key_params_mismatch.
764		// Note that that test doesn't have a full list of signature
765		// algorithms, as it has no access to the iter function.
766		for (i, alg_i) in SignatureAlgorithm::iter().enumerate() {
767			for (j, alg_j) in SignatureAlgorithm::iter().enumerate() {
768				assert_eq!(
769					alg_i == alg_j,
770					i == j,
771					"Algorighm relationship mismatch for algorithm index pair {} and {}",
772					i,
773					j
774				);
775			}
776		}
777	}
778
779	#[cfg(feature = "x509-parser")]
780	mod test_ip_address_from_octets {
781		use super::super::ip_addr_from_octets;
782		use super::super::Error;
783		use std::net::IpAddr;
784
785		#[test]
786		fn ipv4() {
787			let octets = [10, 20, 30, 40];
788
789			let actual = ip_addr_from_octets(&octets).unwrap();
790
791			assert_eq!(IpAddr::from(octets), actual)
792		}
793
794		#[test]
795		fn ipv6() {
796			let octets = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
797
798			let actual = ip_addr_from_octets(&octets).unwrap();
799
800			assert_eq!(IpAddr::from(octets), actual)
801		}
802
803		#[test]
804		fn mismatch() {
805			let incorrect = Vec::from_iter(0..10);
806			let actual = ip_addr_from_octets(&incorrect).unwrap_err();
807
808			assert_eq!(Error::InvalidIpAddressOctetLength(10), actual);
809		}
810
811		#[test]
812		fn none() {
813			let actual = ip_addr_from_octets(&[]).unwrap_err();
814
815			assert_eq!(Error::InvalidIpAddressOctetLength(0), actual);
816		}
817
818		#[test]
819		fn too_many() {
820			let incorrect = Vec::from_iter(0..20);
821			let actual = ip_addr_from_octets(&incorrect).unwrap_err();
822
823			assert_eq!(Error::InvalidIpAddressOctetLength(20), actual);
824		}
825	}
826
827	#[cfg(feature = "x509-parser")]
828	mod test_san_type_from_general_name {
829		use crate::SanType;
830		use std::net::IpAddr;
831		use x509_parser::extensions::GeneralName;
832
833		#[test]
834		fn with_ipv4() {
835			let octets = [1, 2, 3, 4];
836			let value = GeneralName::IPAddress(&octets);
837			let actual = SanType::try_from_general(&value).unwrap();
838
839			assert_eq!(SanType::IpAddress(IpAddr::from(octets)), actual);
840		}
841	}
842}