use alloc::{collections::BTreeSet, vec};
use crate::{
builder::{BuilderProfile, Error, Result},
certificate::TbsCertificate,
ext::{
Extension, ToExtension,
pkix::{
AuthorityKeyIdentifier, BasicConstraints, KeyUsage, KeyUsages, SubjectKeyIdentifier,
},
},
name::Name,
};
use const_oid::db::{rfc2256, rfc4519};
use spki::SubjectPublicKeyInfoRef;
pub fn check_names_encoding(name: &Name, multiple_allowed: bool) -> Result<()> {
let enforce_ordering = vec![
rfc4519::DOMAIN_COMPONENT,
rfc4519::COUNTRY_NAME,
rfc2256::STATE_OR_PROVINCE_NAME,
rfc4519::LOCALITY_NAME,
rfc4519::POSTAL_CODE,
rfc2256::STREET_ADDRESS,
rfc4519::ORGANIZATION_NAME,
rfc4519::SURNAME,
rfc4519::GIVEN_NAME,
rfc4519::ORGANIZATIONAL_UNIT_NAME,
rfc4519::COMMON_NAME,
];
let mut ordering = enforce_ordering.iter();
let mut seen = BTreeSet::new();
for rdn in name.iter_rdn() {
if rdn.len() != 1 {
return Err(Error::NonUniqueRdn);
}
for atv in rdn.iter() {
if !multiple_allowed && !seen.insert(atv.oid) {
return Err(Error::NonUniqueATV);
}
if enforce_ordering.iter().any(|attr| attr == &atv.oid) {
if !ordering.any(|attr| attr == &atv.oid) {
return Err(Error::InvalidAttribute { oid: atv.oid });
}
}
}
}
Ok(())
}
pub fn ca_certificate_naming(subject: &Name) -> Result<()> {
let mut required = BTreeSet::from([
rfc4519::COUNTRY_NAME,
rfc4519::ORGANIZATION_NAME,
rfc4519::COMMON_NAME,
]);
let mut allowed = BTreeSet::from([
rfc4519::COUNTRY_NAME,
rfc2256::STATE_OR_PROVINCE_NAME,
rfc4519::LOCALITY_NAME,
rfc4519::POSTAL_CODE,
rfc2256::STREET_ADDRESS,
rfc4519::ORGANIZATION_NAME,
rfc4519::COMMON_NAME,
]);
check_names_encoding(subject, false)?;
for atv in subject.iter() {
if !allowed.remove(&atv.oid) {
return Err(Error::InvalidAttribute { oid: atv.oid });
}
required.remove(&atv.oid);
}
if !required.is_empty() {
return Err(Error::MissingAttributes);
}
Ok(())
}
pub struct Root {
pub emits_ocsp_response: bool,
subject: Name,
}
impl Root {
pub fn new(emits_ocsp_response: bool, subject: Name) -> Result<Self> {
ca_certificate_naming(&subject)?;
Ok(Self {
emits_ocsp_response,
subject,
})
}
}
impl BuilderProfile for Root {
fn get_issuer(&self, subject: &Name) -> Name {
subject.clone()
}
fn get_subject(&self) -> Name {
self.subject.clone()
}
fn build_extensions(
&self,
spk: SubjectPublicKeyInfoRef<'_>,
_issuer_spk: SubjectPublicKeyInfoRef<'_>,
tbs: &TbsCertificate,
) -> Result<vec::Vec<Extension>> {
let mut extensions: vec::Vec<Extension> = vec::Vec::new();
let ski = SubjectKeyIdentifier::try_from(spk)?;
extensions.push(
AuthorityKeyIdentifier {
key_identifier: Some(ski.0.clone()),
..Default::default()
}
.to_extension(&tbs.subject, &extensions)?,
);
extensions.push(
BasicConstraints {
ca: true,
path_len_constraint: None,
}
.to_extension(&tbs.subject, &extensions)?,
);
let mut key_usage = KeyUsages::KeyCertSign | KeyUsages::CRLSign;
if self.emits_ocsp_response {
key_usage |= KeyUsages::DigitalSignature;
}
extensions.push(KeyUsage(key_usage).to_extension(&tbs.subject, &extensions)?);
extensions.push(ski.to_extension(&tbs.subject, &extensions)?);
Ok(extensions)
}
}
pub mod tls;
pub mod codesigning {
}
#[cfg(test)]
mod tests {
use super::*;
use core::str::FromStr;
#[test]
fn test_check_names() {
assert!(
check_names_encoding(&Name::from_str("C=US,ST=CA").expect("parse name"), false)
.is_err()
);
assert!(
check_names_encoding(&Name::from_str("ST=CA,C=US").expect("parse name"), false).is_ok()
);
assert!(
check_names_encoding(
&Name::from_str("serialNumber=1234,ST=CA,C=US").expect("parse name"),
false
)
.is_ok()
);
}
}