Coverage Report

Created: 2026-04-14 06:46

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