use super::Token;
use crate::{Error, ErrorCode, Result, signer::required};
use structured_asn1::memory::{OwnedVec, Work};
use structured_asn1::{Element, Encoding, Parser, Reader, Span, Tag, universal};
mod check;
pub use check::CheckedResponse;
const FIELD: &str = "timeStampResp";
const STATUS: &str = "pkiStatusInfo";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Status {
Granted = 0,
GrantedWithMods = 1,
Rejection = 2,
Waiting = 3,
RevocationWarning = 4,
RevocationNotification = 5,
}
impl Status {
#[must_use]
pub fn granted(self) -> bool {
matches!(self, Self::Granted | Self::GrantedWithMods)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Failure {
BadAlgorithm = 0,
BadRequest = 2,
BadDataFormat = 5,
TimeNotAvailable = 14,
UnacceptedPolicy = 15,
UnacceptedExtension = 16,
AdditionalInformationUnavailable = 17,
SystemFailure = 25,
}
#[derive(Debug, Clone, Copy)]
pub struct FailureInfo<'a> {
element: Element<'a>,
flags: u32,
}
impl<'a> FailureInfo<'a> {
#[must_use]
pub fn contains(self, reason: Failure) -> bool {
self.flags & (1u32 << reason as u8) != 0
}
#[must_use]
pub fn element(self) -> Element<'a> {
self.element
}
fn read(parent: &Reader<'a>, element: Element<'a>) -> Result<Self> {
const FIELD: &str = "pkiStatusInfo.failInfo";
const KNOWN: u32 = (1 << Failure::BadAlgorithm as u8)
| (1 << Failure::BadRequest as u8)
| (1 << Failure::BadDataFormat as u8)
| (1 << Failure::TimeNotAvailable as u8)
| (1 << Failure::UnacceptedPolicy as u8)
| (1 << Failure::UnacceptedExtension as u8)
| (1 << Failure::AdditionalInformationUnavailable as u8)
| (1 << Failure::SystemFailure as u8);
tag(&element, Tag::primitive(universal::BIT_STRING), FIELD)?;
let bits = parent
.bit_string(&element)
.map_err(|e| Error::encoding(e, FIELD))?;
let bytes = bits.as_bytes();
parent
.charge(1 + bytes.len() as u64)
.map_err(|e| Error::encoding(e, FIELD))?;
if let Some(last) = bytes.last()
&& (*last == 0 || last.trailing_zeros() != u32::from(bits.unused_bits()))
{
return Err(Error::schema(
ErrorCode::MalformedValue,
FIELD,
element.span().start,
));
}
if bytes.len() > 4 {
return Err(Error::schema(
ErrorCode::UnknownField,
FIELD,
element.span().start,
));
}
let mut flags = 0u32;
for (index, byte) in bytes.iter().enumerate() {
flags |= u32::from(byte.reverse_bits()) << (index * 8);
}
if flags & !KNOWN != 0 {
return Err(Error::schema(
ErrorCode::UnknownField,
FIELD,
element.span().start,
));
}
Ok(Self { element, flags })
}
}
#[derive(Debug, Clone, Copy)]
pub struct StatusText<'a> {
value: &'a str,
span: Span,
}
impl<'a> StatusText<'a> {
#[must_use]
pub fn as_str(self) -> &'a str {
self.value
}
#[must_use]
pub fn span(self) -> Span {
self.span
}
}
#[derive(Debug)]
pub struct StatusInfo<'a> {
status: Status,
text: Option<OwnedVec<StatusText<'a>>>,
failure: Option<FailureInfo<'a>>,
span: Span,
}
impl<'a> StatusInfo<'a> {
#[must_use]
pub fn status(&self) -> Status {
self.status
}
#[must_use]
pub fn text(&self) -> Option<&[StatusText<'a>]> {
self.text.as_deref()
}
#[must_use]
pub fn failure(&self) -> Option<FailureInfo<'a>> {
self.failure
}
#[must_use]
pub fn span(&self) -> Span {
self.span
}
fn read(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
tag(element, Tag::constructed(universal::SEQUENCE), STATUS)?;
let mut inside = parent
.enter(element)
.map_err(|e| Error::encoding(e, STATUS))?;
let status_element = required(&mut inside, "pkiStatusInfo.status")?;
tag(
&status_element,
Tag::primitive(universal::INTEGER),
"pkiStatusInfo.status",
)?;
let status = inside
.integer(&status_element)
.map_err(|e| Error::encoding(e, "pkiStatusInfo.status"))?;
let status = match status.as_u64() {
Some(0) => Status::Granted,
Some(1) => Status::GrantedWithMods,
Some(2) => Status::Rejection,
Some(3) => Status::Waiting,
Some(4) => Status::RevocationWarning,
Some(5) => Status::RevocationNotification,
_ => {
return Err(Error::schema(
ErrorCode::MalformedValue,
"pkiStatusInfo.status",
status_element.span().start,
));
}
};
let mut next = inside
.next_element()
.map_err(|e| Error::encoding(e, STATUS))?;
let text = if let Some(element) = next
.as_ref()
.filter(|e| e.tag() == Tag::constructed(universal::SEQUENCE))
{
let values = read_text(&inside, element)?;
next = inside
.next_element()
.map_err(|e| Error::encoding(e, STATUS))?;
Some(values)
} else {
None
};
let failure = next
.map(|element| FailureInfo::read(&inside, element))
.transpose()?;
end(&inside, STATUS)?;
Ok(Self {
status,
text,
failure,
span: element.span(),
})
}
}
#[derive(Debug)]
pub struct Response<'a> {
status: StatusInfo<'a>,
token: Option<OriginalToken<'a>>,
element: Element<'a>,
parser: &'a Parser,
token_depth: u32,
}
#[derive(Debug)]
struct OriginalToken<'a> {
value: Token<'a>,
element: Element<'a>,
}
impl<'a> Response<'a> {
pub fn read(parent: &mut Reader<'a>) -> Result<Self> {
let element = parent
.expect(Tag::constructed(universal::SEQUENCE))
.map_err(|e| Error::encoding(e, FIELD))?;
end(parent, FIELD)?;
Self::read_element(parent, &element)
}
pub fn read_element(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
let mut strict = parent
.reread(element, Encoding::Der)
.map_err(|e| Error::encoding(e, FIELD))?;
let element = strict
.expect(Tag::constructed(universal::SEQUENCE))
.map_err(|e| Error::encoding(e, FIELD))?;
structured_pkix::der::conforms(&strict, &element, FIELD).map_err(Error::pkix)?;
let mut inside = strict
.enter(&element)
.map_err(|e| Error::encoding(e, FIELD))?;
let status_element = required(&mut inside, STATUS)?;
let status = StatusInfo::read(&inside, &status_element)?;
let token_element = inside
.next_element()
.map_err(|e| Error::encoding(e, FIELD))?;
end(&inside, FIELD)?;
if status.status.granted() != token_element.is_some() {
return Err(Error::schema(
ErrorCode::MalformedValue,
FIELD,
element.span().start,
));
}
let token = token_element
.as_ref()
.map(|element| {
let mut reader = inside
.reread(element, Encoding::Der)
.map_err(|e| Error::encoding(e, FIELD))?;
Token::read_from(&mut reader).map(|value| OriginalToken {
value,
element: *element,
})
})
.transpose()?;
Ok(Self {
status,
token,
element,
parser: parent.parser(),
token_depth: inside.depth(),
})
}
#[must_use]
pub fn status(&self) -> &StatusInfo<'a> {
&self.status
}
#[must_use]
pub fn token(&self) -> Option<&Token<'a>> {
self.token.as_ref().map(|token| &token.value)
}
#[must_use]
pub fn element(&self) -> &Element<'a> {
&self.element
}
pub fn token_reader_in<'op>(&self, query: &Reader<'op>) -> Result<Option<Reader<'op>>>
where
'a: 'op,
{
self.token_in(query)
.map(|value| value.map(|(_, reader)| reader))
}
fn token_in<'op>(&self, query: &Reader<'op>) -> Result<Option<(&Token<'a>, Reader<'op>)>>
where
'a: 'op,
{
query
.execution()
.execution_for(self.parser)
.charge_work(1)
.map_err(|e| Error::resource(e, FIELD, self.element.span().start))?;
self.token
.as_ref()
.map(|token| {
self.parser
.read_query_in(
query,
token.element.as_bytes(),
token.element.span().start,
Encoding::Der,
self.token_depth,
)
.map(|reader| (&token.value, reader))
.map_err(|e| Error::encoding(e, FIELD))
})
.transpose()
}
}
fn read_text<'a>(parent: &Reader<'a>, element: &Element<'a>) -> Result<OwnedVec<StatusText<'a>>> {
const FIELD: &str = "pkiStatusInfo.statusString";
let mut inside = parent
.enter(element)
.map_err(|e| Error::encoding(e, FIELD))?;
let work = parent.execution();
let mut text = work.vector();
while let Some(element) = inside
.next_element()
.map_err(|e| Error::encoding(e, FIELD))?
{
tag(&element, Tag::primitive(universal::UTF8_STRING), FIELD)?;
inside
.charge(element.contents().len() as u64)
.map_err(|e| Error::encoding(e, FIELD))?;
let value = core::str::from_utf8(element.contents())
.map_err(|_| Error::schema(ErrorCode::MalformedValue, FIELD, element.span().start))?;
text.try_push(StatusText {
value,
span: element.span(),
})
.map_err(|e| Error::resource(e, FIELD, element.span().start))?;
}
if text.is_empty() {
return Err(Error::schema(
ErrorCode::MissingField,
FIELD,
element.span().start,
));
}
Ok(text.into_owned())
}
fn tag(element: &Element<'_>, expected: Tag, field: &'static str) -> Result<()> {
if element.tag() == expected {
Ok(())
} else {
Err(Error::schema(
ErrorCode::UnexpectedTag,
field,
element.span().start,
))
}
}
fn end(parent: &Reader<'_>, field: &'static str) -> Result<()> {
if parent.is_empty() {
Ok(())
} else {
Err(Error::schema(
ErrorCode::TrailingData,
field,
parent.offset(),
))
}
}