use synta::GeneralizedTime;
use crate::attribute_cert_types::{
AttCertIssuer, AttCertValidityPeriod, AttributeCertificateInfo, Holder,
};
use crate::time_utils::parse_generalized_time;
use crate::GeneralName;
#[derive(Default)]
pub struct AttributeCertificateBuilder {
serial: Option<i64>,
not_before: Option<GeneralizedTime>,
not_after: Option<GeneralizedTime>,
issuer_names: Vec<GeneralName<'static>>,
holder_entity_names: Vec<GeneralName<'static>>,
error: Option<String>,
}
impl AttributeCertificateBuilder {
pub fn new() -> Self {
Self::default()
}
fn set_error(&mut self, msg: String) {
if self.error.is_none() {
self.error = Some(msg);
}
}
pub fn serial_number(mut self, n: i64) -> Self {
self.serial = Some(n);
self
}
pub fn not_before(mut self, s: &str) -> Self {
match parse_generalized_time(s) {
Ok(t) => self.not_before = Some(t),
Err(e) => self.set_error(e),
}
self
}
pub fn not_after(mut self, s: &str) -> Self {
match parse_generalized_time(s) {
Ok(t) => self.not_after = Some(t),
Err(e) => self.set_error(e),
}
self
}
pub fn issuer_rfc822(mut self, email: &str) -> Self {
match synta::IA5String::new(email.to_string()) {
Ok(ia5) => {
let gn: GeneralName<'static> =
unsafe { std::mem::transmute(GeneralName::Rfc822Name(ia5)) };
self.issuer_names.push(gn);
}
Err(e) => self.set_error(format!("issuer_rfc822: invalid IA5String: {e}")),
}
self
}
pub fn issuer_dns(mut self, name: &str) -> Self {
match synta::IA5String::new(name.to_string()) {
Ok(ia5) => {
let gn: GeneralName<'static> =
unsafe { std::mem::transmute(GeneralName::DNSName(ia5)) };
self.issuer_names.push(gn);
}
Err(e) => self.set_error(format!("issuer_dns: invalid IA5String: {e}")),
}
self
}
pub fn holder_entity_name_rfc822(mut self, email: &str) -> Self {
match synta::IA5String::new(email.to_string()) {
Ok(ia5) => {
let gn: GeneralName<'static> =
unsafe { std::mem::transmute(GeneralName::Rfc822Name(ia5)) };
self.holder_entity_names.push(gn);
}
Err(e) => self.set_error(format!("holder_entity_name_rfc822: invalid IA5String: {e}")),
}
self
}
pub fn build(self) -> Result<Vec<u8>, String> {
if let Some(e) = self.error {
return Err(e);
}
let serial = self.serial.ok_or("serial_number is required")?;
if serial <= 0 {
return Err("serial_number must be a positive integer (RFC 5280 §4.1.2.2)".into());
}
let not_before = self.not_before.ok_or("not_before is required")?;
let not_after = self.not_after.ok_or("not_after is required")?;
let issuer = AttCertIssuer::V1Form(self.issuer_names);
let holder = Holder {
base_certificate_id: None,
entity_name: if self.holder_entity_names.is_empty() {
None
} else {
Some(self.holder_entity_names)
},
object_digest_info: None,
};
let sig_alg_oid = synta::ObjectIdentifier::new(crate::oids::SHA256_WITH_RSA)
.map_err(|e| format!("sig alg OID error: {e}"))?;
let sig_alg = crate::AlgorithmIdentifier {
algorithm: sig_alg_oid,
parameters: None,
};
let acinfo = AttributeCertificateInfo {
version: synta::Integer::from(1i64),
holder,
issuer,
signature: sig_alg,
serial_number: synta::Integer::from(serial),
attr_cert_validity_period: AttCertValidityPeriod {
not_before_time: not_before,
not_after_time: not_after,
},
attributes: Vec::new(),
issuer_unique_id: None,
extensions: None,
};
acinfo
.to_der()
.map_err(|e| format!("AttributeCertificateInfo encode error: {e}"))
}
}