1#![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, signing_key } = generate_simple_self_signed(subject_alt_names).unwrap();
24println!("{}", cert.pem());
25println!("{}", signing_key.serialize_pem());
26# }
27```"##
28)]
29#![forbid(unsafe_code)]
30#![forbid(non_ascii_idents)]
31#![deny(missing_docs)]
32#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
33#![warn(unreachable_pub)]
34
35use std::borrow::Cow;
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};
42use std::ops::Deref;
43
44#[cfg(feature = "pem")]
45use pem::Pem;
46use pki_types::CertificateDer;
47use time::{OffsetDateTime, Time};
48use yasna::models::ObjectIdentifier;
49use yasna::models::{GeneralizedTime, UTCTime};
50use yasna::tags::{TAG_BMPSTRING, TAG_TELETEXSTRING, TAG_UNIVERSALSTRING};
51use yasna::DERWriter;
52use yasna::Tag;
53
54use crate::string::{BmpString, Ia5String, PrintableString, TeletexString, UniversalString};
55
56pub use certificate::{
57 date_time_ymd, Attribute, BasicConstraints, Certificate, CertificateParams, CidrSubnet,
58 CustomExtension, DnType, ExtendedKeyUsagePurpose, GeneralSubtree, IsCa, NameConstraints,
59};
60pub use crl::{
61 CertificateRevocationList, CertificateRevocationListParams, CrlDistributionPoint,
62 CrlIssuingDistributionPoint, CrlScope, RevocationReason, RevokedCertParams,
63};
64pub use csr::{CertificateSigningRequest, CertificateSigningRequestParams, PublicKey};
65pub use error::{Error, InvalidAsn1String};
66#[cfg(feature = "crypto")]
67pub use key_pair::KeyPair;
68pub use key_pair::PublicKeyData;
69#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))]
70pub use key_pair::RsaKeySize;
71pub use key_pair::{SigningKey, SubjectPublicKeyInfo};
72#[cfg(feature = "crypto")]
73use ring_like::digest;
74pub use sign_algo::algo::*;
75pub use sign_algo::SignatureAlgorithm;
76
77mod certificate;
78mod crl;
79mod csr;
80mod error;
81mod key_pair;
82mod oid;
83mod ring_like;
84mod sign_algo;
85pub mod string;
86
87#[deprecated(
89 note = "Renamed to `Error`. We recommend to refer to it by fully-qualifying the crate: `rcgen::Error`."
90)]
91pub type RcgenError = Error;
92
93#[derive(PartialEq, Eq)]
95pub struct CertifiedKey<S: SigningKey> {
96 pub cert: Certificate,
98 pub signing_key: S,
100}
101
102#[cfg(feature = "crypto")]
111#[cfg_attr(
112 feature = "pem",
113 doc = r##"
114## Example
115
116```
117use rcgen::{generate_simple_self_signed, CertifiedKey};
118# fn main () {
119// Generate a certificate that's valid for "localhost" and "hello.world.example"
120let subject_alt_names = vec!["hello.world.example".to_string(),
121 "localhost".to_string()];
122
123let CertifiedKey { cert, signing_key } = generate_simple_self_signed(subject_alt_names).unwrap();
124
125// The certificate is now valid for localhost and the domain "hello.world.example"
126println!("{}", cert.pem());
127println!("{}", signing_key.serialize_pem());
128# }
129```
130"##
131)]
132pub fn generate_simple_self_signed(
133 subject_alt_names: impl Into<Vec<String>>,
134) -> Result<CertifiedKey<KeyPair>, Error> {
135 let signing_key = KeyPair::generate()?;
136 let cert = CertificateParams::new(subject_alt_names)?.self_signed(&signing_key)?;
137 Ok(CertifiedKey { cert, signing_key })
138}
139
140#[derive(Debug)]
142pub struct CertifiedIssuer<'a, S> {
143 certificate: Certificate,
144 issuer: Issuer<'a, S>,
145}
146
147impl<'a, S: SigningKey> CertifiedIssuer<'a, S> {
148 pub fn self_signed(params: CertificateParams, signing_key: S) -> Result<Self, Error> {
150 Ok(Self {
151 certificate: params.self_signed(&signing_key)?,
152 issuer: Issuer::new(params, signing_key),
153 })
154 }
155
156 pub fn signed_by(
158 params: CertificateParams,
159 signing_key: S,
160 issuer: &Issuer<'_, impl SigningKey>,
161 ) -> Result<Self, Error> {
162 Ok(Self {
163 certificate: params.signed_by(&signing_key, issuer)?,
164 issuer: Issuer::new(params, signing_key),
165 })
166 }
167
168 #[cfg(feature = "pem")]
170 pub fn pem(&self) -> String {
171 pem::encode_config(&Pem::new("CERTIFICATE", self.der().to_vec()), ENCODE_CONFIG)
172 }
173
174 pub fn der(&self) -> &CertificateDer<'static> {
178 self.certificate.der()
179 }
180}
181
182impl<'a, S> Deref for CertifiedIssuer<'a, S> {
183 type Target = Issuer<'a, S>;
184
185 fn deref(&self) -> &Self::Target {
186 &self.issuer
187 }
188}
189
190impl<'a, S> AsRef<Certificate> for CertifiedIssuer<'a, S> {
191 fn as_ref(&self) -> &Certificate {
192 &self.certificate
193 }
194}
195
196pub struct Issuer<'a, S> {
201 distinguished_name: Cow<'a, DistinguishedName>,
202 key_identifier_method: Cow<'a, KeyIdMethod>,
203 key_usages: Cow<'a, [KeyUsagePurpose]>,
204 signing_key: S,
205}
206
207impl<'a, S: SigningKey> Issuer<'a, S> {
208 pub fn new(params: CertificateParams, signing_key: S) -> Self {
210 Self {
211 distinguished_name: Cow::Owned(params.distinguished_name),
212 key_identifier_method: Cow::Owned(params.key_identifier_method),
213 key_usages: Cow::Owned(params.key_usages),
214 signing_key,
215 }
216 }
217
218 pub fn from_params(params: &'a CertificateParams, signing_key: S) -> Self {
223 Self {
224 distinguished_name: Cow::Borrowed(¶ms.distinguished_name),
225 key_identifier_method: Cow::Borrowed(¶ms.key_identifier_method),
226 key_usages: Cow::Borrowed(¶ms.key_usages),
227 signing_key,
228 }
229 }
230
231 #[cfg(all(feature = "pem", feature = "x509-parser"))]
235 pub fn from_ca_cert_pem(pem_str: &str, signing_key: S) -> Result<Self, Error> {
236 let certificate = pem::parse(pem_str).map_err(|_| Error::CouldNotParseCertificate)?;
237 Self::from_ca_cert_der(&certificate.contents().into(), signing_key)
238 }
239
240 #[cfg(feature = "x509-parser")]
249 pub fn from_ca_cert_der(ca_cert: &CertificateDer<'_>, signing_key: S) -> Result<Self, Error> {
250 let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert)
251 .map_err(|_| Error::CouldNotParseCertificate)?;
252
253 Ok(Self {
254 key_usages: Cow::Owned(KeyUsagePurpose::from_x509(&x509)?),
255 key_identifier_method: Cow::Owned(KeyIdMethod::from_x509(&x509)?),
256 distinguished_name: Cow::Owned(DistinguishedName::from_name(
257 &x509.tbs_certificate.subject,
258 )?),
259 signing_key,
260 })
261 }
262
263 pub fn key_usages(&self) -> &[KeyUsagePurpose] {
265 &self.key_usages
266 }
267
268 pub fn key(&self) -> &S {
270 &self.signing_key
271 }
272}
273
274impl<'a, S> fmt::Debug for Issuer<'a, S> {
275 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277 let Issuer {
279 distinguished_name,
280 key_identifier_method,
281 key_usages,
282 signing_key: _,
283 } = self;
284
285 f.debug_struct("Issuer")
286 .field("distinguished_name", distinguished_name)
287 .field("key_identifier_method", key_identifier_method)
288 .field("key_usages", key_usages)
289 .field("signing_key", &"[elided]")
290 .finish()
291 }
292}
293
294#[cfg(feature = "pem")]
300const ENCODE_CONFIG: pem::EncodeConfig = {
301 let line_ending = match cfg!(target_family = "windows") {
302 true => pem::LineEnding::CRLF,
303 false => pem::LineEnding::LF,
304 };
305 pem::EncodeConfig::new().set_line_ending(line_ending)
306};
307
308#[derive(Debug, PartialEq, Eq, Hash, Clone)]
309#[allow(missing_docs)]
310#[non_exhaustive]
311pub enum SanType {
313 Rfc822Name(Ia5String),
315 DnsName(Ia5String),
316 URI(Ia5String),
317 IpAddress(IpAddr),
318 OtherName((Vec<u64>, OtherNameValue)),
319}
320
321impl SanType {
322 #[cfg(all(test, feature = "x509-parser"))]
323 fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result<Vec<Self>, Error> {
324 let sans = x509
325 .subject_alternative_name()
326 .map_err(|_| Error::CouldNotParseCertificate)?
327 .map(|ext| &ext.value.general_names);
328
329 let Some(sans) = sans else {
330 return Ok(Vec::new());
331 };
332
333 let mut subject_alt_names = Vec::with_capacity(sans.len());
334 for san in sans {
335 subject_alt_names.push(Self::try_from_general(san)?);
336 }
337 Ok(subject_alt_names)
338 }
339}
340
341#[derive(Debug, PartialEq, Eq, Hash, Clone)]
349#[non_exhaustive]
350pub enum OtherNameValue {
351 Utf8String(String),
353}
354
355impl OtherNameValue {
356 fn write_der(&self, writer: DERWriter) {
357 writer.write_tagged(Tag::context(0), |writer| match self {
358 OtherNameValue::Utf8String(s) => writer.write_utf8_string(s),
359 });
360 }
361}
362
363impl<T> From<T> for OtherNameValue
364where
365 T: Into<String>,
366{
367 fn from(t: T) -> Self {
368 OtherNameValue::Utf8String(t.into())
369 }
370}
371
372#[cfg(feature = "x509-parser")]
373fn ip_addr_from_octets(octets: &[u8]) -> Result<IpAddr, Error> {
374 if let Ok(ipv6_octets) = <&[u8; 16]>::try_from(octets) {
375 Ok(Ipv6Addr::from(*ipv6_octets).into())
376 } else if let Ok(ipv4_octets) = <&[u8; 4]>::try_from(octets) {
377 Ok(Ipv4Addr::from(*ipv4_octets).into())
378 } else {
379 Err(Error::InvalidIpAddressOctetLength(octets.len()))
380 }
381}
382
383impl SanType {
384 #[cfg(feature = "x509-parser")]
385 fn try_from_general(name: &x509_parser::extensions::GeneralName<'_>) -> Result<Self, Error> {
386 use x509_parser::der_parser::asn1_rs::{self, FromDer, Tag, TaggedExplicit};
387 Ok(match name {
388 x509_parser::extensions::GeneralName::RFC822Name(name) => {
389 SanType::Rfc822Name((*name).try_into()?)
390 },
391 x509_parser::extensions::GeneralName::DNSName(name) => {
392 SanType::DnsName((*name).try_into()?)
393 },
394 x509_parser::extensions::GeneralName::URI(name) => SanType::URI((*name).try_into()?),
395 x509_parser::extensions::GeneralName::IPAddress(octets) => {
396 SanType::IpAddress(ip_addr_from_octets(octets)?)
397 },
398 x509_parser::extensions::GeneralName::OtherName(oid, value) => {
399 let oid = oid.iter().ok_or(Error::CouldNotParseCertificate)?;
400 let (_, other_name) = TaggedExplicit::<asn1_rs::Any, _, 0>::from_der(value)
402 .map_err(|_| Error::CouldNotParseCertificate)?;
403 let other_name = other_name.into_inner();
404
405 let other_name_value = match other_name.tag() {
406 Tag::Utf8String => OtherNameValue::Utf8String(
407 std::str::from_utf8(other_name.data)
408 .map_err(|_| Error::CouldNotParseCertificate)?
409 .to_owned(),
410 ),
411 _ => return Err(Error::CouldNotParseCertificate),
412 };
413 SanType::OtherName((oid.collect(), other_name_value))
414 },
415 _ => return Err(Error::InvalidNameType),
416 })
417 }
418
419 fn tag(&self) -> u64 {
420 const TAG_OTHER_NAME: u64 = 0;
423 const TAG_RFC822_NAME: u64 = 1;
424 const TAG_DNS_NAME: u64 = 2;
425 const TAG_URI: u64 = 6;
426 const TAG_IP_ADDRESS: u64 = 7;
427
428 match self {
429 SanType::Rfc822Name(_name) => TAG_RFC822_NAME,
430 SanType::DnsName(_name) => TAG_DNS_NAME,
431 SanType::URI(_name) => TAG_URI,
432 SanType::IpAddress(_addr) => TAG_IP_ADDRESS,
433 Self::OtherName(_oid) => TAG_OTHER_NAME,
434 }
435 }
436}
437
438#[derive(Debug, PartialEq, Eq, Hash, Clone)]
440#[non_exhaustive]
441pub enum DnValue {
442 BmpString(BmpString),
444 Ia5String(Ia5String),
446 PrintableString(PrintableString),
448 TeletexString(TeletexString),
450 UniversalString(UniversalString),
452 Utf8String(String),
454}
455
456impl<T> From<T> for DnValue
457where
458 T: Into<String>,
459{
460 fn from(t: T) -> Self {
461 DnValue::Utf8String(t.into())
462 }
463}
464
465#[derive(Debug, Default, PartialEq, Eq, Clone)]
466pub struct DistinguishedName {
477 entries: HashMap<DnType, DnValue>,
478 order: Vec<DnType>,
479}
480
481impl DistinguishedName {
482 pub fn new() -> Self {
484 Self::default()
485 }
486 pub fn get(&self, ty: &DnType) -> Option<&DnValue> {
488 self.entries.get(ty)
489 }
490 pub fn remove(&mut self, ty: DnType) -> bool {
496 let removed = self.entries.remove(&ty).is_some();
497 if removed {
498 self.order.retain(|ty_o| &ty != ty_o);
499 }
500 removed
501 }
502 pub fn push(&mut self, ty: DnType, s: impl Into<DnValue>) {
513 if !self.entries.contains_key(&ty) {
514 self.order.push(ty.clone());
515 }
516 self.entries.insert(ty, s.into());
517 }
518 pub fn iter(&self) -> DistinguishedNameIterator<'_> {
520 DistinguishedNameIterator {
521 distinguished_name: self,
522 iter: self.order.iter(),
523 }
524 }
525
526 #[cfg(feature = "x509-parser")]
527 fn from_name(name: &x509_parser::x509::X509Name) -> Result<Self, Error> {
528 use x509_parser::der_parser::asn1_rs::Tag;
529
530 let mut dn = DistinguishedName::new();
531 for rdn in name.iter() {
532 let mut rdn_iter = rdn.iter();
533 let dn_opt = rdn_iter.next();
534 let attr = if let Some(dn) = dn_opt {
535 if rdn_iter.next().is_some() {
536 return Err(Error::CouldNotParseCertificate);
538 } else {
539 dn
540 }
541 } else {
542 panic!("x509-parser distinguished name set is empty");
543 };
544
545 let attr_type_oid = attr
546 .attr_type()
547 .iter()
548 .ok_or(Error::CouldNotParseCertificate)?;
549 let dn_type = DnType::from_oid(&attr_type_oid.collect::<Vec<_>>());
550 let data = attr.attr_value().data;
551 let try_str =
552 |data| std::str::from_utf8(data).map_err(|_| Error::CouldNotParseCertificate);
553 let dn_value = match attr.attr_value().header.tag() {
554 Tag::BmpString => DnValue::BmpString(BmpString::from_utf16be(data.to_vec())?),
555 Tag::Ia5String => DnValue::Ia5String(try_str(data)?.try_into()?),
556 Tag::PrintableString => DnValue::PrintableString(try_str(data)?.try_into()?),
557 Tag::T61String => DnValue::TeletexString(try_str(data)?.try_into()?),
558 Tag::UniversalString => {
559 DnValue::UniversalString(UniversalString::from_utf32be(data.to_vec())?)
560 },
561 Tag::Utf8String => DnValue::Utf8String(try_str(data)?.to_owned()),
562 _ => return Err(Error::CouldNotParseCertificate),
563 };
564
565 dn.push(dn_type, dn_value);
566 }
567 Ok(dn)
568 }
569}
570
571#[derive(Clone, Debug)]
575pub struct DistinguishedNameIterator<'a> {
576 distinguished_name: &'a DistinguishedName,
577 iter: std::slice::Iter<'a, DnType>,
578}
579
580impl<'a> Iterator for DistinguishedNameIterator<'a> {
581 type Item = (&'a DnType, &'a DnValue);
582
583 fn next(&mut self) -> Option<Self::Item> {
584 self.iter
585 .next()
586 .and_then(|ty| self.distinguished_name.entries.get(ty).map(|v| (ty, v)))
587 }
588}
589
590#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
592pub enum KeyUsagePurpose {
593 DigitalSignature,
595 ContentCommitment,
597 KeyEncipherment,
599 DataEncipherment,
601 KeyAgreement,
603 KeyCertSign,
605 CrlSign,
607 EncipherOnly,
609 DecipherOnly,
611}
612
613impl KeyUsagePurpose {
614 #[cfg(feature = "x509-parser")]
615 fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result<Vec<Self>, Error> {
616 let key_usage = x509
617 .key_usage()
618 .map_err(|_| Error::CouldNotParseCertificate)?
619 .map(|ext| ext.value);
620 let flags = key_usage.map_or(0u16, |k| k.flags).reverse_bits();
622 Ok(Self::from_u16(flags))
623 }
624
625 fn to_u16(self) -> u16 {
628 const FLAG: u16 = 0b1000_0000_0000_0000;
629 FLAG >> match self {
630 KeyUsagePurpose::DigitalSignature => 0,
631 KeyUsagePurpose::ContentCommitment => 1,
632 KeyUsagePurpose::KeyEncipherment => 2,
633 KeyUsagePurpose::DataEncipherment => 3,
634 KeyUsagePurpose::KeyAgreement => 4,
635 KeyUsagePurpose::KeyCertSign => 5,
636 KeyUsagePurpose::CrlSign => 6,
637 KeyUsagePurpose::EncipherOnly => 7,
638 KeyUsagePurpose::DecipherOnly => 8,
639 }
640 }
641
642 #[cfg(feature = "x509-parser")]
645 fn from_u16(value: u16) -> Vec<Self> {
646 [
647 KeyUsagePurpose::DigitalSignature,
648 KeyUsagePurpose::ContentCommitment,
649 KeyUsagePurpose::KeyEncipherment,
650 KeyUsagePurpose::DataEncipherment,
651 KeyUsagePurpose::KeyAgreement,
652 KeyUsagePurpose::KeyCertSign,
653 KeyUsagePurpose::CrlSign,
654 KeyUsagePurpose::EncipherOnly,
655 KeyUsagePurpose::DecipherOnly,
656 ]
657 .iter()
658 .filter_map(|key_usage| {
659 let present = key_usage.to_u16() & value != 0;
660 present.then_some(*key_usage)
661 })
662 .collect()
663 }
664}
665
666#[derive(Debug, PartialEq, Eq, Hash, Clone)]
679#[non_exhaustive]
680pub enum KeyIdMethod {
681 #[cfg(feature = "crypto")]
683 Sha256,
684 #[cfg(feature = "crypto")]
686 Sha384,
687 #[cfg(feature = "crypto")]
689 Sha512,
690 PreSpecified(Vec<u8>),
692}
693
694impl KeyIdMethod {
695 #[cfg(feature = "x509-parser")]
696 fn from_x509(x509: &x509_parser::certificate::X509Certificate<'_>) -> Result<Self, Error> {
697 let key_identifier_method =
698 x509.iter_extensions()
699 .find_map(|ext| match ext.parsed_extension() {
700 x509_parser::extensions::ParsedExtension::SubjectKeyIdentifier(key_id) => {
701 Some(KeyIdMethod::PreSpecified(key_id.0.into()))
702 },
703 _ => None,
704 });
705
706 Ok(match key_identifier_method {
707 Some(method) => method,
708 None => {
709 #[cfg(not(feature = "crypto"))]
710 return Err(Error::UnsupportedSignatureAlgorithm);
711 #[cfg(feature = "crypto")]
712 KeyIdMethod::Sha256
713 },
714 })
715 }
716
717 #[allow(unused_variables)]
725 pub(crate) fn derive(&self, subject_public_key_info: impl AsRef<[u8]>) -> Vec<u8> {
726 #[cfg_attr(not(feature = "crypto"), expect(clippy::let_unit_value))]
727 let digest_method = match &self {
728 #[cfg(feature = "crypto")]
729 Self::Sha256 => &digest::SHA256,
730 #[cfg(feature = "crypto")]
731 Self::Sha384 => &digest::SHA384,
732 #[cfg(feature = "crypto")]
733 Self::Sha512 => &digest::SHA512,
734 Self::PreSpecified(b) => {
735 return b.to_vec();
736 },
737 };
738 #[cfg(feature = "crypto")]
739 {
740 let digest = digest::digest(digest_method, subject_public_key_info.as_ref());
741 digest.as_ref()[0..20].to_vec()
742 }
743 }
744}
745
746fn dt_strip_nanos(dt: OffsetDateTime) -> OffsetDateTime {
747 let time =
755 Time::from_hms(dt.hour(), dt.minute(), dt.second()).expect("invalid or out-of-range time");
756 dt.replace_time(time)
757}
758
759fn dt_to_generalized(dt: OffsetDateTime) -> GeneralizedTime {
760 let date_time = dt_strip_nanos(dt);
761 GeneralizedTime::from_datetime(date_time)
762}
763
764fn write_dt_utc_or_generalized(writer: DERWriter, dt: OffsetDateTime) {
765 if (1950..2050).contains(&dt.year()) {
772 let date_time = dt_strip_nanos(dt);
773 let ut = UTCTime::from_datetime(date_time);
774 writer.write_utctime(&ut);
775 } else {
776 let gt = dt_to_generalized(dt);
777 writer.write_generalized_time(>);
778 }
779}
780
781fn write_distinguished_name(writer: DERWriter, dn: &DistinguishedName) {
782 writer.write_sequence(|writer| {
783 for (ty, content) in dn.iter() {
784 writer.next().write_set(|writer| {
785 writer.next().write_sequence(|writer| {
786 writer.next().write_oid(&ty.to_oid());
787 match content {
788 DnValue::BmpString(s) => writer
789 .next()
790 .write_tagged_implicit(TAG_BMPSTRING, |writer| {
791 writer.write_bytes(s.as_bytes())
792 }),
793
794 DnValue::Ia5String(s) => writer.next().write_ia5_string(s.as_str()),
795
796 DnValue::PrintableString(s) => {
797 writer.next().write_printable_string(s.as_str())
798 },
799 DnValue::TeletexString(s) => writer
800 .next()
801 .write_tagged_implicit(TAG_TELETEXSTRING, |writer| {
802 writer.write_bytes(s.as_bytes())
803 }),
804 DnValue::UniversalString(s) => writer
805 .next()
806 .write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| {
807 writer.write_bytes(s.as_bytes())
808 }),
809 DnValue::Utf8String(s) => writer.next().write_utf8_string(s),
810 }
811 });
812 });
813 }
814 });
815}
816
817fn write_x509_extension(
819 writer: DERWriter,
820 extension_oid: &[u64],
821 is_critical: bool,
822 value_serializer: impl FnOnce(DERWriter),
823) {
824 writer.write_sequence(|writer| {
835 let oid = ObjectIdentifier::from_slice(extension_oid);
836 writer.next().write_oid(&oid);
837 if is_critical {
838 writer.next().write_bool(true);
839 }
840 let bytes = yasna::construct_der(value_serializer);
841 writer.next().write_bytes(&bytes);
842 })
843}
844
845fn write_x509_authority_key_identifier(writer: DERWriter, aki: Vec<u8>) {
847 write_x509_extension(writer, oid::AUTHORITY_KEY_IDENTIFIER, false, |writer| {
858 writer.write_sequence(|writer| {
859 writer
860 .next()
861 .write_tagged_implicit(Tag::context(0), |writer| writer.write_bytes(&aki))
862 });
863 });
864}
865
866#[cfg(feature = "zeroize")]
867impl zeroize::Zeroize for KeyPair {
868 fn zeroize(&mut self) {
869 self.serialized_der.zeroize();
870 }
871}
872
873#[derive(Debug, PartialEq, Eq, Hash, Clone)]
875pub struct SerialNumber {
876 inner: Vec<u8>,
877}
878
879#[allow(clippy::len_without_is_empty)]
880impl SerialNumber {
881 pub fn from_slice(bytes: &[u8]) -> SerialNumber {
883 let inner = bytes.to_vec();
884 SerialNumber { inner }
885 }
886
887 pub fn to_bytes(&self) -> Vec<u8> {
889 self.inner.clone()
890 }
891
892 pub fn len(&self) -> usize {
894 self.inner.len()
895 }
896}
897
898impl fmt::Display for SerialNumber {
899 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
900 let hex: Vec<_> = self.inner.iter().map(|b| format!("{b:02x}")).collect();
901 write!(f, "{}", hex.join(":"))
902 }
903}
904
905impl From<Vec<u8>> for SerialNumber {
906 fn from(inner: Vec<u8>) -> SerialNumber {
907 SerialNumber { inner }
908 }
909}
910
911impl From<u64> for SerialNumber {
912 fn from(u: u64) -> SerialNumber {
913 let inner = u.to_be_bytes().into();
914 SerialNumber { inner }
915 }
916}
917
918impl AsRef<[u8]> for SerialNumber {
919 fn as_ref(&self) -> &[u8] {
920 &self.inner
921 }
922}
923
924#[cfg(test)]
925mod tests {
926 use std::panic::catch_unwind;
927
928 use time::{Date, Month, PrimitiveDateTime};
929
930 use super::*;
931
932 fn times() -> [OffsetDateTime; 2] {
933 let dt_nanos = {
934 let date = Date::from_calendar_date(2020, Month::December, 3).unwrap();
935 let time = Time::from_hms_nano(0, 0, 1, 444).unwrap();
936 PrimitiveDateTime::new(date, time).assume_utc()
937 };
938 let dt_zero = {
939 let date = Date::from_calendar_date(2020, Month::December, 3).unwrap();
940 let time = Time::from_hms_nano(0, 0, 1, 0).unwrap();
941 PrimitiveDateTime::new(date, time).assume_utc()
942 };
943 [dt_nanos, dt_zero]
945 }
946
947 #[test]
948 fn test_dt_utc_strip_nanos() {
949 let times = times();
950
951 let res = catch_unwind(|| UTCTime::from_datetime(times[0]));
953 assert!(res.is_err());
954
955 for dt in times {
957 let date_time = dt_strip_nanos(dt);
958 assert_eq!(date_time.time().nanosecond(), 0);
959 let _ut = UTCTime::from_datetime(date_time);
960 }
961 }
962
963 #[test]
964 fn test_dt_to_generalized() {
965 let times = times();
966
967 for dt in times {
968 let _gt = dt_to_generalized(dt);
969 }
970 }
971
972 #[test]
973 fn signature_algos_different() {
974 for (i, alg_i) in SignatureAlgorithm::iter().enumerate() {
978 for (j, alg_j) in SignatureAlgorithm::iter().enumerate() {
979 assert_eq!(
980 alg_i == alg_j,
981 i == j,
982 "Algorithm relationship mismatch for algorithm index pair {i} and {j}"
983 );
984 }
985 }
986 }
987
988 #[cfg(feature = "x509-parser")]
989 mod test_ip_address_from_octets {
990 use super::super::ip_addr_from_octets;
991 use super::super::Error;
992 use std::net::IpAddr;
993
994 #[test]
995 fn ipv4() {
996 let octets = [10, 20, 30, 40];
997
998 let actual = ip_addr_from_octets(&octets).unwrap();
999
1000 assert_eq!(IpAddr::from(octets), actual)
1001 }
1002
1003 #[test]
1004 fn ipv6() {
1005 let octets = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
1006
1007 let actual = ip_addr_from_octets(&octets).unwrap();
1008
1009 assert_eq!(IpAddr::from(octets), actual)
1010 }
1011
1012 #[test]
1013 fn mismatch() {
1014 let incorrect = Vec::from_iter(0..10);
1015 let actual = ip_addr_from_octets(&incorrect).unwrap_err();
1016
1017 assert_eq!(Error::InvalidIpAddressOctetLength(10), actual);
1018 }
1019
1020 #[test]
1021 fn none() {
1022 let actual = ip_addr_from_octets(&[]).unwrap_err();
1023
1024 assert_eq!(Error::InvalidIpAddressOctetLength(0), actual);
1025 }
1026
1027 #[test]
1028 fn too_many() {
1029 let incorrect = Vec::from_iter(0..20);
1030 let actual = ip_addr_from_octets(&incorrect).unwrap_err();
1031
1032 assert_eq!(Error::InvalidIpAddressOctetLength(20), actual);
1033 }
1034 }
1035
1036 #[cfg(feature = "x509-parser")]
1037 mod test_san_type_from_general_name {
1038 use crate::SanType;
1039 use std::net::IpAddr;
1040 use x509_parser::extensions::GeneralName;
1041
1042 #[test]
1043 fn with_ipv4() {
1044 let octets = [1, 2, 3, 4];
1045 let value = GeneralName::IPAddress(&octets);
1046 let actual = SanType::try_from_general(&value).unwrap();
1047
1048 assert_eq!(SanType::IpAddress(IpAddr::from(octets)), actual);
1049 }
1050 }
1051}