use crate::{Error, ErrorCode};
use structured_asn1::{
Encoding, Oid, Reader,
length::DerLength,
memory::{MemoryError, OwnedVec, Work},
};
use structured_pkix::AlgorithmIdentifier;
const FIELD: &str = "timeStampReq.encoding";
#[derive(Debug, Clone, Copy)]
pub struct RequestExtension<'a> {
pub identifier: Oid<'a>,
pub critical: bool,
pub value: &'a [u8],
}
pub struct NewRequest<'a> {
pub algorithm: AlgorithmIdentifier<'a>,
pub hashed_message: &'a [u8],
pub policy: Option<Oid<'a>>,
pub nonce: Option<&'a [u8]>,
pub cert_req: bool,
pub extensions: &'a [RequestExtension<'a>],
}
#[derive(Debug)]
pub enum RequestEncodingError {
Processing(Error),
Parameters(structured_pkix::Error),
Resource(MemoryError),
}
impl NewRequest<'_> {
pub fn encode_der(&self, parent: &Reader<'_>) -> Result<OwnedVec<u8>, RequestEncodingError> {
if !self.algorithm.same_session(parent) {
return Err(RequestEncodingError::Processing(Error::schema(
ErrorCode::InvalidLocation,
FIELD,
self.algorithm.offset(),
)));
}
let work = parent.execution();
work.charge_work(1)
.map_err(RequestEncodingError::Resource)?;
parameters_der(self.algorithm, parent)?;
let oid = self.algorithm.algorithm().as_bytes();
let parameters = self.algorithm.parameters().unwrap_or(&[]);
let algorithm = add(tlv(oid.len())?, parameters.len())?;
let imprint = add(tlv(algorithm)?, tlv(self.hashed_message.len())?)?;
let mut total = add(3, tlv(imprint)?)?;
if let Some(policy) = self.policy {
total = add(total, tlv(policy.as_bytes().len())?)?;
}
let nonce = if let Some(bytes) = self.nonce {
work.charge_work(bytes.len() as u64)
.map_err(RequestEncodingError::Resource)?;
let first = bytes.iter().position(|b| *b != 0).unwrap_or(bytes.len());
let bytes = &bytes[first..];
let pad = bytes.is_empty() || bytes[0] & 0x80 != 0;
total = add(total, tlv(add(bytes.len(), usize::from(pad))?)?)?;
Some((bytes, pad))
} else {
None
};
if self.cert_req {
total = add(total, 3)?;
}
let mut extensions_len = 0;
for (index, extension) in self.extensions.iter().enumerate() {
work.charge_work(1)
.map_err(RequestEncodingError::Resource)?;
for previous in &self.extensions[..index] {
work.charge_work(add(
previous.identifier.as_bytes().len(),
extension.identifier.as_bytes().len(),
)? as u64)
.map_err(RequestEncodingError::Resource)?;
if previous.identifier == extension.identifier {
return Err(RequestEncodingError::Processing(Error::schema(
ErrorCode::DuplicateField,
FIELD,
self.algorithm.offset(),
)));
}
}
extensions_len = add(extensions_len, tlv(extension_len(extension)?)?)?;
}
if !self.extensions.is_empty() {
total = add(total, tlv(extensions_len)?)?;
}
let length = tlv(total)?;
work.charge_work(length as u64)
.map_err(RequestEncodingError::Resource)?;
let mut bytes = work.vector();
bytes
.try_reserve_exact(length)
.map_err(RequestEncodingError::Resource)?;
header(&mut bytes, 0x30, total)?;
push(&mut bytes, &[2, 1, 1])?;
header(&mut bytes, 0x30, imprint)?;
header(&mut bytes, 0x30, algorithm)?;
value(&mut bytes, 6, oid)?;
push(&mut bytes, parameters)?;
value(&mut bytes, 4, self.hashed_message)?;
if let Some(policy) = self.policy {
value(&mut bytes, 6, policy.as_bytes())?;
}
if let Some((nonce, pad)) = nonce {
header(&mut bytes, 2, nonce.len() + usize::from(pad))?;
if pad {
push(&mut bytes, &[0])?;
}
push(&mut bytes, nonce)?;
}
if self.cert_req {
push(&mut bytes, &[1, 1, 255])?;
}
if !self.extensions.is_empty() {
header(&mut bytes, 0xa0, extensions_len)?;
for extension in self.extensions {
header(&mut bytes, 0x30, extension_len(extension)?)?;
value(&mut bytes, 6, extension.identifier.as_bytes())?;
if extension.critical {
push(&mut bytes, &[1, 1, 255])?;
}
value(&mut bytes, 4, extension.value)?;
}
}
debug_assert_eq!(bytes.len(), length);
Ok(bytes.into_owned())
}
}
fn parameters_der<'a, 'q>(
algorithm: AlgorithmIdentifier<'a>,
parent: &Reader<'q>,
) -> Result<(), RequestEncodingError>
where
'a: 'q,
{
let Some(mut parameters) = algorithm
.parameters_reader_in(parent, FIELD)
.map_err(RequestEncodingError::Parameters)?
else {
return Ok(());
};
let element = parameters
.next_element()
.map_err(|e| RequestEncodingError::Processing(Error::encoding(e, FIELD)))?
.ok_or_else(|| {
RequestEncodingError::Processing(Error::schema(
ErrorCode::MissingField,
FIELD,
parameters.offset(),
))
})?;
let mut strict = parameters
.reread(&element, Encoding::Der)
.map_err(|e| RequestEncodingError::Processing(Error::encoding(e, FIELD)))?;
let element = strict
.next_element()
.map_err(|e| RequestEncodingError::Processing(Error::encoding(e, FIELD)))?
.ok_or_else(|| {
RequestEncodingError::Processing(Error::schema(
ErrorCode::MissingField,
FIELD,
strict.offset(),
))
})?;
structured_pkix::der::conforms(&strict, &element, FIELD)
.map_err(RequestEncodingError::Parameters)
}
fn extension_len(extension: &RequestExtension<'_>) -> Result<usize, RequestEncodingError> {
add(
add(
tlv(extension.identifier.as_bytes().len())?,
if extension.critical { 3 } else { 0 },
)?,
tlv(extension.value.len())?,
)
}
fn add(a: usize, b: usize) -> Result<usize, RequestEncodingError> {
a.checked_add(b)
.ok_or(RequestEncodingError::Resource(MemoryError::WorkExceeded))
}
fn tlv(content: usize) -> Result<usize, RequestEncodingError> {
add(
add(1, DerLength::new(content as u64).as_bytes().len())?,
content,
)
}
fn push<W: Work>(out: &mut OwnedVec<u8, W>, value: &[u8]) -> Result<(), RequestEncodingError> {
out.try_extend_from_slice(value)
.map_err(RequestEncodingError::Resource)
}
fn header<W: Work>(
out: &mut OwnedVec<u8, W>,
tag: u8,
content: usize,
) -> Result<(), RequestEncodingError> {
out.try_push(tag).map_err(RequestEncodingError::Resource)?;
push(out, DerLength::new(content as u64).as_bytes())
}
fn value<W: Work>(
out: &mut OwnedVec<u8, W>,
tag: u8,
value: &[u8],
) -> Result<(), RequestEncodingError> {
header(out, tag, value.len())?;
push(out, value)
}