use core::fmt;
use synta::tag::TAG_SEQUENCE;
use synta::{Boolean, Integer, ObjectIdentifier, OctetStringRef, RawDer, Tag};
use crate::crypto::CertificateSigner;
use crate::{Extension, Time, Validity};
#[derive(Debug)]
pub enum BuilderError {
MissingField(&'static str),
EncodeError(String),
SignError(String),
InvalidField(String),
}
impl fmt::Display for BuilderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BuilderError::MissingField(field) => {
write!(f, "required field not set: {field}")
}
BuilderError::EncodeError(msg) => write!(f, "ASN.1 encode error: {msg}"),
BuilderError::SignError(msg) => write!(f, "signing error: {msg}"),
BuilderError::InvalidField(msg) => write!(f, "invalid field value: {msg}"),
}
}
}
impl std::error::Error for BuilderError {}
pub fn validate_certificate_serial(serial: &synta::Integer) -> Result<(), BuilderError> {
let bytes = serial.as_bytes();
if bytes.is_empty() || bytes[0] & 0x80 != 0 {
return Err(BuilderError::InvalidField(
"serial_number must be a positive integer (RFC 5280 §4.1.2.2)".into(),
));
}
if bytes.iter().all(|&b| b == 0) {
return Err(BuilderError::InvalidField(
"serial_number must be a positive integer (RFC 5280 §4.1.2.2)".into(),
));
}
let significant = if bytes[0] == 0x00 {
bytes.len() - 1
} else {
bytes.len()
};
if significant > 20 {
return Err(BuilderError::InvalidField(
"serial_number must not exceed 20 octets (RFC 5280 §4.1.2.2)".into(),
));
}
Ok(())
}
impl From<synta::Error> for BuilderError {
fn from(e: synta::Error) -> Self {
BuilderError::EncodeError(e.to_string())
}
}
pub struct CertificateBuilder {
issuer: Option<Vec<u8>>,
subject: Option<Vec<u8>>,
spki: Option<Vec<u8>>,
serial: Option<Integer>,
not_before: Option<Time>,
not_after: Option<Time>,
extensions: Vec<(ObjectIdentifier, bool, Vec<u8>)>,
}
impl Default for CertificateBuilder {
fn default() -> Self {
Self::new()
}
}
impl CertificateBuilder {
pub fn new() -> Self {
Self {
issuer: None,
subject: None,
spki: None,
serial: None,
not_before: None,
not_after: None,
extensions: Vec::new(),
}
}
pub fn issuer_name(mut self, name_der: &[u8]) -> Self {
self.issuer = Some(name_der.to_vec());
self
}
pub fn subject_name(mut self, name_der: &[u8]) -> Self {
self.subject = Some(name_der.to_vec());
self
}
pub fn public_key_der(mut self, spki_der: &[u8]) -> Self {
self.spki = Some(spki_der.to_vec());
self
}
pub fn serial_number(mut self, serial: Integer) -> Self {
self.serial = Some(serial);
self
}
pub fn not_valid_before(mut self, t: Time) -> Self {
self.not_before = Some(t);
self
}
pub fn not_valid_after(mut self, t: Time) -> Self {
self.not_after = Some(t);
self
}
pub fn add_extension(
mut self,
oid: ObjectIdentifier,
critical: bool,
value_der: &[u8],
) -> Self {
self.extensions.push((oid, critical, value_der.to_vec()));
self
}
pub fn add_extension_oid(
self,
oid_components: &[u32],
critical: bool,
value_der: &[u8],
) -> Self {
let oid = ObjectIdentifier::new(oid_components)
.expect("add_extension_oid: invalid OID components");
self.add_extension(oid, critical, value_der)
}
pub fn build_tbs(&self, sig_alg_der: &[u8]) -> Result<Vec<u8>, BuilderError> {
let issuer = self
.issuer
.as_deref()
.ok_or(BuilderError::MissingField("issuer_name"))?;
let subject = self
.subject
.as_deref()
.ok_or(BuilderError::MissingField("subject_name"))?;
let spki = self
.spki
.as_deref()
.ok_or(BuilderError::MissingField("public_key"))?;
let serial = self
.serial
.as_ref()
.ok_or(BuilderError::MissingField("serial_number"))?;
validate_certificate_serial(serial)?;
let not_before = self
.not_before
.as_ref()
.ok_or(BuilderError::MissingField("not_valid_before"))?;
let not_after = self
.not_after
.as_ref()
.ok_or(BuilderError::MissingField("not_valid_after"))?;
let mut enc = synta::Encoder::with_capacity(
synta::Encoding::Der,
16 + issuer.len() + subject.len() + spki.len() + 64,
);
enc.start_constructed_no_guard(Tag::universal_constructed(TAG_SEQUENCE))?;
enc.start_constructed_no_guard(Tag::context_specific_constructed(0))?;
enc.encode(&Integer::from_i64(2))?;
enc.end_constructed()?;
enc.encode(serial)?;
enc.write_bytes(sig_alg_der);
enc.encode(&RawDer(issuer))?;
enc.encode(&Validity {
not_before: not_before.clone(),
not_after: not_after.clone(),
})?;
enc.encode(&RawDer(subject))?;
enc.encode(&RawDer(spki))?;
if !self.extensions.is_empty() {
enc.start_constructed_no_guard(Tag::context_specific_constructed(3))?;
enc.start_constructed_no_guard(Tag::universal_constructed(TAG_SEQUENCE))?;
for (oid, critical, value_bytes) in &self.extensions {
let ext = Extension {
extn_id: oid.clone(),
critical: if *critical {
Some(Boolean::new(true))
} else {
None
},
extn_value: OctetStringRef::new(value_bytes),
};
enc.encode(&ext)?;
}
enc.end_constructed()?; enc.end_constructed()?; }
enc.end_constructed()?; Ok(enc.finish()?)
}
pub fn assemble(
tbs_der: &[u8],
sig_alg_der: &[u8],
signature: &[u8],
) -> Result<Vec<u8>, BuilderError> {
use synta::BitStringRef;
let mut enc = synta::Encoder::with_capacity(
synta::Encoding::Der,
tbs_der.len() + sig_alg_der.len() + signature.len() + 8,
);
enc.start_constructed_no_guard(Tag::universal_constructed(TAG_SEQUENCE))?;
enc.write_bytes(tbs_der);
enc.write_bytes(sig_alg_der);
let sig_bstr = BitStringRef::new(signature, 0)
.map_err(|e| BuilderError::EncodeError(e.to_string()))?;
enc.encode(&sig_bstr)?;
enc.end_constructed()?; Ok(enc.finish()?)
}
pub fn sign<S: CertificateSigner>(self, signer: &S) -> Result<Vec<u8>, BuilderError> {
let sig_alg_der = signer
.signature_algorithm_der()
.map_err(|e| BuilderError::SignError(e.to_string()))?;
let tbs_der = self.build_tbs(&sig_alg_der)?;
let signature = signer
.sign_tbs(&tbs_der)
.map_err(|e| BuilderError::SignError(e.to_string()))?;
Self::assemble(&tbs_der, &sig_alg_der, &signature)
}
}
#[cfg(test)]
mod serial_validation_tests {
use super::{validate_certificate_serial, BuilderError};
use synta::Integer;
#[test]
fn negative_rejected() {
let r = validate_certificate_serial(&Integer::from_i64(-1));
assert!(matches!(r, Err(BuilderError::InvalidField(_))));
}
#[test]
fn zero_rejected() {
let r = validate_certificate_serial(&Integer::from_i64(0));
assert!(matches!(r, Err(BuilderError::InvalidField(_))));
}
#[test]
fn too_long_rejected() {
let r = validate_certificate_serial(&Integer::from_bytes(&[0x01u8; 21]));
assert!(matches!(r, Err(BuilderError::InvalidField(_))));
}
#[test]
fn exactly_20_significant_octets_accepted() {
let r = validate_certificate_serial(&Integer::from_bytes(&[0x01u8; 20]));
assert!(r.is_ok());
}
#[test]
fn twenty_octets_with_sign_byte_accepted() {
let mut bytes = vec![0xFFu8; 20];
bytes.insert(0, 0x00);
let r = validate_certificate_serial(&Integer::from_bytes(&bytes));
assert!(r.is_ok());
}
#[test]
fn small_positive_accepted() {
assert!(validate_certificate_serial(&Integer::from_i64(1)).is_ok());
assert!(validate_certificate_serial(&Integer::from_i64(i64::MAX)).is_ok());
}
#[test]
fn from_unsigned_bytes_160_bit_accepted() {
let r = validate_certificate_serial(&Integer::from_unsigned_bytes(&[0xFFu8; 20]));
assert!(r.is_ok());
}
}