use alloc::vec;
use core::fmt;
use der::{Encode, asn1::BitString, referenced::OwnedToRef};
use signature::{
AsyncRandomizedSigner, AsyncSigner, Keypair, RandomizedSigner, Signer, rand_core::CryptoRng,
};
use spki::{
DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier, SignatureBitStringEncoding,
};
use crate::{
AlgorithmIdentifier, SubjectPublicKeyInfo,
certificate::{self, Certificate, TbsCertificate, Version},
crl::{CertificateList, RevokedCert, TbsCertList},
ext::{
Extensions, ToExtension,
pkix::{AuthorityKeyIdentifier, CrlNumber, SubjectKeyIdentifier},
},
serial_number::SerialNumber,
time::{Time, Validity},
};
pub mod profile;
use self::profile::BuilderProfile;
#[deprecated(
since = "0.3.0",
note = "please use `x509_cert::builder::profile::BuilderProfile` instead"
)]
pub use self::profile::BuilderProfile as Profile;
#[deprecated(
since = "0.3.0",
note = "please use `x509_cert::request::RequestBuilder` instead"
)]
pub use crate::request::RequestBuilder;
pub(crate) const NULL_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.0.0");
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Asn1(der::Error),
PublicKey(spki::Error),
Signature(signature::Error),
NonUniqueRdn,
NonUniqueATV,
InvalidAttribute {
oid: ObjectIdentifier,
},
MissingAttributes,
}
impl core::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Asn1(err) => write!(f, "ASN.1 error: {err}"),
Error::PublicKey(err) => write!(f, "public key error: {err}"),
Error::Signature(err) => write!(f, "signature error: {err}"),
Error::NonUniqueRdn => write!(
f,
"Each RelativeDistinguishedName MUST contain exactly one AttributeTypeAndValue."
),
Error::NonUniqueATV => write!(
f,
"Each Name MUST NOT contain more than one instance of a given AttributeTypeAndValue"
),
Error::InvalidAttribute { oid } => write!(
f,
"Non-ordered attribute or invalid attribute found (oid={oid})"
),
Error::MissingAttributes => write!(f, "Not all required elements were specified"),
}
}
}
impl From<der::Error> for Error {
fn from(err: der::Error) -> Error {
Error::Asn1(err)
}
}
impl From<spki::Error> for Error {
fn from(err: spki::Error) -> Error {
Error::PublicKey(err)
}
}
impl From<signature::Error> for Error {
fn from(err: signature::Error) -> Error {
Error::Signature(err)
}
}
pub type Result<T> = core::result::Result<T, Error>;
#[cfg_attr(feature = "std", doc = "```")]
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
pub struct CertificateBuilder<P> {
tbs: TbsCertificate,
extensions: Extensions,
profile: P,
}
impl<P> CertificateBuilder<P>
where
P: BuilderProfile,
{
pub fn new(
profile: P,
serial_number: SerialNumber,
mut validity: Validity,
subject_public_key_info: SubjectPublicKeyInfo,
) -> Result<Self> {
let signature_alg = AlgorithmIdentifier {
oid: NULL_OID,
parameters: None,
};
let subject = profile.get_subject();
let issuer = profile.get_issuer(&subject);
validity.not_before.rfc5280_adjust_utc_time()?;
validity.not_after.rfc5280_adjust_utc_time()?;
let tbs = TbsCertificate {
version: Version::V3,
serial_number,
signature: signature_alg,
issuer,
validity,
subject,
subject_public_key_info,
extensions: None,
issuer_unique_id: None,
subject_unique_id: None,
};
let extensions = Extensions::default();
Ok(Self {
tbs,
extensions,
profile,
})
}
pub fn add_extension<E: ToExtension>(
&mut self,
extension: E,
) -> core::result::Result<(), E::Error> {
let ext = extension.to_extension(&self.tbs.subject, &self.extensions)?;
self.extensions.push(ext);
Ok(())
}
}
pub trait Builder: Sized {
type Output: Sized;
fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey;
fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey;
#[cfg_attr(feature = "std", doc = "```no_run")]
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
fn build<S, Signature>(mut self, signer: &S) -> Result<Self::Output>
where
S: Signer<Signature>,
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
Signature: SignatureBitStringEncoding,
{
let blob = self.finalize(signer)?;
let signature = signer.try_sign(&blob)?.to_bitstring()?;
self.assemble(signature, signer)
}
#[cfg_attr(feature = "std", doc = "```no_run")]
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
fn build_with_rng<S, Signature, R>(mut self, signer: &S, rng: &mut R) -> Result<Self::Output>
where
S: RandomizedSigner<Signature>,
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
Signature: SignatureBitStringEncoding,
R: CryptoRng + ?Sized,
{
let blob = self.finalize(signer)?;
let signature = signer.try_sign_with_rng(rng, &blob)?.to_bitstring()?;
self.assemble(signature, signer)
}
}
impl<P> Builder for CertificateBuilder<P>
where
P: BuilderProfile,
{
type Output = Certificate;
fn finalize<S>(&mut self, cert_signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
let verifying_key = cert_signer.verifying_key();
let signer_pub = SubjectPublicKeyInfo::from_key(&verifying_key)?;
self.tbs.signature = cert_signer.signature_algorithm_identifier()?;
let mut default_extensions = self.profile.build_extensions(
self.tbs.subject_public_key_info.owned_to_ref(),
signer_pub.owned_to_ref(),
&self.tbs,
)?;
self.extensions.append(&mut default_extensions);
if !self.extensions.is_empty() {
self.tbs.extensions = Some(self.extensions.clone());
}
if self.tbs.extensions.is_none() {
if self.tbs.issuer_unique_id.is_some() || self.tbs.subject_unique_id.is_some() {
self.tbs.version = Version::V2;
} else {
self.tbs.version = Version::V1;
}
}
self.tbs.to_der().map_err(Error::from)
}
fn assemble<S>(self, signature: BitString, _signer: &S) -> Result<Self::Output>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
let signature_algorithm = self.tbs.signature.clone();
Ok(Certificate {
tbs_certificate: self.tbs,
signature_algorithm,
signature,
})
}
}
#[allow(async_fn_in_trait)]
pub trait AsyncBuilder: Sized {
type Output: Sized;
fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey;
fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey;
#[cfg_attr(feature = "std", doc = "```no_run")]
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
async fn build_async<S, Signature>(mut self, signer: &S) -> Result<Self::Output>
where
S: AsyncSigner<Signature>,
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
Signature: SignatureBitStringEncoding,
{
let blob = self.finalize(signer)?;
let signature = signer.sign_async(&blob).await?.to_bitstring()?;
self.assemble(signature, signer)
}
#[cfg_attr(feature = "std", doc = "```no_run")]
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
async fn build_with_rng_async<S, Signature, R>(
mut self,
signer: &S,
rng: &mut R,
) -> Result<Self::Output>
where
S: AsyncRandomizedSigner<Signature>,
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
Signature: SignatureBitStringEncoding,
R: CryptoRng + ?Sized,
{
let blob = self.finalize(signer)?;
let signature = signer
.try_sign_with_rng_async(rng, &blob)
.await?
.to_bitstring()?;
self.assemble(signature, signer)
}
}
impl<T> AsyncBuilder for T
where
T: Builder,
{
type Output = <T as Builder>::Output;
fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
<T as Builder>::assemble(self, signature, signer)
}
fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
<T as Builder>::finalize(self, signer)
}
}
pub struct CrlBuilder<P = certificate::Rfc5280>
where
P: certificate::Profile,
{
tbs: TbsCertList<P>,
}
impl<P> CrlBuilder<P>
where
P: certificate::Profile,
{
#[cfg(feature = "std")]
pub fn new(issuer: &Certificate, crl_number: CrlNumber) -> der::Result<Self> {
let this_update = Time::now()?;
Self::new_with_this_update(issuer, crl_number, this_update)
}
pub fn new_with_this_update(
issuer: &Certificate,
crl_number: CrlNumber,
this_update: Time,
) -> der::Result<Self> {
let signature_alg = AlgorithmIdentifier {
oid: NULL_OID,
parameters: None,
};
let issuer_name = issuer.tbs_certificate.subject().clone();
let mut crl_extensions = Extensions::new();
crl_extensions.push(crl_number.to_extension(&issuer_name, &crl_extensions)?);
let aki = match issuer
.tbs_certificate
.get_extension::<AuthorityKeyIdentifier>()?
{
Some((_, aki)) => aki,
None => {
let ski = SubjectKeyIdentifier::try_from(
issuer
.tbs_certificate
.subject_public_key_info()
.owned_to_ref(),
)?;
AuthorityKeyIdentifier {
key_identifier: Some(ski.0.clone()),
..Default::default()
}
}
};
crl_extensions.push(aki.to_extension(&issuer_name, &crl_extensions)?);
let tbs = TbsCertList {
version: Version::V2,
signature: signature_alg,
issuer: issuer_name,
this_update,
next_update: None,
revoked_certificates: None,
crl_extensions: Some(crl_extensions),
};
Ok(Self { tbs })
}
pub fn with_next_update(mut self, next_update: Option<Time>) -> Self {
self.tbs.next_update = next_update;
self
}
pub fn with_certificates<I>(mut self, revoked: I) -> Self
where
I: Iterator<Item = RevokedCert<P>>,
{
let certificates = self
.tbs
.revoked_certificates
.get_or_insert_with(vec::Vec::new);
let mut revoked: vec::Vec<RevokedCert<P>> = revoked.collect();
certificates.append(&mut revoked);
self
}
}
impl<P> Builder for CrlBuilder<P>
where
P: certificate::Profile,
{
type Output = CertificateList<P>;
fn finalize<S>(&mut self, cert_signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
self.tbs.signature = cert_signer.signature_algorithm_identifier()?;
self.tbs.to_der().map_err(Error::from)
}
fn assemble<S>(self, signature: BitString, _signer: &S) -> Result<Self::Output>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
let signature_algorithm = self.tbs.signature.clone();
Ok(CertificateList {
tbs_cert_list: self.tbs,
signature_algorithm,
signature,
})
}
}