#![forbid(unsafe_code)]
#![forbid(non_ascii_idents)]
#![deny(missing_docs)]
#![allow(clippy::complexity, clippy::style, clippy::pedantic)]
use yasna::Tag;
use yasna::models::ObjectIdentifier;
#[cfg(feature = "pem")]
use pem::Pem;
use std::convert::TryInto;
use ring::digest;
use ring::signature::{EcdsaKeyPair, Ed25519KeyPair, RsaKeyPair, RsaEncoding};
use ring::rand::SystemRandom;
use ring::signature::KeyPair as RingKeyPair;
use ring::signature::{self, EcdsaSigningAlgorithm, EdDSAParameters};
use yasna::DERWriter;
use yasna::models::{GeneralizedTime, UTCTime};
use yasna::tags::{TAG_BMPSTRING, TAG_TELETEXSTRING, TAG_UNIVERSALSTRING};
use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time};
use std::collections::HashMap;
use std::fmt;
use std::convert::TryFrom;
use std::error::Error;
use std::net::IpAddr;
use std::str::FromStr;
use std::hash::{Hash, Hasher};
pub struct Certificate {
params :CertificateParams,
key_pair :KeyPair,
}
pub fn generate_simple_self_signed(subject_alt_names :impl Into<Vec<String>>) -> Result<Certificate, RcgenError> {
Certificate::from_params(CertificateParams::new(subject_alt_names))
}
const OID_PKCS_9_AT_EXTENSION_REQUEST :&[u64] = &[1, 2, 840, 113549, 1, 9, 14];
const OID_COUNTRY_NAME :&[u64] = &[2, 5, 4, 6];
const OID_LOCALITY_NAME :&[u64] = &[2, 5, 4, 7];
const OID_STATE_OR_PROVINCE_NAME :&[u64] = &[2, 5, 4, 8];
const OID_ORG_NAME :&[u64] = &[2, 5, 4, 10];
const OID_ORG_UNIT_NAME :&[u64] = &[2, 5, 4, 11];
const OID_COMMON_NAME :&[u64] = &[2, 5, 4, 3];
const OID_EC_PUBLIC_KEY :&[u64] = &[1, 2, 840, 10045, 2, 1];
const OID_EC_SECP_256_R1 :&[u64] = &[1, 2, 840, 10045, 3, 1, 7];
const OID_EC_SECP_384_R1 :&[u64] = &[1, 3, 132, 0, 34];
const OID_RSA_ENCRYPTION :&[u64] = &[1, 2, 840, 113549, 1, 1, 1];
const OID_RSASSA_PSS :&[u64] = &[1, 2, 840, 113549, 1, 1, 10];
const OID_KEY_USAGE :&[u64] = &[2, 5, 29, 15];
const OID_SUBJECT_ALT_NAME :&[u64] = &[2, 5, 29, 17];
const OID_BASIC_CONSTRAINTS :&[u64] = &[2, 5, 29, 19];
const OID_SUBJECT_KEY_IDENTIFIER :&[u64] = &[2, 5, 29, 14];
const OID_AUTHORITY_KEY_IDENTIFIER :&[u64] = &[2, 5, 29, 35];
const OID_EXT_KEY_USAGE :&[u64] = &[2, 5, 29, 37];
const OID_NAME_CONSTRAINTS :&[u64] = &[2, 5, 29, 30];
const OID_PE_ACME :&[u64] = &[1, 3, 6, 1, 5, 5, 7, 1, 31];
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[allow(missing_docs)]
#[non_exhaustive]
pub enum SanType {
Rfc822Name(String),
DnsName(String),
URI(String),
IpAddress(IpAddr),
}
impl SanType {
#[cfg(feature = "x509-parser")]
fn try_from_general(name :&x509_parser::extensions::GeneralName<'_>) -> Result<Self, RcgenError> {
Ok(match name {
x509_parser::extensions::GeneralName::RFC822Name(name) => {
SanType::Rfc822Name((*name).into())
}
x509_parser::extensions::GeneralName::DNSName(name) => {
SanType::DnsName((*name).into())
}
x509_parser::extensions::GeneralName::URI(name) => {
SanType::URI((*name).into())
}
_ => return Err(RcgenError::InvalidNameType),
})
}
fn tag(&self) -> u64 {
const TAG_RFC822_NAME :u64 = 1;
const TAG_DNS_NAME :u64 = 2;
const TAG_URI :u64 = 6;
const TAG_IP_ADDRESS :u64 = 7;
match self {
SanType::Rfc822Name(_name) => TAG_RFC822_NAME,
SanType::DnsName(_name) => TAG_DNS_NAME,
SanType::URI(_name) => TAG_URI,
SanType::IpAddress(_addr) => TAG_IP_ADDRESS,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[allow(missing_docs)]
#[non_exhaustive]
pub enum GeneralSubtree {
Rfc822Name(String),
DnsName(String),
DirectoryName(DistinguishedName),
IpAddress(CidrSubnet),
}
impl GeneralSubtree {
fn tag(&self) -> u64 {
const TAG_RFC822_NAME :u64 = 1;
const TAG_DNS_NAME :u64 = 2;
const TAG_DIRECTORY_NAME :u64 = 4;
const TAG_IP_ADDRESS :u64 = 7;
match self {
GeneralSubtree::Rfc822Name(_name) => TAG_RFC822_NAME,
GeneralSubtree::DnsName(_name) => TAG_DNS_NAME,
GeneralSubtree::DirectoryName(_name) => TAG_DIRECTORY_NAME,
GeneralSubtree::IpAddress(_addr) => TAG_IP_ADDRESS,
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[allow(missing_docs)]
pub enum CidrSubnet {
V4([u8; 4], [u8; 4]),
V6([u8; 16], [u8; 16]),
}
macro_rules! mask {
($t:ty, $d:expr) => {{
let v = <$t>::max_value();
let v = v.checked_shr($d as u32).unwrap_or(0);
(!v).to_be_bytes()
}};
}
impl CidrSubnet {
pub fn from_str(s :&str) -> Result<Self, ()> {
let mut iter = s.split('/');
if let (Some(addr_s), Some(prefix_s)) = (iter.next(), iter.next()) {
let addr = IpAddr::from_str(addr_s).map_err(|_| ())?;
let prefix = u8::from_str(prefix_s).map_err(|_| ())?;
Ok(Self::from_addr_prefix(addr, prefix))
} else {
Err(())
}
}
pub fn from_addr_prefix(addr :IpAddr, prefix :u8) -> Self {
match addr {
IpAddr::V4(addr) => {
Self::from_v4_prefix(addr.octets(), prefix)
},
IpAddr::V6(addr) => {
Self::from_v6_prefix(addr.octets(), prefix)
},
}
}
pub fn from_v4_prefix(addr :[u8; 4], prefix :u8) -> Self {
CidrSubnet::V4(addr, mask!(u32, prefix))
}
pub fn from_v6_prefix(addr :[u8; 16], prefix :u8) -> Self {
CidrSubnet::V6(addr, mask!(u128, prefix))
}
fn to_bytes(&self) -> Vec<u8> {
let mut res = Vec::new();
match self {
CidrSubnet::V4(addr, mask) => {
res.extend_from_slice(addr);
res.extend_from_slice(mask);
},
CidrSubnet::V6(addr, mask) => {
res.extend_from_slice(addr);
res.extend_from_slice(mask);
},
}
res
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[non_exhaustive]
pub enum DnType {
CountryName,
LocalityName,
StateOrProvinceName,
OrganizationName,
OrganizationalUnitName,
CommonName,
CustomDnType(Vec<u64>),
}
impl DnType {
fn to_oid(&self) -> ObjectIdentifier {
let sl = match self {
DnType::CountryName => OID_COUNTRY_NAME,
DnType::LocalityName => OID_LOCALITY_NAME,
DnType::StateOrProvinceName => OID_STATE_OR_PROVINCE_NAME,
DnType::OrganizationName => OID_ORG_NAME,
DnType::OrganizationalUnitName => OID_ORG_UNIT_NAME,
DnType::CommonName => OID_COMMON_NAME,
DnType::CustomDnType(ref oid) => oid.as_slice(),
};
ObjectIdentifier::from_slice(sl)
}
pub fn from_oid(slice :&[u64]) -> Self {
match slice {
OID_COUNTRY_NAME => DnType::CountryName,
OID_LOCALITY_NAME => DnType::LocalityName,
OID_STATE_OR_PROVINCE_NAME => DnType::StateOrProvinceName,
OID_ORG_NAME => DnType::OrganizationName,
OID_ORG_UNIT_NAME => DnType::OrganizationalUnitName,
OID_COMMON_NAME => DnType::CommonName,
oid => DnType::CustomDnType(oid.into())
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[non_exhaustive]
pub enum DnValue {
TeletexString(Vec<u8>),
PrintableString(String),
UniversalString(Vec<u8>),
Utf8String(String),
BmpString(Vec<u8>),
}
impl<T> From<T> for DnValue
where
T :Into<String>
{
fn from(t :T) -> Self {
DnValue::Utf8String(t.into())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DistinguishedName {
entries :HashMap<DnType, DnValue>,
order :Vec<DnType>,
}
impl DistinguishedName {
pub fn new() -> Self {
Self {
entries : HashMap::new(),
order : Vec::new(),
}
}
pub fn get(&self, ty :&DnType) -> Option<&DnValue> {
self.entries.get(ty)
}
pub fn remove(&mut self, ty :DnType) -> bool {
let removed = self.entries.remove(&ty).is_some();
if removed {
self.order.retain(|ty_o| &ty != ty_o);
}
removed
}
pub fn push(&mut self, ty :DnType, s :impl Into<DnValue>) {
if !self.entries.contains_key(&ty) {
self.order.push(ty.clone());
}
self.entries.insert(ty, s.into());
}
pub fn iter(&self) -> DistinguishedNameIterator<'_> {
DistinguishedNameIterator {
distinguished_name :self,
iter :self.order.iter()
}
}
#[cfg(feature = "x509-parser")]
fn from_name(name :&x509_parser::x509::X509Name) -> Result<Self, RcgenError> {
use x509_parser::der_parser::asn1_rs::Tag;
let mut dn = DistinguishedName::new();
for rdn in name.iter() {
let mut rdn_iter = rdn.iter();
let dn_opt = rdn_iter.next();
let attr = if let Some(dn) = dn_opt {
if rdn_iter.next().is_some() {
return Err(RcgenError::CouldNotParseCertificate);
} else {
dn
}
} else {
panic!("x509-parser distinguished name set is empty");
};
let attr_type_oid = attr.attr_type().iter()
.ok_or(RcgenError::CouldNotParseCertificate)?;
let dn_type = DnType::from_oid(&attr_type_oid.collect::<Vec<_>>());
let data = attr.attr_value().data;
let dn_value = match attr.attr_value().header.tag() {
Tag::T61String => DnValue::TeletexString(data.into()),
Tag::PrintableString => {
let data = std::str::from_utf8(data)
.map_err(|_| RcgenError::CouldNotParseCertificate)?;
DnValue::PrintableString(data.to_owned())
},
Tag::UniversalString => DnValue::UniversalString(data.into()),
Tag::Utf8String => {
let data = std::str::from_utf8(data)
.map_err(|_| RcgenError::CouldNotParseCertificate)?;
DnValue::Utf8String(data.to_owned())
},
Tag::BmpString => DnValue::BmpString(data.into()),
_ => return Err(RcgenError::CouldNotParseCertificate),
};
dn.push(dn_type, dn_value);
}
Ok(dn)
}
}
pub struct DistinguishedNameIterator<'a> {
distinguished_name :&'a DistinguishedName,
iter :std::slice::Iter<'a, DnType>,
}
impl <'a> Iterator for DistinguishedNameIterator<'a> {
type Item = (&'a DnType, &'a DnValue);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
.and_then(|ty| {
self.distinguished_name.entries.get(ty).map(|v| (ty, v))
})
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct PublicKey {
raw: Vec<u8>,
alg: &'static SignatureAlgorithm,
}
impl PublicKeyData for PublicKey {
fn alg(&self) -> &SignatureAlgorithm {
self.alg
}
fn raw_bytes(&self) -> &[u8] {
&self.raw
}
}
#[allow(missing_docs)]
pub struct CertificateSigningRequest {
pub params :CertificateParams,
pub public_key :PublicKey,
}
impl CertificateSigningRequest {
#[cfg(all(feature = "pem", feature = "x509-parser"))]
pub fn from_pem(pem_str :&str) -> Result<Self, RcgenError> {
let csr = pem::parse(pem_str)
.or(Err(RcgenError::CouldNotParseCertificationRequest))?;
Self::from_der(&csr.contents)
}
#[cfg(feature = "x509-parser")]
pub fn from_der(csr :&[u8]) -> Result<Self, RcgenError> {
use x509_parser::prelude::FromDer;
let csr = x509_parser::certification_request::X509CertificationRequest::from_der(csr)
.map_err(|_| RcgenError::CouldNotParseCertificationRequest)?.1;
csr.verify_signature().map_err(|_| RcgenError::RingUnspecified)?;
let alg_oid = csr.signature_algorithm.algorithm.iter()
.ok_or(RcgenError::CouldNotParseCertificationRequest)?
.collect::<Vec<_>>();
let alg = SignatureAlgorithm::from_oid(&alg_oid)?;
let info = &csr.certification_request_info;
let mut params = CertificateParams::default();
params.alg = alg;
params.distinguished_name = DistinguishedName::from_name(&info.subject)?;
let raw = info.subject_pki.subject_public_key.data.to_vec();
if let Some(extensions) = csr.requested_extensions() {
for ext in extensions {
match ext {
x509_parser::extensions::ParsedExtension::SubjectAlternativeName(san) => {
for name in &san.general_names {
params.subject_alt_names.push(SanType::try_from_general(name)?);
}
}
_ => return Err(RcgenError::UnsupportedExtension),
}
}
}
Ok(Self {
params,
public_key: PublicKey { alg, raw },
})
}
pub fn serialize_der_with_signer(&self, ca :&Certificate) -> Result<Vec<u8>, RcgenError> {
self.params.serialize_der_with_signer(&self.public_key, ca)
}
#[cfg(feature = "pem")]
pub fn serialize_pem_with_signer(&self, ca :&Certificate) -> Result<String, RcgenError> {
let p = Pem {
tag : "CERTIFICATE".to_string(),
contents : self.params.serialize_der_with_signer(&self.public_key, ca)?,
};
Ok(pem::encode(&p))
}
}
#[allow(missing_docs)]
#[non_exhaustive]
pub struct CertificateParams {
pub alg :&'static SignatureAlgorithm,
pub not_before :OffsetDateTime,
pub not_after :OffsetDateTime,
pub serial_number :Option<u64>,
pub subject_alt_names :Vec<SanType>,
pub distinguished_name :DistinguishedName,
pub is_ca :IsCa,
pub key_usages :Vec<KeyUsagePurpose>,
pub extended_key_usages :Vec<ExtendedKeyUsagePurpose>,
pub name_constraints :Option<NameConstraints>,
pub custom_extensions :Vec<CustomExtension>,
pub key_pair :Option<KeyPair>,
pub use_authority_key_identifier_extension :bool,
pub key_identifier_method :KeyIdMethod,
}
impl Default for CertificateParams {
fn default() -> Self {
let not_before = date_time_ymd(1975, 01, 01);
let not_after = date_time_ymd(4096, 01, 01);
let mut distinguished_name = DistinguishedName::new();
distinguished_name.push(DnType::CommonName, "rcgen self signed cert");
CertificateParams {
alg : &PKCS_ECDSA_P256_SHA256,
not_before,
not_after,
serial_number : None,
subject_alt_names : Vec::new(),
distinguished_name,
is_ca : IsCa::NoCa,
key_usages : Vec::new(),
extended_key_usages : Vec::new(),
name_constraints : None,
custom_extensions : Vec::new(),
key_pair : None,
use_authority_key_identifier_extension : false,
key_identifier_method : KeyIdMethod::Sha256,
}
}
}
impl CertificateParams {
#[cfg(all(feature = "pem", feature = "x509-parser"))]
pub fn from_ca_cert_pem(pem_str :&str, key_pair :KeyPair) -> Result<Self, RcgenError> {
let certificate = pem::parse(pem_str)
.or(Err(RcgenError::CouldNotParseCertificate))?;
Self::from_ca_cert_der(&certificate.contents, key_pair)
}
#[cfg(feature = "x509-parser")]
pub fn from_ca_cert_der(ca_cert :&[u8], key_pair :KeyPair) -> Result<Self, RcgenError> {
let (_remainder, x509) = x509_parser::parse_x509_certificate(ca_cert)
.or(Err(RcgenError::CouldNotParseCertificate))?;
let alg_oid = x509.signature_algorithm.algorithm.iter()
.ok_or(RcgenError::CouldNotParseCertificate)?;
let alg = SignatureAlgorithm::from_oid(&alg_oid.collect::<Vec<_>>())?;
let dn = DistinguishedName::from_name(&x509.tbs_certificate.subject)?;
Ok(
CertificateParams {
alg,
distinguished_name : dn,
key_pair : Some(key_pair),
.. Default::default()
}
)
}
fn write_subject_alt_names(&self, writer :DERWriter) {
Self::write_extension(writer, OID_SUBJECT_ALT_NAME, false, |writer| {
writer.write_sequence(|writer| {
for san in self.subject_alt_names.iter() {
writer.next().write_tagged_implicit(Tag::context(san.tag()), |writer| {
match san {
SanType::Rfc822Name(name) |
SanType::DnsName(name) |
SanType::URI(name) => writer.write_ia5_string(name),
SanType::IpAddress(IpAddr::V4(addr)) => writer.write_bytes(&addr.octets()),
SanType::IpAddress(IpAddr::V6(addr)) => writer.write_bytes(&addr.octets()),
}
});
}
});
});
}
fn write_request<K: PublicKeyData>(&self, pub_key: &K, writer :DERWriter) {
writer.write_sequence(|writer| {
writer.next().write_u8(0);
writer.next().write_sequence(|writer| {
for (ty, content) in self.distinguished_name.iter() {
writer.next().write_set(|writer| {
writer.next().write_sequence(|writer| {
writer.next().write_oid(&ty.to_oid());
match content {
DnValue::TeletexString(s) => writer.next().write_tagged_implicit(TAG_TELETEXSTRING, |writer| {
writer.write_bytes(s)
}),
DnValue::PrintableString(s) => writer.next().write_printable_string(s),
DnValue::UniversalString(s) => writer.next().write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| {
writer.write_bytes(s)
}),
DnValue::Utf8String(s) => writer.next().write_utf8_string(s),
DnValue::BmpString(s) => writer.next().write_tagged_implicit(TAG_BMPSTRING, |writer| {
writer.write_bytes(s)
}),
}
});
});
}
});
pub_key.serialize_public_key_der(writer.next());
writer.next().write_tagged(Tag::context(0), |writer| {
if !self.subject_alt_names.is_empty() {
writer.write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(OID_PKCS_9_AT_EXTENSION_REQUEST);
writer.next().write_oid(&oid);
writer.next().write_set(|writer| {
writer.next().write_sequence(|writer| {
self.write_subject_alt_names(writer.next());
});
});
});
}
});
});
}
fn write_cert<K: PublicKeyData>(&self, writer :DERWriter, pub_key: &K, ca :&Certificate) -> Result<(), RcgenError> {
writer.write_sequence(|writer| {
writer.next().write_tagged(Tag::context(0), |writer| {
writer.write_u8(2);
});
let serial = self.serial_number.unwrap_or_else(|| {
let hash = digest::digest(&digest::SHA256, pub_key.raw_bytes());
let bytes: [u8; 8] = hash.as_ref()[0..8].try_into().unwrap();
u64::from_le_bytes(bytes)
});
writer.next().write_u64(serial);
ca.params.alg.write_alg_ident(writer.next());
write_distinguished_name(writer.next(), &ca.params.distinguished_name);
writer.next().write_sequence(|writer| {
write_dt_utc_or_generalized(writer.next(), self.not_before);
write_dt_utc_or_generalized(writer.next(), self.not_after);
Ok::<(), RcgenError>(())
})?;
write_distinguished_name(writer.next(), &self.distinguished_name);
pub_key.serialize_public_key_der(writer.next());
let not_self_signed = ca.key_pair.public_key_raw() != pub_key.raw_bytes();
let should_write_exts = (not_self_signed && self.use_authority_key_identifier_extension) ||
!self.subject_alt_names.is_empty() ||
!self.extended_key_usages.is_empty() ||
self.name_constraints.iter().any(|c| !c.is_empty()) ||
matches!(self.is_ca, IsCa::ExplicitNoCa) ||
matches!(self.is_ca, IsCa::Ca(_)) ||
!self.custom_extensions.is_empty();
if should_write_exts {
writer.next().write_tagged(Tag::context(3), |writer| {
writer.write_sequence(|writer| {
if not_self_signed && self.use_authority_key_identifier_extension {
Self::write_extension(writer.next(), OID_AUTHORITY_KEY_IDENTIFIER, false, |writer| {
writer.write_sequence(|writer| {
writer.next().write_tagged_implicit(Tag::context(0), |writer| {
writer.write_bytes(ca.get_key_identifier().as_ref())
})
});
});
}
if !self.subject_alt_names.is_empty() {
self.write_subject_alt_names(writer.next());
}
if !self.key_usages.is_empty() {
writer.next().write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(OID_KEY_USAGE);
writer.next().write_oid(&oid);
writer.next().write_bool(true);
let mut bits :u16 = 0;
for entry in self.key_usages.iter() {
let index = match entry {
KeyUsagePurpose::DigitalSignature => 0,
KeyUsagePurpose::ContentCommitment => 1,
KeyUsagePurpose::KeyEncipherment => 2,
KeyUsagePurpose::DataEncipherment => 3,
KeyUsagePurpose::KeyAgreement => 4,
KeyUsagePurpose::KeyCertSign => 5,
KeyUsagePurpose::CrlSign => 6,
KeyUsagePurpose::EncipherOnly => 7,
KeyUsagePurpose::DecipherOnly => 8,
};
bits |= 1 << index;
}
let msb = 16 - bits.leading_zeros();
let nb = if msb <= 8 {
1
} else {
2
};
let bits = bits.reverse_bits().to_be_bytes();
let bits = &bits[..nb];
let der = yasna::construct_der(|writer| {
writer.write_bitvec_bytes(&bits, msb as usize)
});
writer.next().write_bytes(&der);
});
}
if !self.extended_key_usages.is_empty() {
Self::write_extension(writer.next(), OID_EXT_KEY_USAGE, false, |writer| {
writer.write_sequence(|writer| {
for usage in self.extended_key_usages.iter() {
let oid = ObjectIdentifier::from_slice(usage.oid());
writer.next().write_oid(&oid);
}
});
});
}
if let Some(name_constraints) = &self.name_constraints {
if !name_constraints.is_empty() {
Self::write_extension(writer.next(), OID_NAME_CONSTRAINTS, true, |writer| {
writer.write_sequence(|writer| {
if !name_constraints.permitted_subtrees.is_empty() {
write_general_subtrees(writer.next(), 0, &name_constraints.permitted_subtrees);
}
if !name_constraints.excluded_subtrees.is_empty() {
write_general_subtrees(writer.next(), 1, &name_constraints.excluded_subtrees);
}
});
});
}
}
match self.is_ca {
IsCa::Ca(ref constraint) => {
Self::write_extension(writer.next(), OID_SUBJECT_KEY_IDENTIFIER, false, |writer| {
let key_identifier = self.key_identifier(pub_key);
writer.write_bytes(key_identifier.as_ref());
});
Self::write_extension(writer.next(), OID_BASIC_CONSTRAINTS, true, |writer| {
writer.write_sequence(|writer| {
writer.next().write_bool(true); if let BasicConstraints::Constrained(path_len_constraint) = constraint {
writer.next().write_u8(*path_len_constraint);
}
});
});
}
IsCa::ExplicitNoCa => {
Self::write_extension(writer.next(), OID_SUBJECT_KEY_IDENTIFIER, false, |writer| {
let key_identifier = self.key_identifier(pub_key);
writer.write_bytes(key_identifier.as_ref());
});
Self::write_extension(writer.next(), OID_BASIC_CONSTRAINTS, true, |writer| {
writer.write_sequence(|writer| {
writer.next().write_bool(false); });
});
}
IsCa::NoCa => {}
}
for ext in &self.custom_extensions {
writer.next().write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(&ext.oid);
writer.next().write_oid(&oid);
if ext.critical {
writer.next().write_bool(true);
}
writer.next().write_bytes(&ext.content);
});
}
});
});
}
Ok(())
})
}
fn write_extension(writer :DERWriter, extension_oid :&[u64], is_critical :bool, value_serializer :impl FnOnce(DERWriter)) {
writer.write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(extension_oid);
writer.next().write_oid(&oid);
if is_critical {
writer.next().write_bool(true);
}
let bytes = yasna::construct_der(value_serializer);
writer.next().write_bytes(&bytes);
})
}
fn key_identifier<K: PublicKeyData>(&self, pub_key: &K) -> Vec<u8> {
let digest_method = match self.key_identifier_method {
KeyIdMethod::Sha256 => &digest::SHA256,
KeyIdMethod::Sha384 => &digest::SHA384,
KeyIdMethod::Sha512 => &digest::SHA512,
};
let digest = digest::digest(digest_method, pub_key.raw_bytes());
let truncated_digest = &digest.as_ref()[0..20];
truncated_digest.to_vec()
}
fn serialize_der_with_signer<K: PublicKeyData>(&self, pub_key: &K, ca :&Certificate) -> Result<Vec<u8>, RcgenError> {
yasna::try_construct_der(|writer| {
writer.write_sequence(|writer| {
let tbs_cert_list_serialized = yasna::try_construct_der(|writer| {
self.write_cert(writer, pub_key, ca)?;
Ok::<(), RcgenError>(())
})?;
writer.next().write_der(&tbs_cert_list_serialized);
ca.params.alg.write_alg_ident(writer.next());
ca.key_pair.sign(&tbs_cert_list_serialized, writer.next())?;
Ok(())
})
})
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum IsCa {
NoCa,
ExplicitNoCa,
Ca(BasicConstraints),
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum BasicConstraints {
Unconstrained,
Constrained(u8),
}
impl CertificateParams {
pub fn new(subject_alt_names :impl Into<Vec<String>>) -> Self {
let subject_alt_names = subject_alt_names.into()
.into_iter()
.map(|s| SanType::DnsName(s))
.collect::<Vec<_>>();
CertificateParams {
subject_alt_names,
.. Default::default()
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct NameConstraints {
pub permitted_subtrees :Vec<GeneralSubtree>,
pub excluded_subtrees :Vec<GeneralSubtree>,
}
impl NameConstraints {
fn is_empty(&self) -> bool {
self.permitted_subtrees.is_empty() && self.excluded_subtrees.is_empty()
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum KeyUsagePurpose {
DigitalSignature,
ContentCommitment,
KeyEncipherment,
DataEncipherment,
KeyAgreement,
KeyCertSign,
CrlSign,
EncipherOnly,
DecipherOnly,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum ExtendedKeyUsagePurpose {
Any,
ServerAuth,
ClientAuth,
CodeSigning,
EmailProtection,
TimeStamping,
OcspSigning,
}
impl ExtendedKeyUsagePurpose {
fn oid(&self) -> &'static [u64] {
use ExtendedKeyUsagePurpose::*;
match self {
Any => &[2, 5, 29, 37],
ServerAuth => &[1, 3, 6, 1, 5, 5, 7, 3, 1],
ClientAuth => &[1, 3, 6, 1, 5, 5, 7, 3, 2],
CodeSigning => &[1, 3, 6, 1, 5, 5, 7, 3, 3],
EmailProtection => &[1, 3, 6, 1, 5, 5, 7, 3, 4],
TimeStamping => &[1, 3, 6, 1, 5, 5, 7, 3, 8],
OcspSigning => &[1, 3, 6, 1, 5, 5, 7, 3, 9],
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct CustomExtension {
oid :Vec<u64>,
critical :bool,
content :Vec<u8>,
}
impl CustomExtension {
pub fn new_acme_identifier(sha_digest :&[u8]) -> Self {
assert_eq!(sha_digest.len(), 32, "wrong size of sha_digest");
let content = yasna::construct_der(|writer| {
writer.write_bytes(sha_digest);
});
Self {
oid : OID_PE_ACME.to_owned(),
critical : true,
content,
}
}
pub fn from_oid_content(oid :&[u64], content :Vec<u8>) -> Self {
Self {
oid : oid.to_owned(),
critical : false,
content,
}
}
pub fn set_criticality(&mut self, criticality :bool) {
self.critical = criticality;
}
pub fn criticality(&self) -> bool {
self.critical
}
pub fn content(&self) -> &[u8] {
&self.content
}
pub fn oid_components(&self) -> impl Iterator<Item = u64> + '_ {
self.oid.iter().copied()
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[non_exhaustive]
pub enum KeyIdMethod {
Sha256,
Sha384,
Sha512,
}
pub fn date_time_ymd(year :i32, month :u8, day :u8) -> OffsetDateTime {
let month = Month::try_from(month).expect("out-of-range month");
let primitive_dt = PrimitiveDateTime::new(
Date::from_calendar_date(year, month, day).expect("invalid or out-of-range date"),
Time::MIDNIGHT
);
primitive_dt.assume_utc()
}
fn dt_strip_nanos(dt :OffsetDateTime) -> OffsetDateTime {
let time = Time::from_hms(dt.hour(), dt.minute(), dt.second())
.expect("invalid or out-of-range time");
dt.replace_time(time)
}
fn dt_to_generalized(dt :OffsetDateTime) -> GeneralizedTime {
let date_time = dt_strip_nanos(dt);
GeneralizedTime::from_datetime(date_time)
}
fn write_dt_utc_or_generalized(writer :DERWriter, dt :OffsetDateTime) {
if (1950..2050).contains(&dt.year()) {
let date_time = dt_strip_nanos(dt);
let ut = UTCTime::from_datetime(date_time);
writer.write_utctime(&ut);
} else {
let gt = dt_to_generalized(dt);
writer.write_generalized_time(>);
}
}
fn write_distinguished_name(writer :DERWriter, dn :&DistinguishedName) {
writer.write_sequence(|writer| {
for (ty, content) in dn.iter() {
writer.next().write_set(|writer| {
writer.next().write_sequence(|writer| {
writer.next().write_oid(&ty.to_oid());
match content {
DnValue::TeletexString(s) => writer.next().write_tagged_implicit(TAG_TELETEXSTRING, |writer| {
writer.write_bytes(s)
}),
DnValue::PrintableString(s) => writer.next().write_printable_string(s),
DnValue::UniversalString(s) => writer.next().write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| {
writer.write_bytes(s)
}),
DnValue::Utf8String(s) => writer.next().write_utf8_string(s),
DnValue::BmpString(s) => writer.next().write_tagged_implicit(TAG_BMPSTRING, |writer| {
writer.write_bytes(s)
}),
}
});
});
}
});
}
fn write_general_subtrees(writer :DERWriter, tag :u64, general_subtrees :&[GeneralSubtree]) {
writer.write_tagged_implicit(Tag::context(tag), |writer| {
writer.write_sequence(|writer| {
for subtree in general_subtrees.iter() {
writer.next().write_sequence(|writer| {
writer.next().write_tagged_implicit(Tag::context(subtree.tag()), |writer| {
match subtree {
GeneralSubtree::Rfc822Name(name) |
GeneralSubtree::DnsName(name) => writer.write_ia5_string(name),
GeneralSubtree::DirectoryName(name) => write_distinguished_name(writer, name),
GeneralSubtree::IpAddress(subnet) => writer.write_bytes(&subnet.to_bytes()),
}
});
});
}
});
});
}
impl Certificate {
pub fn from_params(mut params :CertificateParams) -> Result<Self, RcgenError> {
let key_pair = if let Some(key_pair) = params.key_pair.take() {
if !key_pair.is_compatible(¶ms.alg) {
return Err(RcgenError::CertificateKeyPairMismatch);
}
key_pair
} else {
KeyPair::generate(¶ms.alg)?
};
Ok(Certificate {
params,
key_pair,
})
}
pub fn get_key_identifier(&self) -> Vec<u8> {
self.params.key_identifier(&self.key_pair)
}
pub fn serialize_der(&self) -> Result<Vec<u8>, RcgenError> {
self.serialize_der_with_signer(&self)
}
pub fn serialize_der_with_signer(&self, ca :&Certificate) -> Result<Vec<u8>, RcgenError> {
self.params.serialize_der_with_signer(&self.key_pair, ca)
}
pub fn serialize_request_der(&self) -> Result<Vec<u8>, RcgenError> {
yasna::try_construct_der(|writer| {
writer.write_sequence(|writer| {
let cert_data = yasna::construct_der(|writer| {
self.params.write_request(&self.key_pair, writer);
});
writer.next().write_der(&cert_data);
self.params.alg.write_alg_ident(writer.next());
self.key_pair.sign(&cert_data, writer.next())?;
Ok(())
})
})
}
pub fn get_key_pair(&self) -> &KeyPair {
&self.key_pair
}
#[cfg(feature = "pem")]
pub fn serialize_pem(&self) -> Result<String, RcgenError> {
let p = Pem {
tag : "CERTIFICATE".to_string(),
contents : self.serialize_der()?,
};
Ok(pem::encode(&p))
}
#[cfg(feature = "pem")]
pub fn serialize_pem_with_signer(&self, ca :&Certificate) -> Result<String, RcgenError> {
let p = Pem {
tag : "CERTIFICATE".to_string(),
contents : self.serialize_der_with_signer(ca)?,
};
Ok(pem::encode(&p))
}
#[cfg(feature = "pem")]
pub fn serialize_request_pem(&self) -> Result<String, RcgenError> {
let p = Pem {
tag : "CERTIFICATE REQUEST".to_string(),
contents : self.serialize_request_der()?,
};
Ok(pem::encode(&p))
}
pub fn serialize_private_key_der(&self) -> Vec<u8> {
self.key_pair.serialize_der()
}
#[cfg(feature = "pem")]
pub fn serialize_private_key_pem(&self) -> String {
self.key_pair.serialize_pem()
}
}
enum SignAlgo {
EcDsa(&'static EcdsaSigningAlgorithm),
EdDsa(&'static EdDSAParameters),
Rsa(),
}
#[allow(clippy::large_enum_variant)]
enum KeyPairKind {
Ec(EcdsaKeyPair),
Ed(Ed25519KeyPair),
Rsa(RsaKeyPair, &'static dyn RsaEncoding),
Remote(Box<dyn RemoteKeyPair + Send + Sync>),
}
impl fmt::Debug for KeyPairKind {
fn fmt(&self, f :&mut fmt::Formatter) -> fmt::Result {
match self {
Self::Ec(key_pair) => write!(f, "{:?}", key_pair),
Self::Ed(key_pair) => write!(f, "{:?}", key_pair),
Self::Rsa(key_pair, _) => write!(f, "{:?}", key_pair),
Self::Remote(_) => write!(f, "Box<dyn RemotePrivateKey>"),
}
}
}
#[derive(Debug)]
pub struct KeyPair {
kind :KeyPairKind,
alg :&'static SignatureAlgorithm,
serialized_der :Vec<u8>,
}
impl KeyPair {
pub fn from_der(der :&[u8]) -> Result<Self, RcgenError> {
Ok(der.try_into()?)
}
#[cfg(feature = "pem")]
pub fn from_pem(pem_str :&str) -> Result<Self, RcgenError> {
let private_key = pem::parse(pem_str)?;
let private_key_der :&[_] = &private_key.contents;
Ok(private_key_der.try_into()?)
}
pub fn from_remote(key_pair :Box<dyn RemoteKeyPair + Send + Sync>) -> Result<Self, RcgenError> {
Ok(Self {
alg : key_pair.algorithm(),
kind : KeyPairKind::Remote(key_pair),
serialized_der : Vec::new(),
})
}
#[cfg(feature = "pem")]
pub fn from_pem_and_sign_algo(pem_str :&str, alg :&'static SignatureAlgorithm) -> Result<Self, RcgenError> {
let private_key = pem::parse(pem_str)?;
let private_key_der :&[_] = &private_key.contents;
Ok(Self::from_der_and_sign_algo(private_key_der, alg)?)
}
pub fn from_der_and_sign_algo(pkcs8 :&[u8], alg :&'static SignatureAlgorithm) -> Result<Self, RcgenError> {
let pkcs8_vec = pkcs8.to_vec();
let kind = if alg == &PKCS_ED25519 {
KeyPairKind::Ed(Ed25519KeyPair::from_pkcs8_maybe_unchecked(pkcs8)?)
} else if alg == &PKCS_ECDSA_P256_SHA256 {
KeyPairKind::Ec(EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, pkcs8)?)
} else if alg == &PKCS_ECDSA_P384_SHA384 {
KeyPairKind::Ec(EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P384_SHA384_ASN1_SIGNING, pkcs8)?)
} else if alg == &PKCS_RSA_SHA256 {
let rsakp = RsaKeyPair::from_pkcs8(pkcs8)?;
KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA256)
} else if alg == &PKCS_RSA_SHA384 {
let rsakp = RsaKeyPair::from_pkcs8(pkcs8)?;
KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA384)
} else if alg == &PKCS_RSA_SHA512 {
let rsakp = RsaKeyPair::from_pkcs8(pkcs8)?;
KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA512)
} else if alg == &PKCS_RSA_PSS_SHA256 {
let rsakp = RsaKeyPair::from_pkcs8(pkcs8)?;
KeyPairKind::Rsa(rsakp, &signature::RSA_PSS_SHA256)
} else {
panic!("Unknown SignatureAlgorithm specified!");
};
Ok(KeyPair {
kind,
alg,
serialized_der : pkcs8_vec,
})
}
fn from_raw(pkcs8: &[u8]) -> Result<(KeyPairKind, &'static SignatureAlgorithm), RcgenError> {
let (kind, alg) = if let Ok(edkp) = Ed25519KeyPair::from_pkcs8_maybe_unchecked(pkcs8) {
(KeyPairKind::Ed(edkp), &PKCS_ED25519)
} else if let Ok(eckp) = EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P256_SHA256_ASN1_SIGNING, pkcs8) {
(KeyPairKind::Ec(eckp), &PKCS_ECDSA_P256_SHA256)
} else if let Ok(eckp) = EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P384_SHA384_ASN1_SIGNING, pkcs8) {
(KeyPairKind::Ec(eckp), &PKCS_ECDSA_P384_SHA384)
} else if let Ok(rsakp) = RsaKeyPair::from_pkcs8(pkcs8) {
(KeyPairKind::Rsa(rsakp, &signature::RSA_PKCS1_SHA256), &PKCS_RSA_SHA256)
} else {
return Err(RcgenError::CouldNotParseKeyPair);
};
Ok((kind, alg))
}
}
pub trait RemoteKeyPair {
fn public_key(&self) -> &[u8];
fn sign(&self, msg :&[u8]) -> Result<Vec<u8>, RcgenError>;
fn algorithm(&self) -> &'static SignatureAlgorithm;
}
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum RcgenError {
CouldNotParseCertificate,
CouldNotParseCertificationRequest,
CouldNotParseKeyPair,
#[cfg(feature = "x509-parser")]
InvalidNameType,
KeyGenerationUnavailable,
#[cfg(feature = "x509-parser")]
UnsupportedExtension,
UnsupportedSignatureAlgorithm,
RingUnspecified,
RingKeyRejected(&'static str),
CertificateKeyPairMismatch,
Time,
#[cfg(feature = "pem")]
PemError(pem::PemError),
RemoteKeyError,
}
impl fmt::Display for RcgenError {
fn fmt(&self, f :&mut fmt::Formatter) -> fmt::Result {
use self::RcgenError::*;
match self {
CouldNotParseCertificate => write!(f, "Could not parse certificate")?,
CouldNotParseCertificationRequest => write!(f, "Could not parse certificate signing \
request")?,
CouldNotParseKeyPair => write!(f, "Could not parse key pair")?,
#[cfg(feature = "x509-parser")]
InvalidNameType => write!(f, "Invalid subject alternative name type")?,
KeyGenerationUnavailable => write!(f, "There is no support for generating \
keys for the given algorithm")?,
UnsupportedSignatureAlgorithm => write!(f, "The requested signature algorithm \
is not supported")?,
#[cfg(feature = "x509-parser")]
UnsupportedExtension => write!(f, "Unsupported extension requested in CSR")?,
RingUnspecified => write!(f, "Unspecified ring error")?,
RingKeyRejected(e) => write!(f, "Key rejected by ring: {}", e)?,
CertificateKeyPairMismatch => write!(f, "The provided certificate's signature \
algorithm is incompatible with the given key pair")?,
Time => write!(f, "Time error")?,
RemoteKeyError => write!(f, "Remote key error")?,
#[cfg(feature = "pem")]
PemError(e) => write!(f, "PEM error: {}", e)?,
};
Ok(())
}
}
impl Error for RcgenError {}
impl From<ring::error::Unspecified> for RcgenError {
fn from(_unspecified :ring::error::Unspecified) -> Self {
RcgenError::RingUnspecified
}
}
impl From<ring::error::KeyRejected> for RcgenError {
fn from(err :ring::error::KeyRejected) -> Self {
RcgenError::RingKeyRejected(err.description_())
}
}
#[cfg(feature = "pem")]
impl From<pem::PemError> for RcgenError {
fn from(e :pem::PemError) -> Self {
RcgenError::PemError(e)
}
}
impl TryFrom<&[u8]> for KeyPair {
type Error = RcgenError;
fn try_from(pkcs8: &[u8]) -> Result<KeyPair, RcgenError> {
let (kind, alg) = KeyPair::from_raw(pkcs8)?;
Ok(KeyPair {
kind,
alg,
serialized_der: pkcs8.to_vec(),
})
}
}
impl TryFrom<Vec<u8>> for KeyPair {
type Error = RcgenError;
fn try_from(pkcs8: Vec<u8>) -> Result<KeyPair, RcgenError> {
let (kind, alg) = KeyPair::from_raw(pkcs8.as_slice())?;
Ok(KeyPair {
kind,
alg,
serialized_der: pkcs8,
})
}
}
impl KeyPair {
pub fn generate(alg :&'static SignatureAlgorithm) -> Result<Self, RcgenError> {
let system_random = SystemRandom::new();
match alg.sign_alg {
SignAlgo::EcDsa(sign_alg) => {
let key_pair_doc = EcdsaKeyPair::generate_pkcs8(sign_alg, &system_random)?;
let key_pair_serialized = key_pair_doc.as_ref().to_vec();
let key_pair = EcdsaKeyPair::from_pkcs8(&sign_alg, &&key_pair_doc.as_ref()).unwrap();
Ok(KeyPair {
kind : KeyPairKind::Ec(key_pair),
alg,
serialized_der : key_pair_serialized,
})
},
SignAlgo::EdDsa(_sign_alg) => {
let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&system_random)?;
let key_pair_serialized = key_pair_doc.as_ref().to_vec();
let key_pair = Ed25519KeyPair::from_pkcs8(&&key_pair_doc.as_ref()).unwrap();
Ok(KeyPair {
kind : KeyPairKind::Ed(key_pair),
alg,
serialized_der : key_pair_serialized,
})
},
SignAlgo::Rsa() => Err(RcgenError::KeyGenerationUnavailable),
}
}
pub fn public_key_raw(&self) -> &[u8] {
self.raw_bytes()
}
pub fn is_compatible(&self, signature_algorithm :&SignatureAlgorithm) -> bool {
self.alg == signature_algorithm
}
pub fn compatible_algs(&self)
-> impl Iterator<Item=&'static SignatureAlgorithm> {
std::iter::once(self.alg)
}
fn sign(&self, msg :&[u8], writer :DERWriter) -> Result<(), RcgenError> {
match &self.kind {
KeyPairKind::Ec(kp) => {
let system_random = SystemRandom::new();
let signature = kp.sign(&system_random, msg)?;
let sig = &signature.as_ref();
writer.write_bitvec_bytes(&sig, &sig.len() * 8);
},
KeyPairKind::Ed(kp) => {
let signature = kp.sign(msg);
let sig = &signature.as_ref();
writer.write_bitvec_bytes(&sig, &sig.len() * 8);
},
KeyPairKind::Rsa(kp, padding_alg) => {
let system_random = SystemRandom::new();
let mut signature = vec![0; kp.public_modulus_len()];
kp.sign(*padding_alg, &system_random,
msg, &mut signature)?;
let sig = &signature.as_ref();
writer.write_bitvec_bytes(&sig, &sig.len() * 8);
},
KeyPairKind::Remote(kp) => {
let signature = kp.sign(msg)?;
writer.write_bitvec_bytes(&signature, &signature.len() * 8);
},
}
Ok(())
}
pub fn public_key_der(&self) -> Vec<u8> {
yasna::construct_der(|writer| self.serialize_public_key_der(writer))
}
#[cfg(feature = "pem")]
pub fn public_key_pem(&self) -> String {
let p = Pem {
tag : "PUBLIC KEY".to_string(),
contents : self.public_key_der(),
};
pem::encode(&p)
}
pub fn serialize_der(&self) -> Vec<u8> {
if let KeyPairKind::Remote(_) = self.kind {
panic!("Serializing a remote key pair is not supported")
}
self.serialized_der.clone()
}
pub fn serialized_der(&self) -> &[u8] {
if let KeyPairKind::Remote(_) = self.kind {
panic!("Serializing a remote key pair is not supported")
}
&self.serialized_der
}
#[cfg(feature = "pem")]
pub fn serialize_pem(&self) -> String {
let p = Pem {
tag : "PRIVATE KEY".to_string(),
contents : self.serialize_der(),
};
pem::encode(&p)
}
}
impl PublicKeyData for KeyPair {
fn alg(&self) -> &SignatureAlgorithm {
self.alg
}
fn raw_bytes(&self) -> &[u8] {
match &self.kind {
KeyPairKind::Ec(kp) => kp.public_key().as_ref(),
KeyPairKind::Ed(kp) => kp.public_key().as_ref(),
KeyPairKind::Rsa(kp, _) => kp.public_key().as_ref(),
KeyPairKind::Remote(kp) => kp.public_key(),
}
}
}
trait PublicKeyData {
fn alg(&self) -> &SignatureAlgorithm;
fn raw_bytes(&self) -> &[u8];
fn serialize_public_key_der(&self, writer :DERWriter) {
writer.write_sequence(|writer| {
self.alg().write_oids_sign_alg(writer.next());
let pk = self.raw_bytes();
writer.next().write_bitvec_bytes(&pk, pk.len() * 8);
})
}
}
#[derive(PartialEq, Eq)]
enum SignatureAlgorithmParams {
None,
Null,
RsaPss {
hash_algorithm :&'static [u64],
salt_length :u64,
},
}
pub struct SignatureAlgorithm {
oids_sign_alg :&'static [&'static [u64]],
sign_alg :SignAlgo,
oid_components :&'static [u64],
params :SignatureAlgorithmParams,
}
impl fmt::Debug for SignatureAlgorithm {
fn fmt(&self, f :&mut fmt::Formatter) -> fmt::Result {
if self == &PKCS_RSA_SHA256 {
write!(f, "PKCS_RSA_SHA256")
} else if self == &PKCS_RSA_SHA384 {
write!(f, "PKCS_RSA_SHA384")
} else if self == &PKCS_RSA_SHA512 {
write!(f, "PKCS_RSA_SHA512")
} else if self == &PKCS_RSA_PSS_SHA256 {
write!(f, "PKCS_RSA_PSS_SHA256")
} else if self == &PKCS_ECDSA_P256_SHA256 {
write!(f, "PKCS_ECDSA_P256_SHA256")
} else if self == &PKCS_ECDSA_P384_SHA384 {
write!(f, "PKCS_ECDSA_P384_SHA384")
} else if self == &PKCS_ED25519 {
write!(f, "PKCS_ED25519")
} else {
write!(f, "Unknown")
}
}
}
impl PartialEq for SignatureAlgorithm {
fn eq(&self, other :&Self) -> bool {
(self.oids_sign_alg, self.oid_components) == (other.oids_sign_alg, other.oid_components)
}
}
impl Eq for SignatureAlgorithm {}
impl Hash for SignatureAlgorithm {
fn hash<H: Hasher>(&self, state: &mut H) {
self.oids_sign_alg.hash(state);
}
}
impl SignatureAlgorithm {
fn iter() -> std::slice::Iter<'static, &'static SignatureAlgorithm> {
static ALGORITHMS :&[&SignatureAlgorithm] = &[
&PKCS_RSA_SHA256,
&PKCS_RSA_SHA384,
&PKCS_RSA_SHA512,
&PKCS_ECDSA_P256_SHA256,
&PKCS_ECDSA_P384_SHA384,
&PKCS_ED25519
];
ALGORITHMS.iter()
}
pub fn from_oid(oid :&[u64]) -> Result<&'static SignatureAlgorithm, RcgenError> {
for algo in Self::iter() {
if algo.oid_components == oid {
return Ok(algo);
}
}
Err(RcgenError::UnsupportedSignatureAlgorithm)
}
}
pub static PKCS_RSA_SHA256 :SignatureAlgorithm = SignatureAlgorithm {
oids_sign_alg :&[&OID_RSA_ENCRYPTION],
sign_alg :SignAlgo::Rsa(),
oid_components : &[1, 2, 840, 113549, 1, 1, 11],
params : SignatureAlgorithmParams::Null,
};
pub static PKCS_RSA_SHA384 :SignatureAlgorithm = SignatureAlgorithm {
oids_sign_alg :&[&OID_RSA_ENCRYPTION],
sign_alg :SignAlgo::Rsa(),
oid_components : &[1, 2, 840, 113549, 1, 1, 12],
params : SignatureAlgorithmParams::Null,
};
pub static PKCS_RSA_SHA512 :SignatureAlgorithm = SignatureAlgorithm {
oids_sign_alg :&[&OID_RSA_ENCRYPTION],
sign_alg :SignAlgo::Rsa(),
oid_components : &[1, 2, 840, 113549, 1, 1, 13],
params : SignatureAlgorithmParams::Null,
};
static PKCS_RSA_PSS_SHA256 :SignatureAlgorithm = SignatureAlgorithm {
oids_sign_alg :&[&OID_RSASSA_PSS],
sign_alg :SignAlgo::Rsa(),
oid_components : &OID_RSASSA_PSS,params : SignatureAlgorithmParams::RsaPss {
hash_algorithm : &[2, 16, 840, 1, 101, 3, 4, 2, 1],
salt_length : 20,
},
};
pub static PKCS_ECDSA_P256_SHA256 :SignatureAlgorithm = SignatureAlgorithm {
oids_sign_alg :&[&OID_EC_PUBLIC_KEY, &OID_EC_SECP_256_R1],
sign_alg :SignAlgo::EcDsa(&signature::ECDSA_P256_SHA256_ASN1_SIGNING),
oid_components : &[1, 2, 840, 10045, 4, 3, 2],
params : SignatureAlgorithmParams::None,
};
pub static PKCS_ECDSA_P384_SHA384 :SignatureAlgorithm = SignatureAlgorithm {
oids_sign_alg :&[&OID_EC_PUBLIC_KEY, &OID_EC_SECP_384_R1],
sign_alg :SignAlgo::EcDsa(&signature::ECDSA_P384_SHA384_ASN1_SIGNING),
oid_components : &[1, 2, 840, 10045, 4, 3, 3],
params : SignatureAlgorithmParams::None,
};
pub static PKCS_ED25519 :SignatureAlgorithm = SignatureAlgorithm {
oids_sign_alg :&[&[1, 3, 101, 112]],
sign_alg :SignAlgo::EdDsa(&signature::ED25519),
oid_components : &[1, 3, 101, 112],
params : SignatureAlgorithmParams::None,
};
impl SignatureAlgorithm {
fn alg_ident_oid(&self) -> ObjectIdentifier {
ObjectIdentifier::from_slice(self.oid_components)
}
fn write_params(&self, writer :&mut yasna::DERWriterSeq) {
match self.params {
SignatureAlgorithmParams::None => (),
SignatureAlgorithmParams::Null => {
writer.next().write_null();
},
SignatureAlgorithmParams::RsaPss {
hash_algorithm, salt_length,
} => {
writer.next().write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(hash_algorithm);
writer.next().write_tagged(Tag::context(0), |writer| {
writer.write_sequence(|writer| {
writer.next().write_oid(&oid);
});
});
writer.next().write_tagged(Tag::context(1), |writer| {
writer.write_sequence(|writer| {
const ID_MGF1 :&[u64] = &[1, 2, 840, 113549, 1, 1, 8];
let oid = ObjectIdentifier::from_slice(ID_MGF1);
writer.next().write_oid(&oid);
writer.next().write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(hash_algorithm);
writer.next().write_oid(&oid);
writer.next().write_null();
});
});
});
writer.next().write_tagged(Tag::context(2), |writer| {
writer.write_u64(salt_length);
});
})
},
}
}
fn write_alg_ident(&self, writer :DERWriter) {
writer.write_sequence(|writer| {
writer.next().write_oid(&self.alg_ident_oid());
self.write_params(writer);
});
}
fn write_oids_sign_alg(&self, writer :DERWriter) {
writer.write_sequence(|writer| {
for oid in self.oids_sign_alg {
let oid = ObjectIdentifier::from_slice(oid);
writer.next().write_oid(&oid);
}
self.write_params(writer);
});
}
}
#[cfg(feature = "zeroize")]
impl zeroize::Zeroize for KeyPair {
fn zeroize(&mut self) {
self.serialized_der.zeroize();
}
}
#[cfg(feature = "zeroize")]
impl zeroize::Zeroize for Certificate {
fn zeroize(&mut self) {
self.params.zeroize();
self.key_pair.zeroize();
}
}
#[cfg(feature = "zeroize")]
impl zeroize::Zeroize for CertificateSigningRequest {
fn zeroize(&mut self) {
self.params.zeroize();
}
}
#[cfg(feature = "zeroize")]
impl zeroize::Zeroize for CertificateParams {
fn zeroize(&mut self) {
self.key_pair.zeroize();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::panic::catch_unwind;
fn get_times() -> [OffsetDateTime; 2] {
let dt_nanos = {
let date = Date::from_calendar_date(2020, Month::December, 3).unwrap();
let time = Time::from_hms_nano(0, 0, 1, 444).unwrap();
PrimitiveDateTime::new(date, time).assume_utc()
};
let dt_zero = {
let date = Date::from_calendar_date(2020, Month::December, 3).unwrap();
let time = Time::from_hms_nano(0, 0, 1, 0).unwrap();
PrimitiveDateTime::new(date, time).assume_utc()
};
[dt_nanos, dt_zero]
}
#[test]
fn test_dt_utc_strip_nanos() {
let times = get_times();
let res = catch_unwind(|| UTCTime::from_datetime(times[0]));
assert!(res.is_err());
for dt in times {
let date_time = dt_strip_nanos(dt);
assert_eq!(date_time.time().nanosecond(), 0);
let _ut = UTCTime::from_datetime(date_time);
}
}
#[test]
fn test_dt_to_generalized() {
let times = get_times();
for dt in times {
let _gt = dt_to_generalized(dt);
}
}
#[test]
fn test_with_key_usages() {
let mut params: CertificateParams = Default::default();
params.key_usages = vec![
KeyUsagePurpose::DigitalSignature,
KeyUsagePurpose::KeyEncipherment,
KeyUsagePurpose::ContentCommitment,
];
params.is_ca = IsCa::Ca(BasicConstraints::Constrained(0));
let cert = Certificate::from_params(params).unwrap();
let der = cert.serialize_der().unwrap();
let (_rem, cert) = x509_parser::parse_x509_certificate(&der).unwrap();
let key_usage_oid_str= "2.5.29.15";
let mut found = false;
for ext in cert.extensions() {
if key_usage_oid_str == ext.oid.to_id_string() {
match ext.parsed_extension() {
x509_parser::extensions::ParsedExtension::KeyUsage(usage) =>{
assert!(usage.flags == 7);
found = true;
}
_ => {}
}
}
}
assert!(found);
}
#[test]
fn test_with_key_usages_decipheronly_only() {
let mut params: CertificateParams = Default::default();
params.key_usages = vec![KeyUsagePurpose::DecipherOnly];
params.is_ca = IsCa::Ca(BasicConstraints::Constrained(0));
let cert = Certificate::from_params(params).unwrap();
let der = cert.serialize_der().unwrap();
let (_rem, cert) = x509_parser::parse_x509_certificate(&der).unwrap();
let key_usage_oid_str= "2.5.29.15";
let mut found = false;
for ext in cert.extensions() {
if key_usage_oid_str == ext.oid.to_id_string() {
match ext.parsed_extension() {
x509_parser::extensions::ParsedExtension::KeyUsage(usage) =>{
assert!(usage.flags == 256);
found = true;
}
_ => {}
}
}
}
assert!(found);
}
#[test]
fn signature_algos_different() {
for (i, alg_i) in SignatureAlgorithm::iter().enumerate() {
for (j, alg_j) in SignatureAlgorithm::iter().enumerate() {
assert_eq!(alg_i == alg_j, i == j,
"Algorighm relationship mismatch for algorithm index pair {} and {}", i, j);
}
}
}
}