1use std::sync::Arc;
4
5use structured_asn1::memory::{Memory, MemoryAccount, MemoryError, OwnedVec, Work};
6use structured_asn1::{Encoding, Limits, Parser};
7use structured_pkix::Certificate;
8
9use super::x509::X509ChainError;
10use crate::policy::{PolicyViolation, ResourcePolicy, resource_name};
11
12pub(crate) trait X509DataSource {
15 fn certificate_count(&self) -> usize;
16 fn certificate(&self, index: usize) -> Option<&[u8]>;
17 fn chain(&self) -> &[usize];
18 fn crls(&self) -> &[Vec<u8>];
19
20 fn encoded_certificates(&self) -> impl ExactSizeIterator<Item = &[u8]> {
21 (0..self.certificate_count())
22 .map(|index| self.certificate(index).expect("source index is in range"))
23 }
24}
25
26impl X509DataSource for super::X509DataInfo {
27 fn certificate_count(&self) -> usize {
28 self.certificates.len()
29 }
30 fn certificate(&self, index: usize) -> Option<&[u8]> {
31 self.certificates.get(index).map(Vec::as_slice)
32 }
33 fn chain(&self) -> &[usize] {
34 &self.certificate_chain
35 }
36 fn crls(&self) -> &[Vec<u8>] {
37 &self.crls
38 }
39}
40
41enum XmlNameText<'a> {
42 Borrowed(&'a str),
43 Rewritten(structured_asn1::memory::OwnedString),
44}
45
46impl XmlNameText<'_> {
47 fn text<'a>(&'a self, memory: &Memory) -> Result<&'a str, X509ChainError> {
48 match self {
49 Self::Borrowed(text) => Ok(text),
50 Self::Rewritten(text) => text.as_str(memory).map_err(|reason| {
51 X509ChainError::Processing(structured_pkix::Error::resource(
52 reason,
53 "name.selector",
54 0,
55 ))
56 }),
57 }
58 }
59}
60
61fn xml_whitespace(byte: u8) -> bool {
62 matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
63}
64
65fn escaped_at(bytes: &[u8], at: usize) -> bool {
66 let mut start = at;
67 while start > 0 && bytes[start - 1] == b'\\' {
68 start -= 1;
69 }
70 (at - start) % 2 == 1
71}
72
73fn xml_name_pieces(
74 text: &str,
75 mut emit: impl FnMut(&str) -> Result<(), X509ChainError>,
76) -> Result<bool, X509ChainError> {
77 let bytes = text.as_bytes();
78 let mut start = 0;
79 while start < bytes.len() && xml_whitespace(bytes[start]) {
80 start += 1;
81 }
82 let mut end = bytes.len();
83 while end > start && xml_whitespace(bytes[end - 1]) && !escaped_at(bytes, end - 1) {
84 end -= 1;
85 }
86 let mut changed = start != 0 || end != bytes.len();
87 let mut piece = |segment: &str| -> Result<bool, X509ChainError> {
88 let input = segment.as_bytes();
89 let mut index = 0;
90 while index < input.len() {
91 if input[index] == b'\\' {
92 index += 2;
93 continue;
94 }
95 if input[index] == b'=' {
96 let alias = segment[..index].eq_ignore_ascii_case("EMAIL");
99 if alias {
100 emit("emailAddress")?;
101 }
102 if index + 1 < input.len() && input[index + 1] == b' ' {
105 emit(if alias { "=" } else { &segment[..index + 1] })?;
106 emit("\\")?;
107 emit(&segment[index + 1..])?;
108 return Ok(true);
109 }
110 if alias {
111 emit(&segment[index..])?;
112 return Ok(true);
113 }
114 break;
115 }
116 index += 1;
117 }
118 emit(segment)?;
119 Ok(false)
120 };
121 let mut at = start;
122 let mut segment = start;
123 while at < end {
124 if bytes[at] == b'\\' {
125 at += 2;
126 continue;
127 }
128 if matches!(bytes[at], b',' | b'+') {
129 let mut cut = at;
130 while cut > segment && xml_whitespace(bytes[cut - 1]) && !escaped_at(bytes, cut - 1) {
131 cut -= 1;
132 }
133 changed |= piece(&text[segment..cut])?;
134 changed |= piece(&text[at..at + 1])?;
135 let previous = at + 1;
136 at = previous;
137 while at < end && xml_whitespace(bytes[at]) {
138 at += 1;
139 }
140 if cut != previous - 1 || at != previous {
143 changed = true;
144 }
145 segment = at;
146 } else {
147 at += 1;
148 }
149 }
150 changed |= piece(&text[segment..end])?;
151 Ok(changed)
152}
153
154fn xml_name_text<'a>(text: &'a str, memory: &Memory) -> Result<XmlNameText<'a>, X509ChainError> {
155 let fail = |reason| {
160 X509ChainError::Processing(structured_pkix::Error::resource(reason, "name.selector", 0))
161 };
162 let work = (text.len() as u64)
163 .checked_mul(12)
164 .and_then(|work| work.checked_add(1))
165 .ok_or_else(|| fail(MemoryError::WorkExceeded))?;
166 memory.charge_work(work).map_err(fail)?;
167 if !xml_name_pieces(text, |_| Ok(()))? {
168 return Ok(XmlNameText::Borrowed(text));
169 }
170 let mut output = memory.string();
171 xml_name_pieces(text, |piece| output.try_push_str(piece).map_err(fail))?;
172 Ok(XmlNameText::Rewritten(output))
173}
174
175impl CertificateSession {
176 pub(crate) fn read_pool<'a>(
177 &'a self,
178 certificates: &'a [Vec<u8>],
179 ) -> Result<OwnedVec<Certificate<'a>>, X509ChainError> {
180 self.read_sources(certificates.iter().map(Vec::as_slice))
181 }
182
183 pub(crate) fn read_sources<'a>(
184 &'a self,
185 certificates: impl ExactSizeIterator<Item = &'a [u8]>,
186 ) -> Result<OwnedVec<Certificate<'a>>, X509ChainError> {
187 let mut pool = self.memory().vector();
188 pool.try_reserve_exact(certificates.len())
189 .map_err(|reason| {
190 self.error(structured_pkix::Error::resource(
191 reason,
192 "certificate.pool",
193 0,
194 ))
195 })?;
196 for encoded in certificates {
197 pool.try_push(self.read(encoded)?).map_err(|reason| {
198 self.error(structured_pkix::Error::resource(
199 reason,
200 "certificate.pool",
201 0,
202 ))
203 })?;
204 }
205 Ok(pool)
206 }
207
208 pub(crate) fn source_names_equal(
209 &self,
210 left: &structured_pkix::name::Name<'_>,
211 right: &structured_pkix::name::Name<'_>,
212 ) -> Result<bool, X509ChainError> {
213 left.matches(right, self.memory())
214 .map_err(|error| self.error(error))
215 }
216
217 pub(crate) fn selector(
218 &self,
219 text: &str,
220 ) -> Result<structured_pkix::name::Selector, X509ChainError> {
221 let normalize = |error| match error {
222 X509ChainError::Processing(error) => self.error(error),
223 error => error,
224 };
225 let text = xml_name_text(text, self.memory()).map_err(normalize)?;
226 structured_pkix::name::Selector::parse(
227 text.text(self.memory()).map_err(normalize)?,
228 self.memory(),
229 )
230 .map_err(|error| self.error(error))
231 }
232
233 #[cfg(test)]
234 pub(crate) fn names_equal(&self, left: &str, right: &str) -> Result<bool, X509ChainError> {
235 let left = self.selector(left)?;
236 let right = self.selector(right)?;
237 left.matches(&right, self.memory())
238 .map_err(|error| self.error(error))
239 }
240
241 #[cfg(test)]
242 fn read_name<'a>(
243 &'a self,
244 bytes: &'a [u8],
245 ) -> Result<structured_pkix::name::Name<'a>, X509ChainError> {
246 let mut reader = self.parser.read(bytes, 0).map_err(|error| {
247 self.error(structured_pkix::Error::new(
248 structured_pkix::ErrorCode::Encoding(error.code()),
249 "name",
250 error.offset(),
251 ))
252 })?;
253 let element = reader
254 .next_element()
255 .map_err(|error| {
256 self.error(structured_pkix::Error::new(
257 structured_pkix::ErrorCode::Encoding(error.code()),
258 "name",
259 error.offset(),
260 ))
261 })?
262 .expect("test Name element");
263 structured_pkix::name::read(&reader, &element, "name").map_err(|error| self.error(error))
264 }
265}
266
267pub(crate) fn verification_instant(
268 time: std::time::SystemTime,
269) -> Result<structured_pkix::temporal::Instant, X509ChainError> {
270 use std::time::UNIX_EPOCH;
271 let nanos = match time.duration_since(UNIX_EPOCH) {
272 Ok(duration) => duration.as_nanos() as i128,
273 Err(error) => -(error.duration().as_nanos() as i128),
274 };
275 let seconds = i64::try_from(nanos.div_euclid(1_000_000_000))
276 .map_err(|_| X509ChainError::CertificateNotValid(0))?;
277 let fraction = nanos.rem_euclid(1_000_000_000) as u32;
278 structured_pkix::temporal::Instant::new(seconds, fraction)
279 .ok_or(X509ChainError::CertificateNotValid(0))
280}
281
282#[cfg(test)]
283pub(crate) fn test_utc_facts() -> structured_pkix::temporal::LeapTable<'static> {
284 structured_pkix::temporal::LeapTable::new(-2_208_988_800, 4_102_444_800, &[])
286 .expect("test UTC coverage")
287}
288
289pub(crate) fn verify_crls<'a>(
291 path: &'a [Certificate<'a>],
292 embedded: &'a [Vec<u8>],
293 additional: &'a [Vec<u8>],
294 options: &super::x509::X509ChainOptions<'_>,
295 provider: &dyn crate::provider::CryptoProvider,
296 session: &'a CertificateSession,
297) -> Result<(), X509ChainError> {
298 use structured_pkix::crl::{
299 Authentication, AuthenticationFailure, CompleteEvidence, EntryLookup, Evaluation,
300 Freshness, ScopeOutcome, SourceOrigin,
301 };
302 use structured_pkix::error::CallbackError;
303 use structured_pkix::path::WorkingKey;
304 use structured_pkix::time::Relation;
305
306 if embedded.is_empty() && additional.is_empty() {
307 return Ok(());
308 }
309 let Some(anchor) = path.last() else {
310 return Ok(());
311 };
312 let execution = session.memory();
313 let mut crls = execution.vector();
314 let count = embedded
315 .len()
316 .checked_add(additional.len())
317 .ok_or_else(|| {
318 session.error(structured_pkix::Error::resource(
319 MemoryError::CapacityOverflow,
320 "certificateList.pool",
321 0,
322 ))
323 })?;
324 crls.try_reserve_exact(count).map_err(|reason| {
325 session.error(structured_pkix::Error::resource(
326 reason,
327 "certificateList.pool",
328 0,
329 ))
330 })?;
331 for bytes in embedded.iter().chain(additional) {
332 let crl = session.read_crl(bytes)?;
333 crls.try_push(crl).map_err(|reason| {
334 session.error(structured_pkix::Error::resource(
335 reason,
336 "certificateList.pool",
337 0,
338 ))
339 })?;
340 }
341 let verifier = ProviderVerifier {
342 provider,
343 allowed: options.certificate_signature_algorithms,
344 };
345 let now = verification_instant(options.verification_time)?.into();
346 let mut issuer_key = WorkingKey::standing(anchor.tbs().public_key());
347 for position in (0..path.len() - 1).rev() {
351 let issuer = &path[position + 1];
352 let target = &path[position];
353 let mut reasons = structured_pkix::crl::Reasons::NONE;
354 for (index, crl) in crls.iter().enumerate() {
355 if !crl
356 .tbs()
357 .issuer()
358 .matches(issuer.tbs().subject(), execution)
359 .map_err(|error| session.error(error))?
360 {
361 continue;
362 }
363 let authority = crl
364 .tbs()
365 .extensions()
366 .authority_key_identifier(execution)
367 .map_err(|error| session.error(error))?;
368 let subject = issuer
369 .tbs()
370 .extensions()
371 .subject_key_identifier(execution)
372 .map_err(|error| session.error(error))?;
373 let identified = if let Some((authority, subject)) = authority
374 .as_ref()
375 .and_then(|authority| authority.key_id())
376 .zip(subject.map(|id| id.as_bytes()))
377 {
378 execution
379 .charge_work(authority.len().max(subject.len()) as u64 + 1)
380 .map_err(|reason| {
381 session.error(structured_pkix::Error::resource(
382 reason,
383 "certificateList.authorityKeyIdentifier",
384 crl.span().start,
385 ))
386 })?;
387 if authority != subject {
388 continue;
389 }
390 true
391 } else {
392 false
393 };
394 let proof = crl
395 .authenticate(issuer, issuer_key, &verifier, execution)
396 .map_err(|error| match error {
397 CallbackError::Processing(error) => session.error(error),
398 CallbackError::Callback(error) => error,
399 })?;
400 let proof = match proof {
401 Authentication::Authenticated(proof) => proof,
402 Authentication::Rejected(AuthenticationFailure::KeyUsage) => {
403 return Err(X509ChainError::InvalidKeyUsage {
404 position: position + 1,
405 required: "cRLSign",
406 });
407 }
408 Authentication::Rejected(AuthenticationFailure::Signature) if !identified => {
409 continue;
410 }
411 Authentication::Rejected(_) | Authentication::Unsupported => {
412 return Err(X509ChainError::InvalidCrl(index));
413 }
414 };
415 validate_crl_serials(crl, session)?;
417 let utc = match options.utc_facts.as_ref() {
418 Some(utc) => utc,
419 None => {
420 if matches!(
421 proof
422 .scope(target, execution)
423 .map_err(|error| session.error(error))?,
424 ScopeOutcome::NotApplicable(_)
425 ) {
426 continue;
427 }
428 return Err(X509ChainError::MissingUtcFacts);
429 }
430 };
431 let evidence = proof
432 .evaluate(
433 target,
434 &Evaluation {
435 now,
436 utc,
437 execution,
438 origin: SourceOrigin::Cached,
439 },
440 )
441 .map_err(|error| session.error(error))?;
442 let facts = match evidence {
443 CompleteEvidence::Facts(facts) => facts,
444 CompleteEvidence::NotApplicable(_) => continue,
445 CompleteEvidence::DeltaNeedsBase => return Err(X509ChainError::InvalidCrl(index)),
446 };
447 match facts.freshness() {
448 Freshness::Current | Freshness::AtNextUpdate => (),
449 Freshness::Uncertain(reason) => {
450 return Err(X509ChainError::CrlTimeUncertain { index, reason });
451 }
452 _ => return Err(X509ChainError::InvalidCrl(index)),
453 }
454 match facts.entry() {
455 EntryLookup::Absent => (),
456 EntryLookup::Found(entry) => {
457 match entry
458 .time_relation(now, utc, execution)
459 .map_err(|error| session.error(error))?
460 {
461 Relation::Before | Relation::At => {
462 return Err(X509ChainError::Revoked(position));
463 }
464 Relation::After => (),
465 Relation::Uncertain(reason) => {
466 return Err(X509ChainError::CrlTimeUncertain { index, reason });
467 }
468 }
469 }
470 EntryLookup::Conflict { .. } | EntryLookup::Unsupported { .. } => {
471 return Err(X509ChainError::InvalidCrl(index));
472 }
473 }
474 execution.charge_work(1).map_err(|reason| {
475 session.error(structured_pkix::Error::resource(
476 reason,
477 "certificateList.reasons",
478 crl.span().start,
479 ))
480 })?;
481 reasons = reasons.union(facts.reasons());
486 }
487 if !reasons.is_empty() && !reasons.is_complete() {
490 return Err(X509ChainError::IncompleteCrlCoverage(position));
491 }
492 issuer_key = issuer_key
493 .subject(target, execution)
494 .map_err(|error| session.error(error))?;
495 }
496 Ok(())
497}
498
499fn validate_crl_serials(
500 crl: &structured_pkix::crl::CertificateList<'_>,
501 session: &CertificateSession,
502) -> Result<(), X509ChainError> {
503 for entry in crl.tbs().revoked_certificates() {
506 let bytes = entry.serial().as_bytes();
507 session
508 .memory()
509 .charge_work(bytes.len() as u64 + 1)
510 .map_err(|reason| {
511 session.error(structured_pkix::Error::resource(
512 reason,
513 "certificateList.serial",
514 entry.span().start,
515 ))
516 })?;
517 super::x509::validate_positive_serial_bytes(bytes, "CRL revoked certificate serial")?;
518 }
519 Ok(())
520}
521
522pub(crate) fn path_bounds(max_chain_depth: usize) -> structured_pkix::path::ValidationBounds {
525 structured_pkix::path::ValidationBounds {
526 depth: max_chain_depth.saturating_sub(1),
527 work: u64::MAX,
530 policies: usize::MAX,
531 }
532}
533
534pub(crate) fn validate_supplied_path<'a>(
535 chain: &[&'a Certificate<'a>],
536 anchor: &'a Certificate<'a>,
537 options: &super::x509::X509ChainOptions<'_>,
538 provider: &dyn crate::provider::CryptoProvider,
539 session: &CertificateSession,
540) -> Result<structured_pkix::path::ValidationFacts<'a>, X509ChainError> {
541 use structured_pkix::path::{
542 Anchor, BuildError, ConstraintSource, Interval, Policy, RejectionReason, Validation,
543 ValidationContext,
544 };
545 let verifier = CertificatePathVerifier {
546 signatures: ProviderVerifier {
547 provider,
548 allowed: options.certificate_signature_algorithms,
549 },
550 chain,
551 anchor,
552 rsa_keys: options.rsa_keys,
553 dsa_keys: options.dsa_keys,
554 };
555 let context = ValidationContext {
556 execution: session.memory(),
557 interval: Interval::at_instant(verification_instant(options.verification_time)?),
558 policy: Policy::default(),
559 bounds: path_bounds(options.max_chain_depth),
560 verifier: &verifier,
561 watch: &(),
562 recognizer: &(),
563 };
564 let result = structured_pkix::path::validate(chain, Anchor::from_certificate(anchor), &context)
565 .map_err(|error| match error {
566 BuildError::Processing(error) => session.error(error),
567 BuildError::Verification(X509ChainError::Processing(error)) => session.error(error),
568 BuildError::Verification(error) => error,
569 BuildError::Recognition(never) => match never {},
570 })?;
571 let position = |source| match source {
572 ConstraintSource::Anchor => chain.len(),
573 ConstraintSource::Certificate(position) => position,
574 };
575 match result {
576 Validation::Accepted(facts) => Ok(facts),
577 Validation::Incomplete(reason) => Err(X509ChainError::PathIncomplete(reason)),
578 Validation::Rejected(rejection) => {
579 let Some(at) = rejection.at.map(position) else {
580 return Err(X509ChainError::UntrustedRoot);
582 };
583 Err(match rejection.reason {
584 RejectionReason::EmptyPath | RejectionReason::AnchorIncluded => {
585 X509ChainError::UntrustedRoot
586 }
587 RejectionReason::Validity => X509ChainError::CertificateNotValid(at),
588 RejectionReason::NotAuthority | RejectionReason::EmptyAuthorityName => {
589 X509ChainError::IssuerNotCa(at)
590 }
591 RejectionReason::KeyCertSign => X509ChainError::InvalidKeyUsage {
592 position: at,
593 required: "keyCertSign",
594 },
595 RejectionReason::PathLength { source, limit } => {
596 X509ChainError::PathLengthExceeded {
597 position: position(source),
598 limit,
599 }
600 }
601 RejectionReason::NameConstraints { source, .. } => {
602 X509ChainError::NameConstraintViolation {
603 position: at,
604 constraining_position: position(source),
605 }
606 }
607 RejectionReason::NameConstraintsOnNonAuthority => {
608 X509ChainError::InvalidNameConstraints { position: at }
609 }
610 RejectionReason::CriticalExtension(oid) => {
611 let text = oid.text(session.memory()).map_err(|reason| {
612 X509ChainError::Processing(structured_pkix::Error::resource(
613 reason,
614 "certificate.extensions",
615 0,
616 ))
617 })?;
618 X509ChainError::UnsupportedCriticalExtension {
619 position: at,
620 oid: text
621 .as_str(session.memory())
622 .map_err(|reason| {
623 X509ChainError::Processing(structured_pkix::Error::resource(
624 reason,
625 "certificate.extensions",
626 0,
627 ))
628 })?
629 .to_owned(),
630 }
631 }
632 RejectionReason::MalformedExtension { extension, .. }
633 if extension == structured_pkix::oid::NAME_CONSTRAINTS =>
634 {
635 X509ChainError::InvalidNameConstraints { position: at }
636 }
637 RejectionReason::MalformedExtension {
638 code,
639 field,
640 offset,
641 ..
642 } => X509ChainError::Processing(structured_pkix::Error::new(code, field, offset)),
643 RejectionReason::Policies => X509ChainError::CertificatePolicies,
644 RejectionReason::Issuer | RejectionReason::Signature => {
645 X509ChainError::InvalidSignature(at)
646 }
647 RejectionReason::UnsupportedSignature => X509ChainError::UnsupportedSignature(at),
648 RejectionReason::DuplicateCertificate { .. } => X509ChainError::InvalidDer {
649 kind: "certificate path",
650 message: "repeated certificate".into(),
651 },
652 _ => X509ChainError::InvalidDer {
653 kind: "certificate path",
654 message: "unhandled static path rejection".into(),
655 },
656 })
657 }
658 }
659}
660
661fn with_effective_spki<T, W: Work>(
662 encoded: &[u8],
663 parameters: Option<&[u8]>,
664 execution: &W,
665 verify: impl FnOnce(&[u8]) -> Result<T, X509ChainError>,
666) -> Result<T, X509ChainError> {
667 let Some(parameters) = parameters else {
668 return verify(encoded);
669 };
670 let effective = encode_effective_spki(encoded, parameters, execution)?;
671 verify(&effective)
672}
673
674#[derive(Debug)]
677pub(crate) struct ValidatedX509Key {
678 pub(crate) effective_spki: Option<OwnedVec<u8>>,
679}
680
681pub(crate) fn effective_target_spki(
682 key: structured_pkix::path::WorkingKey<'_>,
683 execution: &Memory,
684) -> Result<ValidatedX509Key, X509ChainError> {
685 let original = key.key();
686 if !original.algorithm().has_no_parameters() {
687 return Ok(ValidatedX509Key {
688 effective_spki: None,
689 });
690 }
691 match key.parameters() {
692 Some(parameters) => {
693 encode_effective_spki(original.as_bytes(), parameters, execution).map(|spki| {
694 ValidatedX509Key {
695 effective_spki: Some(spki),
696 }
697 })
698 }
699 None => Ok(ValidatedX509Key {
700 effective_spki: None,
701 }),
702 }
703}
704
705fn encode_effective_spki<W: Work>(
706 encoded: &[u8],
707 parameters: &[u8],
708 execution: &W,
709) -> Result<OwnedVec<u8, W>, X509ChainError> {
710 use der::{Decode as _, Encode as _};
711 execution
712 .charge_work(encoded.len() as u64)
713 .map_err(|reason| {
714 X509ChainError::Processing(structured_pkix::Error::resource(
715 reason,
716 "issuer.public_key",
717 0,
718 ))
719 })?;
720 let key = pkcs8::SubjectPublicKeyInfoRef::from_der(encoded).map_err(|error| {
721 X509ChainError::InvalidDer {
722 kind: "issuer public key",
723 message: error.to_string(),
724 }
725 })?;
726 let parameters =
727 der::asn1::AnyRef::from_der(parameters).map_err(|error| X509ChainError::InvalidDer {
728 kind: "inherited issuer parameters",
729 message: error.to_string(),
730 })?;
731 let effective = pkcs8::SubjectPublicKeyInfoRef {
732 algorithm: pkcs8::AlgorithmIdentifierRef {
733 oid: key.algorithm.oid,
734 parameters: Some(parameters),
735 },
736 subject_public_key: key.subject_public_key,
737 };
738 let length = effective
739 .encoded_len()
740 .map_err(|error| X509ChainError::InvalidDer {
741 kind: "effective issuer public key",
742 message: error.to_string(),
743 })?;
744 let mut writer = AccountedDerWriter {
745 bytes: execution.vector(),
746 failure: None,
747 };
748 writer
749 .bytes
750 .try_reserve_exact(usize::try_from(length).map_err(|_| {
751 X509ChainError::Processing(structured_pkix::Error::resource(
752 MemoryError::CapacityOverflow,
753 "issuer.public_key",
754 0,
755 ))
756 })?)
757 .map_err(|error| {
758 X509ChainError::Processing(structured_pkix::Error::resource(
759 error,
760 "issuer.public_key",
761 0,
762 ))
763 })?;
764 if let Err(error) = effective.encode(&mut writer) {
765 return Err(match writer.failure {
766 Some(reason) => X509ChainError::Processing(structured_pkix::Error::resource(
767 reason,
768 "issuer.public_key",
769 0,
770 )),
771 None => X509ChainError::InvalidDer {
772 kind: "effective issuer public key",
773 message: error.to_string(),
774 },
775 });
776 }
777 Ok(writer.bytes)
778}
779
780struct AccountedDerWriter<W: Work> {
781 bytes: OwnedVec<u8, W>,
782 failure: Option<MemoryError>,
783}
784
785impl<W: Work> der::Writer for AccountedDerWriter<W> {
786 fn write(&mut self, bytes: &[u8]) -> der::Result<()> {
787 self.bytes.try_extend_from_slice(bytes).map_err(|error| {
788 self.failure = Some(error);
789 der::ErrorKind::Failed.into()
790 })
791 }
792}
793
794pub(crate) struct ProviderVerifier<'a> {
796 pub(crate) provider: &'a dyn crate::provider::CryptoProvider,
797 pub(crate) allowed: Option<&'a crate::policy::CertificateSignatureAlgorithms>,
798}
799
800impl<W: Work> structured_pkix::path::Verify<W> for ProviderVerifier<'_> {
801 type Error = X509ChainError;
802
803 fn verify(
804 &self,
805 algorithm: &structured_pkix::algorithm::AlgorithmIdentifier<'_>,
806 key: structured_pkix::path::WorkingKey<'_>,
807 signed: &[u8],
808 signature: structured_asn1::BitString<'_>,
809 execution: &W,
810 ) -> Result<structured_pkix::path::Verified, Self::Error> {
811 self.verify_with_key_check(algorithm, key, signed, signature, execution, |_| Ok(()))
812 }
813}
814
815impl ProviderVerifier<'_> {
816 fn verify_with_key_check<W: Work>(
817 &self,
818 algorithm: &structured_pkix::algorithm::AlgorithmIdentifier<'_>,
819 key: structured_pkix::path::WorkingKey<'_>,
820 signed: &[u8],
821 signature: structured_asn1::BitString<'_>,
822 execution: &W,
823 check: impl FnOnce(&[u8]) -> Result<(), X509ChainError>,
824 ) -> Result<structured_pkix::path::Verified, X509ChainError> {
825 use x509_parser::prelude::FromDer as _;
826 let key_algorithm = key.key().algorithm();
827 let parameters = key.parameters();
828 execution
829 .charge_work(
830 algorithm.as_bytes().len() as u64
831 + key_algorithm.algorithm().as_bytes().len() as u64
832 + parameters.map_or(0, |bytes| bytes.len() as u64),
833 )
834 .map_err(|reason| {
835 X509ChainError::Processing(structured_pkix::Error::resource(
836 reason,
837 "certificate.signature",
838 0,
839 ))
840 })?;
841 let (rest, identifier) = x509_parser::x509::AlgorithmIdentifier::from_der(
842 algorithm.as_bytes(),
843 )
844 .map_err(|error| X509ChainError::InvalidDer {
845 kind: "signature algorithm",
846 message: error.to_string(),
847 })?;
848 if !rest.is_empty() {
849 return Err(X509ChainError::InvalidDer {
850 kind: "signature algorithm",
851 message: "trailing data".into(),
852 });
853 }
854 let signature = signature
855 .octets()
856 .ok_or_else(|| X509ChainError::InvalidDer {
857 kind: "certificate signature",
858 message: "signature is not octet-aligned".into(),
859 })?;
860 let algorithm = super::x509::authorize_x509_signature_algorithm(
862 &identifier,
863 self.provider,
864 self.allowed,
865 )?;
866 if matches!(algorithm, crate::provider::X509SignatureAlgorithm::Dsa(_))
867 && key_algorithm.algorithm().as_bytes() == dsa::OID.as_bytes()
868 && parameters.is_none()
869 {
870 return Ok(structured_pkix::path::Verified::Unsupported);
874 }
875 let verify = |spki: &[u8]| {
876 check(spki)?;
877 self.provider
878 .verify_x509_signature(algorithm, signed, signature, spki)
879 .map_err(X509ChainError::from)
880 };
881 let valid = if parameters.is_some() && parameters != key_algorithm.parameters() {
882 with_effective_spki(key.key().as_bytes(), parameters, execution, verify)?
886 } else {
887 verify(key.key().as_bytes())?
888 };
889 Ok(if valid {
890 structured_pkix::path::Verified::Yes
891 } else {
892 structured_pkix::path::Verified::No
893 })
894 }
895}
896
897pub struct CertificatePathVerifier<'a, 'p> {
902 signatures: ProviderVerifier<'p>,
903 chain: &'p [&'a Certificate<'a>],
904 anchor: &'a Certificate<'a>,
905 rsa_keys: crate::policy::RsaKeyPolicy,
906 dsa_keys: crate::policy::DsaKeyPolicy,
907}
908
909impl<'a, 'p> CertificatePathVerifier<'a, 'p> {
910 pub(crate) fn new(
911 provider: &'p dyn crate::provider::CryptoProvider,
912 trust: &'p crate::policy::KeyTrustPolicy,
913 chain: &'p [&'a Certificate<'a>],
914 anchor: &'a Certificate<'a>,
915 ) -> Self {
916 Self {
917 signatures: ProviderVerifier {
918 provider,
919 allowed: trust.certificate_signature_algorithms.as_ref(),
920 },
921 chain,
922 anchor,
923 rsa_keys: trust.rsa_keys,
924 dsa_keys: trust.dsa_keys,
925 }
926 }
927}
928
929impl<W: Work> structured_pkix::path::Verify<W> for CertificatePathVerifier<'_, '_> {
930 type Error = X509ChainError;
931
932 fn verify(
933 &self,
934 algorithm: &structured_pkix::algorithm::AlgorithmIdentifier<'_>,
935 key: structured_pkix::path::WorkingKey<'_>,
936 signed: &[u8],
937 signature: structured_asn1::BitString<'_>,
938 execution: &W,
939 ) -> Result<structured_pkix::path::Verified, Self::Error> {
940 let raw = key.key().as_bytes();
941 execution
942 .charge_work(self.chain.len() as u64)
943 .map_err(|reason| {
944 X509ChainError::Processing(structured_pkix::Error::resource(
945 reason,
946 "issuer.public_key",
947 0,
948 ))
949 })?;
950 let position = if std::ptr::eq(raw, self.anchor.tbs().public_key().as_bytes()) {
951 self.chain.len()
952 } else {
953 self.chain
954 .iter()
955 .position(|certificate| {
956 std::ptr::eq(raw, certificate.tbs().public_key().as_bytes())
957 })
958 .ok_or_else(|| X509ChainError::InvalidDer {
959 kind: "issuer public key",
960 message: "key outside supplied path".into(),
961 })?
962 };
963 self.signatures.verify_with_key_check(
964 algorithm,
965 key,
966 signed,
967 signature,
968 execution,
969 |effective| {
970 super::x509::validate_issuer_spki_policy(
971 effective,
972 position,
973 self.rsa_keys,
974 self.dsa_keys,
975 )
976 },
977 )
978 }
979}
980
981pub(crate) enum PublicKey<'a> {
983 Rsa(structured_pkix::key::rsa::PublicKey<'a>),
984 Ec(structured_pkix::key::ec::PublicKey<'a>),
985}
986
987#[derive(Debug)]
989pub(crate) struct CertificateSession {
990 parser: Parser,
991 work_limit: usize,
992 memory_limit: usize,
993}
994
995impl CertificateSession {
996 pub(crate) fn new(resources: &ResourcePolicy) -> Self {
997 let work_limit = resources
998 .max_x509_work
999 .min(crate::hard_limits::X509_WORK_CEILING);
1000 let memory_limit = resources
1001 .max_x509_workspace_bytes
1002 .min(crate::hard_limits::X509_WORKSPACE_BYTE_CEILING);
1003 let memory = Memory::new(Arc::new(MemoryAccount::new(memory_limit, work_limit)));
1004 Self::with_account(work_limit, memory_limit, memory)
1005 }
1006
1007 pub(crate) fn into_parser(self) -> Parser {
1009 self.parser
1010 }
1011
1012 pub(crate) fn share(&self) -> Self {
1013 Self::with_account(self.work_limit, self.memory_limit, self.memory().clone())
1014 }
1015
1016 fn with_account(work_limit: usize, memory_limit: usize, memory: Memory) -> Self {
1017 Self {
1018 parser: Parser::new(
1019 Encoding::Der,
1020 Limits {
1021 max_depth: crate::hard_limits::ASN1_DEPTH_CEILING,
1022 max_elements: work_limit as u64,
1023 max_element_length: u64::MAX,
1027 max_work: work_limit as u64,
1028 },
1029 memory,
1030 ),
1031 work_limit,
1032 memory_limit,
1033 }
1034 }
1035
1036 pub(crate) fn require_resources(
1037 &self,
1038 resources: &ResourcePolicy,
1039 ) -> Result<(), PolicyViolation> {
1040 if self.work_limit != resources.max_x509_work
1041 || self.memory_limit != resources.max_x509_workspace_bytes
1042 {
1043 return Err(PolicyViolation::KeyTrust {
1044 reason: "certificate accounting must retain one immutable resource policy",
1045 });
1046 }
1047 Ok(())
1048 }
1049
1050 pub(crate) fn memory(&self) -> &Memory {
1051 self.parser.memory()
1052 }
1053
1054 pub(crate) fn read<'a>(&'a self, bytes: &'a [u8]) -> Result<Certificate<'a>, X509ChainError> {
1055 Certificate::read(&self.parser, bytes, 0).map_err(|error| self.error(error))
1056 }
1057
1058 pub(crate) fn read_public_key<'a>(
1061 &'a self,
1062 spki: &'a [u8],
1063 ) -> Result<PublicKey<'a>, structured_pkix::Error> {
1064 use structured_pkix::key::{ec, rsa};
1065 self.with_public_key_info(spki, |info, execution| {
1066 if info.algorithm().algorithm() == structured_pkix::oid::EC_PUBLIC_KEY {
1068 ec::read(structured_pkix::path::WorkingKey::standing(info), execution)
1069 .map(PublicKey::Ec)
1070 } else {
1071 rsa::read(info, execution).map(PublicKey::Rsa)
1072 }
1073 })
1074 }
1075
1076 pub(crate) fn read_dsa_public_key<'a>(
1077 &'a self,
1078 spki: &'a [u8],
1079 ) -> Result<structured_pkix::key::dsa::PublicKey<'a>, structured_pkix::Error> {
1080 self.with_public_key_info(spki, |info, execution| {
1081 structured_pkix::key::dsa::read(
1082 structured_pkix::path::WorkingKey::standing(info),
1083 execution,
1084 )
1085 })
1086 }
1087
1088 fn with_public_key_info<'a, T>(
1089 &'a self,
1090 spki: &'a [u8],
1091 inspect: impl FnOnce(
1092 structured_pkix::PublicKeyInfo<'a>,
1093 &structured_asn1::memory::Execution<'_>,
1094 ) -> Result<T, structured_pkix::Error>,
1095 ) -> Result<T, structured_pkix::Error> {
1096 use structured_pkix::key::PublicKeyInfo;
1097 use structured_pkix::{Error, ErrorCode};
1098 const FIELD: &str = "subjectPublicKeyInfo";
1099 let encoding = |error: structured_asn1::Error| {
1100 Error::new(ErrorCode::Encoding(error.code()), FIELD, error.offset())
1101 };
1102 let mut reader = self.parser.read(spki, 0).map_err(encoding)?;
1103 let element = reader
1104 .next_element()
1105 .map_err(encoding)?
1106 .ok_or_else(|| Error::new(ErrorCode::MissingField, FIELD, 0))?;
1107 let info = PublicKeyInfo::read_element(&reader, &element, FIELD)?;
1108 if !reader.is_empty() {
1110 return Err(Error::new(ErrorCode::TrailingData, FIELD, reader.offset()));
1111 }
1112 let execution = reader.execution();
1113 inspect(info, &execution)
1114 }
1115
1116 pub(crate) fn read_crl<'a>(
1117 &'a self,
1118 bytes: &'a [u8],
1119 ) -> Result<structured_pkix::crl::CertificateList<'a>, X509ChainError> {
1120 structured_pkix::crl::CertificateList::read(&self.parser, bytes, 0)
1121 .map_err(|error| self.error(error))
1122 }
1123
1124 pub(crate) fn parse_error(&self, error: X509ChainError) -> super::ParseError {
1125 match error {
1126 X509ChainError::Policy(error) => super::ParseError::Policy(error),
1127 X509ChainError::Processing(error) => super::ParseError::Certificate(error),
1128 error => super::ParseError::InvalidStructure(error.to_string()),
1129 }
1130 }
1131
1132 #[cfg(test)]
1133 pub(crate) fn metadata(
1134 &self,
1135 bytes: &[u8],
1136 ) -> Result<super::parse::ParsedX509Certificate, X509ChainError> {
1137 let certificate = self.read(bytes)?;
1138 super::parse::certificate_metadata(&certificate, self).map_err(|error| match error {
1139 super::ParseError::Policy(error) => X509ChainError::Policy(error),
1140 super::ParseError::Certificate(error) => X509ChainError::Processing(error),
1141 error => X509ChainError::InvalidDer {
1142 kind: "certificate metadata",
1143 message: error.to_string(),
1144 },
1145 })
1146 }
1147
1148 pub(crate) fn error(&self, error: structured_pkix::Error) -> X509ChainError {
1149 if error.is_resource() {
1150 use structured_asn1::{ErrorCode, Limit};
1151 let memory = matches!(
1152 error.code(),
1153 structured_pkix::ErrorCode::Encoding(
1154 ErrorCode::LimitExceeded(Limit::Memory)
1155 | ErrorCode::AllocatorExhausted
1156 | ErrorCode::CapacityOverflow
1157 )
1158 );
1159 return PolicyViolation::ResourceLimitExceeded {
1160 resource: if memory {
1161 resource_name::X509_WORKSPACE_BYTES
1162 } else {
1163 resource_name::X509_WORK
1164 },
1165 maximum: if memory {
1166 self.memory_limit
1167 } else {
1168 self.work_limit
1169 },
1170 }
1171 .into();
1172 }
1173 X509ChainError::Processing(error)
1174 }
1175}
1176
1177#[cfg(test)]
1178mod tests {
1179 use super::*;
1180 use structured_pkix::path::{Verified, Verify as _, WorkingKey};
1181
1182 #[test]
1183 fn dsa_public_key_keeps_borrowed_components_and_absent_parameters() {
1184 let bytes =
1187 b"\x30\x12\x30\x09\x06\x07\x2a\x86\x48\xce\x38\x04\x01\x03\x05\x00\x02\x02\x00\x80";
1188 let session = CertificateSession::new(&ResourcePolicy::default());
1189 let key = session.read_dsa_public_key(bytes).expect("DSA structure");
1190 assert!(key.parameters().is_none());
1191 assert_eq!(key.y().as_bytes(), [0x80]);
1192 assert_eq!(key.y().integer().as_bytes(), [0, 0x80]);
1193 assert_eq!(key.y().as_bytes().as_ptr(), bytes[19..].as_ptr());
1194 }
1195
1196 #[test]
1197 fn algorithm_inspection_faults_never_become_strict_crypto_identifiers() {
1198 let session = CertificateSession::new(&ResourcePolicy::default());
1201 let bytes = [0x30, 0x05, 0x06, 0x01, 0x2a, 0x25, 0x00];
1202 let mut reader = session
1203 .parser
1204 .read(&bytes, 500)
1205 .expect("funded source reader");
1206 let element = reader
1207 .next_element()
1208 .expect("structural sequence")
1209 .expect("one algorithm element");
1210 let reading = structured_pkix::algorithm::read_inspection(&reader, &element, "algorithm")
1211 .expect("recoverable parameter fault");
1212 assert!(reading.fault().is_some());
1213 assert!(reading.identifier().is_none());
1214 assert_eq!(reading.parameters(), Some(&bytes[5..]));
1215 assert_eq!(
1216 reading
1217 .parameters_span()
1218 .expect("original parameter range")
1219 .start,
1220 505
1221 );
1222 assert!(std::ptr::eq(reading.as_bytes().as_ptr(), bytes.as_ptr()));
1223 assert!(structured_pkix::algorithm::read(&reader, &element, "algorithm").is_err());
1224 }
1225
1226 #[test]
1227 fn shared_general_name_queries_keep_semantics_and_work_limits() {
1228 use structured_pkix::general_name::{self, GeneralName};
1230 let session = CertificateSession::new(&ResourcePolicy::default());
1231 for (left, right, expected) in [
1232 (
1233 GeneralName::Dns("EXAMPLE.test"),
1234 GeneralName::Dns("example.TEST"),
1235 true,
1236 ),
1237 (
1238 GeneralName::Email("User@example.test"),
1239 GeneralName::Email("user@example.test"),
1240 false,
1241 ),
1242 (
1243 GeneralName::Uri("HTTP://Example.test:80/a/../%7e"),
1244 GeneralName::Uri("http://example.test/~"),
1245 true,
1246 ),
1247 ] {
1248 assert_eq!(
1249 left.matches(&right, session.memory())
1250 .expect("funded GeneralName comparison"),
1251 expected
1252 );
1253 }
1254 let resources = ResourcePolicy {
1255 max_x509_work: 1,
1256 ..Default::default()
1257 };
1258 let limited = CertificateSession::new(&resources);
1259 let reader = limited
1260 .parser
1261 .read(&[], 500)
1262 .expect("empty producing reader");
1263 let error = general_name::matches(
1264 &reader,
1265 &GeneralName::Dns("example.test"),
1266 &GeneralName::Dns("EXAMPLE.test"),
1267 777,
1268 "certificate.generalName",
1269 )
1270 .expect_err("GeneralName work must be denied");
1271 assert!(error.is_resource());
1272 assert_eq!(error.offset(), 777);
1273 assert!(matches!(limited.error(error), X509ChainError::Policy(_)));
1274 }
1275
1276 #[test]
1277 fn xml_name_adapter_preserves_escaped_delimiters_and_spaces() {
1278 let session = CertificateSession::new(&ResourcePolicy::default());
1280 for (input, expected) in [
1281 ("CN=Alice,O=Example", "CN=Alice,O=Example"),
1282 (" \tCN=Alice ,\n O=Example\r ", "CN=Alice,O=Example"),
1283 (
1284 r"CN=Alice\,Bob + OU=Ops\+Dev , O=Example",
1285 r"CN=Alice\,Bob+OU=Ops\+Dev,O=Example",
1286 ),
1287 (r"CN= Alice\ , O=Example", r"CN=\ Alice\ ,O=Example"),
1288 ("CN=Ștefan, O=Exemplu", "CN=Ștefan,O=Exemplu"),
1289 ] {
1290 let adapted = xml_name_text(input, session.memory()).expect("lexical adaptation");
1291 assert_eq!(
1292 adapted.text(session.memory()).expect("funded text"),
1293 expected
1294 );
1295 let _ = session.selector(input).expect("strict shared selector");
1296 }
1297 let canonical =
1298 xml_name_text("CN=Alice,O=Example", session.memory()).expect("canonical text");
1299 assert!(matches!(canonical, XmlNameText::Borrowed(_)));
1300 assert_eq!(session.memory().used(), 0);
1301 }
1302
1303 #[test]
1304 fn xml_name_adapter_reserves_before_rewriting() {
1305 let memory = Memory::new(Arc::new(MemoryAccount::new(0, 0)));
1307 assert!(xml_name_text("CN=Alice", &memory).is_err());
1308 assert_eq!(memory.work_used(), 0);
1309 assert_eq!(memory.used(), 0);
1310 let memory = Memory::new(Arc::new(MemoryAccount::new(0, 4096)));
1311 assert!(xml_name_text(" CN=Alice ", &memory).is_err());
1312 assert_eq!(memory.used(), 0);
1313 }
1314
1315 #[test]
1316 fn xml_name_selector_preserves_pseudonym_case_exact_semantics() {
1317 let session = CertificateSession::new(&ResourcePolicy::default());
1320 assert!(
1321 !session
1322 .names_equal("pseudonym=Alice", "pseudonym=alice")
1323 .expect("supported pseudonym")
1324 );
1325 assert!(
1326 !session
1327 .names_equal("pseudonym=Straße", "pseudonym=STRASSE")
1328 .expect("supported Unicode pseudonym")
1329 );
1330 assert!(
1331 session
1332 .names_equal(r" pseudonym=\ Alice\ ", "pseudonym=Alice")
1333 .expect("insignificant space preparation")
1334 );
1335 let bytes = [
1336 0x30, 16, 0x31, 14, 0x30, 12, 6, 3, 0x55, 4, 65, 12, 5, b'A', b'l', b'i', b'c', b'e',
1337 ];
1338 let name = session.read_name(&bytes).expect("borrowed pseudonym Name");
1339 assert!(
1340 session
1341 .selector("pseudonym=Alice")
1342 .expect("same selector")
1343 .matches_name(&name, session.memory())
1344 .expect("same comparison")
1345 );
1346 assert!(
1347 !session
1348 .selector("pseudonym=alice")
1349 .expect("different selector")
1350 .matches_name(&name, session.memory())
1351 .expect("different comparison")
1352 );
1353 }
1354
1355 #[test]
1356 fn name_wire_rendering_preserves_invalid_text_without_accepting_it_as_text() {
1357 let session = CertificateSession::new(&ResourcePolicy::default());
1361 let bytes = [0x30, 12, 0x31, 10, 0x30, 8, 6, 3, 0x55, 4, 3, 12, 1, 0xff];
1362 let name = session.read_name(&bytes).expect("wire Name");
1363 let text = name
1364 .rfc4514(session.memory())
1365 .expect("lossless wire rendering");
1366 assert_eq!(
1367 text.as_str(session.memory()).expect("rendered text"),
1368 "CN=#0C01FF"
1369 );
1370 let selector = session.selector("CN=#0C01FF").expect("opaque selector");
1371 assert!(
1372 selector
1373 .matches_name(&name, session.memory())
1374 .expect("opaque wire identity")
1375 );
1376 }
1377
1378 #[test]
1379 fn inherited_issuer_parameters_are_provided_without_changing_source() {
1380 use der::{Decode as _, Encode as _};
1382 let y = [2, 1, 1];
1383 let parameters = [0x30, 9, 2, 1, 23, 2, 1, 11, 2, 1, 2];
1384 for encoded_parameters in [None, Some(der::asn1::AnyRef::NULL)] {
1385 let key = pkcs8::SubjectPublicKeyInfoRef {
1386 algorithm: pkcs8::AlgorithmIdentifierRef {
1387 oid: dsa::OID,
1388 parameters: encoded_parameters,
1389 },
1390 subject_public_key: der::asn1::BitStringRef::new(0, &y).expect("public bits"),
1391 }
1392 .to_der()
1393 .expect("SPKI encoding");
1394 let source = key.clone();
1395 let session = CertificateSession::new(&ResourcePolicy::default());
1396 with_effective_spki(&key, Some(¶meters), session.memory(), |effective| {
1397 let parsed =
1398 pkcs8::SubjectPublicKeyInfoRef::from_der(effective).expect("effective SPKI");
1399 assert_eq!(
1400 parsed
1401 .algorithm
1402 .parameters
1403 .expect("inherited params")
1404 .to_der()
1405 .expect("DER"),
1406 parameters
1407 );
1408 assert_eq!(parsed.subject_public_key.raw_bytes(), y);
1409 assert!(session.memory().used() >= effective.len());
1410 Ok(())
1411 })
1412 .expect("inherited key is supported");
1413 assert_eq!(key, source);
1414 assert_eq!(session.memory().used(), 0);
1415 }
1416 }
1417
1418 #[test]
1419 fn inherited_key_workspace_is_reserved_before_provider_execution() {
1420 use der::Encode as _;
1422 let key = pkcs8::SubjectPublicKeyInfoRef {
1423 algorithm: pkcs8::AlgorithmIdentifierRef {
1424 oid: dsa::OID,
1425 parameters: None,
1426 },
1427 subject_public_key: der::asn1::BitStringRef::new(0, &[2, 1, 1]).expect("public bits"),
1428 }
1429 .to_der()
1430 .expect("SPKI encoding");
1431 let session = CertificateSession::new(&ResourcePolicy {
1432 max_x509_workspace_bytes: 0,
1433 ..ResourcePolicy::default()
1434 });
1435 let called = std::cell::Cell::new(false);
1436 let result = with_effective_spki(&key, Some(&[0x30, 0]), session.memory(), |_| {
1437 called.set(true);
1438 Ok(())
1439 });
1440 assert!(matches!(result, Err(X509ChainError::Processing(error)) if error.is_resource()));
1441 assert!(!called.get());
1442 assert_eq!(session.memory().used(), 0);
1443 }
1444
1445 #[test]
1446 fn standalone_issuer_key_borrows_original_without_workspace() {
1447 use der::Encode as _;
1449 let key = pkcs8::SubjectPublicKeyInfoRef {
1450 algorithm: pkcs8::AlgorithmIdentifierRef {
1451 oid: dsa::OID,
1452 parameters: None,
1453 },
1454 subject_public_key: der::asn1::BitStringRef::new(0, &[2, 1, 1]).expect("public bits"),
1455 }
1456 .to_der()
1457 .expect("SPKI encoding");
1458 let session = CertificateSession::new(&ResourcePolicy {
1459 max_x509_workspace_bytes: 0,
1460 max_x509_work: 0,
1461 ..ResourcePolicy::default()
1462 });
1463 with_effective_spki(&key, None, session.memory(), |effective| {
1464 assert_eq!(effective.as_ptr(), key.as_ptr());
1465 assert_eq!(effective.len(), key.len());
1466 Ok(())
1467 })
1468 .expect("borrowed key needs no adapter workspace");
1469 assert_eq!(session.memory().used(), 0);
1470 assert_eq!(session.memory().work_used(), 0);
1471 }
1472
1473 #[test]
1474 fn certificate_decoding_shares_work_across_failed_candidates() {
1475 let resources = ResourcePolicy {
1477 max_x509_work: 2,
1478 ..ResourcePolicy::default()
1479 };
1480 let session = CertificateSession::new(&resources);
1481 assert!(session.read(&[0x30, 0]).is_err());
1482 assert!(matches!(
1483 session.read(&[0x30, 0]),
1484 Err(X509ChainError::Policy(_))
1485 ));
1486 }
1487
1488 #[test]
1489 fn certificate_parser_keeps_exact_signed_and_key_ranges() {
1490 let pair = rcgen::KeyPair::generate().expect("test key generation");
1492 let cert = rcgen::CertificateParams::new(Vec::<String>::new())
1493 .expect("empty SAN list")
1494 .self_signed(&pair)
1495 .expect("test certificate signing");
1496 let resources = ResourcePolicy::default();
1497 let session = CertificateSession::new(&resources);
1498 let parsed = session.read(cert.der()).expect("generated certificate");
1499 let start = cert.der().as_ptr() as usize;
1500 let end = start + cert.der().len();
1501 for bytes in [parsed.signed_bytes(), parsed.tbs().public_key().as_bytes()] {
1502 assert!((start..end).contains(&(bytes.as_ptr() as usize)));
1503 assert!(bytes.as_ptr() as usize + bytes.len() <= end);
1504 }
1505 }
1506
1507 #[test]
1508 fn issuer_edge_cannot_reset_exhausted_certificate_work() {
1509 let pair = rcgen::KeyPair::generate().expect("test key generation");
1511 let cert = rcgen::CertificateParams::new(Vec::<String>::new())
1512 .expect("empty SAN list")
1513 .self_signed(&pair)
1514 .expect("test certificate signing");
1515 let resources = ResourcePolicy {
1516 max_x509_work: 0,
1517 ..ResourcePolicy::default()
1518 };
1519 let session = CertificateSession::new(&resources);
1520 let result = super::super::x509::certificate_signature_matches_with_provider(
1521 cert.der(),
1522 cert.der(),
1523 crate::provider::default_provider(),
1524 None,
1525 &session,
1526 );
1527 assert!(session.read(cert.der()).is_err());
1528 assert!(matches!(result, Err(X509ChainError::Policy(_))));
1529 }
1530
1531 #[test]
1532 fn completed_candidate_does_not_mask_path_resource_exhaustion() {
1533 let pair = rcgen::KeyPair::generate().expect("test key generation");
1535 let cert = rcgen::CertificateParams::new(Vec::<String>::new())
1536 .expect("empty SAN list")
1537 .self_signed(&pair)
1538 .expect("test certificate signing");
1539 let info = super::super::parse::X509DataInfo {
1540 certificates: vec![cert.der().to_vec()],
1541 parsed_certificates: vec![
1542 super::super::parse::parse_x509_certificate(cert.der())
1543 .expect("generated certificate metadata"),
1544 ],
1545 ..Default::default()
1546 };
1547 let resources = ResourcePolicy {
1548 max_x509_work: 0,
1549 ..ResourcePolicy::default()
1550 };
1551 let session = CertificateSession::new(&resources);
1552 let result = super::super::parse::build_x509_certificate_paths_to_selector_targets(
1553 &info,
1554 0,
1555 &[0],
1556 super::super::parse::X509PathBounds {
1557 max_depth: 2,
1558 max_candidate_paths: 2,
1559 },
1560 crate::provider::default_provider(),
1561 None,
1562 &session,
1563 );
1564 assert!(matches!(
1565 result,
1566 Err(super::super::parse::X509ChainBuildError::Policy(_))
1567 ));
1568 }
1569
1570 #[test]
1571 fn shared_certificate_verifier_uses_exact_source_ranges() {
1572 let pair = rcgen::KeyPair::generate().expect("test key generation");
1574 let cert = rcgen::CertificateParams::new(Vec::<String>::new())
1575 .expect("empty SAN list")
1576 .self_signed(&pair)
1577 .expect("test certificate signing");
1578 let session = CertificateSession::new(&ResourcePolicy::default());
1579 let parsed = session.read(cert.der()).expect("generated certificate");
1580 let verifier = ProviderVerifier {
1581 provider: crate::provider::default_provider(),
1582 allowed: None,
1583 };
1584 assert_eq!(
1585 verifier
1586 .verify(
1587 &parsed.tbs().signature(),
1588 WorkingKey::standing(parsed.tbs().public_key()),
1589 parsed.signed_bytes(),
1590 parsed.signature(),
1591 session.memory()
1592 )
1593 .expect("signature execution succeeds"),
1594 Verified::Yes
1595 );
1596 let mut altered = parsed.signed_bytes().to_vec();
1597 *altered.last_mut().expect("nonempty signed bytes") ^= 1;
1598 assert_eq!(
1599 verifier
1600 .verify(
1601 &parsed.tbs().signature(),
1602 WorkingKey::standing(parsed.tbs().public_key()),
1603 &altered,
1604 parsed.signature(),
1605 session.memory()
1606 )
1607 .expect("corrupt signature execution succeeds"),
1608 Verified::No
1609 );
1610 }
1611
1612 #[test]
1613 fn published_scoped_verifier_preserves_both_current_work_ceilings() {
1614 let pair = rcgen::KeyPair::generate().expect("generate scoped verifier test key");
1617 let certificate = rcgen::CertificateParams::new(Vec::<String>::new())
1618 .expect("valid scoped verifier certificate parameters")
1619 .self_signed(&pair)
1620 .expect("sign scoped verifier test certificate");
1621 let source = CertificateSession::new(&ResourcePolicy::default());
1622 let certificate = source
1623 .read(certificate.der())
1624 .expect("read original certificate");
1625 let verifier = ProviderVerifier {
1626 provider: crate::provider::default_provider(),
1627 allowed: None,
1628 };
1629 for (local, aggregate) in [(0, 100000), (100000, 0), (100000, 100000)] {
1630 let memory = Memory::new(Arc::new(MemoryAccount::new(1 << 20, aggregate)));
1631 let operation = Parser::new(
1632 Encoding::Der,
1633 Limits {
1634 max_work: local,
1635 ..Limits::default()
1636 },
1637 memory,
1638 );
1639 let reader = operation.read(&[], 0).expect("create current execution");
1640 let result = verifier.verify(
1641 &certificate.tbs().signature(),
1642 WorkingKey::standing(certificate.tbs().public_key()),
1643 certificate.signed_bytes(),
1644 certificate.signature(),
1645 &reader.execution(),
1646 );
1647 if local == 0 || aggregate == 0 {
1648 assert!(
1649 matches!(result, Err(X509ChainError::Processing(error)) if error == structured_pkix::Error::resource(MemoryError::WorkExceeded, "certificate.signature", 0))
1650 );
1651 } else {
1652 assert_eq!(result.expect("funded verification succeeds"), Verified::Yes);
1653 }
1654 }
1655 }
1656
1657 #[test]
1658 fn discovery_defers_a_dsa_key_whose_parameters_need_inheritance() {
1659 use der::{Decode as _, Encode as _};
1664 let pair = rcgen::KeyPair::generate().expect("test key");
1665 let cert = rcgen::CertificateParams::new(Vec::<String>::new())
1666 .expect("test parameters")
1667 .self_signed(&pair)
1668 .expect("test certificate");
1669 let source = x509_cert::Certificate::from_der(cert.der()).expect("test schema");
1670 let tbs = source.tbs_certificate();
1671 let algorithm = x509_cert::spki::AlgorithmIdentifierOwned {
1672 oid: der::asn1::ObjectIdentifier::new_unwrap("1.2.840.10040.4.3"),
1673 parameters: None,
1674 };
1675 let key = x509_cert::SubjectPublicKeyInfo {
1676 algorithm: x509_cert::spki::AlgorithmIdentifierOwned {
1677 oid: dsa::OID,
1678 parameters: None,
1679 },
1680 subject_public_key: der::asn1::BitString::from_bytes(&[2, 1, 1]).expect("DSA integer"),
1681 };
1682 let mut body = Vec::new();
1683 for field in [
1684 tbs.serial_number().to_der(),
1685 algorithm.to_der(),
1686 tbs.issuer().to_der(),
1687 tbs.validity().to_der(),
1688 tbs.subject().to_der(),
1689 key.to_der(),
1690 ] {
1691 body.extend(field.expect("test TBS field"));
1692 }
1693 let tbs = der::asn1::Any::new(der::Tag::Sequence, body).expect("TBS sequence");
1694 let mut body = tbs.to_der().expect("test TBS");
1695 body.extend(algorithm.to_der().expect("test outer algorithm"));
1696 body.extend(source.signature().to_der().expect("test signature"));
1697 let bytes = der::asn1::Any::new(der::Tag::Sequence, body)
1698 .expect("certificate sequence")
1699 .to_der()
1700 .expect("test encoding");
1701 let session = CertificateSession::new(&ResourcePolicy::default());
1702 let parsed = session.read(&bytes).expect("DSA certificate");
1703 let verifier = ProviderVerifier {
1704 provider: crate::provider::default_provider(),
1705 allowed: None,
1706 };
1707 let checked = core::cell::Cell::new(false);
1708 let result = verifier.verify_with_key_check(
1709 &parsed.tbs().signature(),
1710 WorkingKey::standing(parsed.tbs().public_key()),
1711 parsed.signed_bytes(),
1712 parsed.signature(),
1713 session.memory(),
1714 |_| {
1715 checked.set(true);
1716 Ok(())
1717 },
1718 );
1719 assert!(matches!(result, Ok(Verified::Unsupported)), "{result:?}");
1720 assert!(
1721 !checked.get(),
1722 "no strength/provider check of an incomplete key"
1723 );
1724 let mut other_bytes = bytes.clone();
1725 *other_bytes.last_mut().expect("signature bytes") ^= 1;
1726 let info = super::super::parse::X509DataInfo {
1727 parsed_certificates: vec![
1728 session.metadata(&bytes).expect("leaf metadata"),
1729 session.metadata(&other_bytes).expect("issuer metadata"),
1730 ],
1731 certificates: vec![bytes.clone(), other_bytes.clone()],
1732 ..Default::default()
1733 };
1734 let paths = super::super::parse::build_x509_certificate_paths_to_selector_targets(
1735 &info,
1736 0,
1737 &[1],
1738 super::super::parse::X509PathBounds {
1739 max_depth: 2,
1740 max_candidate_paths: 4,
1741 },
1742 crate::provider::default_provider(),
1743 None,
1744 &session,
1745 )
1746 .expect("deferred edge remains a prospective path");
1747 assert_eq!(paths.len(), 1);
1748 assert_eq!(paths[0], [0, 1]);
1749 let anchor = session.read(&other_bytes).expect("candidate anchor");
1750 let anchors = [other_bytes.clone()];
1751 let options = super::super::x509::X509ChainOptions {
1752 utc_facts: None,
1753 trusted_certs: &anchors,
1754 verification_time: std::time::SystemTime::now(),
1755 max_chain_depth: 2,
1756 check_crls: false,
1757 allowed_extended_key_usages: None,
1758 rsa_keys: crate::policy::RsaKeyPolicy::default(),
1759 dsa_keys: crate::policy::DsaKeyPolicy::default(),
1760 certificate_signature_algorithms: None,
1761 };
1762 let validation = validate_supplied_path(
1763 &[&parsed],
1764 &anchor,
1765 &options,
1766 crate::provider::default_provider(),
1767 &session,
1768 );
1769 assert!(
1770 matches!(validation, Err(X509ChainError::UnsupportedSignature(0))),
1771 "a deferred edge is never authenticated: {validation:?}"
1772 );
1773 }
1774
1775 #[test]
1776 fn public_key_info_parser_enforces_certificate_workspace_policy() {
1777 use base64::Engine as _;
1779 let pair = rcgen::KeyPair::generate().expect("test key generation");
1780 let cert = rcgen::CertificateParams::new(Vec::<String>::new())
1781 .expect("empty SAN list")
1782 .self_signed(&pair)
1783 .expect("test certificate signing");
1784 let encoded = base64::engine::general_purpose::STANDARD.encode(cert.der());
1785 let xml = format!(
1786 "<KeyInfo xmlns='http://www.w3.org/2000/09/xmldsig#'><X509Data><X509Certificate>{encoded}</X509Certificate></X509Data></KeyInfo>"
1787 );
1788 let document = crate::xml::dom::Document::parse(&xml).expect("generated XML");
1789 let resources = ResourcePolicy {
1790 max_x509_workspace_bytes: 0,
1791 ..ResourcePolicy::default()
1792 };
1793 let mut session = super::super::parse::KeyInfoParsingSession::new(&resources)
1794 .expect("valid resource policy");
1795 assert!(matches!(
1796 session.parse(document.root_element()),
1797 Err(super::super::ParseError::Policy(
1798 PolicyViolation::ResourceLimitExceeded {
1799 resource: resource_name::X509_WORKSPACE_BYTES,
1800 maximum: 0
1801 }
1802 ))
1803 ));
1804 }
1805}