1use 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#[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 pub fn of_token(token: &Token<'a>, parent: &Reader<'a>) -> Result<Self> {
53 const FIELD: &str = "archiveTimeStampV3.atsHashIndexV3";
54 let signer = token.signer();
55 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 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 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
106pub 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 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 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 #[must_use]
202 pub fn algorithm(&self) -> &AlgorithmIdentifier<'a> {
203 &self.algorithm
204 }
205 #[must_use]
207 pub fn certificates(&self) -> &[&'a [u8]] {
208 &self.certificates
209 }
210 #[must_use]
212 pub fn revocations(&self) -> &[&'a [u8]] {
213 &self.revocations
214 }
215 #[must_use]
217 pub fn attribute_values(&self) -> &[&'a [u8]] {
218 &self.attribute_values
219 }
220 #[must_use]
222 pub fn as_bytes(&self) -> &'a [u8] {
223 self.element.as_bytes()
224 }
225 #[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
265pub enum IndexList {
266 Certificates,
268 Revocations,
270 AttributeValues,
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq)]
277pub struct Unmatched {
278 pub list: IndexList,
280 pub ordinal: usize,
282}
283
284#[derive(Debug, Clone, Copy, PartialEq, Eq)]
286pub struct AttributeValue {
287 pub attribute: usize,
289 pub value: usize,
291}
292
293#[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 #[must_use]
309 pub fn is_valid(&self) -> bool {
310 self.unmatched.is_empty()
311 }
312 #[must_use]
314 pub fn unmatched(&self) -> &[Unmatched] {
315 &self.unmatched
316 }
317 #[must_use]
319 pub fn certificates(&self) -> &[bool] {
320 &self.certificates
321 }
322 #[must_use]
324 pub fn revocations(&self) -> &[bool] {
325 &self.revocations
326 }
327 #[must_use]
330 pub fn attribute_values(&self) -> &[(AttributeValue, bool)] {
331 &self.attribute_values
332 }
333}
334
335pub type CoverageResult<E> = core::result::Result<Resolution<IndexCoverage>, DigestError<E>>;
338
339pub 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 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
453fn 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
478type 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 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 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 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
590fn 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 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 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
690pub 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
719pub 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}