Skip to main content

structured_cms/timestamp/
request_fields.rs

1//! Paid nonce/policy comparisons, separate from full imprint or trust checks.
2
3use super::{ImprintOperand, Request, TokenInfo};
4use crate::{Error, Result};
5use structured_asn1::{Reader, memory::Work};
6
7/// A failure retains the operand whose original source locates the cause.
8#[derive(Debug)]
9pub struct RequestFieldError {
10    /// Token versus original request source.
11    pub operand: ImprintOperand,
12    /// Original processing/resource cause, never a semantic mismatch.
13    pub source: Error,
14}
15
16/// Optional requested-field facts; omitted request fields mean unchecked.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct RequestFields {
19    /// Equality of the requested nonce, including absence from the token.
20    pub nonce: Option<bool>,
21    /// Equality of the explicitly requested policy identifier.
22    pub policy: Option<bool>,
23}
24
25impl TokenInfo<'_> {
26    /// Compares requested nonce/policy after preflighting both original sessions.
27    ///
28    /// RFC3161 ยง2.4.2 requires the same nonce when one was requested. The common
29    /// INTEGER decoder supplies minimal numeric views without re-encoding the
30    /// original input. Every byte comparison is admitted by current token work.
31    /// These facts do not imply complete imprint equality, freshness or trust.
32    /// <https://www.rfc-editor.org/rfc/rfc3161.html#section-2.4.2>
33    pub fn matches_request_fields(
34        &self,
35        requested: &Request<'_>,
36        token_parent: &Reader<'_>,
37        requested_parent: &Reader<'_>,
38    ) -> core::result::Result<RequestFields, RequestFieldError> {
39        self.imprint()
40            .require_session(token_parent)
41            .map_err(|source| RequestFieldError {
42                operand: ImprintOperand::Token,
43                source,
44            })?;
45        requested
46            .imprint()
47            .require_session(requested_parent)
48            .map_err(|source| RequestFieldError {
49                operand: ImprintOperand::Requested,
50                source,
51            })?;
52        token_parent
53            .execution()
54            .charge_work(3)
55            .map_err(|reason| RequestFieldError {
56                operand: ImprintOperand::Token,
57                source: Error::resource(reason, "tstInfo", self.span().start),
58            })?;
59        let compare = |left: &[u8], right: &[u8], field, at| {
60            same(left, right, token_parent, field, at).map_err(|source| RequestFieldError {
61                operand: ImprintOperand::Token,
62                source,
63            })
64        };
65        let nonce = match requested.nonce() {
66            None => None,
67            Some(asked) => Some(match self.nonce() {
68                None => false,
69                Some(written) => compare(
70                    written.value().as_bytes(),
71                    asked.as_bytes(),
72                    "tstInfo.nonce",
73                    written.element().span().start,
74                )?,
75            }),
76        };
77        let policy = match requested.policy() {
78            None => None,
79            Some(asked) => Some(compare(
80                self.policy().as_bytes(),
81                asked.as_bytes(),
82                "tstInfo.policy",
83                self.span().start,
84            )?),
85        };
86        Ok(RequestFields { nonce, policy })
87    }
88}
89
90fn same(
91    left: &[u8],
92    right: &[u8],
93    parent: &Reader<'_>,
94    field: &'static str,
95    at: u64,
96) -> Result<bool> {
97    parent
98        .execution()
99        .charge_work(1 + left.len().min(right.len()) as u64)
100        .map_err(|reason| Error::resource(reason, field, at))?;
101    Ok(left == right)
102}