use super::{end, sequence, tag};
use crate::signer::required;
use crate::{Error, ErrorCode, Result};
use structured_asn1::memory::{OwnedVec, Work};
use structured_asn1::{Element, Encoding, Integer, Oid, Parser, ScopedReader, Tag, universal};
use structured_pkix::Extension;
const FIELD: &str = "attributeCertificate.aaControls";
pub const IDENTIFIER: Oid<'static> = Oid::from_bytes(&[43, 6, 1, 5, 5, 7, 1, 6]);
#[derive(Debug)]
pub struct AaControls<'a> {
parser: &'a Parser,
pub element: Element<'a>,
pub path_length: Option<Integer<'a>>,
pub path_length_element: Option<Element<'a>>,
pub permitted_element: Option<Element<'a>>,
pub permitted: OwnedVec<Oid<'a>>,
pub excluded_element: Option<Element<'a>>,
pub excluded: OwnedVec<Oid<'a>>,
pub unspecified_element: Option<Element<'a>>,
pub permit_unspecified: bool,
}
impl<'a> AaControls<'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 mut next = inside
.next_element()
.map_err(|e| Error::encoding(e, FIELD))?;
let path_length_element = super::optional(
&mut inside,
&mut next,
Tag::primitive(universal::INTEGER),
FIELD,
)?;
let path_length = path_length_element
.map(|encoded| {
let number = inside
.integer(&encoded)
.map_err(|e| Error::encoding(e, FIELD))?;
if number.is_negative() {
return Err(Error::schema(
ErrorCode::MalformedValue,
FIELD,
encoded.span().start,
));
}
Ok(number)
})
.transpose()?;
let permitted_element =
super::optional(&mut inside, &mut next, Tag::context(true, 0), FIELD)?;
let excluded_element =
super::optional(&mut inside, &mut next, Tag::context(true, 1), FIELD)?;
let unspecified_element = super::optional(
&mut inside,
&mut next,
Tag::primitive(universal::BOOLEAN),
FIELD,
)?;
super::no_pending(next, FIELD)?;
let permit_unspecified = match unspecified_element {
Some(encoded) => {
let value = inside
.boolean(&encoded)
.map_err(|e| Error::encoding(e, FIELD))?;
if value {
return Err(Error::schema(
ErrorCode::DefaultWritten,
FIELD,
encoded.span().start,
));
}
false
}
None => true,
};
let permitted = identifiers(&inside, permitted_element)?;
let excluded = identifiers(&inside, excluded_element)?;
Ok(Self {
parser: parent.parser(),
element,
path_length,
path_length_element,
permitted_element,
permitted,
excluded_element,
excluded,
unspecified_element,
permit_unspecified,
})
}
pub fn read_extension<W: Work>(extension: &Extension<'a>, work: &W) -> Result<Self> {
let execution = work.execution_for(extension.parser());
execution
.charge_work(9)
.map_err(|e| Error::resource(e, FIELD, extension.span().start))?;
if extension.kind() != IDENTIFIER {
return Err(Error::schema(
ErrorCode::UnexpectedContentType,
FIELD,
extension.span().start,
));
}
let mut reader = extension
.reader_scoped(&execution)
.map_err(|e| Error::encoding(e, FIELD))?;
Self::read(&mut reader)
}
pub fn allows<W: Work>(&self, kind: Oid<'_>, work: &W) -> Result<bool> {
let execution = work.execution_for(self.parser);
execution
.charge_work(1)
.map_err(|e| Error::resource(e, FIELD, self.element.span().start))?;
for (list, answer) in [(&self.excluded, false), (&self.permitted, true)] {
for identifier in list {
let amount = identifier.as_bytes().len().min(kind.as_bytes().len()) as u64 + 1;
execution
.charge_work(amount)
.map_err(|e| Error::resource(e, FIELD, self.element.span().start))?;
if *identifier == kind {
return Ok(answer);
}
}
}
Ok(self.permit_unspecified)
}
pub fn permits_distance<W: Work>(&self, distance: u64, work: &W) -> Result<bool> {
let execution = work.execution_for(self.parser);
execution
.charge_work(10)
.map_err(|e| Error::resource(e, FIELD, self.element.span().start))?;
Ok(match self.path_length {
None => true,
Some(number) => match number.as_u64() {
Some(bound) => distance <= bound,
None => true,
},
})
}
}
fn identifiers<'a>(
parent: &ScopedReader<'a, '_>,
element: Option<Element<'a>>,
) -> Result<OwnedVec<Oid<'a>>> {
let execution = parent.execution();
let mut result = execution.vector();
if let Some(encoded) = element {
let mut inside = parent
.enter(&encoded)
.map_err(|e| Error::encoding(e, FIELD))?;
while let Some(value) = inside
.next_element()
.map_err(|e| Error::encoding(e, FIELD))?
{
tag(&value, Tag::primitive(universal::OID), FIELD)?;
result
.try_reserve(1)
.map_err(|e| Error::resource(e, FIELD, value.span().start))?;
let identifier = inside.oid(&value).map_err(|e| Error::encoding(e, FIELD))?;
result
.try_push(identifier)
.map_err(|e| Error::resource(e, FIELD, value.span().start))?;
}
}
Ok(result.into_owned())
}