use super::{end, sequence, tag};
use crate::attribute::Attribute;
use crate::signer::required;
use crate::{Error, ErrorCode, Result};
use structured_asn1::memory::{OwnedVec, Work};
use structured_asn1::{BitString, Element, Encoding, Oid, Parser, ScopedReader, Tag, universal};
const FIELD: &str = "attributeCertificate.clearance";
pub const IDENTIFIER: Oid<'static> = Oid::from_bytes(&[85, 4, 55]);
pub const LEGACY_IDENTIFIER: Oid<'static> = Oid::from_bytes(&[85, 1, 5, 55]);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Syntax {
Current,
Legacy,
}
#[derive(Debug, Clone, Copy)]
pub struct SecurityCategory<'a> {
parser: &'a Parser,
depth: u32,
pub element: Element<'a>,
pub kind: Oid<'a>,
pub kind_element: Element<'a>,
pub wrapper: Element<'a>,
pub value: Element<'a>,
}
impl<'a> SecurityCategory<'a> {
pub fn value_reader<'operation, W: Work>(
&self,
work: &'operation W,
) -> Result<ScopedReader<'a, 'operation>>
where
'a: 'operation,
{
work.read_scoped_source(
self.parser,
self.value.as_bytes(),
self.value.span().start,
Encoding::Der,
self.depth,
)
.map_err(|e| Error::encoding(e, FIELD))
}
}
#[derive(Debug)]
pub struct Clearance<'a> {
pub element: Element<'a>,
pub syntax: Syntax,
pub policy: Oid<'a>,
pub policy_element: Element<'a>,
pub classes: Option<BitString<'a>>,
pub classes_element: Option<Element<'a>>,
pub categories_element: Option<Element<'a>>,
pub categories: OwnedVec<SecurityCategory<'a>>,
}
impl<'a> Clearance<'a> {
pub fn read(parent: &mut ScopedReader<'a, '_>) -> Result<Self> {
let element = required(parent, FIELD)?;
let result = Self::read_element(parent, &element)?;
end(parent, FIELD)?;
Ok(result)
}
pub fn read_element(parent: &ScopedReader<'a, '_>, element: &Element<'a>) -> Result<Self> {
let mut der = parent
.reread(element, Encoding::Der)
.map_err(|e| Error::encoding(e, FIELD))?;
let element = required(&mut der, FIELD)?;
tag(&element, sequence(), FIELD)?;
end(&der, FIELD)?;
let mut inside = der.enter(&element).map_err(|e| Error::encoding(e, FIELD))?;
let policy_element = required(&mut inside, FIELD)?;
let syntax = if policy_element.tag() == Tag::context(false, 0) {
Syntax::Legacy
} else {
tag(&policy_element, Tag::primitive(universal::OID), FIELD)?;
Syntax::Current
};
let policy = inside
.oid(&policy_element)
.map_err(|e| Error::encoding(e, FIELD))?;
let class_tag = match syntax {
Syntax::Current => Tag::primitive(universal::BIT_STRING),
Syntax::Legacy => Tag::context(false, 1),
};
let set_tag = match syntax {
Syntax::Current => Tag::constructed(universal::SET),
Syntax::Legacy => Tag::context(true, 2),
};
let mut next = inside
.next_element()
.map_err(|e| Error::encoding(e, FIELD))?;
let classes_element = super::optional(&mut inside, &mut next, class_tag, FIELD)?;
let classes = classes_element
.map(|encoded| {
let bits = inside
.bit_string(&encoded)
.map_err(|e| Error::encoding(e, FIELD))?;
if !bits.is_empty() && !bits.bit(bits.len() - 1) {
return Err(Error::schema(
ErrorCode::MalformedValue,
FIELD,
encoded.span().start,
));
}
if bits.len() == 2 && bits.as_bytes() == [0x40] {
return Err(Error::schema(
ErrorCode::DefaultWritten,
FIELD,
encoded.span().start,
));
}
Ok(bits)
})
.transpose()?;
let categories_element = next;
let execution = inside.execution();
let mut categories = execution.vector();
if let Some(set) = categories_element {
tag(&set, set_tag, FIELD)?;
let mut entries = inside.enter(&set).map_err(|e| Error::encoding(e, FIELD))?;
let mut previous = None;
while let Some(entry) = entries
.next_element()
.map_err(|e| Error::encoding(e, FIELD))?
{
if let Some(before) = previous
&& !structured_pkix::der::set_ordered(&entries, before, &entry, FIELD)
.map_err(Error::pkix)?
{
return Err(Error::schema(
ErrorCode::SetOrder,
FIELD,
entry.span().start,
));
}
categories
.try_reserve(1)
.map_err(|e| Error::resource(e, FIELD, entry.span().start))?;
categories
.try_push(category(&entries, &entry)?)
.map_err(|e| Error::resource(e, FIELD, entry.span().start))?;
previous = Some(entry.as_bytes());
}
}
end(&inside, FIELD)?;
Ok(Self {
element,
syntax,
policy,
policy_element,
classes,
classes_element,
categories_element,
categories: categories.into_owned(),
})
}
pub fn read_attribute<W: Work>(attribute: &Attribute<'a>, work: &W) -> Result<OwnedVec<Self>> {
let execution = attribute.query_in(work);
execution
.charge_work(8)
.map_err(|e| Error::resource(e, FIELD, attribute.span().start))?;
let current = attribute.kind() == IDENTIFIER;
if !current && attribute.kind() != LEGACY_IDENTIFIER {
return Err(Error::schema(
ErrorCode::UnexpectedContentType,
FIELD,
attribute.span().start,
));
}
if attribute.values().is_empty() {
return Err(Error::schema(
ErrorCode::AttributeCardinality,
FIELD,
attribute.span().start,
));
}
let mut result = execution.vector();
result
.try_reserve_exact(attribute.values().len())
.map_err(|e| Error::resource(e, FIELD, attribute.span().start))?;
for index in 0..attribute.values().len() {
let mut reader = attribute
.value_reader_scoped(index, &execution, FIELD)?
.ok_or_else(|| {
Error::schema(
ErrorCode::AttributeCardinality,
FIELD,
attribute.span().start,
)
})?;
let value = Self::read(&mut reader)?;
if current && value.syntax == Syntax::Legacy {
return Err(Error::schema(
ErrorCode::AttributeSyntax,
FIELD,
value.policy_element.span().start,
));
}
result
.try_push(value)
.map_err(|e| Error::resource(e, FIELD, reader.offset()))?;
}
Ok(result.into_owned())
}
#[must_use]
pub fn has_class(&self, position: u64) -> bool {
match self.classes {
Some(bits) => bits.bit(position),
None => position == 1,
}
}
}
fn category<'a>(
parent: &ScopedReader<'a, '_>,
element: &Element<'a>,
) -> Result<SecurityCategory<'a>> {
tag(element, sequence(), FIELD)?;
let mut inside = parent
.enter(element)
.map_err(|e| Error::encoding(e, FIELD))?;
let kind_element = required(&mut inside, FIELD)?;
tag(&kind_element, Tag::context(false, 0), FIELD)?;
let kind = inside
.oid(&kind_element)
.map_err(|e| Error::encoding(e, FIELD))?;
let wrapper = required(&mut inside, FIELD)?;
tag(&wrapper, Tag::context(true, 1), FIELD)?;
end(&inside, FIELD)?;
let mut nested = inside
.enter(&wrapper)
.map_err(|e| Error::encoding(e, FIELD))?;
let value = required(&mut nested, FIELD)?;
end(&nested, FIELD)?;
structured_pkix::der::conforms(&nested, &value, FIELD).map_err(Error::pkix)?;
Ok(SecurityCategory {
parser: parent.parser(),
depth: nested.depth(),
element: *element,
kind,
kind_element,
wrapper,
value,
})
}