use crate::base::id::TrustAnchorId;
use crate::base::reader::Reader;
use crate::{Error, Result};
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Property {
TrustAnchorId(TrustAnchorId),
TrustAnchorGroups(Vec<Vec<u8>>),
TrustAnchorNegotiation,
Unknown {
property_type: u16,
data: Vec<u8>,
},
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct CertificatePropertyList {
pub properties: Vec<Property>,
}
impl CertificatePropertyList {
pub fn trust_anchor_id(&self) -> Option<&TrustAnchorId> {
self.properties.iter().find_map(|property| match property {
Property::TrustAnchorId(id) => Some(id),
_ => None,
})
}
pub fn requires_trust_anchor_negotiation(&self) -> bool {
self.properties
.iter()
.any(|p| matches!(p, Property::TrustAnchorNegotiation))
}
pub fn parse(bytes: &[u8]) -> Result<Self> {
let mut reader = Reader::new(bytes);
let mut list = Reader::new(reader.vector(2, "property list")?);
if !reader.is_empty() {
return Err(Error::Malformed("property list trailing data"));
}
let mut properties = Vec::new();
while !list.is_empty() {
let property_type = list.uint(2, "property type")? as u16;
let data = list.vector(2, "property data")?;
properties.push(match property_type {
0 => Property::TrustAnchorId(TrustAnchorId::from_binary(data)?),
1 => {
let mut patterns_reader = Reader::new(data);
let mut inner = Reader::new(patterns_reader.vector(2, "trust anchor groups")?);
if !patterns_reader.is_empty() {
return Err(Error::Malformed("trust anchor groups"));
}
let mut patterns = Vec::new();
while !inner.is_empty() {
patterns.push(inner.vector(1, "trust anchor ID pattern")?.to_vec());
}
Property::TrustAnchorGroups(patterns)
}
2 => {
if !data.is_empty() {
return Err(Error::Malformed("trust anchor negotiation"));
}
Property::TrustAnchorNegotiation
}
_ => Property::Unknown {
property_type,
data: data.to_vec(),
},
});
}
Ok(Self { properties })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_demo_property_list() {
let bytes = hex::decode(
"00240000000781fd590101010100010011000f0e81fd5981fd59010102020101018000020000",
)
.unwrap();
let list = CertificatePropertyList::parse(&bytes).unwrap();
let id = list.trust_anchor_id().unwrap();
assert_eq!(id.to_dotted(), "32473.1.1.1.1");
assert!(list.requires_trust_anchor_negotiation());
let groups: Vec<_> = list
.properties
.iter()
.filter_map(|p| match p {
Property::TrustAnchorGroups(patterns) => Some(patterns),
_ => None,
})
.collect();
assert_eq!(groups.len(), 1);
assert_eq!(groups[0].len(), 1);
assert!(CertificatePropertyList::parse(&bytes[..bytes.len() - 1]).is_err());
}
#[test]
fn rejects_truncated_lists() {
assert!(CertificatePropertyList::parse(&[]).is_err());
assert!(CertificatePropertyList::parse(&[0, 1, 0]).is_err());
}
}