use super::{end, sequence, tag};
use crate::attribute::Attribute;
use crate::signer::required;
use crate::{Error, ErrorCode, Result};
use core::fmt;
use structured_asn1::memory::{OwnedVec, Work};
use structured_asn1::{Element, Encoding, Oid, ScopedReader, Tag, universal};
use structured_pkix::general_name::{self, GeneralName};
const FIELD: &str = "attributeCertificate.svceAuthInfo";
const SERVICE: &str = "attributeCertificate.svceAuthInfo.service";
const IDENT: &str = "attributeCertificate.svceAuthInfo.ident";
const AUTH: &str = "attributeCertificate.svceAuthInfo.authInfo";
pub const AUTHENTICATION_INFO: Oid<'static> = Oid::from_bytes(&[43, 6, 1, 5, 5, 7, 10, 1]);
pub const ACCESS_IDENTITY: Oid<'static> = Oid::from_bytes(&[43, 6, 1, 5, 5, 7, 10, 2]);
#[derive(Clone, Copy)]
enum Syntax {
Authentication,
Access,
}
pub struct SvceAuthInfo<'a> {
element: Element<'a>,
service_element: Element<'a>,
service: GeneralName<'a>,
ident_element: Element<'a>,
ident: GeneralName<'a>,
auth_info: Option<Element<'a>>,
}
impl fmt::Debug for SvceAuthInfo<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("SvceAuthInfo")
.field("element", &self.element.span())
.field("service", &self.service_element.span())
.field("ident", &self.ident_element.span())
.field("auth_info", &self.auth_info.map(Element::span))
.finish()
}
}
impl<'a> SvceAuthInfo<'a> {
pub fn read(parent: &mut ScopedReader<'a, '_>) -> Result<Self> {
let element = required(parent, FIELD)?;
let reading = Self::read_element(parent, &element)?;
end(parent, FIELD)?;
Ok(reading)
}
pub fn read_element(parent: &ScopedReader<'a, '_>, element: &Element<'a>) -> Result<Self> {
Self::read_fields(parent, element, Syntax::Authentication)
}
fn read_fields(
parent: &ScopedReader<'a, '_>,
element: &Element<'a>,
syntax: Syntax,
) -> 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 service_element = required(&mut inside, SERVICE)?;
let ident_element = required(&mut inside, IDENT)?;
let auth_info = inside
.next_element()
.map_err(|e| Error::encoding(e, AUTH))?;
if let Some(auth) = auth_info {
tag(&auth, Tag::primitive(universal::OCTET_STRING), AUTH)?;
if matches!(syntax, Syntax::Access) {
return Err(Error::schema(
ErrorCode::AttributeSyntax,
AUTH,
auth.span().start,
));
}
}
end(&inside, FIELD)?;
let service =
general_name::read(&inside, &service_element, SERVICE).map_err(Error::pkix)?;
let ident = general_name::read(&inside, &ident_element, IDENT).map_err(Error::pkix)?;
Ok(Self {
element,
service_element,
service,
ident_element,
ident,
auth_info,
})
}
pub fn read_attribute<W: Work>(attribute: &Attribute<'a>, work: &W) -> Result<OwnedVec<Self>> {
let execution = attribute.query_in(work);
execution
.charge_work(17)
.map_err(|e| Error::resource(e, FIELD, attribute.span().start))?;
let syntax = if attribute.kind() == AUTHENTICATION_INFO {
Syntax::Authentication
} else if attribute.kind() == ACCESS_IDENTITY {
Syntax::Access
} else {
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 values = execution.vector();
values
.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 element = required(&mut reader, FIELD)?;
let value = Self::read_fields(&reader, &element, syntax)?;
end(&reader, FIELD)?;
values
.try_push(value)
.map_err(|e| Error::resource(e, FIELD, element.span().start))?;
}
Ok(values.into_owned())
}
#[must_use]
pub fn element(&self) -> Element<'a> {
self.element
}
#[must_use]
pub fn service(&self) -> &GeneralName<'a> {
&self.service
}
#[must_use]
pub fn service_element(&self) -> Element<'a> {
self.service_element
}
#[must_use]
pub fn ident(&self) -> &GeneralName<'a> {
&self.ident
}
#[must_use]
pub fn ident_element(&self) -> Element<'a> {
self.ident_element
}
#[must_use]
pub fn auth_info(&self) -> Option<&'a [u8]> {
self.auth_info.map(Element::contents)
}
#[must_use]
pub fn auth_info_element(&self) -> Option<Element<'a>> {
self.auth_info
}
}