use {
crate::{
asn1::{
rfc3280::{self, AttributeTypeAndValue, DirectoryString, Name},
rfc4519::{
OID_COMMON_NAME, OID_COUNTRY_NAME, OID_ORGANIZATIONAL_UNIT_NAME,
OID_ORGANIZATION_NAME,
},
rfc5280,
rfc5652::{CertificateChoices, IssuerAndSerialNumber},
},
CertificateKeyAlgorithm, CmsError,
},
bcder::{
decode::{self, Constructed},
encode::Values,
Integer, Mode, Oid, Utf8String,
},
bytes::Bytes,
std::{
convert::{TryFrom, TryInto},
str::FromStr,
},
};
#[derive(Clone, Debug, Default)]
pub struct RelativeDistinguishedName(rfc3280::RelativeDistinguishedName);
impl RelativeDistinguishedName {
pub fn common_name(&self) -> Result<Option<String>, decode::Error> {
self.find_attribute_string(Oid(Bytes::from(OID_COMMON_NAME.as_ref())))
}
pub fn set_common_name(&mut self, value: &str) -> Result<(), bcder::string::CharSetError> {
self.set_attribute_string(Oid(Bytes::from(OID_COMMON_NAME.as_ref())), value)
}
pub fn country_name(&self) -> Result<Option<String>, decode::Error> {
self.find_attribute_string(Oid(Bytes::from(OID_COUNTRY_NAME.as_ref())))
}
pub fn set_country_name(&mut self, value: &str) -> Result<(), bcder::string::CharSetError> {
self.set_attribute_string(Oid(Bytes::from(OID_COUNTRY_NAME.as_ref())), value)
}
pub fn organization_name(&self) -> Result<Option<String>, decode::Error> {
self.find_attribute_string(Oid(Bytes::from(OID_ORGANIZATION_NAME.as_ref())))
}
pub fn set_organization_name(
&mut self,
value: &str,
) -> Result<(), bcder::string::CharSetError> {
self.set_attribute_string(Oid(Bytes::from(OID_ORGANIZATION_NAME.as_ref())), value)
}
pub fn organizational_unit_name(&self) -> Result<Option<String>, decode::Error> {
self.find_attribute_string(Oid(Bytes::from(OID_ORGANIZATIONAL_UNIT_NAME.as_ref())))
}
pub fn set_organizational_unit_name(
&mut self,
value: &str,
) -> Result<(), bcder::string::CharSetError> {
self.set_attribute_string(
Oid(Bytes::from(OID_ORGANIZATIONAL_UNIT_NAME.as_ref())),
value,
)
}
fn find_attribute(&self, attribute: Oid) -> Option<&AttributeTypeAndValue> {
self.0.iter().find(|attr| attr.typ == attribute)
}
fn find_attribute_mut(&mut self, attribute: Oid) -> Option<&mut AttributeTypeAndValue> {
self.0.iter_mut().find(|attr| attr.typ == attribute)
}
fn find_attribute_string(&self, attribute: Oid) -> Result<Option<String>, decode::Error> {
if let Some(attr) = self.find_attribute(attribute) {
attr.value.clone().decode(|cons| {
let value = DirectoryString::take_from(cons)?;
Ok(Some(value.to_string()))
})
} else {
Ok(None)
}
}
pub fn set_attribute_string(
&mut self,
attribute: Oid,
value: &str,
) -> Result<(), bcder::string::CharSetError> {
let ds = DirectoryString::Utf8String(Utf8String::from_str(value)?);
let captured = bcder::Captured::from_values(Mode::Der, ds);
if let Some(mut attr) = self.find_attribute_mut(attribute.clone()) {
attr.value = captured;
Ok(())
} else {
self.0.push(AttributeTypeAndValue {
typ: attribute,
value: captured,
});
Ok(())
}
}
}
impl From<RelativeDistinguishedName> for Name {
fn from(rdn: RelativeDistinguishedName) -> Self {
let mut seq = rfc3280::RdnSequence::default();
seq.push(rdn.0);
Self::RdnSequence(seq)
}
}
impl TryFrom<&Name> for RelativeDistinguishedName {
type Error = CmsError;
fn try_from(name: &Name) -> Result<Self, Self::Error> {
match name {
Name::RdnSequence(seq) => match seq.iter().next() {
Some(rdn) => Ok(RelativeDistinguishedName(rdn.clone())),
None => Err(CmsError::DistinguishedNameParseError),
},
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Certificate {
serial_number: Integer,
subject: Name,
issuer: Name,
pub public_key: CertificatePublicKey,
raw_cert: rfc5280::Certificate,
}
impl Certificate {
pub fn from_parsed_asn1(cert: rfc5280::Certificate) -> Result<Self, CmsError> {
Ok(Self {
serial_number: cert.tbs_certificate.serial_number.clone(),
subject: cert.tbs_certificate.subject.clone(),
issuer: cert.tbs_certificate.issuer.clone(),
public_key: (&cert.tbs_certificate.subject_public_key_info).try_into()?,
raw_cert: cert,
})
}
pub fn from_der(data: &[u8]) -> Result<Self, CmsError> {
let cert = Constructed::decode(data, Mode::Der, |cons| {
crate::asn1::rfc5280::Certificate::take_from(cons)
})?;
Ok(Self {
serial_number: cert.tbs_certificate.serial_number.clone(),
subject: cert.tbs_certificate.subject.clone(),
issuer: cert.tbs_certificate.issuer.clone(),
public_key: (&cert.tbs_certificate.subject_public_key_info).try_into()?,
raw_cert: cert,
})
}
pub fn from_pem(data: &[u8]) -> Result<Self, CmsError> {
let pem = pem::parse(data)?;
Self::from_der(&pem.contents)
}
pub fn serial_number(&self) -> &Integer {
&self.serial_number
}
pub fn subject(&self) -> &Name {
&self.subject
}
pub fn subject_dn(&self) -> Result<RelativeDistinguishedName, CmsError> {
RelativeDistinguishedName::try_from(&self.subject)
}
pub fn issuer(&self) -> &Name {
&self.issuer
}
pub fn issuer_dn(&self) -> Result<RelativeDistinguishedName, CmsError> {
RelativeDistinguishedName::try_from(&self.issuer)
}
pub fn public_key(&self) -> &CertificatePublicKey {
&self.public_key
}
pub fn raw_certificate(&self) -> &crate::asn1::rfc5280::Certificate {
&self.raw_cert
}
pub fn as_ber(&self) -> Result<Vec<u8>, CmsError> {
let mut res = Vec::<u8>::new();
self.raw_cert
.encode_ref()
.write_encoded(Mode::Ber, &mut res)?;
Ok(res)
}
pub fn as_der(&self) -> Result<Vec<u8>, CmsError> {
let mut res = Vec::<u8>::new();
self.raw_cert
.encode_ref()
.write_encoded(Mode::Der, &mut res)?;
Ok(res)
}
pub fn as_pem(&self) -> Result<String, CmsError> {
Ok(pem::encode(&pem::Pem {
tag: "CERTIFICATE".to_string(),
contents: self.as_der()?,
}))
}
}
impl TryFrom<&CertificateChoices> for Certificate {
type Error = CmsError;
fn try_from(cert: &CertificateChoices) -> Result<Self, Self::Error> {
match cert {
CertificateChoices::Certificate(cert) => Self::try_from(cert.as_ref()),
_ => Err(CmsError::UnknownCertificateFormat),
}
}
}
impl TryFrom<&crate::asn1::rfc5280::Certificate> for Certificate {
type Error = CmsError;
fn try_from(cert: &crate::asn1::rfc5280::Certificate) -> Result<Self, Self::Error> {
let serial_number = cert.tbs_certificate.serial_number.clone();
let subject = cert.tbs_certificate.subject.clone();
let issuer = cert.tbs_certificate.issuer.clone();
let public_key =
CertificatePublicKey::try_from(&cert.tbs_certificate.subject_public_key_info)?;
Ok(Self {
serial_number,
subject,
issuer,
public_key,
raw_cert: cert.clone(),
})
}
}
impl From<Certificate> for IssuerAndSerialNumber {
fn from(cert: Certificate) -> Self {
Self {
issuer: cert.subject,
serial_number: cert.serial_number,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CertificatePublicKey {
pub algorithm: CertificateKeyAlgorithm,
pub key: Vec<u8>,
}
impl TryFrom<&crate::asn1::rfc5280::SubjectPublicKeyInfo> for CertificatePublicKey {
type Error = CmsError;
fn try_from(info: &crate::asn1::rfc5280::SubjectPublicKeyInfo) -> Result<Self, Self::Error> {
let algorithm = CertificateKeyAlgorithm::try_from(&info.algorithm)?;
let key = info.subject_public_key.octet_bytes().to_vec();
Ok(Self { algorithm, key })
}
}
pub fn certificate_is_subset_of(
a_serial: &Integer,
a_name: &Name,
b_serial: &Integer,
b_name: &Name,
) -> bool {
if a_serial != b_serial {
return false;
}
let Name::RdnSequence(a_sequence) = &a_name;
let Name::RdnSequence(b_sequence) = &b_name;
a_sequence.iter().all(|rdn| b_sequence.contains(rdn))
}