use super::{unix_time::UnixTime, CertType, Certificate, Field, OptionsMap};
use crate::{public, Result, Signature, SigningKey};
use alloc::{string::String, vec::Vec};
#[cfg(feature = "rand_core")]
use rand_core::CryptoRngCore;
#[cfg(feature = "std")]
use std::time::SystemTime;
#[cfg(doc)]
use crate::PrivateKey;
#[cfg_attr(
all(feature = "ed25519", feature = "getrandom", feature = "std"),
doc = " ```"
)]
#[cfg_attr(
not(all(feature = "ed25519", feature = "getrandom", feature = "std")),
doc = " ```ignore"
)]
pub struct Builder {
public_key: public::KeyData,
nonce: Vec<u8>,
serial: Option<u64>,
cert_type: Option<CertType>,
key_id: Option<String>,
valid_principals: Option<Vec<String>>,
valid_after: UnixTime,
valid_before: UnixTime,
critical_options: OptionsMap,
extensions: OptionsMap,
comment: Option<String>,
}
impl Builder {
pub const RECOMMENDED_NONCE_SIZE: usize = 16;
pub fn new(
nonce: impl Into<Vec<u8>>,
public_key: impl Into<public::KeyData>,
valid_after: u64,
valid_before: u64,
) -> Result<Self> {
let valid_after =
UnixTime::new(valid_after).map_err(|_| Field::ValidAfter.invalid_error())?;
let valid_before =
UnixTime::new(valid_before).map_err(|_| Field::ValidBefore.invalid_error())?;
if valid_before < valid_after {
return Err(Field::ValidBefore.invalid_error());
}
Ok(Self {
nonce: nonce.into(),
public_key: public_key.into(),
serial: None,
cert_type: None,
key_id: None,
valid_principals: None,
valid_after,
valid_before,
critical_options: OptionsMap::new(),
extensions: OptionsMap::new(),
comment: None,
})
}
#[cfg(feature = "std")]
pub fn new_with_validity_times(
nonce: impl Into<Vec<u8>>,
public_key: impl Into<public::KeyData>,
valid_after: SystemTime,
valid_before: SystemTime,
) -> Result<Self> {
let valid_after =
UnixTime::try_from(valid_after).map_err(|_| Field::ValidAfter.invalid_error())?;
let valid_before =
UnixTime::try_from(valid_before).map_err(|_| Field::ValidBefore.invalid_error())?;
Self::new(nonce, public_key, valid_after.into(), valid_before.into())
}
#[cfg(feature = "rand_core")]
pub fn new_with_random_nonce(
rng: &mut impl CryptoRngCore,
public_key: impl Into<public::KeyData>,
valid_after: u64,
valid_before: u64,
) -> Result<Self> {
let mut nonce = vec![0u8; Self::RECOMMENDED_NONCE_SIZE];
rng.fill_bytes(&mut nonce);
Self::new(nonce, public_key, valid_after, valid_before)
}
pub fn serial(&mut self, serial: u64) -> Result<&mut Self> {
if self.serial.is_some() {
return Err(Field::Serial.invalid_error());
}
self.serial = Some(serial);
Ok(self)
}
pub fn cert_type(&mut self, cert_type: CertType) -> Result<&mut Self> {
if self.cert_type.is_some() {
return Err(Field::Type.invalid_error());
}
self.cert_type = Some(cert_type);
Ok(self)
}
pub fn key_id(&mut self, key_id: impl Into<String>) -> Result<&mut Self> {
if self.key_id.is_some() {
return Err(Field::KeyId.invalid_error());
}
self.key_id = Some(key_id.into());
Ok(self)
}
pub fn valid_principal(&mut self, principal: impl Into<String>) -> Result<&mut Self> {
match &mut self.valid_principals {
Some(principals) => principals.push(principal.into()),
None => self.valid_principals = Some(vec![principal.into()]),
}
Ok(self)
}
pub fn all_principals_valid(&mut self) -> Result<&mut Self> {
self.valid_principals = Some(Vec::new());
Ok(self)
}
pub fn critical_option(
&mut self,
name: impl Into<String>,
data: impl Into<String>,
) -> Result<&mut Self> {
let name = name.into();
let data = data.into();
if self.critical_options.contains_key(&name) {
return Err(Field::CriticalOptions.invalid_error());
}
self.critical_options.insert(name, data);
Ok(self)
}
pub fn extension(
&mut self,
name: impl Into<String>,
data: impl Into<String>,
) -> Result<&mut Self> {
let name = name.into();
let data = data.into();
if self.extensions.contains_key(&name) {
return Err(Field::Extensions.invalid_error());
}
self.extensions.insert(name, data);
Ok(self)
}
pub fn comment(&mut self, comment: impl Into<String>) -> Result<&mut Self> {
if self.comment.is_some() {
return Err(Field::Comment.invalid_error());
}
self.comment = Some(comment.into());
Ok(self)
}
pub fn sign<S: SigningKey>(self, signing_key: &S) -> Result<Certificate> {
let valid_principals = match self.valid_principals {
Some(principals) => principals,
None => return Err(Field::ValidPrincipals.invalid_error()),
};
let mut cert = Certificate {
nonce: self.nonce,
public_key: self.public_key,
serial: self.serial.unwrap_or_default(),
cert_type: self.cert_type.unwrap_or_default(),
key_id: self.key_id.unwrap_or_default(),
valid_principals,
valid_after: self.valid_after,
valid_before: self.valid_before,
critical_options: self.critical_options,
extensions: self.extensions,
reserved: Vec::new(),
comment: self.comment.unwrap_or_default(),
signature_key: signing_key.public_key(),
signature: Signature::placeholder(),
};
let mut tbs_cert = Vec::new();
cert.encode_tbs(&mut tbs_cert)?;
cert.signature = signing_key.try_sign(&tbs_cert)?;
#[cfg(debug_assertions)]
cert.validate_at(
cert.valid_after.into(),
&[cert.signature_key.fingerprint(Default::default())],
)?;
Ok(cert)
}
}