Skip to main content

structured_cms/timestamp/
accuracy.rs

1//! Accuracy schema over original integer views and independently owned faults.
2
3use super::{Accuracy, record};
4use crate::{Error, ErrorCode, Result};
5use structured_asn1::memory::{OwnedVec, Work};
6use structured_asn1::{Element, Integer, Reader, Span, Tag, universal};
7
8const FIELD: &str = "tstInfo.accuracy";
9
10/// The optional component selected by the RFC3161 Accuracy schema.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Component {
13    /// Optional universal INTEGER seconds component.
14    Seconds,
15    /// Optional primitive IMPLICIT [0] milliseconds component.
16    Millis,
17    /// Optional primitive IMPLICIT [1] microseconds component.
18    Micros,
19}
20
21impl Component {
22    const fn index(self) -> usize {
23        match self {
24            Self::Seconds => 0,
25            Self::Millis => 1,
26            Self::Micros => 2,
27        }
28    }
29}
30
31/// Original wire spelling and the common INTEGER decoder's minimal suffix.
32#[derive(Debug, Clone, Copy)]
33pub struct ComponentReading<'a> {
34    element: Element<'a>,
35    value: Integer<'a>,
36}
37impl<'a> ComponentReading<'a> {
38    /// Original whole TLV, distinguishing omitted fields from explicit zero.
39    #[must_use]
40    pub fn element(self) -> Element<'a> {
41        self.element
42    }
43    /// Numeric borrowed view, without another padding scan or a repaired copy.
44    #[must_use]
45    pub fn value(self) -> Integer<'a> {
46        self.value
47    }
48}
49
50/// Why the original component cannot be represented by a nonnegative u64.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum DeviationReason {
53    /// Original two's-complement INTEGER is below zero.
54    Negative,
55    /// Original nonnegative INTEGER exceeds the scalar representation.
56    TooWide,
57}
58
59/// The unrepresentable component remains present, with its original location.
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct DeviationError {
62    /// Original component that could not be represented.
63    pub component: Component,
64    /// Whole original component TLV range.
65    pub span: Span,
66    /// No fallback value was substituted for this reason.
67    pub reason: DeviationReason,
68}
69
70/// Readable scalar fields and conformance faults, without token acceptance.
71///
72/// Fixed component storage cannot grow with attacker-controlled field count.
73/// Diagnostics retain their own capacity charge; callers must consider them
74/// before accepting a token/proof. Unknown or out-of-order fields and readable
75/// nonminimal INTEGERs remain faults. Identical original duplicates retain the
76/// first reading; conflicting original spellings refuse rather than selecting
77/// a value. This recovery is an inspection contract, not specified by RFC3161.
78#[derive(Debug)]
79pub struct AccuracyReading<'a> {
80    parts: [Option<ComponentReading<'a>>; 3],
81    faults: OwnedVec<Error>,
82    span: Span,
83}
84
85impl<'a> AccuracyReading<'a> {
86    /// Reads SEQUENCE with optional seconds and primitive IMPLICIT [0]/[1].
87    ///
88    /// RFC3161 ยง2.4.2 gives millis/micros the range 1..999 and defaults each
89    /// absent component to zero. Present empty Accuracy is zero, whereas an
90    /// omitted entire Accuracy provides no bound. Readable out-of-range values
91    /// are retained exactly with a fault, never clamped. Resource failures abort.
92    /// <https://www.rfc-editor.org/rfc/rfc3161#section-2.4.2>
93    pub fn read(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
94        if element.tag() != Tag::constructed(universal::SEQUENCE) {
95            return Err(Error::schema(
96                ErrorCode::UnexpectedTag,
97                FIELD,
98                element.span().start,
99            ));
100        }
101        let mut inside = parent
102            .enter(element)
103            .map_err(|error| Error::encoding(error, FIELD))?;
104        let execution = parent.execution();
105        let mut faults = execution.vector();
106        let mut parts: [Option<ComponentReading<'a>>; 3] = [None; 3];
107        let mut last = None;
108        while let Some(field) = inside
109            .next_element()
110            .map_err(|error| Error::encoding(error, FIELD))?
111        {
112            let at = field.span().start;
113            execution
114                .charge_work(1)
115                .map_err(|reason| Error::resource(reason, FIELD, at))?;
116            let component = if field.tag() == Tag::primitive(universal::INTEGER) {
117                Component::Seconds
118            } else if field.tag() == Tag::context(false, 0) {
119                Component::Millis
120            } else if field.tag() == Tag::context(false, 1) {
121                Component::Micros
122            } else {
123                record(
124                    &mut faults,
125                    Error::schema(ErrorCode::UnknownField, FIELD, at),
126                )?;
127                continue;
128            };
129            let index = component.index();
130            if let Some(earlier) = parts[index] {
131                if earlier.element.as_bytes().len() != field.as_bytes().len() {
132                    return Err(Error::schema(ErrorCode::DuplicateField, FIELD, at));
133                }
134                execution
135                    .charge_work(field.as_bytes().len() as u64)
136                    .map_err(|reason| Error::resource(reason, FIELD, at))?;
137                if earlier.element.as_bytes() != field.as_bytes() {
138                    return Err(Error::schema(ErrorCode::DuplicateField, FIELD, at));
139                }
140                record(
141                    &mut faults,
142                    Error::schema(ErrorCode::DuplicateField, FIELD, at),
143                )?;
144                continue;
145            }
146            if last.is_some_and(|last| index < last) {
147                record(&mut faults, Error::schema(ErrorCode::FieldOrder, FIELD, at))?;
148            }
149            last = Some(last.map_or(index, |last| last.max(index)));
150            let (value, fault) = inside
151                .integer_reading(&field)
152                .map_err(|error| Error::encoding(error, FIELD))?
153                .into_parts();
154            if let Some(fault) = fault {
155                record(&mut faults, Error::encoding(fault, FIELD))?;
156            }
157            let number = value.as_u64();
158            if number.is_none()
159                || (component != Component::Seconds
160                    && !number.is_some_and(|number| (1..=999).contains(&number)))
161            {
162                record(
163                    &mut faults,
164                    Error::schema(ErrorCode::MalformedValue, FIELD, at),
165                )?;
166            }
167            parts[index] = Some(ComponentReading {
168                element: field,
169                value,
170            });
171        }
172        Ok(Self {
173            parts,
174            faults: faults.into_owned(),
175            span: element.span(),
176        })
177    }
178
179    /// Original component, absent only when no such field was written.
180    #[must_use]
181    pub fn component(&self, component: Component) -> Option<ComponentReading<'a>> {
182        self.parts[component.index()]
183    }
184    /// Original complete Accuracy encoding range.
185    #[must_use]
186    pub fn span(&self) -> Span {
187        self.span
188    }
189    /// Original schema/conformance faults; accessing them performs no new scan.
190    #[must_use]
191    pub fn faults(&self) -> &[Error] {
192        &self.faults
193    }
194    /// Move diagnostic storage without cloning paths or refunding live capacity.
195    #[must_use]
196    pub fn into_faults(self) -> OwnedVec<Error> {
197        self.faults
198    }
199
200    /// Exact nonnegative deviation, independently of conformance acceptance.
201    /// Negative/wide components are typed failures, never zero or `u64::MAX`.
202    pub fn deviation(&self) -> core::result::Result<Accuracy, DeviationError> {
203        let mut values = [0; 3];
204        for component in [Component::Seconds, Component::Millis, Component::Micros] {
205            let index = component.index();
206            if let Some(part) = self.parts[index] {
207                values[index] = part.value.as_u64().ok_or_else(|| DeviationError {
208                    component,
209                    span: part.element.span(),
210                    reason: if part.value.is_negative() {
211                        DeviationReason::Negative
212                    } else {
213                        DeviationReason::TooWide
214                    },
215                })?;
216            }
217        }
218        Ok(Accuracy::new(values[0], values[1], values[2]))
219    }
220}