use synta::tag::TAG_SEQUENCE;
use synta::Tag;
use synta::{Decoder, Encoding, Integer, Null, ObjectIdentifier, RawDer};
use crate::builder::BuilderError;
use crate::crmf_types::{
AttributeTypeAndValue, CertReqMsg, CertRequest, CertTemplate, PKIPublicationInfo,
ProofOfPossession, SinglePubInfo,
};
use crate::{GeneralNameSpec, Name, SubjectPublicKeyInfo};
pub const PUB_METHOD_DONT_CARE: i64 = 0;
pub const PUB_METHOD_X500: i64 = 1;
pub const PUB_METHOD_WEB: i64 = 2;
pub const PUB_METHOD_LDAP: i64 = 3;
pub struct CertReqMsgBuilder {
cert_req_id: i64,
subject_der: Option<Vec<u8>>,
issuer_der: Option<Vec<u8>>,
spki_der: Option<Vec<u8>>,
popo_ra_verified: bool,
pub_info_action: i64,
pub_infos: Vec<(i64, Option<GeneralNameSpec>)>,
}
impl Default for CertReqMsgBuilder {
fn default() -> Self {
Self::new()
}
}
impl CertReqMsgBuilder {
pub fn new() -> Self {
Self {
cert_req_id: 0,
subject_der: None,
issuer_der: None,
spki_der: None,
popo_ra_verified: false,
pub_info_action: 1, pub_infos: Vec::new(),
}
}
pub fn cert_req_id(mut self, id: i64) -> Self {
self.cert_req_id = id;
self
}
pub fn subject_name(mut self, name_der: &[u8]) -> Self {
self.subject_der = Some(name_der.to_vec());
self
}
pub fn issuer_name(mut self, name_der: &[u8]) -> Self {
self.issuer_der = Some(name_der.to_vec());
self
}
pub fn public_key_der(mut self, spki_der: &[u8]) -> Self {
self.spki_der = Some(spki_der.to_vec());
self
}
pub fn popo_ra_verified(mut self) -> Self {
self.popo_ra_verified = true;
self
}
pub fn publication_action(mut self, action: i64) -> Self {
self.pub_info_action = action;
self
}
pub fn add_pub_info(mut self, pub_method: i64) -> Self {
self.pub_infos.push((pub_method, None));
self
}
pub fn pub_location_uri(mut self, pub_method: i64, uri: &str) -> Self {
self.pub_infos
.push((pub_method, Some(GeneralNameSpec::uri(uri))));
self
}
pub fn pub_location_rfc822(mut self, pub_method: i64, email: &str) -> Self {
self.pub_infos
.push((pub_method, Some(GeneralNameSpec::rfc822(email))));
self
}
pub fn pub_location_dns(mut self, pub_method: i64, host: &str) -> Self {
self.pub_infos
.push((pub_method, Some(GeneralNameSpec::dns(host))));
self
}
pub fn pub_location_directory_name(mut self, pub_method: i64, name_der: &[u8]) -> Self {
self.pub_infos
.push((pub_method, Some(GeneralNameSpec::directory_name(name_der))));
self
}
pub fn build(self) -> Result<Vec<u8>, BuilderError> {
let subject_bytes = self.subject_der;
let issuer_bytes = self.issuer_der;
let spki_bytes = self.spki_der;
let subject = match subject_bytes {
Some(ref bytes) => Some(
Decoder::new(bytes, Encoding::Der)
.decode::<Name<'_>>()
.map_err(|e| BuilderError::EncodeError(e.to_string()))?,
),
None => None,
};
let issuer = match issuer_bytes {
Some(ref bytes) => Some(
Decoder::new(bytes, Encoding::Der)
.decode::<Name<'_>>()
.map_err(|e| BuilderError::EncodeError(e.to_string()))?,
),
None => None,
};
let public_key = match spki_bytes {
Some(ref bytes) => Some(
Decoder::new(bytes, Encoding::Der)
.decode::<SubjectPublicKeyInfo<'_>>()
.map_err(|e| BuilderError::EncodeError(e.to_string()))?,
),
None => None,
};
let cert_template = CertTemplate {
subject,
issuer,
public_key,
..Default::default()
};
let popo = if self.popo_ra_verified {
Some(ProofOfPossession::RaVerified(Null))
} else {
None
};
let pub_info_der_opt = if !self.pub_infos.is_empty() {
Some(build_publication_info_der(
self.pub_info_action,
&self.pub_infos,
)?)
} else {
None
};
let attr_der_opt: Option<Vec<u8>> = match pub_info_der_opt {
Some(ref pi_der) => {
let oid =
ObjectIdentifier::new(crate::crmf_types::ID_REG_CTRL_PKI_PUBLICATION_INFO)
.map_err(|e| BuilderError::EncodeError(e.to_string()))?;
let attr = AttributeTypeAndValue {
r#type: oid,
value: RawDer(pi_der),
};
Some(
attr.to_der()
.map_err(|e| BuilderError::EncodeError(e.to_string()))?,
)
}
None => None,
};
let controls: Option<Vec<AttributeTypeAndValue<'_>>> = match attr_der_opt {
Some(ref a_der) => {
let attr_decoded = Decoder::new(a_der, Encoding::Der)
.decode::<AttributeTypeAndValue<'_>>()
.map_err(|e| BuilderError::EncodeError(e.to_string()))?;
Some(vec![attr_decoded])
}
None => None,
};
let msg = CertReqMsg {
cert_req: CertRequest {
cert_req_id: Integer::from_i64(self.cert_req_id),
cert_template,
controls,
},
popo,
reg_info: None,
};
msg.to_der()
.map_err(|e| BuilderError::EncodeError(e.to_string()))
}
}
fn build_publication_info_der(
action: i64,
pub_infos: &[(i64, Option<GeneralNameSpec>)],
) -> Result<Vec<u8>, BuilderError> {
let mut gn_bufs: Vec<Option<Vec<u8>>> = Vec::with_capacity(pub_infos.len());
for (_, spec_opt) in pub_infos {
let buf = match spec_opt {
None => None,
Some(spec) => {
let gn = spec
.to_general_name()
.map_err(|e| BuilderError::EncodeError(e.to_string()))?;
Some(
gn.to_der()
.map_err(|e| BuilderError::EncodeError(e.to_string()))?,
)
}
};
gn_bufs.push(buf);
}
let mut single_infos: Vec<SinglePubInfo<'_>> = Vec::with_capacity(pub_infos.len());
for (i, (method, _)) in pub_infos.iter().enumerate() {
let pub_location = match gn_bufs[i] {
None => None,
Some(ref bytes) => Some(
Decoder::new(bytes, Encoding::Der)
.decode::<crate::GeneralName<'_>>()
.map_err(|e| BuilderError::EncodeError(e.to_string()))?,
),
};
single_infos.push(SinglePubInfo {
pub_method: Integer::from_i64(*method),
pub_location,
});
}
let pub_info = PKIPublicationInfo {
action: Integer::from_i64(action),
pub_infos: Some(single_infos),
};
pub_info
.to_der()
.map_err(|e| BuilderError::EncodeError(e.to_string()))
}
pub struct CertReqMessagesBuilder {
requests: Vec<Vec<u8>>,
}
impl Default for CertReqMessagesBuilder {
fn default() -> Self {
Self::new()
}
}
impl CertReqMessagesBuilder {
pub fn new() -> Self {
Self {
requests: Vec::new(),
}
}
pub fn add_request_der(mut self, cert_req_msg_der: &[u8]) -> Self {
self.requests.push(cert_req_msg_der.to_vec());
self
}
pub fn build(self) -> Result<Vec<u8>, BuilderError> {
let total: usize = self.requests.iter().map(|r| r.len()).sum();
let mut enc = synta::Encoder::with_capacity(Encoding::Der, total + 4);
enc.start_constructed_no_guard(Tag::universal_constructed(TAG_SEQUENCE))
.map_err(|e| BuilderError::EncodeError(e.to_string()))?;
for req_der in &self.requests {
enc.encode(&RawDer(req_der))
.map_err(|e| BuilderError::EncodeError(e.to_string()))?;
}
enc.end_constructed()
.map_err(|e| BuilderError::EncodeError(e.to_string()))?;
enc.finish()
.map_err(|e| BuilderError::EncodeError(e.to_string()))
}
}