use alloc::vec;
use der::{Encode, asn1::BitString};
use signature::Keypair;
use spki::{
AlgorithmIdentifier, DynSignatureAlgorithmIdentifier, EncodePublicKey, SubjectPublicKeyInfo,
};
use crate::{
builder::{Builder, Error, NULL_OID, Result},
ext::ToExtension,
name::Name,
request::{CertReq, CertReqInfo, ExtensionReq, attributes::AsAttribute},
};
pub struct RequestBuilder {
info: CertReqInfo,
extension_req: ExtensionReq,
}
impl RequestBuilder {
pub fn new(subject: Name) -> Result<Self> {
let version = Default::default();
let algorithm = AlgorithmIdentifier {
oid: NULL_OID,
parameters: None,
};
let public_key = SubjectPublicKeyInfo {
algorithm,
subject_public_key: BitString::from_bytes(&[]).expect("unable to parse empty object"),
};
let attributes = Default::default();
let extension_req = Default::default();
Ok(Self {
info: CertReqInfo {
version,
subject,
public_key,
attributes,
},
extension_req,
})
}
pub fn add_extension<E: ToExtension>(
&mut self,
extension: E,
) -> core::result::Result<(), E::Error> {
let ext = extension.to_extension(&self.info.subject, &self.extension_req.0)?;
self.extension_req.0.push(ext);
Ok(())
}
pub fn add_attribute<A: AsAttribute>(&mut self, attribute: &A) -> Result<()> {
let attr = attribute.to_attribute()?;
self.info.attributes.insert(attr)?;
Ok(())
}
}
impl Builder for RequestBuilder {
type Output = CertReq;
fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
where
S: Keypair + DynSignatureAlgorithmIdentifier,
S::VerifyingKey: EncodePublicKey,
{
let verifying_key = signer.verifying_key();
let public_key = SubjectPublicKeyInfo::from_key(&verifying_key)?;
self.info.public_key = public_key;
self.info
.attributes
.insert(self.extension_req.clone().try_into()?)?;
self.info.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 algorithm = signer.signature_algorithm_identifier()?;
Ok(CertReq {
info: self.info,
algorithm,
signature,
})
}
}