structured_pkix/path.rs
1//! Certification paths: which sequences of certificates lead from a
2//! certificate to an anchor the caller trusts, and what that costs.
3//!
4//! One signature can have several valid paths, and a report shows them,
5//! because a cross-certified provider is recognized under one scheme by one
6//! path and under another by another. That is not the same as enumerating
7//! every path: a certificate set somebody else controls can hold a
8//! combinatorial number of them, and a browser asked for all of them stops
9//! answering. So the search is bounded in every direction it can grow, and
10//! when a bound is reached the answer says so. Silence about truncation
11//! would be a worse answer than the truncation.
12//!
13//! Where the bound is on how many paths are returned, which paths are kept
14//! is not the order they were found in. A second path to the same anchor
15//! naming the same policies tells a reader nothing the first did not; a path
16//! to another anchor, or one naming other policies, is the whole reason
17//! several paths are shown at all. So a path that differs in either is kept
18//! ahead of a repeat of one already held.
19//!
20//! An anchor is an input, never a discovery (RFC 5280 6.1): a self-signed
21//! certificate that happens to sit in the same pool is a certificate like
22//! any other, and a path that ends at one the caller did not name ends
23//! nowhere.
24
25mod error;
26mod name_space;
27mod policy;
28mod validation;
29
30pub use error::{BuildError, BuildResult};
31pub use validation::{
32 ConstraintSource, EffectiveAnchor, NameConstraintFailure, Rejection, RejectionReason,
33 Validation, ValidationBounds, ValidationContext, ValidationFacts, validate, validate_effective,
34};
35
36use crate::algorithm::AlgorithmIdentifier;
37use crate::certificate::{Certificate, Validity};
38use crate::error::Result;
39use crate::extension::Recognizer;
40use crate::key::PublicKeyInfo;
41use crate::name::Name;
42use structured_asn1::memory::{Memory, OwnedMap, OwnedVec};
43use structured_asn1::{BitString, Oid};
44
45/// What the caller trusts, as the trust context named it.
46///
47/// A name and a key, because that is what an anchor is: the certificate it
48/// was published in, where there is one, is kept for the report and is not
49/// what makes it an anchor. Two anchors with the same name and different
50/// keys are two anchors, and a path is built to each.
51#[derive(Debug, Clone, Copy)]
52pub struct Anchor<'a> {
53 name: &'a Name<'a>,
54 key: PublicKeyInfo<'a>,
55 certificate: Option<&'a Certificate<'a>>,
56}
57
58impl<'a> Anchor<'a> {
59 /// An anchor the trust context supplies as a name and a key.
60 #[must_use]
61 pub fn new(name: &'a Name<'a>, key: PublicKeyInfo<'a>) -> Self {
62 Self {
63 name,
64 key,
65 certificate: None,
66 }
67 }
68
69 /// An anchor the trust context supplies as a certificate, whose subject
70 /// and key it is. The certificate is not part of any path built to it
71 /// (RFC 5280 6.1), and is kept so a report can name where the anchor
72 /// came from and so that what it says about the names below it is
73 /// obeyed.
74 #[must_use]
75 pub fn from_certificate(certificate: &'a Certificate<'a>) -> Self {
76 Self {
77 name: certificate.tbs().subject(),
78 key: certificate.tbs().public_key(),
79 certificate: Some(certificate),
80 }
81 }
82
83 /// The name a certificate issued under this anchor carries as its issuer.
84 #[must_use]
85 pub fn name(&self) -> &'a Name<'a> {
86 self.name
87 }
88
89 /// The public key information, as encoded, which is what verifies the
90 /// signature of the certificate below it.
91 #[must_use]
92 pub fn key(&self) -> PublicKeyInfo<'a> {
93 self.key
94 }
95
96 /// The certificate the anchor was published in, where the trust context
97 /// had one.
98 #[must_use]
99 pub fn certificate(&self) -> Option<&'a Certificate<'a>> {
100 self.certificate
101 }
102
103 /// Whether two anchors are the same one: the same name and the same key,
104 /// which is what an anchor is.
105 fn same(self, other: Self, execution: &Memory) -> Result<bool> {
106 execution
107 .charge_work(self.key.as_bytes().len().max(other.key.as_bytes().len()) as u64)
108 .map_err(|reason| self.name.resource(reason))?;
109 if self.key.as_bytes() != other.key.as_bytes() {
110 return Ok(false);
111 }
112 self.name.matches(other.name, execution)
113 }
114}
115
116/// What a verifier says about one signature.
117///
118/// The third answer is the one that matters: an algorithm this build cannot
119/// verify is not a signature that passed. Collapsing it into a no would say
120/// the certificate is broken, and into a yes would say it is good; both are
121/// claims nobody made.
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
123#[non_exhaustive]
124pub enum Verified {
125 /// The signature is the issuer's over these bytes.
126 Yes,
127 /// The signature is not.
128 No,
129 /// This build cannot answer: the algorithm, the key or the parameters
130 /// are outside what it verifies.
131 Unsupported,
132}
133
134/// The key a certificate's signature is checked with, and the parameters
135/// that are in force for it.
136///
137/// "If the public key algorithm parameters are absent, the working public key
138/// parameters are inherited" from the certificate above (RFC 5280 6.1.2 (c)
139/// and 6.1.4 (e)). A key written without them is not a key anything can be
140/// checked with on its own: the curve or the group it belongs to is written
141/// once, in an authority above it, and every key below stands in that same
142/// one. A verifier handed the encoding alone would have to answer that it
143/// cannot say, and a valid path would go unreported.
144///
145/// Where the key carries its own parameters, those are the ones in force and
146/// nothing is inherited. This is the ordinary case: a named curve, an RSA
147/// modulus, an Edwards key.
148#[derive(Debug, Clone, Copy)]
149pub struct WorkingKey<'a> {
150 key: PublicKeyInfo<'a>,
151 /// Parameters whose algorithm compatibility was admitted and established
152 /// during the transition. Getters never defer an untrusted OID comparison.
153 inherited: Option<&'a [u8]>,
154}
155
156impl<'a> WorkingKey<'a> {
157 /// A key that stands on its own, which is what a trust anchor's is: there
158 /// is nothing above it to inherit from.
159 #[must_use]
160 pub fn standing(key: PublicKeyInfo<'a>) -> Self {
161 Self {
162 key,
163 inherited: None,
164 }
165 }
166
167 /// The key itself, as the certificate wrote it.
168 #[must_use]
169 pub fn key(self) -> PublicKeyInfo<'a> {
170 self.key
171 }
172
173 /// The parameters in force: the key's own where it has them, and the ones
174 /// carried down from above where it does not and belongs to the same
175 /// algorithm, subject to its specific key profile. An absent PSS parameter
176 /// field means unrestricted PSS usage and does not inherit restrictions
177 /// (RFC4055 §3.3 case2).
178 ///
179 /// "If the certificate subjectPublicKey algorithm and the
180 /// `working_public_key_algorithm` are different, set the
181 /// `working_public_key_parameters` to null" (RFC 5280 6.1.4 (e)). Nothing
182 /// is carried across a change of algorithm: a curve is not a modulus,
183 /// and an authority on one algorithm certifying a key on another says
184 /// nothing about how that key is written. Handing them over anyway gives
185 /// a verifier a key described by parameters that are not its own, which
186 /// it can only refuse.
187 ///
188 /// The generic RFC5280 transition treats explicit NULL and omitted
189 /// parameters alike. A specific key profile may define absence differently,
190 /// as PSS does. This projection does not validate algorithm-specific syntax:
191 /// the provider also receives the original SPKI and must reject a spelling
192 /// forbidden by its algorithm contract.
193 ///
194 /// `None` means no effective parameters were supplied or inherited. It
195 /// does not itself prove that the key's encoding is valid for its algorithm.
196 #[must_use]
197 pub fn parameters(self) -> Option<&'a [u8]> {
198 if !self.key.algorithm().has_no_parameters() {
199 return self.key.algorithm().parameters();
200 }
201 self.inherited
202 }
203
204 /// Project the subject's effective key using the current parameter state.
205 /// This is only the RFC 5280 §6.1.4(d)–(e) state transition: it does not
206 /// authenticate the certificate, bind issuer names, or establish trust.
207 /// <https://www.rfc-editor.org/rfc/rfc5280#section-6.1.4>
208 ///
209 /// A caller projecting a key from an already validated path starts at its
210 /// anchor and visits the original certificates in issuer-to-subject order.
211 /// Returned SPKI and parameters borrow those sources; no key catalogue,
212 /// copy or repeated signature verification is needed. The current execution
213 /// account pays for each transition and data-dependent OID comparison.
214 ///
215 /// # Panics
216 /// Only if the internal unit-meter invariant is violated: that meter
217 /// always admits local work and cannot produce a truncated transition.
218 pub fn subject(self, certificate: &'a Certificate<'a>, execution: &Memory) -> Result<Self> {
219 execution
220 .charge_work(1)
221 .map_err(|reason| certificate.tbs().subject().resource(reason))?;
222 // The unit meter never returns local truncation; the same transition
223 // core still enforces the shared execution domain before comparisons.
224 self.below(certificate, execution, &mut ())
225 .map(|key| key.expect("unit meter never truncates parameter projection"))
226 }
227
228 /// The working key one certificate further down: this certificate's own
229 /// key, standing in the parameters in force where it wrote none.
230 ///
231 /// The algorithm those parameters belong to travels with them, since a
232 /// key further down inherits only from a key of its own algorithm.
233 fn below(
234 self,
235 certificate: &'a Certificate<'a>,
236 execution: &Memory,
237 meter: &mut impl Meter,
238 ) -> Result<Option<Self>> {
239 let key = certificate.tbs().public_key();
240 let mut inherited = None;
241 if key.algorithm().has_no_parameters()
242 && let Some(parameters) = self.parameters()
243 {
244 let previous = self.key.algorithm().algorithm().as_bytes();
245 let current = key.algorithm().algorithm().as_bytes();
246 // RFC4055 §§3.1,3.3 case2 overrides generic parameter inheritance
247 // for a PSS SPKI with absent parameters: no key restrictions.
248 // https://www.rfc-editor.org/rfc/rfc4055#section-3.3
249 // NULL is invalid PSS syntax (§3.1), not an inherited-parameter
250 // placeholder. Keep it in the original SPKI and supply no parent
251 // parameters that an adapter could use to repair its spelling.
252 let pss = crate::oid::RSASSA_PSS.as_bytes();
253 if current.len() == pss.len() {
254 let reads = (pss.len() as u64) * 2;
255 if !meter.spend(reads / cost::COMPARED)? {
256 return Ok(None);
257 }
258 execution
259 .charge_work(reads)
260 .map_err(|reason| certificate.tbs().subject().resource(reason))?;
261 if current == pss {
262 return Ok(Some(Self {
263 key,
264 inherited: None,
265 }));
266 }
267 }
268 if previous.len() == current.len() {
269 if core::ptr::eq(previous, current) {
270 inherited = Some(parameters);
271 } else {
272 let reads = (current.len() as u64).checked_mul(2).ok_or_else(|| {
273 certificate
274 .tbs()
275 .subject()
276 .resource(structured_asn1::memory::MemoryError::WorkExceeded)
277 })?;
278 if !meter.spend(reads / cost::COMPARED)? {
279 return Ok(None);
280 }
281 execution
282 .charge_work(reads)
283 .map_err(|reason| certificate.tbs().subject().resource(reason))?;
284 if previous == current {
285 inherited = Some(parameters);
286 }
287 }
288 }
289 }
290 Ok(Some(Self { key, inherited }))
291 }
292}
293
294/// Who checks a signature.
295///
296/// Which algorithms may verify what belongs to the algorithm registry rather
297/// than to this crate, so the check is asked for rather than performed here.
298/// A caller that supplies no verifier gets no path: nothing else in a path
299/// binds one certificate to the next.
300pub trait Verify {
301 /// The original policy, provider or resource failure of this implementation.
302 type Error;
303
304 /// Whether the signature over `signed` is the one the key in `key`
305 /// makes under `algorithm`.
306 ///
307 /// The key arrives with the parameters in force rather than as the
308 /// encoding alone, since a certificate may omit them and stand in the
309 /// ones an authority above it wrote.
310 ///
311 /// A key may also be offered before those are known: while a path is
312 /// being looked for, an authority is asked about with the key exactly as
313 /// its certificate wrote it, because what it stands in is above and
314 /// above has not been reached. An implementation that cannot judge a key
315 /// without parameters it was not given answers `Unsupported` rather than
316 /// `No`. The two are not interchangeable here: `No` prunes the branch,
317 /// and a key that merely could not be judged yet is asked about again
318 /// from the anchor down.
319 fn verify(
320 &self,
321 algorithm: &AlgorithmIdentifier<'_>,
322 key: WorkingKey<'_>,
323 signed: &[u8],
324 signature: BitString<'_>,
325 execution: &Memory,
326 ) -> core::result::Result<Verified, Self::Error>;
327}
328
329/// Nothing is verified, which is what a caller without a registry has.
330impl Verify for () {
331 type Error = core::convert::Infallible;
332 fn verify(
333 &self,
334 _algorithm: &AlgorithmIdentifier<'_>,
335 _key: WorkingKey<'_>,
336 _signed: &[u8],
337 _signature: BitString<'_>,
338 _execution: &Memory,
339 ) -> core::result::Result<Verified, Self::Error> {
340 Ok(Verified::Unsupported)
341 }
342}
343
344/// Whether the search has run out of time.
345///
346/// There is no clock here: this crate builds without an operating system, so
347/// wall time is something the caller watches and this asks about. The work
348/// bound below is the one that always applies, and this one narrows it where
349/// the caller can measure.
350pub trait Watch {
351 /// Whether the time the caller allowed has passed.
352 fn out_of_time(&self) -> bool;
353}
354
355/// No clock, so no deadline: the work bound is what stops the search.
356impl Watch for () {
357 fn out_of_time(&self) -> bool {
358 false
359 }
360}
361
362/// What charges work and says whether there is any left.
363///
364/// The search holds the meter, and the planes that work out policies and
365/// names are given it rather than a copy of what it counts: a plane with its
366/// own budget is a second bound the caller never set, and one with none
367/// spends whatever a list somebody else wrote asks for. One trait rather
368/// than the search itself, so those planes do not carry the verifier, the
369/// clock and the recognizer through their signatures to reach the counter.
370pub(crate) trait Meter {
371 /// Charges work, and says whether there is any budget left. Both the
372 /// work bound and the caller's clock are answered here, since running
373 /// out of either stops the same search.
374 fn spend(&mut self, work: u64) -> Result<bool>;
375}
376
377/// No meter, so no bound: for asking a plane what it works out rather than
378/// what it spends, which is what the tests of one plane are about.
379impl Meter for () {
380 fn spend(&mut self, _work: u64) -> Result<bool> {
381 Ok(true)
382 }
383}
384
385/// How far the search may go before it stops and says so.
386///
387/// Every direction a search can grow has a number here, because a
388/// certificate set somebody else controls grows in all of them: deeper
389/// chains, wider fan-out at each step, more paths that differ in one
390/// certificate, more policies in each certificate. The defaults are what a
391/// browser can spend on a document somebody sent, not what a server can
392/// spend on its own inventory.
393#[derive(Debug, Clone, Copy)]
394pub struct Bounds {
395 /// How many certificates a path may hold, the anchor aside.
396 pub depth: usize,
397 /// How many times a certificate may be entered at some depth.
398 pub visited: usize,
399 /// How many candidate issuers may be considered in total.
400 pub edges: usize,
401 /// How much work the checks along the way may cost.
402 pub work: u64,
403 /// How many paths may be returned.
404 pub paths: usize,
405 /// How many graph nodes, retained parent edges and expected policy OIDs
406 /// one path's policy processing may hold together. Scratch capacity is
407 /// additionally admitted by the shared execution account.
408 pub policies: usize,
409}
410
411impl Default for Bounds {
412 fn default() -> Self {
413 Self {
414 depth: 12,
415 visited: 4_096,
416 edges: 16_384,
417 work: 1_000_000,
418 paths: 8,
419 policies: 1_024,
420 }
421 }
422}
423
424/// Which bound stopped the search, where one did.
425///
426/// A report says which, because the answers differ: a search that ran out of
427/// paths has more to show, and a search that ran out of depth was looking in
428/// the wrong place.
429#[derive(Debug, Clone, Copy, PartialEq, Eq)]
430#[non_exhaustive]
431pub enum Truncation {
432 /// A path reached the depth bound and was not followed further.
433 Depth,
434 /// The count of entered certificates reached its bound.
435 Visited,
436 /// The count of considered candidates reached its bound.
437 Edges,
438 /// The checks along the way cost what the caller allowed.
439 Work,
440 /// The caller's clock ran out.
441 Time,
442 /// More paths were found than the caller asked for, and one that was
443 /// found is not among the ones returned.
444 Paths,
445 /// Working out what a path was issued under grew past what the caller
446 /// allowed it to hold, so that path was not settled.
447 Policies,
448}
449
450impl Truncation {
451 /// Whether reaching this bound ends the whole search rather than one
452 /// branch of it.
453 ///
454 /// The counts of work, time, entered certificates and considered
455 /// candidates are spent once for the whole search, so reaching one
456 /// leaves nothing further to look at. Depth, room for paths and the size
457 /// of a policy tree are reached by one branch or one path, and the
458 /// search goes on to the next.
459 fn halts(self) -> bool {
460 matches!(self, Self::Work | Self::Time | Self::Visited | Self::Edges)
461 }
462}
463
464/// One certification path: the certificates from the target up, the anchor
465/// they lead to, and what the authorities along it agreed the target was
466/// issued under.
467#[derive(Debug)]
468pub struct Path<'a> {
469 certificates: OwnedVec<&'a Certificate<'a>>,
470 anchor: Anchor<'a>,
471 policies: OwnedVec<Oid<'a>>,
472}
473
474impl<'a> Path<'a> {
475 /// The certificates, the target first and the one the anchor issued
476 /// last. The anchor's own certificate is not among them.
477 #[must_use]
478 pub fn certificates(&self) -> &[&'a Certificate<'a>] {
479 &self.certificates
480 }
481
482 /// The anchor this path ends at.
483 #[must_use]
484 pub fn anchor(&self) -> Anchor<'a> {
485 self.anchor
486 }
487
488 /// The policies every authority along the path agreed the target was
489 /// issued under, narrowed to the ones the caller asked about.
490 ///
491 /// Empty says the path names no policy the caller asked about, which is
492 /// a path only because nobody along it demanded that it name one.
493 ///
494 /// A set rather than a list, and returned in one order whatever order
495 /// the certificates wrote it in, so that two paths naming one set are
496 /// told apart by walking them once.
497 #[must_use]
498 pub fn policies(&self) -> &[Oid<'a>] {
499 &self.policies
500 }
501
502 /// How many certificates the path holds.
503 #[must_use]
504 pub fn len(&self) -> usize {
505 self.certificates.len()
506 }
507
508 /// Whether the path holds no certificates, which no path this builder
509 /// returns does.
510 #[must_use]
511 pub fn is_empty(&self) -> bool {
512 self.certificates.is_empty()
513 }
514
515 /// Whether two paths say the same thing: the same names carrying the
516 /// same keys, up to the same anchor.
517 ///
518 /// Serial numbers and validity are not part of it, so an authority that
519 /// re-issued its own certificate is one route rather than two. What
520 /// makes a second path worth showing is that it goes somewhere else or
521 /// says something else, and neither of those is a new serial number.
522 fn same_route(
523 &self,
524 chain: &[&'a Certificate<'a>],
525 anchor: Anchor<'a>,
526 execution: &Memory,
527 ) -> Result<bool> {
528 if self.certificates.len() != chain.len() || !self.anchor.same(anchor, execution)? {
529 return Ok(false);
530 }
531 for (ours, theirs) in self.certificates.iter().zip(chain) {
532 execution
533 .charge_work(
534 ours.tbs()
535 .public_key()
536 .as_bytes()
537 .len()
538 .max(theirs.tbs().public_key().as_bytes().len()) as u64,
539 )
540 .map_err(|reason| ours.tbs().subject().resource(reason))?;
541 if ours.tbs().public_key().as_bytes() != theirs.tbs().public_key().as_bytes()
542 || !ours
543 .tbs()
544 .subject()
545 .matches(theirs.tbs().subject(), execution)?
546 {
547 return Ok(false);
548 }
549 }
550 Ok(true)
551 }
552
553 /// Whether a path would tell a reader what this one already told them:
554 /// the same anchor, and the same policies. What is worth keeping when
555 /// there is room for fewer paths than were found is one of each of
556 /// these, not the first ones found.
557 /// Both sets arrive in one order, which is what makes this a walk down
558 /// the two rather than a search of one for each member of the other:
559 /// the second is the square of a count the caller's policy ceiling
560 /// allows, and it is repeated against every path being held.
561 fn same_meaning(
562 &self,
563 anchor: Anchor<'a>,
564 policies: &[Oid<'a>],
565 execution: &Memory,
566 ) -> Result<bool> {
567 if !self.anchor.same(anchor, execution)? || self.policies.len() != policies.len() {
568 return Ok(false);
569 }
570 for (ours, theirs) in self.policies.iter().zip(policies) {
571 execution
572 .charge_work(ours.as_bytes().len().max(theirs.as_bytes().len()) as u64)
573 .map_err(|reason| self.anchor.name.resource(reason))?;
574 if ours != theirs {
575 return Ok(false);
576 }
577 }
578 Ok(true)
579 }
580}
581
582/// What the search found, and what it did not get to.
583#[derive(Debug)]
584pub struct Outcome<'a> {
585 paths: OwnedVec<Path<'a>>,
586 truncated: Option<Truncation>,
587}
588
589impl<'a> Outcome<'a> {
590 fn empty(execution: &Memory) -> Self {
591 Self {
592 paths: execution.vector(),
593 truncated: None,
594 }
595 }
596
597 /// The paths, shortest first, and among paths of one length in the order
598 /// the caller's inputs put them in.
599 #[must_use]
600 pub fn paths(&self) -> &[Path<'a>] {
601 &self.paths
602 }
603
604 /// Which bound stopped the search, where one did. `None` says the search
605 /// finished: every path there is, is here.
606 #[must_use]
607 pub fn truncated(&self) -> Option<Truncation> {
608 self.truncated
609 }
610
611 /// Whether the search was stopped rather than finished, which a report
612 /// must say rather than claim it enumerated everything.
613 #[must_use]
614 pub fn is_truncated(&self) -> bool {
615 self.truncated.is_some()
616 }
617}
618
619/// The UTC-label interval the path is built for, retaining leap/fractional labels.
620///
621/// A signature is placed in an interval rather than at an instant, because
622/// what a timestamp proves is an upper bound and the lower one is usually
623/// unknown. A certificate takes part in a path only where it was valid
624/// across the whole of it: resolving at one end would let a signature made
625/// before a certificate existed pass on the strength of a later moment.
626///
627/// Ordinary whole-second constructors remain available. Use the `Instant`
628/// constructors for fractional or inserted-second endpoints; caller-supplied
629/// leap facts authenticate their occurrence separately from this comparison.
630#[derive(Debug, Clone, Copy, PartialEq, Eq)]
631pub struct Interval {
632 from: crate::temporal::Instant,
633 to: crate::temporal::Instant,
634}
635
636impl Interval {
637 /// The interval between two moments, whichever way round the caller
638 /// hands them: an interval is a pair of bounds, not an ordering.
639 #[must_use]
640 pub fn between(first: i64, second: i64) -> Self {
641 Self::between_instants(
642 crate::temporal::Instant::whole(first),
643 crate::temporal::Instant::whole(second),
644 )
645 }
646
647 /// The interval that is one moment, which is what a caller has when the
648 /// signing time is known exactly and nothing wider has to be covered.
649 #[must_use]
650 pub fn at(moment: i64) -> Self {
651 Self::at_instant(crate::temporal::Instant::whole(moment))
652 }
653
654 /// The interval between two lossless labels, in either input order.
655 #[must_use]
656 pub fn between_instants(
657 first: crate::temporal::Instant,
658 second: crate::temporal::Instant,
659 ) -> Self {
660 Self {
661 from: first.min(second),
662 to: first.max(second),
663 }
664 }
665
666 /// A point interval retaining the supplied leap flag and nanoseconds.
667 #[must_use]
668 pub fn at_instant(moment: crate::temporal::Instant) -> Self {
669 Self {
670 from: moment,
671 to: moment,
672 }
673 }
674
675 /// The earlier bound's lossy POSIX projection, without fractional precision.
676 #[must_use]
677 pub fn from(self) -> i64 {
678 self.from.seconds()
679 }
680
681 /// The later bound's lossy POSIX projection, without fractional precision.
682 #[must_use]
683 pub fn to(self) -> i64 {
684 self.to.seconds()
685 }
686
687 /// The earlier lossless UTC label.
688 #[must_use]
689 pub fn from_instant(self) -> crate::temporal::Instant {
690 self.from
691 }
692
693 /// The later lossless UTC label.
694 #[must_use]
695 pub fn to_instant(self) -> crate::temporal::Instant {
696 self.to
697 }
698
699 /// Whether a validity interval holds the whole of this one.
700 fn inside(self, validity: Validity) -> bool {
701 validity.not_before().instant() <= self.from && self.to <= validity.not_after().instant()
702 }
703}
704
705/// What the caller was asking about, before any certificate was read.
706///
707/// The defaults are a validator with no policy of its own: it asks what the
708/// authorities agreed on rather than whether they agreed on one thing, and
709/// reads what the certificates say rather than overriding it. A caller with
710/// a policy of its own says so here, and gets paths that satisfy it or none.
711#[derive(Debug, Clone, Copy)]
712pub struct Policy<'a> {
713 /// The policies the caller is asking about. Empty asks about all of
714 /// them, and a path then carries whatever its authorities agreed on.
715 pub wanted: &'a [Oid<'a>],
716 /// Whether every path must name a policy, rather than only those an
717 /// authority along the way demanded it of.
718 pub explicit: bool,
719 /// Whether a policy one authority maps to another's is honoured.
720 pub mapping: bool,
721 /// Whether the identifier standing for any policy is read as the
722 /// certificate meant it.
723 pub any: bool,
724}
725
726impl Default for Policy<'_> {
727 fn default() -> Self {
728 Self {
729 wanted: &[],
730 explicit: false,
731 mapping: true,
732 any: true,
733 }
734 }
735}
736
737/// What the search charges for the checks it runs, so that a pool somebody
738/// else controls cannot buy an unbounded amount of them. The numbers are
739/// relative to each other rather than to any clock: a signature check is
740/// what a path spends most of its budget on, and a name comparison is a
741/// walk over two names.
742mod cost {
743 /// Reading an extension of a certificate the search is considering.
744 pub(super) const EXTENSION: u64 = 8;
745 /// Reaching for two names to compare them, before what they hold.
746 pub(super) const NAME: u64 = 16;
747 /// How many bytes of the two names one unit of work buys where they are
748 /// compared. Comparing them reads both: the parts of a name holding
749 /// several attributes are ordered against each other, and values that
750 /// are not byte-identical are folded through to the end. What that
751 /// costs is the size of what a pool somebody else wrote put there, so
752 /// it is charged by that size rather than at a flat rate.
753 pub(super) const COMPARED: u64 = 16;
754 /// Asking the verifier about one signature.
755 pub(super) const SIGNATURE: u64 = 1_024;
756 /// Holding one certificate to the constraints above it.
757 pub(super) const CHECK: u64 = 32;
758 /// Looking at one entry of a list a document decides the length of: a
759 /// policy a certificate names, a mapping it declares, a region an
760 /// authority wrote, a node of the working out being indexed.
761 ///
762 /// Charged per entry rather than per list, because the length is what
763 /// somebody else chose. What the entry holds is charged where it is
764 /// read, which for most of these is a lookup in an index and nothing
765 /// more.
766 pub(super) const ENTRY: u64 = 4;
767 /// How many bytes of a certificate one unit of work buys where the whole
768 /// of it is read, which is where its identity is taken.
769 pub(super) const HASHED: u64 = 64;
770}
771
772/// Everything the search asks of the caller: what is trusted, when, how far
773/// to look, what is being asked about, and who answers the questions this
774/// crate does not.
775///
776/// One value rather than a row of arguments, because these travel together:
777/// a path built against other anchors, or for another interval, is another
778/// question rather than the same one asked differently.
779pub struct Context<'a, 'p, V: Verify, W: Watch, R: Recognizer> {
780 /// The current operation's shared execution and storage domain.
781 /// Retained certificate accounts never implicitly finance this search.
782 pub execution: &'p Memory,
783 /// The anchors a path may end at, in the order a report should prefer.
784 pub anchors: &'p [Anchor<'a>],
785 /// The interval the path is built for.
786 pub interval: Interval,
787 /// How far the search may go before it stops and says so.
788 pub bounds: Bounds,
789 /// What the caller is asking about the policies a path was issued under.
790 pub policy: Policy<'a>,
791 /// Who checks a signature.
792 pub verifier: &'p V,
793 /// Who says the time the caller allowed has passed.
794 pub watch: &'p W,
795 /// Which extensions a scheme understands beyond the ones this crate
796 /// reads, since a critical extension nobody understood is a certificate
797 /// no path goes through.
798 pub recognizer: &'p R,
799}
800
801/// The state a search carries, so the bounds are counted in one place.
802struct Search<'a, 'p, V: Verify, W: Watch, R: Recognizer> {
803 execution: &'p Memory,
804 /// The candidates as the caller supplied them, unread until a path
805 /// needs one.
806 supplied: &'p [&'a Certificate<'a>],
807 /// The certificate the question is about, which is not a candidate
808 /// issuer of itself.
809 target: &'a Certificate<'a>,
810 /// The candidates folded to one each, worked out the first time the
811 /// search looks for an issuer.
812 ///
813 /// Not before then: a target an anchor issued is settled by the anchors
814 /// alone, and reading a pool of certificates no path goes through would
815 /// spend the budget that answer needs before the answer is reached. A
816 /// document may carry any number of them, so that is a path a caller
817 /// holds the whole of being reported as left out.
818 pool: Option<OwnedVec<&'a Certificate<'a>>>,
819 anchors: &'p [Anchor<'a>],
820 interval: Interval,
821 bounds: Bounds,
822 policy: Policy<'a>,
823 verifier: &'p V,
824 watch: &'p W,
825 recognizer: &'p R,
826 visited: usize,
827 edges: usize,
828 spent: u64,
829 /// The bound that ended the search, where one did.
830 halted: Option<Truncation>,
831 /// What a bound left out without ending the search.
832 noted: Option<Truncation>,
833 found: OwnedVec<Path<'a>>,
834 /// What the verifier has already said, so that a subpath shared by many
835 /// paths costs one check rather than one per path. Two certificates
836 /// stand for one answer here by where their bytes are, since that is
837 /// what was handed to the verifier; a second copy of one certificate is
838 /// a second entry and not a wrong one.
839 signatures: OwnedMap<(usize, usize, usize), Verified>,
840 /// What each certificate says about itself, which does not depend on
841 /// where in a path it sits. A cross-certified pool reaches the same
842 /// certificate at many depths and on many chains, and reading its
843 /// extensions again each time would charge a caller once per visit for
844 /// an answer that cannot have changed.
845 properties: OwnedMap<usize, Properties<'a>>,
846}
847
848/// What one certificate says about itself, worked out once.
849#[derive(Debug, Clone, Copy)]
850struct Properties<'a> {
851 /// Common facts, or no facts when the local operation could not finish.
852 facts: Option<validation::CertificateFacts<'a>>,
853 /// Whether it can stand in a path at all: valid across the interval, and
854 /// carrying no critical extension nobody understood.
855 usable: bool,
856 /// What it says about issuing certificates, or `None` where it says it
857 /// issues none.
858 issues: Option<crate::constraints::BasicConstraints>,
859 /// Whether it is an anchor's own certificate: it carries the name and
860 /// key of one, and signs itself. Both, because a certificate carrying an
861 /// anchor's identity under somebody else's signature is a link to that
862 /// anchor rather than the anchor.
863 publishes_itself: bool,
864}
865
866impl<V: Verify, W: Watch, R: Recognizer> Meter for Search<'_, '_, V, W, R> {
867 fn spend(&mut self, work: u64) -> Result<bool> {
868 Self::spend(self, work)
869 }
870}
871
872impl<'a, V: Verify, W: Watch, R: Recognizer> validation::Driver<'a, R> for Search<'a, '_, V, W, R> {
873 type VerificationError = V::Error;
874
875 fn verified(
876 &mut self,
877 certificate: &'a Certificate<'a>,
878 key: WorkingKey<'a>,
879 ) -> BuildResult<Option<Verified>, V::Error, R::Error> {
880 Self::verified(self, certificate, key)
881 }
882
883 fn facts(
884 &mut self,
885 certificate: &'a Certificate<'a>,
886 _settings: &validation::Settings<'a, '_, R>,
887 ) -> BuildResult<Option<validation::CertificateFacts<'a>>, V::Error, R::Error> {
888 Ok(properties(self, certificate)?.facts)
889 }
890}
891
892impl<V: Verify, W: Watch, R: Recognizer> Search<'_, '_, V, W, R> {
893 /// Charges work, and says whether the search may go on.
894 fn spend(&mut self, work: u64) -> Result<bool> {
895 debug_assert!(self.spent <= self.bounds.work);
896 if work > self.bounds.work - self.spent {
897 self.stop(Truncation::Work);
898 return Ok(false);
899 }
900 self.execution
901 .charge_work(work)
902 .map_err(|reason| self.target.tbs().subject().resource(reason))?;
903 self.spent += work;
904 if self.watch.out_of_time() {
905 self.stop(Truncation::Time);
906 return Ok(false);
907 }
908 Ok(true)
909 }
910
911 /// Charges for comparing two names, by what comparing them reads.
912 ///
913 /// Before the comparison rather than after it: a bound that is charged
914 /// once the work is done is a bound the last comparison was never held
915 /// to, and a pool of long names is a set of last comparisons.
916 fn spend_names(&mut self, left: &Name<'_>, right: &Name<'_>) -> Result<bool> {
917 validation::spend_names(self, left, right)
918 }
919
920 /// Records which bound stopped the search, keeping the first one: it is
921 /// the one that decided the answer.
922 fn stop(&mut self, at: Truncation) {
923 // Two kinds of bound, kept apart because they are two statements. A
924 // bound that ends the whole search decides the answer and is what
925 // the answer reports; one that ended a branch or left a path out is
926 // worth saying only where nothing ended the search. Keeping one
927 // field for both would let the first branch to run out of depth
928 // swallow the count of certificates that later stopped everything,
929 // and the search would carry on past a bound it had reached while
930 // naming the wrong reason for what it did not do.
931 let recorded = if at.halts() {
932 &mut self.halted
933 } else {
934 &mut self.noted
935 };
936 if recorded.is_none() {
937 *recorded = Some(at);
938 }
939 }
940
941 /// Whether a bound has ended the search.
942 ///
943 /// Running out of room for paths is not one of them: what is worth
944 /// keeping is decided when a path is settled rather than by which was
945 /// found first, so a search that has filled its room keeps looking for
946 /// something better to put in it, under the bounds that remain.
947 fn stopped(&self) -> bool {
948 self.halted.is_some()
949 }
950
951 /// What the answer says about being incomplete: the bound that ended the
952 /// search where one did, and otherwise what was left out along the way.
953 fn truncated(&self) -> Option<Truncation> {
954 self.halted.or(self.noted)
955 }
956
957 /// Asks the verifier about one signature, or repeats what it said about
958 /// the same one before. `None` says the budget ran out before it could
959 /// be asked, which is not an answer about the signature.
960 ///
961 /// The charge is here rather than at the call sites, and only where the
962 /// verifier is actually reached. Repeating what is already known costs a
963 /// lookup, and charging a cryptographic check for it would undo what
964 /// remembering the answer was for: a sub-path shared by many chains
965 /// would spend the budget once per chain that walks it, and the search
966 /// would stop before reaching paths it had already done the work for.
967 fn verified(
968 &mut self,
969 certificate: &Certificate<'_>,
970 key: WorkingKey<'_>,
971 ) -> BuildResult<Option<Verified>, V::Error, R::Error> {
972 // The parameters are part of what was asked, not only the key: one
973 // key standing in two different sets of inherited parameters is two
974 // questions, and answering the second from the first would carry an
975 // answer across paths that do not share what the key stands in.
976 let at = (
977 certificate.signed_bytes().as_ptr() as usize,
978 key.key().as_bytes().as_ptr() as usize,
979 key.parameters().map_or(0, |held| held.as_ptr() as usize),
980 );
981 if let Some(answer) = self
982 .signatures
983 .get(&at, self.execution)
984 .map_err(|reason| certificate.tbs().subject().resource(reason))?
985 {
986 return Ok(Some(*answer));
987 }
988 if !self.spend(cost::SIGNATURE)? {
989 return Ok(None);
990 }
991 // Admit the cache allocation before invoking the external verifier.
992 self.signatures
993 .try_insert(at, Verified::Unsupported)
994 .map_err(|reason| certificate.tbs().subject().resource(reason))?;
995 let answer = self
996 .verifier
997 .verify(
998 &certificate.signature_algorithm(),
999 key,
1000 certificate.signed_bytes(),
1001 certificate.signature(),
1002 self.execution,
1003 )
1004 .map_err(BuildError::Verification)?;
1005 *self
1006 .signatures
1007 .get_mut(&at, self.execution)
1008 .map_err(|reason| certificate.tbs().subject().resource(reason))?
1009 .expect("the admitted signature cache entry") = answer;
1010 Ok(Some(answer))
1011 }
1012}
1013
1014/// Admits anchor-folding work before mutation in both local and aggregate budgets.
1015fn spend_anchor(
1016 context: &Context<'_, '_, impl Verify, impl Watch, impl Recognizer>,
1017 spent: &mut u64,
1018 halted: &mut Option<Truncation>,
1019 origin: &Name<'_>,
1020 work: u64,
1021) -> Result<bool> {
1022 debug_assert!(*spent <= context.bounds.work);
1023 if work > context.bounds.work - *spent {
1024 *halted = Some(Truncation::Work);
1025 return Ok(false);
1026 }
1027 if context.watch.out_of_time() {
1028 *halted = Some(Truncation::Time);
1029 return Ok(false);
1030 }
1031 context
1032 .execution
1033 .charge_work(work)
1034 .map_err(|reason| origin.resource(reason))?;
1035 *spent += work;
1036 Ok(true)
1037}
1038
1039/// The anchors a search will look at, with the repeats among the caller's
1040/// folded away, and what folding them spent.
1041///
1042/// Two anchors of one name and one key are one anchor, and the paths to them
1043/// are one path. A trust context assembled from several stores hands the same
1044/// root over more than once, and every copy would buy an edge and a name
1045/// comparison to settle a path that was already settled, so a bound would
1046/// report the answer as truncated over nothing that was left out.
1047///
1048/// Folded by what makes two anchors one, which is the same name and the same
1049/// key however the name was written, and the same certificate or none behind
1050/// both. Folding by the bytes of the name instead would leave one key under
1051/// two spellings as two anchors, and placed in front of a key that does
1052/// verify they can spend a tight bound before it is reached: the path that
1053/// exists is then not returned, which is a lost answer rather than a charge.
1054///
1055/// The keys are grouped first, because they are compared as bytes and a trust
1056/// store's are all different: an anchor is looked at once, and the comparison
1057/// that reads two names runs only where a key was supplied twice, which is
1058/// the case being folded. Comparing every pair would be the square of a count
1059/// for the sake of the pairs that cannot match.
1060fn fold_anchors<'a>(
1061 context: &Context<'a, '_, impl Verify, impl Watch, impl Recognizer>,
1062 spent: &mut u64,
1063 halted: &mut Option<Truncation>,
1064) -> Result<OwnedVec<Anchor<'a>>> {
1065 let mut keyed: OwnedMap<&[u8], OwnedVec<usize>> = context.execution.map();
1066 let mut anchors: OwnedVec<Anchor<'a>> = context.execution.vector();
1067 'anchors: for anchor in context.anchors {
1068 let key = anchor.key().as_bytes();
1069 if !spend_anchor(
1070 context,
1071 spent,
1072 halted,
1073 anchor.name(),
1074 cost::CHECK + key.len() as u64 / cost::HASHED,
1075 )? {
1076 break;
1077 }
1078
1079 let fail = |reason| anchor.name().resource(reason);
1080 if keyed.get(&key, context.execution).map_err(fail)?.is_none() {
1081 keyed
1082 .try_insert(key, context.execution.vector())
1083 .map_err(fail)?;
1084 }
1085 let group = keyed
1086 .get_mut(&key, context.execution)
1087 .map_err(fail)?
1088 .expect("the group was inserted before lookup");
1089 for at in group.iter() {
1090 let theirs: &Name<'_> = anchors[*at].name();
1091 let bytes = (theirs.as_bytes().len() as u64)
1092 .checked_add(anchor.name().as_bytes().len() as u64)
1093 .ok_or_else(|| {
1094 anchor
1095 .name()
1096 .resource(structured_asn1::memory::MemoryError::WorkExceeded)
1097 })?;
1098 if !spend_anchor(
1099 context,
1100 spent,
1101 halted,
1102 anchor.name(),
1103 cost::NAME + bytes / cost::COMPARED,
1104 )? {
1105 break 'anchors;
1106 }
1107 if !theirs.matches(anchor.name(), context.execution)? {
1108 continue;
1109 }
1110
1111 // The names and the key agree, and one thing is left that tells
1112 // two such anchors apart: what each was published in. A trust
1113 // context that supplied the anchor as a constrained certificate
1114 // bound the names below it, and the same name and key supplied
1115 // without one bound nothing. They are two statements about one
1116 // key, and folding them would let the order of the caller's list
1117 // decide which constraint holds and so whether a path exists at
1118 // all.
1119 let held = anchors[*at].certificate();
1120 let bytes = published_bytes(held)
1121 .checked_add(published_bytes(anchor.certificate()))
1122 .ok_or_else(|| {
1123 anchor
1124 .name()
1125 .resource(structured_asn1::memory::MemoryError::WorkExceeded)
1126 })?;
1127 if !spend_anchor(
1128 context,
1129 spent,
1130 halted,
1131 anchor.name(),
1132 cost::CHECK + bytes / cost::HASHED,
1133 )? {
1134 break 'anchors;
1135 }
1136 if published_alike(held, anchor.certificate(), context.execution)? {
1137 continue 'anchors;
1138 }
1139 }
1140 group.try_push(anchors.len()).map_err(fail)?;
1141 anchors.try_push(*anchor).map_err(fail)?;
1142 }
1143 Ok(anchors)
1144}
1145
1146/// The candidates of a pool, each of them once and the target not among
1147/// them.
1148///
1149/// A pool that holds one certificate twice holds one certificate, and folding
1150/// the copies before the search rather than at each step is what makes that
1151/// true of what the search spends as well as of what it finds. A pool
1152/// somebody else wrote may repeat a certificate leading nowhere as often as
1153/// it likes, and charging every copy would let the copies spend the search
1154/// before the issuer that does lead somewhere is reached.
1155///
1156/// The folding is itself charged, because it is itself work somebody else
1157/// decides the size of: the identity of a certificate is its bytes, and there
1158/// may be any number of certificates of any length.
1159fn fold_pool<'a, V: Verify, W: Watch, R: Recognizer>(
1160 search: &mut Search<'a, '_, V, W, R>,
1161 pool: &[&'a Certificate<'a>],
1162 target: &'a Certificate<'a>,
1163) -> BuildResult<OwnedVec<&'a Certificate<'a>>, V::Error, R::Error> {
1164 let mut named: OwnedMap<usize, ()> = search.execution.map();
1165 let mut seen: OwnedMap<&[u8], ()> = search.execution.map();
1166 let mut unique = search.execution.vector();
1167 for candidate in pool {
1168 let bytes = candidate.as_bytes();
1169
1170 // Looking at a candidate at all, which every one of them costs.
1171 if !search.spend(cost::CHECK)? {
1172 break;
1173 }
1174
1175 // A pool that names one certificate over and over names it once,
1176 // and recognising that costs a pointer. Reading the whole encoding
1177 // is what telling a separately written copy from it costs, and
1178 // charging that for every repeat lets the repeats spend the search
1179 // before the issuer that does lead somewhere is reached.
1180 if named
1181 .try_insert(bytes.as_ptr() as usize, ())
1182 .map_err(|reason| candidate.tbs().subject().resource(reason))?
1183 .is_some()
1184 {
1185 continue;
1186 }
1187 if !search.spend(bytes.len() as u64 / cost::HASHED)? {
1188 break;
1189 }
1190
1191 // The target is not a candidate issuer of itself, and a caller who
1192 // put it here was told that changes nothing. Leaving it among the
1193 // candidates makes that false of what the search spends: the cycle
1194 // check refuses it, but only after an edge and a name comparison
1195 // have been charged for it, so a bound tight enough to notice
1196 // reports the answer as truncated over a certificate that could
1197 // never have been followed.
1198 search
1199 .execution
1200 .charge_work(bytes.len().max(target.as_bytes().len()) as u64)
1201 .map_err(|reason| candidate.tbs().subject().resource(reason))?;
1202 if bytes != target.as_bytes()
1203 && seen
1204 .try_insert(bytes, ())
1205 .map_err(|reason| candidate.tbs().subject().resource(reason))?
1206 .is_none()
1207 {
1208 unique
1209 .try_push(*candidate)
1210 .map_err(|reason| candidate.tbs().subject().resource(reason))?;
1211 }
1212 }
1213 Ok(unique)
1214}
1215
1216/// Builds the paths from one certificate to the anchors the caller trusts.
1217///
1218/// The pool is where issuers are looked for, and holding the target itself
1219/// changes nothing: a certificate never appears twice in one path
1220/// (RFC 5280 6.1), so it cannot be its own issuer.
1221///
1222/// # Errors
1223///
1224/// A spent budget, which says this run gave up rather than that the
1225/// certificates are wrong.
1226pub fn build<'a, V: Verify, W: Watch, R: Recognizer>(
1227 target: &'a Certificate<'a>,
1228 pool: &[&'a Certificate<'a>],
1229 context: &Context<'a, '_, V, W, R>,
1230) -> BuildResult<Outcome<'a>, V::Error, R::Error> {
1231 // A path ends at an anchor, so a caller who named none has asked a
1232 // question whose complete answer is no paths. Reading the pool to find
1233 // that out spends a budget on work that cannot change the answer, and a
1234 // bound reached while doing it would report the one conclusive answer
1235 // there is as though something had been left out, which reads to a
1236 // caller as worth asking again.
1237 if context.anchors.is_empty() {
1238 return Ok(Outcome::empty(context.execution));
1239 }
1240
1241 // And a path is built for a moment. A target the caller's interval does
1242 // not hold is not one any path can end at, whatever the anchors and the
1243 // pool say, so folding them to find that out is the same spending on an
1244 // answer that cannot change. The question is asked again where every
1245 // certificate of a path is held to it, and asking it here first is what
1246 // keeps a bound from calling this conclusive answer a partial one.
1247 if !context.interval.inside(target.tbs().validity()) {
1248 return Ok(Outcome::empty(context.execution));
1249 }
1250
1251 let mut spent = 0u64;
1252 let mut halted = None;
1253 let anchors = fold_anchors(context, &mut spent, &mut halted)?;
1254 if halted.is_some() {
1255 return Ok(Outcome {
1256 paths: context.execution.vector(),
1257 truncated: halted,
1258 });
1259 }
1260
1261 let mut search = Search {
1262 execution: context.execution,
1263 supplied: pool,
1264 target,
1265 pool: None,
1266 anchors: &anchors,
1267 interval: context.interval,
1268 bounds: context.bounds,
1269 policy: context.policy,
1270 verifier: context.verifier,
1271 watch: context.watch,
1272 recognizer: context.recognizer,
1273 visited: 0,
1274 edges: 0,
1275 spent,
1276 halted,
1277 noted: None,
1278 found: context.execution.vector(),
1279 signatures: context.execution.map(),
1280 properties: context.execution.map(),
1281 };
1282
1283 // The certificate an anchor was published in is not a step on a path to
1284 // that anchor: "when the trust anchor is provided in the form of a
1285 // self-signed certificate, this self-signed certificate is not included
1286 // as part of the prospective certification path" (RFC 5280 6.1). That
1287 // holds of the first position as much as of the rest, and the rest
1288 // already keep it. Settling such a target reports the anchor as issued
1289 // by itself, which is the anchor named twice rather than a chain of
1290 // authority, and it is what a caller asking about a root would read as
1291 // the root having been certified by somebody.
1292 //
1293 // A self-signed certificate can chain nowhere else, since the only name
1294 // that issued it is its own, so there is nothing further to look for
1295 // and the empty answer is the whole of it.
1296 let known = properties(&mut search, target)?;
1297 let mut chain = context.execution.vector();
1298 if known.usable && !known.publishes_itself {
1299 walk(&mut search, target, &mut chain)?;
1300 }
1301
1302 // Shortest first: a longer path to the same place says the same thing
1303 // through more parties, and a reader reads the first one. Among paths
1304 // of one length the order is the one the caller's inputs put them in,
1305 // which is what makes the same inputs give the same answer twice.
1306 // Include the discovery position in the comparison so the allocation-free
1307 // heap sort preserves the documented order among equal-length paths.
1308 let mut ordered = context.execution.vector();
1309 let found = core::mem::replace(&mut search.found, context.execution.vector());
1310 for (position, path) in found.into_iter().enumerate() {
1311 ordered
1312 .try_push((position, path))
1313 .map_err(|reason| target.tbs().subject().resource(reason))?;
1314 }
1315 ordered
1316 .try_sort_unstable_by(|left, right| {
1317 Ok::<_, structured_asn1::memory::MemoryError>(
1318 (left.1.len(), left.0).cmp(&(right.1.len(), right.0)),
1319 )
1320 })
1321 .map_err(|error| target.tbs().subject().resource(error.into()))?;
1322 for (_, path) in ordered {
1323 search
1324 .found
1325 .try_push(path)
1326 .map_err(|reason| target.tbs().subject().resource(reason))?;
1327 }
1328 let truncated = search.truncated();
1329 Ok(Outcome {
1330 paths: search.found,
1331 truncated,
1332 })
1333}
1334
1335/// Follows one certificate up: to every anchor that could have issued it,
1336/// and to every certificate in the pool that could have.
1337fn walk<'a, V: Verify, W: Watch, R: Recognizer>(
1338 search: &mut Search<'a, '_, V, W, R>,
1339 certificate: &'a Certificate<'a>,
1340 chain: &mut OwnedVec<&'a Certificate<'a>>,
1341) -> BuildResult<(), V::Error, R::Error> {
1342 // One candidate cursor per active certificate replaces recursive DFS.
1343 // Both stacks are admitted by the request domain before their growth.
1344 let mut cursors = search.execution.vector();
1345 let mut entering = certificate;
1346 'enter: loop {
1347 if search.stopped() {
1348 break;
1349 }
1350 if search.visited >= search.bounds.visited {
1351 search.stop(Truncation::Visited);
1352 break;
1353 }
1354 search.visited += 1;
1355 if !search.spend(cost::CHECK)? {
1356 break;
1357 }
1358 if chain.len() >= search.bounds.depth {
1359 search.stop(Truncation::Depth);
1360 break;
1361 }
1362 chain
1363 .try_push(entering)
1364 .map_err(|reason| entering.tbs().subject().resource(reason))?;
1365 cursors
1366 .try_push(0usize)
1367 .map_err(|reason| entering.tbs().subject().resource(reason))?;
1368 settle(search, entering, chain)?;
1369
1370 loop {
1371 if search.stopped() {
1372 break 'enter;
1373 }
1374 let current = *chain.last().expect("the active DFS certificate");
1375 let cursor = cursors.last_mut().expect("its candidate cursor");
1376 if let Some(candidate) = next_issuer(
1377 search,
1378 current,
1379 chain,
1380 chain.len() >= search.bounds.depth,
1381 cursor,
1382 )? {
1383 entering = candidate;
1384 continue 'enter;
1385 }
1386 chain.pop();
1387 cursors.pop();
1388 if chain.is_empty() {
1389 break 'enter;
1390 }
1391 }
1392 }
1393 chain.clear();
1394 Ok(())
1395}
1396
1397/// Ends the chain at every anchor that issued the certificate on top of it,
1398/// which is where a path becomes one.
1399fn settle<'a, V: Verify, W: Watch, R: Recognizer>(
1400 search: &mut Search<'a, '_, V, W, R>,
1401 certificate: &'a Certificate<'a>,
1402 chain: &[&'a Certificate<'a>],
1403) -> BuildResult<(), V::Error, R::Error> {
1404 for at in 0..search.anchors.len() {
1405 let anchor = search.anchors[at];
1406 if search.stopped() {
1407 break;
1408 }
1409 if search.edges >= search.bounds.edges {
1410 search.stop(Truncation::Edges);
1411 break;
1412 }
1413 search.edges += 1;
1414 if !search.spend_names(certificate.tbs().issuer(), anchor.name())? {
1415 break;
1416 }
1417 if !certificate
1418 .tbs()
1419 .issuer()
1420 .matches(anchor.name(), search.execution)?
1421 {
1422 continue;
1423 }
1424 // The signature is checked where the path is walked from the anchor
1425 // down, since that is where the parameters a key may stand in are
1426 // known. Nothing is lost by not asking here: this is the last edge
1427 // of the path, so the check is one step away either way.
1428 let Some(policies) = validate_candidate(search, chain, anchor)? else {
1429 continue;
1430 };
1431 keep(search, chain, anchor, policies)?;
1432 }
1433 Ok(())
1434}
1435
1436/// Puts a settled path among the ones that will be returned, where there is
1437/// room for it or where what it says is not already there.
1438///
1439/// A repeat of a route already held is dropped and is not truncation: it is
1440/// the same path spelled through a re-issued certificate, and nothing was
1441/// lost by not showing it twice. Dropping a path that does say something
1442/// else is truncation, and so is making room for one by putting out a
1443/// repeat of something already shown.
1444/// The chain is held rather than copied until there is somewhere to put it:
1445/// a cross-certified pool settles the same route many times over, and a copy
1446/// made before the question is asked is a copy made for every one of them.
1447fn keep<'a, V: Verify, W: Watch, R: Recognizer>(
1448 search: &mut Search<'a, '_, V, W, R>,
1449 chain: &[&'a Certificate<'a>],
1450 anchor: Anchor<'a>,
1451 policies: OwnedVec<Oid<'a>>,
1452) -> BuildResult<(), V::Error, R::Error> {
1453 // Telling this path from the ones already held reads their names, the
1454 // keys under them and their policies again, once per path held, and how
1455 // long a name or a key encoding is is the pool's to decide. Both are
1456 // read from the front, so two of one length that part near the end are
1457 // read almost to the end. The charge is taken for the whole scan before
1458 // it runs.
1459 //
1460 // Obeyed where there is anything to compare against: a charge that the
1461 // budget cannot meet is the answer that this scan is not going to
1462 // happen, and running it anyway is the reading the charge exists to
1463 // stop. What is then not known is whether this route says what a held
1464 // one already says, so it is not shown, and the answer is marked
1465 // truncated by the charge that failed.
1466 //
1467 // Where nothing is held there is nothing to compare and no scan to
1468 // refuse. The first path a search settles is kept whatever the budget
1469 // says, since dropping it would lose an answer for work nobody was
1470 // going to do.
1471 let held = search.found.len() as u64;
1472 if held > 0 {
1473 if !search.spend(chain.len() as u64)? {
1474 return Ok(());
1475 }
1476 let overflow = || {
1477 anchor
1478 .name
1479 .resource(structured_asn1::memory::MemoryError::WorkExceeded)
1480 };
1481 let mut read = 0u64;
1482 for certificate in chain {
1483 let tbs = certificate.tbs();
1484 let bytes =
1485 tbs.subject().as_bytes().len() as u64 + tbs.public_key().as_bytes().len() as u64;
1486 read = read.checked_add(bytes).ok_or_else(overflow)?;
1487 }
1488 let each = (read / cost::COMPARED)
1489 .checked_add(cost::NAME)
1490 .and_then(|work| work.checked_add(policies.len() as u64))
1491 .ok_or_else(overflow)?;
1492 let work = each.checked_mul(held).ok_or_else(overflow)?;
1493 if !search.spend(work)? {
1494 return Ok(());
1495 }
1496 }
1497
1498 // The same route saying the same thing. A re-issued certificate is one
1499 // route spelled twice, but an authority that re-issued under another
1500 // policy said something else through it, and that is precisely the
1501 // distinct outcome several paths exist to preserve: dropping it as a
1502 // repeat would throw away the answer while reporting nothing missing.
1503 for held in &search.found {
1504 if held.same_route(chain, anchor, search.execution)?
1505 && held.same_meaning(anchor, &policies, search.execution)?
1506 {
1507 return Ok(());
1508 }
1509 }
1510 if search.found.len() < search.bounds.paths {
1511 let mut certificates = search.execution.vector();
1512 certificates
1513 .try_extend_from_slice(chain)
1514 .map_err(|reason| anchor.name.resource(reason))?;
1515 search
1516 .found
1517 .try_push(Path {
1518 certificates,
1519 anchor,
1520 policies,
1521 })
1522 .map_err(|reason| anchor.name.resource(reason))?;
1523 return Ok(());
1524 }
1525
1526 // No room. A path saying something none of the held ones say is worth
1527 // more than a second path saying what one of them already said, so it
1528 // takes the place of the last such repeat. Either way a path that was
1529 // found is not being returned, which the answer has to say.
1530 search.stop(Truncation::Paths);
1531 for held in &search.found {
1532 if held.same_meaning(anchor, &policies, search.execution)? {
1533 return Ok(());
1534 }
1535 }
1536 let mut repeat = None;
1537 for at in (0..search.found.len()).rev() {
1538 let candidate = &search.found[at];
1539 let mut count = 0;
1540 for held in &search.found {
1541 if held.same_meaning(candidate.anchor, &candidate.policies, search.execution)? {
1542 count += 1;
1543 if count > 1 {
1544 break;
1545 }
1546 }
1547 }
1548 if count > 1 {
1549 repeat = Some(at);
1550 break;
1551 }
1552 }
1553 if let Some(at) = repeat {
1554 // The repeat is taken out and the new path put on the end, rather
1555 // than dropped into the slot the repeat held. Paths of one length
1556 // come back in the order the caller's inputs put them in, and the
1557 // sort that arranges them is by length alone: a path written into
1558 // an earlier slot would sit in front of paths found before it, and
1559 // nothing afterwards would move it back.
1560 let moves = core::mem::size_of_val(&search.found[at..]);
1561 search
1562 .execution
1563 .charge_work(moves as u64)
1564 .map_err(|reason| anchor.name.resource(reason))?;
1565 search.found[at..].rotate_left(1);
1566 search.found.pop();
1567 let mut certificates = search.execution.vector();
1568 certificates
1569 .try_extend_from_slice(chain)
1570 .map_err(|reason| anchor.name.resource(reason))?;
1571 search
1572 .found
1573 .try_push(Path {
1574 certificates,
1575 anchor,
1576 policies,
1577 })
1578 .map_err(|reason| anchor.name.resource(reason))?;
1579 }
1580 Ok(())
1581}
1582
1583/// Follows the chain further up: to every certificate in the pool that could
1584/// have issued the one on top of it.
1585///
1586/// `deepest` says the chain has reached the length the caller allowed, so
1587/// that a candidate found here is one the depth bound refuses to step into
1588/// rather than one to walk. It is what turns the bound into a report: the
1589/// answer says it was reached where a candidate was there to be refused,
1590/// and says nothing where the chain had nowhere to go regardless.
1591fn next_issuer<'a, V: Verify, W: Watch, R: Recognizer>(
1592 search: &mut Search<'a, '_, V, W, R>,
1593 certificate: &'a Certificate<'a>,
1594 chain: &[&'a Certificate<'a>],
1595 deepest: bool,
1596 at: &mut usize,
1597) -> BuildResult<Option<&'a Certificate<'a>>, V::Error, R::Error> {
1598 // The candidates, folded the first time one is looked for. Every later
1599 // position reads the same folded list.
1600 if search.pool.is_none() {
1601 let supplied = search.supplied;
1602 let target = search.target;
1603 search.pool = Some(fold_pool(search, supplied, target)?);
1604 }
1605 // By position rather than by iterator: what follows this borrows the
1606 // search to walk further up, and a candidate is one shared reference,
1607 // so reading it out ends the borrow the loop would otherwise hold.
1608 while let Some(candidate) = search.pool.as_ref().and_then(|pool| pool.get(*at)).copied() {
1609 *at += 1;
1610 if search.stopped() {
1611 break;
1612 }
1613 if search.edges >= search.bounds.edges {
1614 search.stop(Truncation::Edges);
1615 break;
1616 }
1617 search.edges += 1;
1618 if !search.spend_names(certificate.tbs().issuer(), candidate.tbs().subject())? {
1619 break;
1620 }
1621 // A certificate appears once in a path (RFC 5280 6.1), which is
1622 // also what keeps a cycle from being followed round.
1623 //
1624 // Asked of where the certificate is rather than of what it holds.
1625 // The pool was folded before the search so that no two candidates
1626 // carry the same bytes, and the target was taken out of it, so two
1627 // certificates of one chain hold the same bytes exactly when they
1628 // are the same one. Comparing the encodings would read both from
1629 // end to end for an answer their addresses already give, and a pool
1630 // of long re-issued certificates that part only near the end makes
1631 // that a full scan per candidate per position of the chain, where
1632 // the charge just taken covers two names. It is the identity the
1633 // rest of the search already works by: what a certificate says
1634 // about itself is remembered under the same address.
1635 let mut repeated = false;
1636 for held in chain {
1637 search
1638 .execution
1639 .charge_work(1)
1640 .map_err(|reason| certificate.tbs().subject().resource(reason))?;
1641 if core::ptr::eq(held.as_bytes(), candidate.as_bytes()) {
1642 repeated = true;
1643 break;
1644 }
1645 }
1646 if repeated {
1647 continue;
1648 }
1649 if !certificate
1650 .tbs()
1651 .issuer()
1652 .matches(candidate.tbs().subject(), search.execution)?
1653 {
1654 continue;
1655 }
1656 // Everything cheap first, and the signature last: a candidate that
1657 // could not have issued anything, or was not valid when it would
1658 // have had to be, is refused for what it says rather than for what
1659 // its key does, and the check that costs the most is the one a
1660 // hostile pool would most like to buy in quantity.
1661 //
1662 // The certificate an anchor was published in is not a link in a path
1663 // to that anchor: "when the trust anchor is provided in the form of
1664 // a self-signed certificate, this self-signed certificate is not
1665 // included as part of the prospective certification path"
1666 // (RFC 5280 6.1). Walking into it would produce the path that was
1667 // already found, spelled one certificate longer.
1668 //
1669 // Self-signed is the whole of that rule, and names are not enough to
1670 // establish it. A provider recognized under two schemes has its
1671 // identity certified by both, and an authority rolling its key over
1672 // certifies the new key with the old one: the first carries the
1673 // anchor's name and key under somebody else's signature, the second
1674 // carries one name on both sides under the old key's. Both are
1675 // ordinary links, and skipping either would lose a whole route while
1676 // saying nothing about having lost it.
1677 let known = properties(search, candidate)?;
1678 if known.publishes_itself || !known.usable || known.issues.is_none() {
1679 continue;
1680 }
1681 if deepest {
1682 // A candidate that names itself the issuer and can issue is a
1683 // branch the depth bound is refusing, and refusing it is what
1684 // the answer has to say. Whether its signature would have held
1685 // is exactly what the search does not get to find out, so the
1686 // check is not spent on a candidate it will not follow. Once
1687 // one is refused the rest say the same thing, and the search
1688 // has other branches to spend its budget on.
1689 search.stop(Truncation::Depth);
1690 break;
1691 }
1692 // The key is asked about here, where the answer prunes the branch: a
1693 // candidate that did not sign this certificate is not walked into,
1694 // and a pool of such candidates costs one check each rather than a
1695 // subtree each.
1696 //
1697 // It is asked as the certificate wrote it, since what a key standing
1698 // in parameters written above stands in is exactly what this search
1699 // has not found yet. So only a definite no prunes. A registry that
1700 // cannot judge a key on those terms says that it cannot rather than
1701 // saying no, and the branch is walked so the question can be put
1702 // again from the anchor down, where the parameters in force are
1703 // known. Refusing on anything short of a no would lose whole routes.
1704 //
1705 // Asking only where the key carries its own parameters would prune
1706 // almost nothing: an algorithm that takes none writes a null, which
1707 // is written the same way as omitting them, so a pool of ordinary
1708 // wrong-key authorities would be walked in full and could spend the
1709 // whole budget before the one that signed this certificate is
1710 // reached. What tells the two apart is the registry's answer, and
1711 // the registry is who this asks.
1712 let key = WorkingKey::standing(candidate.tbs().public_key());
1713 let Some(answer) = search.verified(certificate, key)? else {
1714 break;
1715 };
1716 if answer == Verified::No {
1717 continue;
1718 }
1719 return Ok(Some(candidate));
1720 }
1721 Ok(None)
1722}
1723
1724/// Whether two anchors were published the same way: in the same certificate,
1725/// or in none. It is the last thing that tells apart two anchors of one name
1726/// and one key, because the certificate an anchor was published in is what
1727/// says how the names below it are constrained.
1728fn published_alike(
1729 left: Option<&Certificate<'_>>,
1730 right: Option<&Certificate<'_>>,
1731 execution: &Memory,
1732) -> Result<bool> {
1733 match (left, right) {
1734 (None, None) => Ok(true),
1735 (Some(ours), Some(theirs)) => binds_alike(ours, theirs, execution),
1736 _ => Ok(false),
1737 }
1738}
1739
1740/// Whether two certificates of one name and one key say the same thing about
1741/// the names below them.
1742///
1743/// By what is read rather than by every byte. An anchor's certificate is not
1744/// a link in any path: the one thing the search takes from it is what it
1745/// says about the space below, which is the regions it names, and nothing
1746/// else about it is consulted anywhere. A trust store holding a root
1747/// re-issued with a new serial, a new validity or a new signature holds one
1748/// statement of trust written twice, and keeping both makes the search try
1749/// each of them at every position where an anchor could end a path: a tight
1750/// bound on edges is then spent on copies, and a path through the one that
1751/// matches is lost.
1752///
1753/// The one extension compared is therefore the one enforced. Basic
1754/// constraints were compared here while an anchor's regions were honoured
1755/// only under an asserted authority bit; they are not read of an anchor any
1756/// more, and comparing them would split anchors that behave identically.
1757fn binds_alike(
1758 ours: &Certificate<'_>,
1759 theirs: &Certificate<'_>,
1760 execution: &Memory,
1761) -> Result<bool> {
1762 if same(ours, theirs) {
1763 return Ok(true);
1764 }
1765 let ours = ours
1766 .tbs()
1767 .extensions()
1768 .get(crate::oid::NAME_CONSTRAINTS, execution)?;
1769 let theirs = theirs
1770 .tbs()
1771 .extensions()
1772 .get(crate::oid::NAME_CONSTRAINTS, execution)?;
1773 Ok(match (ours, theirs) {
1774 (None, None) => true,
1775 (Some(ours), Some(theirs)) => {
1776 ours.is_critical() == theirs.is_critical() && ours.value() == theirs.value()
1777 }
1778 _ => false,
1779 })
1780}
1781
1782/// How many times comparing a name may read it.
1783///
1784/// Once, where every part holds one attribute: the parts line up in the
1785/// order they were written and each pair is compared where it stands. A part
1786/// holding several is a set, whose order carries no meaning, so both sides
1787/// are put in order before they are lined up, and a sort of `n` reads its
1788/// input about `log2(n)` times over. The widest part decides it, since one
1789/// wide part is enough to pay for.
1790///
1791/// A document decides how many attributes a part holds, so this factor is
1792/// theirs to choose too: without it a name of one very wide part is charged
1793/// as though it were read straight through, and the sort runs to the end
1794/// with nothing left to stop it.
1795fn sorting_rounds(name: &Name<'_>) -> u64 {
1796 let widest = name
1797 .parts()
1798 .iter()
1799 .map(|part| part.attributes().len())
1800 .max()
1801 .unwrap_or(1)
1802 .max(1);
1803 u64::from(widest.ilog2()) + 1
1804}
1805
1806/// How many bytes comparing an anchor's published certificate reads on its
1807/// side, which is none where it was published as a name and a key.
1808fn published_bytes(certificate: Option<&Certificate<'_>>) -> u64 {
1809 certificate.map_or(0, |certificate| certificate.as_bytes().len() as u64)
1810}
1811
1812/// Whether two certificates are the same one: the bytes they arrived as. A
1813/// pool that holds one certificate twice holds one certificate.
1814fn same(left: &Certificate<'_>, right: &Certificate<'_>) -> bool {
1815 core::ptr::eq(left.as_bytes(), right.as_bytes()) || left.as_bytes() == right.as_bytes()
1816}
1817
1818/// Whether a certificate is one an anchor was published in: the same name
1819/// and the same key, however the trust context supplied the anchor itself.
1820fn publishes_anchor<V: Verify, W: Watch, R: Recognizer>(
1821 search: &mut Search<'_, '_, V, W, R>,
1822 certificate: &Certificate<'_>,
1823) -> BuildResult<bool, V::Error, R::Error> {
1824 let anchors = search.anchors;
1825 for anchor in anchors {
1826 if !search.spend_names(certificate.tbs().subject(), anchor.name())? {
1827 break;
1828 }
1829 if !certificate
1830 .tbs()
1831 .subject()
1832 .matches(anchor.name(), search.execution)?
1833 {
1834 continue;
1835 }
1836
1837 // The keys are compared byte for byte, and how long a key encoding
1838 // is is decided by whoever wrote the certificate. Two encodings of
1839 // one length that differ near the end are read to the end, and a
1840 // pool can hold a candidate for every anchor: charged at a flat
1841 // rate, one certificate and a handful of anchors buy the reading of
1842 // their product while the bound barely moves. The name comparison
1843 // above is charged by what it reads for the same reason, and this
1844 // is the other side of the same question.
1845 let key = certificate.tbs().public_key().as_bytes();
1846 let against = anchor.key().as_bytes();
1847 let read = (key.len() as u64 + against.len() as u64) / cost::COMPARED;
1848 if !search.spend(read)? {
1849 break;
1850 }
1851 if key == against {
1852 return Ok(true);
1853 }
1854 }
1855 Ok(false)
1856}
1857
1858/// What one certificate says about itself, read the first time it is seen
1859/// and repeated afterwards.
1860///
1861/// Every answer here is a property of the certificate and of the question
1862/// the caller asked, neither of which changes while a search runs, so the
1863/// second visit costs a lookup rather than the extensions again.
1864fn properties<'a, V: Verify, W: Watch, R: Recognizer>(
1865 search: &mut Search<'a, '_, V, W, R>,
1866 certificate: &'a Certificate<'a>,
1867) -> BuildResult<Properties<'a>, V::Error, R::Error> {
1868 let at = certificate.as_bytes().as_ptr() as usize;
1869 if let Some(known) = search
1870 .properties
1871 .get(&at, search.execution)
1872 .map_err(|reason| certificate.tbs().subject().resource(reason))?
1873 {
1874 return Ok(*known);
1875 }
1876 let settings = validation::Settings {
1877 execution: search.execution,
1878 interval: search.interval,
1879 policy: search.policy,
1880 policies: search.bounds.policies,
1881 recognizer: search.recognizer,
1882 };
1883 let facts = validation::certificate_facts(certificate, &settings, search)?;
1884 let usable = facts.is_some_and(|facts| facts.rejected.is_none());
1885 let self_issued = facts.is_some_and(|facts| facts.self_issued);
1886 let issuing = facts.and_then(|facts| {
1887 if facts.issuing.is_none() {
1888 facts.basic
1889 } else {
1890 None
1891 }
1892 });
1893 // Anchor publication identity is search metadata, not a certificate-path
1894 // acceptance rule. Ask the expensive self-signature only after cheap facts.
1895 let publishes_itself = usable
1896 && self_issued
1897 && publishes_anchor(search, certificate)?
1898 && search.verified(
1899 certificate,
1900 WorkingKey::standing(certificate.tbs().public_key()),
1901 )? == Some(Verified::Yes);
1902 let known = Properties {
1903 facts,
1904 usable,
1905 issues: issuing,
1906 publishes_itself,
1907 };
1908 search
1909 .properties
1910 .try_insert(at, known)
1911 .map_err(|reason| certificate.tbs().subject().resource(reason))?;
1912 Ok(known)
1913}
1914
1915/// Whether the chain, read down from the anchor, is a path: every
1916/// certificate within the names the ones above it allowed, under a policy
1917/// they agreed on, and inside the depth they permitted (RFC 5280 6.1.3 and
1918/// 6.1.4).
1919///
1920/// The answer carries what the path was issued under, because that is not a
1921/// property of any one certificate: it is what is left after every authority
1922/// along the path has narrowed it.
1923///
1924/// The chain is held with the target first, so it is read backwards: the
1925/// certificate the anchor issued is where the path starts.
1926fn validate_candidate<'a, V: Verify, W: Watch, R: Recognizer>(
1927 search: &mut Search<'a, '_, V, W, R>,
1928 chain: &[&'a Certificate<'a>],
1929 anchor: Anchor<'a>,
1930) -> BuildResult<Option<OwnedVec<Oid<'a>>>, V::Error, R::Error> {
1931 let settings = validation::Settings {
1932 execution: search.execution,
1933 interval: search.interval,
1934 policy: search.policy,
1935 policies: search.bounds.policies,
1936 recognizer: search.recognizer,
1937 };
1938 // Every adjacent issuer and this terminal anchor were matched before
1939 // settle selected the chain. Preserve that construction witness rather
1940 // than normalizing all the same names a second time during processing.
1941 match validation::process(
1942 validation::Chain::Constructed(chain),
1943 anchor,
1944 &settings,
1945 search,
1946 )? {
1947 Validation::Accepted(facts) => Ok(Some(facts.into_policies())),
1948 Validation::Rejected(_) => Ok(None),
1949 Validation::Incomplete(reason) => {
1950 if !search.stopped() {
1951 search.stop(reason);
1952 }
1953 Ok(None)
1954 }
1955 }
1956}
1957
1958#[cfg(test)]
1959mod tests;