Skip to main content

structured_cms/timestamp/
request_encoding.rs

1//! Caller-driven RFC3161 request construction, without transport or randomness.
2
3use crate::{Error, ErrorCode};
4use structured_asn1::{
5    Encoding, Oid, Reader,
6    length::DerLength,
7    memory::{MemoryError, OwnedVec, Work},
8};
9use structured_pkix::AlgorithmIdentifier;
10
11const FIELD: &str = "timeStampReq.encoding";
12
13/// One newly supplied request extension. Its value is the complete encoded
14/// extension payload, not an OCTET STRING wrapper. Interpretation is external.
15#[derive(Debug, Clone, Copy)]
16pub struct RequestExtension<'a> {
17    /// Registered extension identifier.
18    pub identifier: Oid<'a>,
19    /// Explicit criticality; false is omitted from DER.
20    pub critical: bool,
21    /// Original caller-provided payload; copied only into the final output.
22    pub value: &'a [u8],
23}
24
25/// New request values. This is a construction input, not a computed imprint
26/// receipt or evidence that the chosen hash is permitted. Compute the imprint
27/// through the selected provider before supplying its bytes here.
28///
29/// Nonce bytes are an unsigned magnitude, generated by the caller's entropy
30/// source. Empty means zero; None means absent. The encoder never invents a
31/// nonce, clock, policy, default algorithm or extension interpretation.
32pub struct NewRequest<'a> {
33    /// Actual selected hash identifier with its original parameter provenance.
34    pub algorithm: AlgorithmIdentifier<'a>,
35    /// Finished message digest, with the width checked by the selected provider.
36    pub hashed_message: &'a [u8],
37    /// Explicit requested TSA policy, if any.
38    pub policy: Option<Oid<'a>>,
39    /// Newly generated unsigned nonce magnitude, without ASN.1 framing.
40    pub nonce: Option<&'a [u8]>,
41    /// Whether the response must carry the TSA certificate.
42    pub cert_req: bool,
43    /// New extensions, in caller order; duplicate identifiers are rejected.
44    pub extensions: &'a [RequestExtension<'a>],
45}
46
47/// Construction refuses before returning any partially encoded request.
48#[derive(Debug)]
49pub enum RequestEncodingError {
50    /// Original schema/session/DER processing cause.
51    Processing(Error),
52    /// Original PKIX parameter syntax or source refusal.
53    Parameters(structured_pkix::Error),
54    /// Capacity, arithmetic or cumulative work refusal.
55    Resource(MemoryError),
56}
57
58impl NewRequest<'_> {
59    /// Encode one complete DER `TimeStampReq`. The reader must be from the
60    /// identifier's producing session, optionally rebound to current Work.
61    /// New values are admitted through that joint execution; retained capacity
62    /// keeps its actual allocation charge after the reader is dropped.
63    ///
64    /// RFC3161 §§2.4.1/3 and X.690 §11.5 require ordered DER fields and omission
65    /// of DEFAULT FALSE. Parameters must already be DER-conformant; no opaque
66    /// BER payload is silently rewritten or interpreted as a different hash.
67    /// <https://www.rfc-editor.org/rfc/rfc3161#section-2.4.1>
68    pub fn encode_der(&self, parent: &Reader<'_>) -> Result<OwnedVec<u8>, RequestEncodingError> {
69        if !self.algorithm.same_session(parent) {
70            return Err(RequestEncodingError::Processing(Error::schema(
71                ErrorCode::InvalidLocation,
72                FIELD,
73                self.algorithm.offset(),
74            )));
75        }
76        let work = parent.execution();
77        work.charge_work(1)
78            .map_err(RequestEncodingError::Resource)?;
79        parameters_der(self.algorithm, parent)?;
80        let oid = self.algorithm.algorithm().as_bytes();
81        let parameters = self.algorithm.parameters().unwrap_or(&[]);
82        let algorithm = add(tlv(oid.len())?, parameters.len())?;
83        let imprint = add(tlv(algorithm)?, tlv(self.hashed_message.len())?)?;
84        let mut total = add(3, tlv(imprint)?)?;
85        if let Some(policy) = self.policy {
86            total = add(total, tlv(policy.as_bytes().len())?)?;
87        }
88        let nonce = if let Some(bytes) = self.nonce {
89            work.charge_work(bytes.len() as u64)
90                .map_err(RequestEncodingError::Resource)?;
91            let first = bytes.iter().position(|b| *b != 0).unwrap_or(bytes.len());
92            let bytes = &bytes[first..];
93            let pad = bytes.is_empty() || bytes[0] & 0x80 != 0;
94            total = add(total, tlv(add(bytes.len(), usize::from(pad))?)?)?;
95            Some((bytes, pad))
96        } else {
97            None
98        };
99        if self.cert_req {
100            total = add(total, 3)?;
101        }
102        let mut extensions_len = 0;
103        for (index, extension) in self.extensions.iter().enumerate() {
104            work.charge_work(1)
105                .map_err(RequestEncodingError::Resource)?;
106            for previous in &self.extensions[..index] {
107                work.charge_work(add(
108                    previous.identifier.as_bytes().len(),
109                    extension.identifier.as_bytes().len(),
110                )? as u64)
111                    .map_err(RequestEncodingError::Resource)?;
112                if previous.identifier == extension.identifier {
113                    return Err(RequestEncodingError::Processing(Error::schema(
114                        ErrorCode::DuplicateField,
115                        FIELD,
116                        self.algorithm.offset(),
117                    )));
118                }
119            }
120            extensions_len = add(extensions_len, tlv(extension_len(extension)?)?)?;
121        }
122        if !self.extensions.is_empty() {
123            total = add(total, tlv(extensions_len)?)?;
124        }
125        let length = tlv(total)?;
126        work.charge_work(length as u64)
127            .map_err(RequestEncodingError::Resource)?;
128        let mut bytes = work.vector();
129        bytes
130            .try_reserve_exact(length)
131            .map_err(RequestEncodingError::Resource)?;
132        header(&mut bytes, 0x30, total)?;
133        push(&mut bytes, &[2, 1, 1])?;
134        header(&mut bytes, 0x30, imprint)?;
135        header(&mut bytes, 0x30, algorithm)?;
136        value(&mut bytes, 6, oid)?;
137        push(&mut bytes, parameters)?;
138        value(&mut bytes, 4, self.hashed_message)?;
139        if let Some(policy) = self.policy {
140            value(&mut bytes, 6, policy.as_bytes())?;
141        }
142        if let Some((nonce, pad)) = nonce {
143            header(&mut bytes, 2, nonce.len() + usize::from(pad))?;
144            if pad {
145                push(&mut bytes, &[0])?;
146            }
147            push(&mut bytes, nonce)?;
148        }
149        if self.cert_req {
150            push(&mut bytes, &[1, 1, 255])?;
151        }
152        if !self.extensions.is_empty() {
153            header(&mut bytes, 0xa0, extensions_len)?;
154            for extension in self.extensions {
155                header(&mut bytes, 0x30, extension_len(extension)?)?;
156                value(&mut bytes, 6, extension.identifier.as_bytes())?;
157                if extension.critical {
158                    push(&mut bytes, &[1, 1, 255])?;
159                }
160                value(&mut bytes, 4, extension.value)?;
161            }
162        }
163        debug_assert_eq!(bytes.len(), length);
164        Ok(bytes.into_owned())
165    }
166}
167
168fn parameters_der<'a, 'q>(
169    algorithm: AlgorithmIdentifier<'a>,
170    parent: &Reader<'q>,
171) -> Result<(), RequestEncodingError>
172where
173    'a: 'q,
174{
175    let Some(mut parameters) = algorithm
176        .parameters_reader_in(parent, FIELD)
177        .map_err(RequestEncodingError::Parameters)?
178    else {
179        return Ok(());
180    };
181    let element = parameters
182        .next_element()
183        .map_err(|e| RequestEncodingError::Processing(Error::encoding(e, FIELD)))?
184        .ok_or_else(|| {
185            RequestEncodingError::Processing(Error::schema(
186                ErrorCode::MissingField,
187                FIELD,
188                parameters.offset(),
189            ))
190        })?;
191    // Re-read the strict header as well as its contents: using the BER Element
192    // directly would retain a non-minimal length that DER never permits.
193    let mut strict = parameters
194        .reread(&element, Encoding::Der)
195        .map_err(|e| RequestEncodingError::Processing(Error::encoding(e, FIELD)))?;
196    let element = strict
197        .next_element()
198        .map_err(|e| RequestEncodingError::Processing(Error::encoding(e, FIELD)))?
199        .ok_or_else(|| {
200            RequestEncodingError::Processing(Error::schema(
201                ErrorCode::MissingField,
202                FIELD,
203                strict.offset(),
204            ))
205        })?;
206    structured_pkix::der::conforms(&strict, &element, FIELD)
207        .map_err(RequestEncodingError::Parameters)
208}
209
210fn extension_len(extension: &RequestExtension<'_>) -> Result<usize, RequestEncodingError> {
211    add(
212        add(
213            tlv(extension.identifier.as_bytes().len())?,
214            if extension.critical { 3 } else { 0 },
215        )?,
216        tlv(extension.value.len())?,
217    )
218}
219fn add(a: usize, b: usize) -> Result<usize, RequestEncodingError> {
220    a.checked_add(b)
221        .ok_or(RequestEncodingError::Resource(MemoryError::WorkExceeded))
222}
223fn tlv(content: usize) -> Result<usize, RequestEncodingError> {
224    add(
225        add(1, DerLength::new(content as u64).as_bytes().len())?,
226        content,
227    )
228}
229fn push<W: Work>(out: &mut OwnedVec<u8, W>, value: &[u8]) -> Result<(), RequestEncodingError> {
230    out.try_extend_from_slice(value)
231        .map_err(RequestEncodingError::Resource)
232}
233fn header<W: Work>(
234    out: &mut OwnedVec<u8, W>,
235    tag: u8,
236    content: usize,
237) -> Result<(), RequestEncodingError> {
238    out.try_push(tag).map_err(RequestEncodingError::Resource)?;
239    push(out, DerLength::new(content as u64).as_bytes())
240}
241fn value<W: Work>(
242    out: &mut OwnedVec<u8, W>,
243    tag: u8,
244    value: &[u8],
245) -> Result<(), RequestEncodingError> {
246    header(out, tag, value.len())?;
247    push(out, value)
248}