Skip to main content

structured_cms/
archive.rs

1//! `CAdES` `archive-time-stamp-v3` and its `ats-hash-index-v3` (ETSI EN 319 122-1
2//! V1.3.1 §5.5).
3//!
4//! An `archive-time-stamp-v3` is an unsigned attribute of a signer whose value
5//! is a time-stamp token. That token's own signer carries one
6//! `ats-hash-index-v3`: the hashes of the root `SignedData` certificates,
7//! revocation values and the signer's unsigned attribute values the
8//! time-stamp protects. The token's message imprint covers the content type,
9//! the content hash, the signer's signed fields and that index.
10//!
11//! [`token_reader`] yields the token of the attribute, [`HashIndex::of_token`]
12//! reads its index, [`check_index`] matches the index entries against the
13//! original elements, and [`imprint_input`] gives the ranges the token's
14//! imprint is checked over. Which archive time-stamps a level requires, and
15//! trust, stay with the caller.
16
17use crate::attribute::Attribute;
18use crate::digest::DigestError;
19use crate::signed::{CertificateChoice, SignedData};
20use crate::signer::{SignerInfo, required};
21use crate::timestamp::Token;
22use crate::{Error, ErrorCode, Result, oid};
23use core::cmp::Ordering;
24use structured_asn1::digest::{Digest, Resolution};
25use structured_asn1::memory::{OwnedVec, SortError, Work};
26use structured_asn1::{Element, Encoding, Reader, Span, Tag, universal};
27use structured_pkix::AlgorithmIdentifier;
28use structured_pkix::algorithm::digest::{self as asn_digest, AsnDigest};
29
30/// One `ATSHashIndexV3` value (EN 319 122-1 §5.5.2), in its original DER.
31#[derive(Debug)]
32pub struct HashIndex<'a> {
33    algorithm: AlgorithmIdentifier<'a>,
34    certificates: OwnedVec<&'a [u8]>,
35    revocations: OwnedVec<&'a [u8]>,
36    attribute_values: OwnedVec<&'a [u8]>,
37    element: Element<'a>,
38}
39
40impl<'a> HashIndex<'a> {
41    /// Reads the single `ats-hash-index-v3` of an archive time-stamp token.
42    ///
43    /// EN 319 122-1 §5.5.3 requires exactly one such attribute in the token's
44    /// unsigned attributes, and §5.5.2 exactly one value, with the whole
45    /// attribute DER encoded.
46    ///
47    /// # Errors
48    ///
49    /// A missing or repeated attribute, a value count other than one, an
50    /// attribute that is not DER, a value that is not an `ATSHashIndexV3`, a
51    /// foreign reader, or a spent work ceiling.
52    pub fn of_token(token: &Token<'a>, parent: &Reader<'a>) -> Result<Self> {
53        const FIELD: &str = "archiveTimeStampV3.atsHashIndexV3";
54        let signer = token.signer();
55        // The token's parser funds the search below, not another session's.
56        signer.require_session(parent, FIELD)?;
57        let Some(unsigned) = signer.unsigned_attributes() else {
58            return Err(Error::schema(
59                ErrorCode::MissingField,
60                FIELD,
61                signer.span().start,
62            ));
63        };
64        let attribute = unsigned
65            .unique_in(oid::ATS_HASH_INDEX_V3, parent)?
66            .ok_or_else(|| {
67                Error::schema(
68                    ErrorCode::MissingField,
69                    FIELD,
70                    unsigned.element().span().start,
71                )
72            })?;
73        attribute.single(FIELD)?;
74        // The value keeps its original nesting depth here, so its contents are
75        // read under the depth ceiling that applies where they are written.
76        let mut reader = attribute
77            .value_reader_in(0, parent, FIELD)?
78            .ok_or_else(|| Error::schema(ErrorCode::MissingField, FIELD, attribute.span().start))?;
79        let value = required(&mut reader, FIELD)?;
80        // §5.5.2 "The ats-hash-index-v3 attribute shall be DER encoded": the
81        // Attribute SEQUENCE, attrType and attrValues SET headers are checked
82        // here without descending into the value; `read` checks the value.
83        let mut strict = parent
84            .reread(attribute.element(), Encoding::Der)
85            .map_err(|error| Error::encoding(error, FIELD))?;
86        let whole = strict
87            .expect(Tag::constructed(universal::SEQUENCE))
88            .map_err(|error| Error::encoding(error, FIELD))?;
89        let mut fields = strict
90            .enter(&whole)
91            .map_err(|error| Error::encoding(error, FIELD))?;
92        fields
93            .expect(Tag::primitive(universal::OID))
94            .map_err(|error| Error::encoding(error, FIELD))?;
95        let set = fields
96            .expect(Tag::constructed(universal::SET))
97            .map_err(|error| Error::encoding(error, FIELD))?;
98        let mut values = fields
99            .enter(&set)
100            .map_err(|error| Error::encoding(error, FIELD))?;
101        required(&mut values, FIELD)?;
102        Self::read(&reader, &value)
103    }
104}
105
106/// A reader over the one time-stamp token of an `archive-time-stamp-v3`
107/// attribute, for [`Token::read_from`].
108///
109/// EN 319 122-1 §5.5.3 gives the attribute exactly one `AttributeValue`.
110///
111/// # Errors
112///
113/// An attribute of another type, a value count other than one, a foreign
114/// reader, or a spent work ceiling.
115pub fn token_reader<'q, 'a: 'q>(
116    attribute: &Attribute<'a>,
117    parent: &Reader<'q>,
118) -> Result<Reader<'q>> {
119    const FIELD: &str = "archiveTimeStampV3";
120    if attribute.kind() != oid::ARCHIVE_TIMESTAMP_V3 {
121        return Err(Error::schema(
122            ErrorCode::AttributeSyntax,
123            FIELD,
124            attribute.span().start,
125        ));
126    }
127    attribute.single(FIELD)?;
128    attribute
129        .value_reader_in(0, parent, FIELD)?
130        .ok_or_else(|| Error::schema(ErrorCode::MissingField, FIELD, attribute.span().start))
131}
132
133impl<'a> HashIndex<'a> {
134    /// Reads one `ATSHashIndexV3` value, rereading it under DER (§5.5.2).
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use std::sync::Arc;
140    /// use structured_asn1::Parser;
141    /// use structured_asn1::memory::{Memory, MemoryAccount};
142    /// use structured_cms::archive::HashIndex;
143    ///
144    /// // SHA-256, one certificate hash, no revocation or attribute hashes.
145    /// let mut index = vec![0x30, 53, 0x30, 11, 6, 9, 0x60, 0x86, 0x48, 1, 0x65, 3, 4, 2, 1];
146    /// index.extend([0x30, 34, 4, 32]);
147    /// index.extend([7; 32]);
148    /// index.extend([0x30, 0, 0x30, 0]);
149    /// let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(4096, 4096))));
150    /// let mut reader = parser.read(&index, 0).unwrap();
151    /// let value = reader.next_element().unwrap().unwrap();
152    /// let read = HashIndex::read(&reader, &value).unwrap();
153    /// assert_eq!(read.certificates(), &[&[7; 32][..]]);
154    /// assert!(read.revocations().is_empty() && read.attribute_values().is_empty());
155    /// assert_eq!(read.as_bytes(), index.as_slice());
156    /// ```
157    ///
158    /// # Errors
159    ///
160    /// A value that is not DER, not the four-field sequence, or whose index
161    /// entries are not OCTET STRINGs; a spent work ceiling.
162    pub fn read(parent: &Reader<'a>, value: &Element<'a>) -> Result<Self> {
163        const FIELD: &str = "atsHashIndexV3";
164        let mut strict = parent
165            .reread(value, Encoding::Der)
166            .map_err(|error| Error::encoding(error, FIELD))?;
167        let element = strict
168            .expect(Tag::constructed(universal::SEQUENCE))
169            .map_err(|error| Error::encoding(error, FIELD))?;
170        let mut inside = strict
171            .enter(&element)
172            .map_err(|error| Error::encoding(error, FIELD))?;
173        // Annex D gives hashIndAlgorithm no DEFAULT: it is always written.
174        let algorithm_element = required(&mut inside, "atsHashIndexV3.hashIndAlgorithm")?;
175        let algorithm = structured_pkix::algorithm::read(
176            &inside,
177            &algorithm_element,
178            "atsHashIndexV3.hashIndAlgorithm",
179        )
180        .map_err(Error::pkix)?;
181        let certificates = hashes(&mut inside, "atsHashIndexV3.certificatesHashIndex")?;
182        let revocations = hashes(&mut inside, "atsHashIndexV3.crlsHashIndex")?;
183        let attribute_values = hashes(&mut inside, "atsHashIndexV3.unsignedAttrValuesHashIndex")?;
184        if !inside.is_empty() {
185            return Err(Error::schema(
186                ErrorCode::TrailingData,
187                FIELD,
188                inside.offset(),
189            ));
190        }
191        Ok(Self {
192            algorithm,
193            certificates,
194            revocations,
195            attribute_values,
196            element,
197        })
198    }
199
200    /// The algorithm every index entry and the message imprint are hashed with.
201    #[must_use]
202    pub fn algorithm(&self) -> &AlgorithmIdentifier<'a> {
203        &self.algorithm
204    }
205    /// `certificatesHashIndex` entries, in their written order.
206    #[must_use]
207    pub fn certificates(&self) -> &[&'a [u8]] {
208        &self.certificates
209    }
210    /// `crlsHashIndex` entries, in their written order.
211    #[must_use]
212    pub fn revocations(&self) -> &[&'a [u8]] {
213        &self.revocations
214    }
215    /// `unsignedAttrValuesHashIndex` entries, in their written order.
216    #[must_use]
217    pub fn attribute_values(&self) -> &[&'a [u8]] {
218        &self.attribute_values
219    }
220    /// The original `ATSHashIndexV3` encoding, the last part of the imprint input.
221    #[must_use]
222    pub fn as_bytes(&self) -> &'a [u8] {
223        self.element.as_bytes()
224    }
225    /// The original range of the index value.
226    #[must_use]
227    pub fn span(&self) -> Span {
228        self.element.span()
229    }
230}
231
232fn hashes<'a>(inside: &mut Reader<'a>, field: &'static str) -> Result<OwnedVec<&'a [u8]>> {
233    let list = required(inside, field)?;
234    if list.tag() != Tag::constructed(universal::SEQUENCE) {
235        return Err(Error::schema(
236            ErrorCode::UnexpectedTag,
237            field,
238            list.span().start,
239        ));
240    }
241    let mut members = inside
242        .enter(&list)
243        .map_err(|error| Error::encoding(error, field))?;
244    let mut entries = inside.execution().vector();
245    while let Some(entry) = members
246        .next_element()
247        .map_err(|error| Error::encoding(error, field))?
248    {
249        if entry.tag() != Tag::primitive(universal::OCTET_STRING) {
250            return Err(Error::schema(
251                ErrorCode::UnexpectedTag,
252                field,
253                entry.span().start,
254            ));
255        }
256        entries
257            .try_push(entry.contents())
258            .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
259    }
260    Ok(entries.into_owned())
261}
262
263/// Which list of the index an entry belongs to.
264#[derive(Debug, Clone, Copy, PartialEq, Eq)]
265pub enum IndexList {
266    /// `certificatesHashIndex`.
267    Certificates,
268    /// `crlsHashIndex`.
269    Revocations,
270    /// `unsignedAttrValuesHashIndex`.
271    AttributeValues,
272}
273
274/// An index entry that matches no original element, which makes the whole
275/// `ats-hash-index-v3` invalid (EN 319 122-1 §5.5.2).
276#[derive(Debug, Clone, Copy, PartialEq, Eq)]
277pub struct Unmatched {
278    /// The list holding the entry.
279    pub list: IndexList,
280    /// The entry's position in that list.
281    pub ordinal: usize,
282}
283
284/// One unsigned attribute value of the archived signer.
285#[derive(Debug, Clone, Copy, PartialEq, Eq)]
286pub struct AttributeValue {
287    /// Position of the attribute in the signer's unsigned attributes.
288    pub attribute: usize,
289    /// Position of the value in that attribute's values.
290    pub value: usize,
291}
292
293/// What an `ats-hash-index-v3` protects in the root `SignedData` and signer.
294///
295/// An element without an entry is not an error: it was added after the
296/// time-stamp, or is not protected by it. Only an entry without an element
297/// invalidates the index (§5.5.2).
298#[derive(Debug)]
299pub struct IndexCoverage {
300    certificates: OwnedVec<bool>,
301    revocations: OwnedVec<bool>,
302    attribute_values: OwnedVec<(AttributeValue, bool)>,
303    unmatched: OwnedVec<Unmatched>,
304}
305
306impl IndexCoverage {
307    /// Every index entry matches an original element.
308    #[must_use]
309    pub fn is_valid(&self) -> bool {
310        self.unmatched.is_empty()
311    }
312    /// Entries that match no element, in list and written order.
313    #[must_use]
314    pub fn unmatched(&self) -> &[Unmatched] {
315        &self.unmatched
316    }
317    /// Whether each certificate choice, in original order, has an entry.
318    #[must_use]
319    pub fn certificates(&self) -> &[bool] {
320        &self.certificates
321    }
322    /// Whether each revocation choice, in original order, has an entry.
323    #[must_use]
324    pub fn revocations(&self) -> &[bool] {
325        &self.revocations
326    }
327    /// Every unsigned attribute value of the signer, in original order, and
328    /// whether it has an entry.
329    #[must_use]
330    pub fn attribute_values(&self) -> &[(AttributeValue, bool)] {
331        &self.attribute_values
332    }
333}
334
335/// Result of matching an index, or the provider's answer that it cannot hash
336/// with the index algorithm.
337pub type CoverageResult<E> = core::result::Result<Resolution<IndexCoverage>, DigestError<E>>;
338
339/// Matches every entry of `index` against the original elements it may name.
340///
341/// Each certificate and revocation choice of the root `message` is hashed over
342/// its whole original encoding, and each unsigned attribute value of `signer`
343/// over its `attrType` encoding followed by the value's encoding (§5.5.2).
344/// Nothing is re-encoded: an entry computed over another encoding of the same
345/// value does not match, and an `OtherCertificateFormat`, which §5.5.2 has its
346/// producer write in DER, is hashed as written. Only the root `SignedData`
347/// fields count; an element carried elsewhere, such as in a revocation-values
348/// attribute, names no entry. The index algorithm is resolved through
349/// `provider`; an algorithm it does not know or that is malformed is answered
350/// as such, before anything is hashed.
351///
352/// # Errors
353///
354/// A zero-sized provider, a message or index from another parser than
355/// `parent`'s, a `signer` that is not one of `message`'s, a spent work ceiling,
356/// a provider failure, or a provider output whose width contradicts its
357/// identity.
358pub fn check_index<'a, P: AsnDigest<'a>>(
359    index: &HashIndex<'_>,
360    message: &SignedData<'_>,
361    signer: &SignerInfo<'_>,
362    provider: &'a P,
363    parent: &Reader<'_>,
364) -> CoverageResult<P::Error> {
365    let execution = parent.execution();
366    let at = index.span().start;
367    let field = "atsHashIndexV3";
368    let refusal = |reason| DigestError::Processing(Error::resource(reason, field, at));
369    admit(index, message, signer, provider, parent).map_err(DigestError::Processing)?;
370    let identity = match asn_digest::resolve(provider, index.algorithm(), &execution)
371        .map_err(|error| DigestError::Processing(Error::pkix(error)))?
372    {
373        Resolution::Supported(identity) => identity,
374        Resolution::Unsupported => return Ok(Resolution::Unsupported),
375        Resolution::Malformed => return Ok(Resolution::Malformed),
376    };
377    let hasher = Hasher {
378        provider,
379        identity: &identity,
380        field,
381        at,
382    };
383
384    let certificate_hashes = hasher.hash_each(
385        message
386            .certificate_choices()
387            .iter()
388            .map(|choice| match choice {
389                CertificateChoice::X509(ordinal) => message.certificates()[*ordinal].as_bytes(),
390                CertificateChoice::Extended(element)
391                | CertificateChoice::AttributeV1(element)
392                | CertificateChoice::AttributeV2(element)
393                | CertificateChoice::Other(element) => element.as_bytes(),
394            }),
395        &execution,
396    )?;
397    let revocation_hashes = hasher.hash_each(
398        message
399            .revocations()
400            .iter()
401            .map(|revocation| revocation.as_bytes()),
402        &execution,
403    )?;
404
405    let attributes = signer
406        .unsigned_attributes()
407        .map_or(&[][..], |set| set.items());
408    let (value_hashes, mut values) = hasher.hash_values(attributes, &execution)?;
409
410    let mut unmatched = execution.vector();
411    let certificates = mark(
412        &hasher,
413        &certificate_hashes,
414        index.certificates(),
415        IndexList::Certificates,
416        &mut unmatched,
417        &execution,
418        refusal,
419    )?;
420    let revocations = mark(
421        &hasher,
422        &revocation_hashes,
423        index.revocations(),
424        IndexList::Revocations,
425        &mut unmatched,
426        &execution,
427        refusal,
428    )?;
429    let covered = mark(
430        &hasher,
431        &value_hashes,
432        index.attribute_values(),
433        IndexList::AttributeValues,
434        &mut unmatched,
435        &execution,
436        refusal,
437    )?;
438    // The final projection is a pass over every value, paid before it runs.
439    execution
440        .charge_work(values.len() as u64)
441        .map_err(refusal)?;
442    for (entry, flag) in values.iter_mut().zip(covered.iter()) {
443        entry.1 = *flag;
444    }
445    Ok(Resolution::Supported(IndexCoverage {
446        certificates,
447        revocations,
448        attribute_values: values.into_owned(),
449        unmatched: unmatched.into_owned(),
450    }))
451}
452
453// Refuses before any work: a zero-sized provider has no instance identity,
454// only the producing parser's ceilings may fund the hashes and comparisons,
455// and the archived signer is one of the root message's own (§5.5.1).
456fn admit<P>(
457    index: &HashIndex<'_>,
458    message: &SignedData<'_>,
459    signer: &SignerInfo<'_>,
460    provider: &P,
461    parent: &Reader<'_>,
462) -> Result<()> {
463    const FIELD: &str = "atsHashIndexV3";
464    let at = index.span().start;
465    crate::provider::require_instance(provider, FIELD, at)?;
466    message.require_session(parent, FIELD)?;
467    message.require_signer(signer, FIELD)?;
468    if !index.algorithm().same_session(parent) {
469        return Err(Error::pkix(structured_pkix::Error::new(
470            structured_pkix::ErrorCode::ForeignReader,
471            FIELD,
472            at,
473        )));
474    }
475    Ok(())
476}
477
478// The value hashes and, in the same order, each value's position and flag.
479type ValueHashes<O, W> = (OwnedVec<O, W>, OwnedVec<(AttributeValue, bool), W>);
480
481struct Hasher<'p, 'a, P: Digest<'a>> {
482    provider: &'a P,
483    identity: &'p P::Identity,
484    field: &'static str,
485    at: u64,
486}
487
488impl<'a, P: Digest<'a>> Hasher<'_, 'a, P> {
489    fn hash<W: Work>(
490        &self,
491        parts: &[&[u8]],
492        execution: &W,
493    ) -> core::result::Result<P::Output, DigestError<P::Error>> {
494        execution.charge_work(1).map_err(|reason| {
495            DigestError::Processing(Error::resource(reason, self.field, self.at))
496        })?;
497        let mut state = match self
498            .provider
499            .start(self.identity, execution)
500            .map_err(DigestError::Callback)?
501        {
502            Resolution::Supported(state) => state,
503            Resolution::Unsupported | Resolution::Malformed => {
504                return Err(DigestError::Unstreamable);
505            }
506        };
507        for part in parts {
508            self.provider
509                .update(&mut state, part, execution)
510                .map_err(DigestError::Callback)?;
511        }
512        let output = self
513            .provider
514            .finish(state, execution)
515            .map_err(DigestError::Callback)?;
516        if let Some(expected) = self.provider.width(self.identity) {
517            let actual = output.as_ref().len();
518            if actual != expected {
519                return Err(DigestError::OutputWidth { expected, actual });
520            }
521        }
522        Ok(output)
523    }
524
525    // One hash per whole encoding, in order, with exact storage up front.
526    fn hash_each<'e, W: Work>(
527        &self,
528        encodings: impl ExactSizeIterator<Item = &'e [u8]>,
529        execution: &W,
530    ) -> core::result::Result<OwnedVec<P::Output, W>, DigestError<P::Error>> {
531        let refusal =
532            |reason| DigestError::Processing(Error::resource(reason, self.field, self.at));
533        let mut outputs = execution.vector();
534        outputs
535            .try_reserve_exact(encodings.len())
536            .map_err(refusal)?;
537        for encoding in encodings {
538            outputs
539                .try_push(self.hash(&[encoding], execution)?)
540                .map_err(refusal)?;
541        }
542        Ok(outputs)
543    }
544
545    // One hash per unsigned attribute value, over its attrType encoding and the
546    // value's encoding (§5.5.2), with the value's position and an unset flag.
547    fn hash_values<W: Work>(
548        &self,
549        attributes: &[Attribute<'_>],
550        execution: &W,
551    ) -> core::result::Result<ValueHashes<P::Output, W>, DigestError<P::Error>> {
552        let refusal =
553            |reason| DigestError::Processing(Error::resource(reason, self.field, self.at));
554        let mut hashes = execution.vector();
555        let mut values = execution.vector();
556        for (attribute_ordinal, attribute) in attributes.iter().enumerate() {
557            // Each attribute inspected is paid, including one with no values.
558            execution.charge_work(1).map_err(refusal)?;
559            for (value_ordinal, value) in attribute.values().iter().enumerate() {
560                hashes.try_reserve(1).map_err(refusal)?;
561                values.try_reserve(1).map_err(refusal)?;
562                let hash = self.hash(&[attribute.kind_encoding(), value.as_bytes()], execution)?;
563                hashes.try_push(hash).map_err(refusal)?;
564                values
565                    .try_push((
566                        AttributeValue {
567                            attribute: attribute_ordinal,
568                            value: value_ordinal,
569                        },
570                        false,
571                    ))
572                    .map_err(refusal)?;
573            }
574        }
575        Ok((hashes, values))
576    }
577
578    fn matches<W: Work>(
579        &self,
580        output: &P::Output,
581        entry: &[u8],
582        execution: &W,
583    ) -> core::result::Result<bool, DigestError<P::Error>> {
584        self.provider
585            .matches(self.identity, output, entry, execution)
586            .map_err(DigestError::Callback)
587    }
588}
589
590// Sorts the element hashes once and finds, by binary search, the one hash each
591// entry could equal: the first not ordered before it, or the last hash when the
592// entry is ordered after all of them. The provider's own comparison then judges
593// that pair, so every entry reaches the provider whenever any hash exists, at
594// one provider comparison and log n byte comparisons. `Digest::matches` answers
595// exact byte equality (its strategy governs how bytes are compared, not which
596// differing bytes are equal), so no other hash can match an entry its nearest
597// one does not, and every hash need not be tried. Equal hashes name equal
598// encodings and are all marked.
599fn mark<'a, P: Digest<'a>>(
600    digest: &Hasher<'_, 'a, P>,
601    hashes: &[P::Output],
602    entries: &[&[u8]],
603    list: IndexList,
604    unmatched: &mut OwnedVec<Unmatched, structured_asn1::memory::Execution<'_>>,
605    execution: &impl Work,
606    refusal: impl Fn(structured_asn1::memory::MemoryError) -> DigestError<P::Error> + Copy,
607) -> core::result::Result<OwnedVec<bool>, DigestError<P::Error>> {
608    // The two linear passes below (sort order and coverage flags) are paid up
609    // front; the sort and the lookups pay their comparisons as they go.
610    execution
611        .charge_work(2 * hashes.len() as u64)
612        .map_err(refusal)?;
613    let mut order = execution.vector();
614    order.try_reserve_exact(hashes.len()).map_err(refusal)?;
615    for ordinal in 0..hashes.len() {
616        order.try_push(ordinal).map_err(refusal)?;
617    }
618    order
619        .try_sort_unstable_by(|left, right| {
620            compare(hashes[*left].as_ref(), hashes[*right].as_ref(), execution)
621        })
622        .map_err(|error| match error {
623            SortError::Resource(reason) | SortError::Comparison(reason) => refusal(reason),
624        })?;
625    let mut covered = execution.vector();
626    covered.try_reserve_exact(hashes.len()).map_err(refusal)?;
627    for _ in 0..hashes.len() {
628        covered.try_push(false).map_err(refusal)?;
629    }
630    for (ordinal, entry) in entries.iter().enumerate() {
631        let first = lower_bound(&order, hashes, entry, execution).map_err(refusal)?;
632        // No hash at all leaves nothing an entry could name.
633        let nearest = order.get(first).or_else(|| order.last());
634        let matched = match nearest {
635            Some(&candidate) => digest.matches(&hashes[candidate], entry, execution)?,
636            None => false,
637        };
638        if let Some(&nearest) = nearest
639            && matched
640        {
641            let candidate = hashes[nearest].as_ref();
642            let mut position =
643                lower_bound(&order, hashes, candidate, execution).map_err(refusal)?;
644            while position < order.len()
645                && compare(hashes[order[position]].as_ref(), candidate, execution)
646                    .map_err(refusal)?
647                    == Ordering::Equal
648            {
649                covered[order[position]] = true;
650                position += 1;
651            }
652        } else {
653            unmatched.try_reserve(1).map_err(refusal)?;
654            unmatched
655                .try_push(Unmatched { list, ordinal })
656                .map_err(refusal)?;
657        }
658    }
659    Ok(covered.into_owned())
660}
661
662fn lower_bound<O: AsRef<[u8]>>(
663    order: &[usize],
664    hashes: &[O],
665    entry: &[u8],
666    execution: &impl Work,
667) -> core::result::Result<usize, structured_asn1::memory::MemoryError> {
668    let mut from = 0;
669    let mut to = order.len();
670    while from < to {
671        let middle = from + (to - from) / 2;
672        if compare(hashes[order[middle]].as_ref(), entry, execution)? == Ordering::Less {
673            from = middle + 1;
674        } else {
675            to = middle;
676        }
677    }
678    Ok(from)
679}
680
681fn compare(
682    left: &[u8],
683    right: &[u8],
684    execution: &impl Work,
685) -> core::result::Result<Ordering, structured_asn1::memory::MemoryError> {
686    execution.charge_work(left.len().min(right.len()) as u64 + 1)?;
687    Ok(left.cmp(right))
688}
689
690/// The message-imprint input of an `archive-time-stamp-v3` (EN 319 122-1
691/// §5.5.3), as original ranges in their required order: the content type
692/// encoding, `content_hash`, the signer's `version` through `signature`
693/// encodings, and the `ATSHashIndexV3` encoding.
694///
695/// `content_hash` is the hash of the signed content, computed as for the
696/// `message-digest` attribute but with the time-stamp's imprint algorithm; for
697/// detached content the caller supplies it. The returned ranges are a
698/// [`crate::stream::Source`] for the token's
699/// [`crate::timestamp::MessageImprint::check_content`].
700///
701/// # Errors
702///
703/// A `signer` that is not one of `message`'s own signers.
704pub fn imprint_input<'s>(
705    message: &SignedData<'s>,
706    signer: &SignerInfo<'s>,
707    index: &HashIndex<'s>,
708    content_hash: &'s [u8],
709) -> Result<[&'s [u8]; 4]> {
710    message.require_signer(signer, "archiveTimeStampV3")?;
711    Ok([
712        message.content().kind_encoding(),
713        content_hash,
714        signer.signed_fields(),
715        index.as_bytes(),
716    ])
717}
718
719/// Whether the index algorithm is the time-stamp's imprint algorithm, as
720/// §5.5.2 requires. Both are compared as resolved standard digests; an
721/// algorithm either side does not name as one is answered as such.
722///
723/// # Errors
724///
725/// A spent work ceiling.
726pub fn same_algorithm<W: Work>(
727    index: &HashIndex<'_>,
728    imprint: &AlgorithmIdentifier<'_>,
729    execution: &W,
730) -> Result<Resolution<bool>> {
731    let left = asn_digest::algorithm(index.algorithm(), execution).map_err(Error::pkix)?;
732    let right = asn_digest::algorithm(imprint, execution).map_err(Error::pkix)?;
733    Ok(match (left, right) {
734        (Resolution::Supported(left), Resolution::Supported(right)) => {
735            Resolution::Supported(left == right)
736        }
737        (Resolution::Malformed, _) | (_, Resolution::Malformed) => Resolution::Malformed,
738        _ => Resolution::Unsupported,
739    })
740}