use super::{HashAlgorithmReading, MessageImprint};
use crate::{Error, ErrorCode, Result};
use structured_asn1::{Element, Integer, Oid, Parser, Reader, Span, Tag, universal};
use structured_pkix::Extensions;
#[derive(Debug)]
pub struct Request<'a> {
imprint: MessageImprint<'a>,
policy: Option<Oid<'a>>,
nonce: Option<Integer<'a>>,
cert_req: bool,
extensions: Option<Extensions<'a>>,
span: Span,
parser: &'a Parser,
}
impl<'a> Request<'a> {
pub fn read(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
const FIELD: &str = "timeStampReq";
if element.tag() != Tag::constructed(universal::SEQUENCE) {
return Err(Error::schema(
ErrorCode::UnexpectedTag,
FIELD,
element.span().start,
));
}
let mut inside = parent
.enter(element)
.map_err(|source| Error::encoding(source, FIELD))?;
let version = crate::signer::required(&mut inside, "timeStampReq.version")?;
if version.tag() != Tag::primitive(universal::INTEGER) {
return Err(Error::schema(
ErrorCode::UnexpectedTag,
"timeStampReq.version",
version.span().start,
));
}
let number = inside
.integer(&version)
.map_err(|source| Error::encoding(source, "timeStampReq.version"))?;
if number.as_u64() != Some(1) {
return Err(Error::schema(
ErrorCode::UnsupportedVersion,
"timeStampReq.version",
version.span().start,
));
}
let imprint = crate::signer::required(&mut inside, "timeStampReq.messageImprint")?;
let imprint = MessageImprint::read_fields(
&inside,
&imprint,
super::imprint::ImprintFields {
enclosing: "timeStampReq.messageImprint",
algorithm: "timeStampReq.messageImprint.hashAlgorithm",
hashed: "timeStampReq.messageImprint.hashedMessage",
},
)?;
if !imprint.faults().is_empty()
|| !matches!(imprint.algorithm(), HashAlgorithmReading::Identifier(_))
{
return Err(Error::schema(
ErrorCode::MalformedValue,
"timeStampReq.messageImprint",
imprint.span().start,
));
}
let mut output = Self {
imprint,
policy: None,
nonce: None,
cert_req: false,
extensions: None,
span: element.span(),
parser: parent.parser(),
};
let mut previous = 0;
while let Some(element) = inside
.next_element()
.map_err(|source| Error::encoding(source, FIELD))?
{
let ordinal = match element.tag() {
tag if tag == Tag::primitive(universal::OID) => 1,
tag if tag == Tag::primitive(universal::INTEGER) => 2,
tag if tag == Tag::primitive(universal::BOOLEAN) => 3,
tag if tag == Tag::context(true, 0) => 4,
_ => {
return Err(Error::schema(
ErrorCode::UnknownField,
FIELD,
element.span().start,
));
}
};
if ordinal <= previous {
return Err(Error::schema(
if ordinal == previous {
ErrorCode::DuplicateField
} else {
ErrorCode::FieldOrder
},
FIELD,
element.span().start,
));
}
previous = ordinal;
output.read_optional(&inside, &element, ordinal)?;
}
Ok(output)
}
fn read_optional(
&mut self,
inside: &Reader<'a>,
element: &Element<'a>,
ordinal: u8,
) -> Result<()> {
match ordinal {
1 => {
self.policy = Some(
inside
.oid(element)
.map_err(|source| Error::encoding(source, "timeStampReq.reqPolicy"))?,
);
}
2 => {
self.nonce = Some(
inside
.integer(element)
.map_err(|source| Error::encoding(source, "timeStampReq.nonce"))?,
);
}
3 => {
self.cert_req = inside
.boolean(element)
.map_err(|source| Error::encoding(source, "timeStampReq.certReq"))?;
if !self.cert_req && inside.encoding() == structured_asn1::Encoding::Der {
return Err(Error::schema(
ErrorCode::DefaultWritten,
"timeStampReq.certReq",
element.span().start,
));
}
}
4 => {
self.extensions = Some(
Extensions::read_implicit(inside, element, 0, "timeStampReq.extensions")
.map_err(Error::pkix)?,
);
}
_ => unreachable!("ordinal comes from the four schema tags above"),
}
Ok(())
}
#[must_use]
pub fn imprint(&self) -> &MessageImprint<'a> {
&self.imprint
}
#[must_use]
pub fn policy(&self) -> Option<Oid<'a>> {
self.policy
}
#[must_use]
pub fn nonce(&self) -> Option<Integer<'a>> {
self.nonce
}
#[must_use]
pub fn cert_req(&self) -> bool {
self.cert_req
}
#[must_use]
pub fn extensions(&self) -> Option<&Extensions<'a>> {
self.extensions.as_ref()
}
#[must_use]
pub fn span(&self) -> Span {
self.span
}
#[must_use]
pub fn same_session(&self, parent: &Reader<'_>) -> bool {
core::ptr::eq(self.parser, parent.parser())
}
}