1#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
8use std::collections::HashSet;
9#[cfg(feature = "xmldsig")]
10use std::time::SystemTime;
11
12#[cfg(feature = "xmldsig")]
13use crate::xmldsig::{DigestAlgorithm, SignatureAlgorithm, UriTypeSet, XPathHereSemantics};
14#[cfg(feature = "xmlenc")]
15use crate::xmlenc::{
16 DataEncryptionAlgorithm, KeyTransportAlgorithm, KeyWrapAlgorithm, OaepDigestAlgorithm,
17};
18
19#[cfg(feature = "xmlenc")]
20mod key_establishment;
21#[cfg(feature = "xmlenc")]
22pub use key_establishment::{
23 KeyAgreementAlgorithm, KeyDerivationAlgorithm, KeyEstablishmentPolicy,
24};
25
26#[cfg(feature = "xmldsig")]
29#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
30pub enum RsaPssPermission {
31 #[default]
33 ExactOnly,
34 AllSupported,
36}
37
38#[cfg(feature = "xmldsig")]
39impl RsaPssPermission {
40 fn permits(self, algorithm: SignatureAlgorithm) -> bool {
41 self == Self::AllSupported && matches!(algorithm, SignatureAlgorithm::RsaPss(_))
42 }
43}
44
45pub(crate) mod resource_name {
50 pub const XML_NODES: &str = "XML nodes";
51 pub const XML_DEPTH: &str = "XML element depth";
52 pub const XML_NAMESPACE_BINDINGS: &str = "XML namespace bindings";
53 pub const SIGNATURE_REFERENCES: &str = "signature references";
54 #[cfg(feature = "xmlenc")]
55 pub const ENCRYPTION_REFERENCES: &str = "encryption key references";
56 pub const VERIFICATION_SIGNATURES: &str = "verification signatures";
57 pub const REFERENCE_TRANSFORMS: &str = "reference transforms";
58 pub const XML_BASE_COMPONENTS: &str = "XML Base components";
59 pub const XML_BASE_RESOLUTION_BYTES: &str = "XML Base resolution bytes";
60 pub const CANONICALIZED_BYTES: &str = "canonicalized bytes";
61 pub const EXTERNAL_RESOURCE_BYTES: &str = "external resource bytes";
62 pub const AGGREGATE_EXTERNAL_RESOURCE_BYTES: &str = "aggregate external resource bytes";
63 pub const ENCRYPTION_PLAINTEXT_BYTES: &str = "encryption plaintext bytes";
64 #[cfg(feature = "xmlenc")]
65 pub const AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES: &str =
66 "aggregate encryption CipherValue bytes";
67 pub const XML_DOCUMENT: &str = "XML document";
68 pub const XML_PARSE_WORK_BYTES: &str = "cumulative XML parse-work bytes";
69 pub const ENCRYPTION_RECIPIENTS: &str = "encryption recipients";
70 pub const ENCRYPTION_METADATA_BYTES: &str = "encryption metadata bytes";
71 pub const KEY_CANDIDATES: &str = "key candidates";
72 pub const KEY_IMPORT_KDF_WORK: &str = "key import KDF work";
73 pub const KEY_IMPORT_KDF_MEMORY: &str = "key import KDF memory bytes";
74 #[cfg(feature = "xmlenc")]
75 pub const KEY_ESTABLISHMENT_HASH_BLOCKS: &str = "key establishment hash blocks";
76 #[cfg(feature = "xmlenc")]
77 pub const KEY_ESTABLISHMENT_OWNED_BYTES: &str = "key establishment owned bytes";
78 #[cfg(feature = "xmlenc")]
79 pub const DH_MODULUS_BITS: &str = "DH modulus bits";
80 #[cfg(feature = "xmlenc")]
81 pub const DH_SUBGROUP_BITS: &str = "DH subgroup bits";
82 #[cfg(feature = "xmlenc")]
83 pub const KEY_ESTABLISHMENT_MODULAR_WORK: &str = "key establishment modular work";
84 pub const KEY_INFO_REFERENCE_DEPTH: &str = "KeyInfoReference depth";
85 pub const BASE64_TRANSFORM_INPUT_BYTES: &str = "Base64 transform input bytes";
86 pub const BASE64_TRANSFORM_OUTPUT_BYTES: &str = "Base64 transform output bytes";
87 pub const XPATH_EXPRESSIONS: &str = "XPath expressions";
88 pub const XPATH_EXPRESSION_BYTES: &str = "XPath expression bytes";
89 pub const XPATH_EXPRESSION_COMPLEXITY: &str = "XPath expression complexity";
90 pub const XPATH_CONTEXT_EVALUATIONS: &str = "XPath context evaluations";
91 pub const XPATH_EVALUATION_WORK: &str = "XPath evaluation work";
92 pub const XPATH_MIRROR_STRING_BYTES: &str = "XPath mirror string bytes";
93 pub const XPATH_STRING_WORK_BYTES: &str = "XPath string-processing work bytes";
94 pub const XPATH_NAMESPACE_BINDINGS: &str = "XPath namespace bindings";
95 pub const XPATH_NAMESPACE_BYTES: &str = "XPath namespace bytes";
96 pub const XPATH_FILTERS: &str = "XPath filters";
97 pub const NODE_SET_FILTER_WORK: &str = "node-set filter work";
98 pub const NODE_SET_ENTRIES: &str = "node-set entries";
99 pub const NODE_SET_OWNED_STRING_BYTES: &str = "node-set owned string bytes";
100 pub const NODE_SET_CUMULATIVE_OWNED_STRING_BYTES: &str =
101 "cumulative node-set owned string bytes";
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
106#[non_exhaustive]
107pub enum PolicyViolation {
108 #[error("{operation} policy rejects algorithm {algorithm}")]
110 Algorithm {
111 operation: &'static str,
113 algorithm: String,
115 },
116 #[cfg(feature = "xmldsig")]
118 #[error("HMAC output length must be between {minimum} and {maximum} bits: got {actual}")]
119 HmacOutputLength {
120 minimum: usize,
122 maximum: usize,
124 actual: usize,
126 },
127 #[error("{resource} exceeds policy maximum {maximum}: got {actual}")]
129 ResourceLimit {
130 resource: &'static str,
132 maximum: usize,
134 actual: usize,
136 },
137 #[error("{resource} exceeds policy maximum {maximum}")]
139 ResourceLimitExceeded {
140 resource: &'static str,
142 maximum: usize,
144 },
145 #[error("key import KDF iterations must be between 1 and {maximum}")]
147 KdfIterationsOutsideLimit {
148 maximum: usize,
150 },
151 #[error("{resource} has invalid policy limit {actual}: {requirement}")]
153 InvalidResourceLimit {
154 resource: &'static str,
156 requirement: &'static str,
158 actual: usize,
160 },
161 #[error("key/trust policy rejected the operation: {reason}")]
163 KeyTrust {
164 reason: &'static str,
166 },
167 #[error("XML input policy rejected the operation: {reason}")]
169 XmlInput {
170 reason: &'static str,
172 },
173 #[error("{operation} URI policy rejected the operation: {reason}")]
175 Uri {
176 operation: &'static str,
178 reason: &'static str,
180 },
181 #[error(
183 "{operation} policy requires {key_type} keys between {minimum_bits} and {maximum_bits} bits: got {actual_bits}"
184 )]
185 KeySize {
186 operation: &'static str,
188 key_type: &'static str,
190 minimum_bits: usize,
192 maximum_bits: usize,
194 actual_bits: usize,
196 },
197 #[error("{operation} policy rejects invalid {key_type} key material: {reason}")]
199 InvalidKeyMaterial {
200 operation: &'static str,
202 key_type: &'static str,
204 reason: &'static str,
206 },
207}
208
209#[cfg(feature = "xmldsig")]
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub struct HmacPolicy {
213 pub minimum_key_bits: usize,
215 pub minimum_output_bits: usize,
217}
218
219#[cfg(feature = "xmldsig")]
220impl Default for HmacPolicy {
221 fn default() -> Self {
222 Self {
223 minimum_key_bits: 128,
224 minimum_output_bits: 128,
225 }
226 }
227}
228
229#[cfg(feature = "xmldsig")]
230impl HmacPolicy {
231 pub(crate) fn validate(self) -> Result<(), PolicyViolation> {
232 if self.minimum_key_bits == 0 || self.minimum_output_bits == 0 {
233 return Err(PolicyViolation::KeyTrust {
234 reason: "HMAC minimum key and output lengths must be nonzero",
235 });
236 }
237 Ok(())
238 }
239
240 pub(crate) fn validate_key_bytes(self, key_bytes: usize) -> Result<(), PolicyViolation> {
241 self.validate_key_bits(key_bytes.saturating_mul(8))
242 }
243
244 pub(crate) fn validate_key_bits(self, key_bits: usize) -> Result<(), PolicyViolation> {
245 if key_bits < self.minimum_key_bits {
246 return Err(PolicyViolation::InvalidKeyMaterial {
247 operation: "HMAC",
248 key_type: "symmetric",
249 reason: "secret is shorter than the configured minimum",
250 });
251 }
252 Ok(())
253 }
254
255 pub(crate) fn validate_output(
256 self,
257 algorithm: SignatureAlgorithm,
258 selected_bits: usize,
259 ) -> Result<(), PolicyViolation> {
260 let maximum = algorithm
261 .hmac_output_bits()
262 .ok_or_else(|| PolicyViolation::Algorithm {
263 operation: "HMAC",
264 algorithm: algorithm.uri().to_owned(),
265 })?;
266 let minimum = self.minimum_output_bits.max(80).max(maximum / 2);
271 if selected_bits < minimum || selected_bits > maximum {
272 return Err(PolicyViolation::HmacOutputLength {
273 minimum,
274 maximum,
275 actual: selected_bits,
276 });
277 }
278 Ok(())
279 }
280}
281
282#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
284#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285pub struct RsaKeyPolicy {
286 pub minimum_modulus_bits: usize,
288}
289
290#[cfg(feature = "xmldsig")]
292#[derive(Debug, Clone, Copy, PartialEq, Eq)]
293pub struct DsaKeyPolicy {
294 pub minimum_modulus_bits: usize,
296}
297
298#[cfg(feature = "xmldsig")]
299impl Default for DsaKeyPolicy {
300 fn default() -> Self {
301 Self {
302 minimum_modulus_bits: 2048,
303 }
304 }
305}
306
307#[cfg(feature = "xmldsig")]
308impl DsaKeyPolicy {
309 pub fn validate(&self) -> Result<(), PolicyViolation> {
311 validate_modulus_minimum(
312 "minimum DSA modulus bits",
313 self.minimum_modulus_bits,
314 64,
315 "minimum must be a nonzero multiple of 64 bits",
316 crate::hard_limits::DSA_MODULUS_BIT_CEILING,
317 )
318 }
319
320 pub(crate) fn validate_modulus_bits(&self, actual_bits: usize) -> Result<(), PolicyViolation> {
321 self.validate()?;
322 if !(self.minimum_modulus_bits..=crate::hard_limits::DSA_MODULUS_BIT_CEILING)
323 .contains(&actual_bits)
324 {
325 return Err(PolicyViolation::KeySize {
326 operation: "verification",
327 key_type: "DSA",
328 minimum_bits: self.minimum_modulus_bits,
329 maximum_bits: crate::hard_limits::DSA_MODULUS_BIT_CEILING,
330 actual_bits,
331 });
332 }
333 Ok(())
334 }
335}
336
337#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
338impl Default for RsaKeyPolicy {
339 fn default() -> Self {
340 Self {
341 minimum_modulus_bits: 2048,
342 }
343 }
344}
345
346#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
347impl RsaKeyPolicy {
348 pub fn validate(&self) -> Result<(), PolicyViolation> {
350 validate_modulus_minimum(
351 "minimum RSA modulus bits",
352 self.minimum_modulus_bits,
353 8,
354 "minimum must be a nonzero whole-byte width",
355 crate::hard_limits::RSA_MODULUS_BIT_CEILING,
356 )
357 }
358
359 pub(crate) fn validate_components(
360 &self,
361 operation: &'static str,
362 modulus: &[u8],
363 exponent: &[u8],
364 ) -> Result<usize, PolicyViolation> {
365 self.validate()?;
366 let modulus = modulus
367 .iter()
368 .position(|byte| *byte != 0)
369 .map(|start| &modulus[start..])
370 .ok_or(PolicyViolation::InvalidKeyMaterial {
371 operation,
372 key_type: "RSA",
373 reason: "modulus is zero",
374 })?;
375 let modulus_bits = modulus
376 .len()
377 .checked_mul(8)
378 .and_then(|width| width.checked_sub(modulus[0].leading_zeros() as usize))
379 .ok_or(PolicyViolation::InvalidKeyMaterial {
380 operation,
381 key_type: "RSA",
382 reason: "modulus width overflows",
383 })?;
384 let exponent = if exponent.is_empty() || exponent.len() > 8 {
385 None
386 } else {
387 let mut bytes = [0_u8; 8];
388 bytes[8 - exponent.len()..].copy_from_slice(exponent);
389 Some(u64::from_be_bytes(bytes))
390 };
391 self.validate_public_metadata(operation, modulus_bits, exponent)
392 }
393
394 pub(crate) fn validate_public_metadata(
395 &self,
396 operation: &'static str,
397 modulus_bits: usize,
398 exponent: Option<u64>,
399 ) -> Result<usize, PolicyViolation> {
400 self.validate()?;
401 if modulus_bits == 0 {
402 return Err(PolicyViolation::InvalidKeyMaterial {
403 operation,
404 key_type: "RSA",
405 reason: "modulus is zero",
406 });
407 }
408 if !(self.minimum_modulus_bits..=crate::hard_limits::RSA_MODULUS_BIT_CEILING)
409 .contains(&modulus_bits)
410 {
411 return Err(PolicyViolation::KeySize {
412 operation,
413 key_type: "RSA",
414 minimum_bits: self.minimum_modulus_bits,
415 maximum_bits: crate::hard_limits::RSA_MODULUS_BIT_CEILING,
416 actual_bits: modulus_bits,
417 });
418 }
419 let exponent = exponent.ok_or(PolicyViolation::InvalidKeyMaterial {
420 operation,
421 key_type: "RSA",
422 reason: "public exponent has invalid encoding",
423 })?;
424 if !(3..=((1_u64 << 33) - 1)).contains(&exponent) || exponent % 2 == 0 {
425 return Err(PolicyViolation::InvalidKeyMaterial {
426 operation,
427 key_type: "RSA",
428 reason: "public exponent is outside the supported odd range",
429 });
430 }
431 Ok(modulus_bits.div_ceil(8))
432 }
433}
434
435#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
436fn validate_modulus_minimum(
437 resource: &'static str,
438 minimum_bits: usize,
439 alignment_bits: usize,
440 requirement: &'static str,
441 ceiling: usize,
442) -> Result<(), PolicyViolation> {
443 if minimum_bits == 0 || !minimum_bits.is_multiple_of(alignment_bits) {
444 return Err(PolicyViolation::InvalidResourceLimit {
445 resource,
446 requirement,
447 actual: minimum_bits,
448 });
449 }
450 ResourcePolicy::within(resource, minimum_bits, ceiling)
451}
452
453#[derive(Debug, Clone, PartialEq, Eq)]
455pub struct ResourcePolicy {
456 pub max_xml_nodes: usize,
458 pub max_xml_depth: usize,
460 pub max_xml_namespace_bindings: usize,
463 pub max_references: usize,
465 pub max_signatures: usize,
467 pub max_transforms_per_reference: usize,
469 pub max_xml_base_components: usize,
471 pub max_xml_base_resolution_bytes: usize,
473 pub max_canonicalized_bytes: usize,
475 pub max_external_resource_bytes: usize,
477 pub max_external_resource_total_bytes: usize,
479 pub max_encryption_plaintext_bytes: usize,
481 pub max_xml_document_bytes: usize,
483 pub max_xml_parse_work_bytes: usize,
485 pub max_encryption_recipients: usize,
487 pub max_encryption_metadata_bytes: usize,
489 pub max_key_candidates: usize,
492 pub max_key_import_kdf_work: usize,
495 pub max_key_import_kdf_memory_bytes: usize,
497 pub max_key_info_reference_depth: usize,
499 pub max_base64_transform_input_bytes: usize,
501 pub max_base64_transform_output_bytes: usize,
503 pub max_xpath_expressions: usize,
505 pub max_xpath_expression_bytes: usize,
507 pub max_xpath_expression_complexity: usize,
509 pub max_xpath_context_evaluations: usize,
511 pub max_xpath_evaluation_work: usize,
513 pub max_xpath_mirror_string_bytes: usize,
515 pub max_xpath_string_work_bytes: usize,
517 pub max_xpath_namespace_bindings: usize,
519 pub max_xpath_namespace_bytes: usize,
521 pub max_xpath_filters: usize,
523 pub max_node_set_filter_work: usize,
525 pub max_node_set_entries: usize,
528 pub max_node_set_owned_string_bytes: usize,
530 pub max_node_set_cumulative_owned_string_bytes: usize,
532}
533
534impl Default for ResourcePolicy {
535 fn default() -> Self {
536 Self {
537 max_xml_nodes: crate::hard_limits::XML_DOCUMENT_NODE_CEILING as usize,
538 max_xml_depth: crate::hard_limits::XML_DOCUMENT_DEPTH_CEILING,
539 max_xml_namespace_bindings: crate::hard_limits::XML_NAMESPACE_BINDING_CEILING,
540 max_references: crate::hard_limits::SIGNATURE_REFERENCE_CEILING,
541 max_signatures: crate::hard_limits::VERIFICATION_SIGNATURE_CEILING,
542 max_transforms_per_reference: crate::hard_limits::REFERENCE_TRANSFORM_CEILING,
543 max_xml_base_components: crate::hard_limits::XML_BASE_COMPONENT_CEILING,
544 max_xml_base_resolution_bytes: crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING,
545 max_canonicalized_bytes: crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING,
546 max_external_resource_bytes: crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING,
547 max_external_resource_total_bytes:
548 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING,
549 max_encryption_plaintext_bytes: crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING,
550 max_xml_document_bytes: crate::hard_limits::XML_DOCUMENT_BYTE_CEILING,
551 max_xml_parse_work_bytes: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
552 max_encryption_recipients: crate::hard_limits::ENCRYPTION_RECIPIENT_CEILING,
553 max_encryption_metadata_bytes: crate::hard_limits::ENCRYPTION_METADATA_BYTE_CEILING,
554 max_key_candidates: crate::hard_limits::KEY_CANDIDATE_CEILING,
555 max_key_import_kdf_work: crate::hard_limits::KEY_IMPORT_KDF_WORK_CEILING as usize,
556 max_key_import_kdf_memory_bytes: crate::hard_limits::KEY_IMPORT_KDF_MEMORY_CEILING,
557 max_key_info_reference_depth: crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING,
558 max_base64_transform_input_bytes:
559 crate::hard_limits::BASE64_TRANSFORM_INPUT_BYTE_CEILING,
560 max_base64_transform_output_bytes:
561 crate::hard_limits::BASE64_TRANSFORM_OUTPUT_BYTE_CEILING,
562 max_xpath_expressions: crate::hard_limits::XPATH_EXPRESSION_COUNT_CEILING,
563 max_xpath_expression_bytes: crate::hard_limits::XPATH_EXPRESSION_BYTE_CEILING,
564 max_xpath_expression_complexity:
565 crate::hard_limits::XPATH_EXPRESSION_COMPLEXITY_CEILING,
566 max_xpath_context_evaluations: crate::hard_limits::XPATH_CONTEXT_EVALUATION_CEILING,
567 max_xpath_evaluation_work: crate::hard_limits::XPATH_EVALUATION_WORK_CEILING,
568 max_xpath_mirror_string_bytes: crate::hard_limits::XPATH_MIRROR_STRING_BYTE_CEILING,
569 max_xpath_string_work_bytes: crate::hard_limits::XPATH_STRING_WORK_BYTE_CEILING,
570 max_xpath_namespace_bindings: crate::hard_limits::XPATH_NAMESPACE_BINDING_CEILING,
571 max_xpath_namespace_bytes: crate::hard_limits::XPATH_NAMESPACE_BYTE_CEILING,
572 max_xpath_filters: crate::hard_limits::XPATH_FILTER_COUNT_CEILING,
573 max_node_set_filter_work: crate::hard_limits::NODE_SET_FILTER_WORK_CEILING,
574 max_node_set_entries: 262_144,
575 max_node_set_owned_string_bytes: crate::hard_limits::NODE_SET_OWNED_STRING_BYTE_CEILING,
576 max_node_set_cumulative_owned_string_bytes:
577 crate::hard_limits::NODE_SET_CUMULATIVE_OWNED_STRING_BYTE_CEILING,
578 }
579 }
580}
581
582impl ResourcePolicy {
583 pub fn validate(&self) -> Result<(), PolicyViolation> {
585 Self::within(
586 resource_name::XML_NODES,
587 self.max_xml_nodes,
588 crate::hard_limits::XML_DOCUMENT_NODE_CEILING as usize,
589 )?;
590 Self::within(
591 resource_name::XML_DEPTH,
592 self.max_xml_depth,
593 crate::hard_limits::XML_DOCUMENT_DEPTH_CEILING,
594 )?;
595 Self::within(
596 resource_name::XML_NAMESPACE_BINDINGS,
597 self.max_xml_namespace_bindings,
598 crate::hard_limits::XML_NAMESPACE_BINDING_CEILING,
599 )?;
600 Self::within(
601 resource_name::CANONICALIZED_BYTES,
602 self.max_canonicalized_bytes,
603 crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING,
604 )?;
605 Self::within(
606 resource_name::SIGNATURE_REFERENCES,
607 self.max_references,
608 crate::hard_limits::SIGNATURE_REFERENCE_CEILING,
609 )?;
610 Self::within(
611 resource_name::VERIFICATION_SIGNATURES,
612 self.max_signatures,
613 crate::hard_limits::VERIFICATION_SIGNATURE_CEILING,
614 )?;
615 Self::within(
616 resource_name::REFERENCE_TRANSFORMS,
617 self.max_transforms_per_reference,
618 crate::hard_limits::REFERENCE_TRANSFORM_CEILING,
619 )?;
620 Self::within(
621 resource_name::XML_BASE_COMPONENTS,
622 self.max_xml_base_components,
623 crate::hard_limits::XML_BASE_COMPONENT_CEILING,
624 )?;
625 Self::within(
626 resource_name::XML_BASE_RESOLUTION_BYTES,
627 self.max_xml_base_resolution_bytes,
628 crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING,
629 )?;
630 Self::within(
631 resource_name::XML_DOCUMENT,
632 self.max_xml_document_bytes,
633 crate::hard_limits::XML_DOCUMENT_BYTE_CEILING,
634 )?;
635 Self::within(
636 resource_name::XML_PARSE_WORK_BYTES,
637 self.max_xml_parse_work_bytes,
638 crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
639 )?;
640 Self::within(
641 resource_name::EXTERNAL_RESOURCE_BYTES,
642 self.max_external_resource_bytes,
643 crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING,
644 )?;
645 Self::within(
646 resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
647 self.max_external_resource_total_bytes,
648 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING,
649 )?;
650 Self::within(
651 resource_name::ENCRYPTION_PLAINTEXT_BYTES,
652 self.max_encryption_plaintext_bytes,
653 crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING,
654 )?;
655 Self::within(
656 resource_name::ENCRYPTION_RECIPIENTS,
657 self.max_encryption_recipients,
658 crate::hard_limits::ENCRYPTION_RECIPIENT_CEILING,
659 )?;
660 Self::within(
661 resource_name::ENCRYPTION_METADATA_BYTES,
662 self.max_encryption_metadata_bytes,
663 crate::hard_limits::ENCRYPTION_METADATA_BYTE_CEILING,
664 )?;
665 for (resource, selected, ceiling) in [
666 (
667 resource_name::KEY_CANDIDATES,
668 self.max_key_candidates,
669 crate::hard_limits::KEY_CANDIDATE_CEILING,
670 ),
671 (
672 resource_name::KEY_IMPORT_KDF_WORK,
673 self.max_key_import_kdf_work,
674 crate::hard_limits::KEY_IMPORT_KDF_WORK_CEILING as usize,
675 ),
676 (
677 resource_name::KEY_IMPORT_KDF_MEMORY,
678 self.max_key_import_kdf_memory_bytes,
679 crate::hard_limits::KEY_IMPORT_KDF_MEMORY_CEILING,
680 ),
681 (
682 resource_name::KEY_INFO_REFERENCE_DEPTH,
683 self.max_key_info_reference_depth,
684 crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING,
685 ),
686 (
687 resource_name::BASE64_TRANSFORM_INPUT_BYTES,
688 self.max_base64_transform_input_bytes,
689 crate::hard_limits::BASE64_TRANSFORM_INPUT_BYTE_CEILING,
690 ),
691 (
692 resource_name::BASE64_TRANSFORM_OUTPUT_BYTES,
693 self.max_base64_transform_output_bytes,
694 crate::hard_limits::BASE64_TRANSFORM_OUTPUT_BYTE_CEILING,
695 ),
696 (
697 resource_name::XPATH_EXPRESSIONS,
698 self.max_xpath_expressions,
699 crate::hard_limits::XPATH_EXPRESSION_COUNT_CEILING,
700 ),
701 (
702 resource_name::XPATH_EXPRESSION_BYTES,
703 self.max_xpath_expression_bytes,
704 crate::hard_limits::XPATH_EXPRESSION_BYTE_CEILING,
705 ),
706 (
707 resource_name::XPATH_EXPRESSION_COMPLEXITY,
708 self.max_xpath_expression_complexity,
709 crate::hard_limits::XPATH_EXPRESSION_COMPLEXITY_CEILING,
710 ),
711 (
712 resource_name::XPATH_CONTEXT_EVALUATIONS,
713 self.max_xpath_context_evaluations,
714 crate::hard_limits::XPATH_CONTEXT_EVALUATION_CEILING,
715 ),
716 (
717 resource_name::XPATH_EVALUATION_WORK,
718 self.max_xpath_evaluation_work,
719 crate::hard_limits::XPATH_EVALUATION_WORK_CEILING,
720 ),
721 (
722 resource_name::XPATH_MIRROR_STRING_BYTES,
723 self.max_xpath_mirror_string_bytes,
724 crate::hard_limits::XPATH_MIRROR_STRING_BYTE_CEILING,
725 ),
726 (
727 resource_name::XPATH_STRING_WORK_BYTES,
728 self.max_xpath_string_work_bytes,
729 crate::hard_limits::XPATH_STRING_WORK_BYTE_CEILING,
730 ),
731 (
732 resource_name::XPATH_NAMESPACE_BINDINGS,
733 self.max_xpath_namespace_bindings,
734 crate::hard_limits::XPATH_NAMESPACE_BINDING_CEILING,
735 ),
736 (
737 resource_name::XPATH_NAMESPACE_BYTES,
738 self.max_xpath_namespace_bytes,
739 crate::hard_limits::XPATH_NAMESPACE_BYTE_CEILING,
740 ),
741 (
742 resource_name::XPATH_FILTERS,
743 self.max_xpath_filters,
744 crate::hard_limits::XPATH_FILTER_COUNT_CEILING,
745 ),
746 (
747 resource_name::NODE_SET_FILTER_WORK,
748 self.max_node_set_filter_work,
749 crate::hard_limits::NODE_SET_FILTER_WORK_CEILING,
750 ),
751 (
752 resource_name::NODE_SET_ENTRIES,
753 self.max_node_set_entries,
754 crate::hard_limits::NODE_SET_ENTRY_CEILING,
755 ),
756 (
757 resource_name::NODE_SET_OWNED_STRING_BYTES,
758 self.max_node_set_owned_string_bytes,
759 crate::hard_limits::NODE_SET_OWNED_STRING_BYTE_CEILING,
760 ),
761 (
762 resource_name::NODE_SET_CUMULATIVE_OWNED_STRING_BYTES,
763 self.max_node_set_cumulative_owned_string_bytes,
764 crate::hard_limits::NODE_SET_CUMULATIVE_OWNED_STRING_BYTE_CEILING,
765 ),
766 ] {
767 Self::within(resource, selected, ceiling)?;
768 }
769 Ok(())
770 }
771
772 pub(crate) fn validate_xml_document_len(&self, actual: usize) -> Result<(), PolicyViolation> {
773 if actual > self.max_xml_document_bytes {
774 return Err(PolicyViolation::ResourceLimit {
775 resource: resource_name::XML_DOCUMENT,
776 maximum: self.max_xml_document_bytes,
777 actual,
778 });
779 }
780 Ok(())
781 }
782
783 #[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
784 pub fn validate_key_candidates(&self, actual: usize) -> Result<(), PolicyViolation> {
786 if actual > self.max_key_candidates {
787 return Err(PolicyViolation::ResourceLimit {
788 resource: resource_name::KEY_CANDIDATES,
789 maximum: self.max_key_candidates,
790 actual,
791 });
792 }
793 Ok(())
794 }
795
796 #[cfg(feature = "xmldsig")]
798 pub fn validate_key_info_reference_depth(&self, actual: usize) -> Result<(), PolicyViolation> {
799 if actual > self.max_key_info_reference_depth {
800 return Err(PolicyViolation::ResourceLimit {
801 resource: resource_name::KEY_INFO_REFERENCE_DEPTH,
802 maximum: self.max_key_info_reference_depth,
803 actual,
804 });
805 }
806 Ok(())
807 }
808
809 pub(crate) fn effective_xml_nodes(&self) -> u32 {
810 u32::try_from(self.max_xml_nodes)
811 .unwrap_or(crate::hard_limits::XML_DOCUMENT_NODE_CEILING)
812 .min(crate::hard_limits::XML_DOCUMENT_NODE_CEILING)
813 }
814
815 #[cfg(feature = "xmldsig")]
816 pub(crate) fn effective_canonicalized_bytes(&self) -> usize {
817 self.max_canonicalized_bytes
818 .min(crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING)
819 }
820
821 #[cfg(feature = "xmldsig")]
822 pub(crate) fn effective_xml_base_components(&self) -> usize {
823 self.max_xml_base_components
824 .min(crate::hard_limits::XML_BASE_COMPONENT_CEILING)
825 }
826
827 #[cfg(feature = "xmldsig")]
828 pub(crate) fn effective_xml_base_resolution_bytes(&self) -> usize {
829 self.max_xml_base_resolution_bytes
830 .min(crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING)
831 }
832
833 fn within(
834 resource: &'static str,
835 selected: usize,
836 ceiling: usize,
837 ) -> Result<(), PolicyViolation> {
838 if selected > ceiling {
839 return Err(PolicyViolation::ResourceLimit {
840 resource,
841 maximum: ceiling,
842 actual: selected,
843 });
844 }
845 Ok(())
846 }
847
848 #[cfg(feature = "xmldsig")]
849 fn nonzero_within(
850 resource: &'static str,
851 selected: usize,
852 ceiling: usize,
853 ) -> Result<(), PolicyViolation> {
854 if selected == 0 {
855 return Err(PolicyViolation::InvalidResourceLimit {
856 resource,
857 requirement: "limit must be nonzero",
858 actual: selected,
859 });
860 }
861 Self::within(resource, selected, ceiling)
862 }
863}
864
865#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
867pub struct XmlInputPolicy {
868 pub allow_internal_dtd: bool,
870}
871
872#[cfg(feature = "xmldsig")]
874#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
875pub enum SameDocumentIdSemantics {
876 #[default]
878 Specification,
879 XmlSecBarename,
885 XmlSecVisa3d,
889}
890
891#[cfg(feature = "xmldsig")]
893#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
894pub enum EcdsaSignatureValueEncoding {
895 #[default]
897 XmlDsig,
898 XmlSecAsn1Der,
900}
901
902#[cfg(feature = "xmldsig")]
904#[derive(Debug, Clone, Default, PartialEq, Eq)]
905pub struct TransformPolicy {
906 pub allowed_algorithms: Option<HashSet<String>>,
908 pub xpath_here_semantics: XPathHereSemantics,
910 pub same_document_id_semantics: SameDocumentIdSemantics,
912}
913
914#[cfg(feature = "xmldsig")]
916#[derive(Debug, Clone, Copy, PartialEq, Eq)]
917pub struct UriPolicy {
918 pub references: UriTypeSet,
920 pub retrieval_methods: UriTypeSet,
922 pub key_info_references: UriTypeSet,
924}
925
926#[cfg(feature = "xmldsig")]
927impl Default for UriPolicy {
928 fn default() -> Self {
929 Self {
930 references: UriTypeSet::SAME_DOCUMENT,
931 retrieval_methods: UriTypeSet::SAME_DOCUMENT,
932 key_info_references: UriTypeSet::SAME_DOCUMENT,
933 }
934 }
935}
936
937#[cfg(feature = "xmldsig")]
939#[derive(Debug, Clone, Copy, PartialEq, Eq)]
940pub struct KeySourcePolicy {
941 pub preset_key: bool,
943 pub key_name: bool,
945 pub key_value: bool,
947 pub der_encoded_key_value: bool,
949 pub x509_data: bool,
951 pub key_info_reference: bool,
953}
954
955#[cfg(feature = "xmldsig")]
956impl Default for KeySourcePolicy {
957 fn default() -> Self {
958 Self {
959 preset_key: true,
960 key_name: true,
961 key_value: true,
962 der_encoded_key_value: true,
963 x509_data: true,
964 key_info_reference: true,
965 }
966 }
967}
968
969#[cfg(feature = "xmldsig")]
971#[derive(Debug, Clone, PartialEq, Eq, Hash)]
972#[non_exhaustive]
973pub enum ExtendedKeyPurpose {
974 ServerAuth,
976 ClientAuth,
978 CodeSigning,
980 EmailProtection,
982 TimeStamping,
984 OcspSigning,
986 Other(Vec<u64>),
988}
989
990#[cfg(feature = "xmldsig")]
992#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
993pub enum VerificationTrustMode {
994 #[default]
996 RequireTrustedKey,
997 CryptographicOnly,
999}
1000
1001#[cfg(feature = "xmldsig")]
1003#[derive(Debug, Clone, PartialEq, Eq)]
1004pub struct KeyTrustPolicy {
1005 pub mode: VerificationTrustMode,
1007 pub verify_x509_chains: bool,
1009 pub max_x509_chain_depth: usize,
1011 pub max_x509_candidate_paths: usize,
1013 pub allowed_legacy_signature_algorithms: HashSet<SignatureAlgorithm>,
1015 pub parameterized_rsa_pss: RsaPssPermission,
1017 pub certificate_signature_algorithms: Option<CertificateSignatureAlgorithms>,
1022 pub rsa_keys: RsaKeyPolicy,
1024 pub dsa_keys: DsaKeyPolicy,
1026 pub allowed_extended_key_usages: HashSet<ExtendedKeyPurpose>,
1032 pub check_crls: bool,
1035 pub verification_time: Option<SystemTime>,
1037}
1038
1039#[cfg(feature = "xmldsig")]
1040impl Default for KeyTrustPolicy {
1041 fn default() -> Self {
1042 Self {
1043 mode: VerificationTrustMode::RequireTrustedKey,
1044 verify_x509_chains: false,
1045 max_x509_chain_depth: crate::hard_limits::X509_CHAIN_DEPTH_CEILING,
1046 max_x509_candidate_paths: crate::hard_limits::X509_CANDIDATE_PATH_CEILING,
1047 allowed_legacy_signature_algorithms: HashSet::new(),
1048 parameterized_rsa_pss: RsaPssPermission::ExactOnly,
1049 certificate_signature_algorithms: None,
1050 rsa_keys: RsaKeyPolicy::default(),
1051 dsa_keys: DsaKeyPolicy::default(),
1052 allowed_extended_key_usages: HashSet::new(),
1053 check_crls: false,
1054 verification_time: None,
1055 }
1056 }
1057}
1058
1059#[cfg(feature = "xmldsig")]
1060#[derive(Debug, Clone, PartialEq, Eq)]
1061pub enum CertificateSignatureAlgorithms {
1063 AllSupported,
1066 Allowlist(HashSet<crate::provider::X509SignatureAlgorithm>),
1068}
1069
1070#[cfg(feature = "xmldsig")]
1071impl KeyTrustPolicy {
1072 pub(crate) fn check_certificate_signature_algorithm(
1073 algorithm: crate::provider::X509SignatureAlgorithm,
1074 allowed: Option<&CertificateSignatureAlgorithms>,
1075 ) -> Result<(), PolicyViolation> {
1076 let permitted = match allowed {
1080 Some(CertificateSignatureAlgorithms::AllSupported) => true,
1081 Some(CertificateSignatureAlgorithms::Allowlist(allowed)) => {
1082 allowed.contains(&algorithm)
1083 }
1084 None => !matches!(
1085 algorithm,
1086 crate::provider::X509SignatureAlgorithm::PostQuantum(_)
1087 ),
1088 };
1089 if !permitted {
1090 return Err(PolicyViolation::Algorithm {
1091 operation: "certificate/CRL verification",
1092 algorithm: algorithm
1093 .oid()
1094 .map_or_else(|| format!("{algorithm:?}"), str::to_owned),
1095 });
1096 }
1097 Ok(())
1098 }
1099
1100 pub(crate) fn validate(&self) -> Result<(), PolicyViolation> {
1101 if self.check_crls && !self.verify_x509_chains {
1102 return Err(PolicyViolation::KeyTrust {
1103 reason: "CRL checking requires X.509 chain validation",
1104 });
1105 }
1106 if self
1107 .allowed_extended_key_usages
1108 .iter()
1109 .any(|purpose| match purpose {
1110 ExtendedKeyPurpose::Other(arcs) => {
1111 arcs.len() < 2 || arcs[0] > 2 || (arcs[0] < 2 && arcs[1] > 39)
1112 }
1113 _ => false,
1114 })
1115 {
1116 return Err(PolicyViolation::KeyTrust {
1117 reason: "custom extended key purposes must contain valid OID arcs",
1118 });
1119 }
1120 self.rsa_keys.validate()?;
1121 self.dsa_keys.validate()?;
1122 ResourcePolicy::nonzero_within(
1123 "X.509 chain depth",
1124 self.max_x509_chain_depth,
1125 crate::hard_limits::X509_CHAIN_DEPTH_CEILING,
1126 )?;
1127 ResourcePolicy::nonzero_within(
1128 "X.509 candidate paths",
1129 self.max_x509_candidate_paths,
1130 crate::hard_limits::X509_CANDIDATE_PATH_CEILING,
1131 )
1132 }
1133}
1134
1135#[cfg(feature = "xmldsig")]
1137#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1138pub enum ManifestProcessing {
1139 #[default]
1141 Ignore,
1142 Process,
1144}
1145
1146#[cfg(feature = "xmldsig")]
1148#[derive(Debug, Clone, Default)]
1149pub struct VerificationPolicy {
1150 pub parameterized_rsa_pss: RsaPssPermission,
1153 pub signature_algorithms: Option<HashSet<SignatureAlgorithm>>,
1157 pub digest_algorithms: Option<HashSet<DigestAlgorithm>>,
1159 pub hmac: HmacPolicy,
1161 pub ecdsa_signature_value_encoding: EcdsaSignatureValueEncoding,
1163 pub key_trust: KeyTrustPolicy,
1165 pub key_sources: KeySourcePolicy,
1167 pub uris: UriPolicy,
1169 pub transforms: TransformPolicy,
1171 pub manifest_processing: ManifestProcessing,
1173 pub xml: XmlInputPolicy,
1175 pub resources: ResourcePolicy,
1177}
1178
1179#[cfg(feature = "xmldsig")]
1180impl VerificationPolicy {
1181 pub(crate) fn check_digest_algorithm(
1182 &self,
1183 algorithm: DigestAlgorithm,
1184 ) -> Result<(), PolicyViolation> {
1185 if self
1186 .digest_algorithms
1187 .as_ref()
1188 .map_or(!algorithm.requires_explicit_permission(), |allowed| {
1189 allowed.contains(&algorithm)
1190 })
1191 {
1192 Ok(())
1193 } else {
1194 Err(PolicyViolation::Algorithm {
1195 operation: "verification",
1196 algorithm: algorithm.uri().to_owned(),
1197 })
1198 }
1199 }
1200 pub fn validate(&self) -> Result<(), PolicyViolation> {
1202 self.resources.validate()?;
1203 self.key_trust.validate()?;
1204 self.hmac.validate()
1205 }
1206
1207 pub fn check_signature_algorithm(
1209 &self,
1210 algorithm: SignatureAlgorithm,
1211 ) -> Result<(), PolicyViolation> {
1212 if (matches!(algorithm, SignatureAlgorithm::PostQuantum(_))
1213 || algorithm.requires_explicit_permission())
1214 && !self
1215 .signature_algorithms
1216 .as_ref()
1217 .is_some_and(|allowed| allowed.contains(&algorithm))
1218 {
1219 return Err(PolicyViolation::Algorithm {
1220 operation: "verification",
1221 algorithm: algorithm.uri().to_owned(),
1222 });
1223 }
1224 if (algorithm.uses_legacy_pss_hash()
1225 || matches!(
1226 algorithm,
1227 SignatureAlgorithm::RsaSha1
1228 | SignatureAlgorithm::DsaSha1
1229 | SignatureAlgorithm::HmacSha1
1230 | SignatureAlgorithm::EcdsaSha1
1231 ))
1232 && !self
1233 .key_trust
1234 .allowed_legacy_signature_algorithms
1235 .contains(&algorithm)
1236 && !self.key_trust.parameterized_rsa_pss.permits(algorithm)
1237 {
1238 return Err(PolicyViolation::Algorithm {
1239 operation: "verification",
1240 algorithm: algorithm.uri().to_string(),
1241 });
1242 }
1243 if self
1244 .signature_algorithms
1245 .as_ref()
1246 .is_some_and(|allowed| !allowed.contains(&algorithm))
1247 && !self.parameterized_rsa_pss.permits(algorithm)
1248 {
1249 return Err(PolicyViolation::Algorithm {
1250 operation: "verification",
1251 algorithm: algorithm.uri().to_string(),
1252 });
1253 }
1254 Ok(())
1255 }
1256}
1257
1258#[cfg(feature = "xmldsig")]
1260#[derive(Debug, Clone, Default)]
1261pub struct SigningPolicy {
1262 pub parameterized_rsa_pss: RsaPssPermission,
1264 pub signature_algorithms: Option<HashSet<SignatureAlgorithm>>,
1267 pub digest_algorithms: Option<HashSet<DigestAlgorithm>>,
1269 pub hmac: HmacPolicy,
1271 pub ecdsa_signature_value_encoding: EcdsaSignatureValueEncoding,
1273 pub rsa_keys: RsaKeyPolicy,
1275 pub dsa_keys: DsaKeyPolicy,
1277 pub uris: UriPolicy,
1280 pub transforms: TransformPolicy,
1282 pub manifest_processing: ManifestProcessing,
1284 pub xml: XmlInputPolicy,
1286 pub resources: ResourcePolicy,
1288}
1289
1290#[cfg(feature = "xmldsig")]
1291impl SigningPolicy {
1292 pub fn validate(&self) -> Result<(), PolicyViolation> {
1294 self.resources.validate()?;
1295 self.rsa_keys.validate()?;
1296 self.dsa_keys.validate()?;
1297 self.hmac.validate()
1298 }
1299
1300 pub(crate) fn check_signature_algorithm(
1301 &self,
1302 algorithm: SignatureAlgorithm,
1303 ) -> Result<(), PolicyViolation> {
1304 if self.parameterized_rsa_pss.permits(algorithm) {
1305 return Ok(());
1306 }
1307 check_signing_algorithm(
1308 self.signature_algorithms.as_ref(),
1309 algorithm,
1310 algorithm.signing_allowed(),
1311 algorithm.uri(),
1312 )
1313 }
1314
1315 pub(crate) fn check_digest_algorithm(
1316 &self,
1317 algorithm: DigestAlgorithm,
1318 ) -> Result<(), PolicyViolation> {
1319 check_signing_algorithm(
1320 self.digest_algorithms.as_ref(),
1321 algorithm,
1322 algorithm.signing_allowed(),
1323 algorithm.uri(),
1324 )
1325 }
1326}
1327
1328#[cfg(feature = "xmldsig")]
1329fn check_signing_algorithm<T: Eq + std::hash::Hash>(
1330 allowlist: Option<&HashSet<T>>,
1331 algorithm: T,
1332 default_allowed: bool,
1333 uri: &str,
1334) -> Result<(), PolicyViolation> {
1335 if allowlist.map_or(default_allowed, |allowed| allowed.contains(&algorithm)) {
1336 Ok(())
1337 } else {
1338 Err(PolicyViolation::Algorithm {
1339 operation: "signing",
1340 algorithm: uri.to_owned(),
1341 })
1342 }
1343}
1344
1345#[cfg(feature = "xmlenc")]
1347pub(crate) fn check_content_algorithm(
1348 allowlist: Option<&HashSet<DataEncryptionAlgorithm>>,
1349 algorithm: DataEncryptionAlgorithm,
1350 operation: &'static str,
1351) -> Result<(), PolicyViolation> {
1352 if allowlist.map_or(!algorithm.requires_explicit_permission(), |allowed| {
1353 allowed.contains(&algorithm)
1354 }) {
1355 Ok(())
1356 } else {
1357 Err(PolicyViolation::Algorithm {
1358 operation,
1359 algorithm: algorithm.uri().to_owned(),
1360 })
1361 }
1362}
1363
1364#[cfg(feature = "xmlenc")]
1366#[derive(Debug, Clone, Default)]
1367pub struct EncryptionPolicy {
1368 pub data_algorithms: Option<HashSet<DataEncryptionAlgorithm>>,
1370 pub key_transport_algorithms: Option<HashSet<KeyTransportAlgorithm>>,
1372 pub key_wrap_algorithms: Option<HashSet<KeyWrapAlgorithm>>,
1374 pub oaep_digests: Option<HashSet<OaepDigestAlgorithm>>,
1376 pub rsa_keys: RsaKeyPolicy,
1378 pub key_establishment: KeyEstablishmentPolicy,
1380 pub xml: XmlInputPolicy,
1382 pub resources: ResourcePolicy,
1384}
1385
1386#[cfg(feature = "xmlenc")]
1387impl EncryptionPolicy {
1388 pub fn validate(&self) -> Result<(), PolicyViolation> {
1390 self.resources.validate()?;
1391 self.key_establishment.validate()?;
1392 self.rsa_keys.validate()
1393 }
1394}
1395
1396#[cfg(feature = "xmlenc")]
1398#[derive(Debug, Clone, Default)]
1399pub struct DecryptionPolicy {
1400 pub uris: UriPolicy,
1402 pub transforms: TransformPolicy,
1404 pub data_algorithms: Option<HashSet<DataEncryptionAlgorithm>>,
1406 pub key_transport_algorithms: Option<HashSet<KeyTransportAlgorithm>>,
1408 pub key_wrap_algorithms: Option<HashSet<KeyWrapAlgorithm>>,
1410 pub oaep_digests: Option<HashSet<OaepDigestAlgorithm>>,
1412 pub rsa_keys: RsaKeyPolicy,
1415 pub key_establishment: KeyEstablishmentPolicy,
1417 pub xml: XmlInputPolicy,
1419 pub resources: ResourcePolicy,
1421}
1422
1423#[cfg(feature = "xmlenc")]
1424impl DecryptionPolicy {
1425 pub fn validate(&self) -> Result<(), PolicyViolation> {
1427 self.resources.validate()?;
1428 self.key_establishment.validate()?;
1429 self.rsa_keys.validate()
1430 }
1431}
1432
1433#[cfg(test)]
1434mod tests {
1435 use super::*;
1436
1437 #[cfg(all(feature = "legacy-algorithms", feature = "xmldsig"))]
1438 #[test]
1439 fn legacy_digest_permission_is_explicit_and_operation_local() {
1440 for algorithm in [DigestAlgorithm::Md5, DigestAlgorithm::Ripemd160] {
1443 assert!(
1444 VerificationPolicy::default()
1445 .check_digest_algorithm(algorithm)
1446 .is_err()
1447 );
1448 assert!(
1449 SigningPolicy::default()
1450 .check_digest_algorithm(algorithm)
1451 .is_err()
1452 );
1453 let verification = VerificationPolicy {
1454 digest_algorithms: Some(HashSet::from([algorithm])),
1455 ..VerificationPolicy::default()
1456 };
1457 let signing = SigningPolicy {
1458 digest_algorithms: Some(HashSet::from([algorithm])),
1459 ..SigningPolicy::default()
1460 };
1461 assert!(verification.check_digest_algorithm(algorithm).is_ok());
1462 assert!(signing.check_digest_algorithm(algorithm).is_ok());
1463 assert!(
1464 verification
1465 .check_digest_algorithm(DigestAlgorithm::Sha256)
1466 .is_err()
1467 );
1468 assert!(
1469 signing
1470 .check_digest_algorithm(DigestAlgorithm::Sha256)
1471 .is_err()
1472 );
1473 }
1474 }
1475
1476 #[cfg(feature = "xmldsig")]
1477 #[test]
1478 fn certificate_permissions_keep_exact_pss_and_compatibility_distinct() {
1479 use crate::provider::X509SignatureAlgorithm;
1480 use crate::xmldsig::{DigestAlgorithm, PqAlgorithm};
1481 let exact = X509SignatureAlgorithm::RsaPss {
1484 digest: DigestAlgorithm::Sha256,
1485 mgf_digest: DigestAlgorithm::Sha256,
1486 salt_len: 32,
1487 };
1488 let allowed = CertificateSignatureAlgorithms::Allowlist(HashSet::from([exact]));
1489 assert!(
1490 KeyTrustPolicy::check_certificate_signature_algorithm(exact, Some(&allowed)).is_ok()
1491 );
1492 for salt_len in [0, 17, 64, 255] {
1493 let method = X509SignatureAlgorithm::RsaPss {
1494 digest: DigestAlgorithm::Sha256,
1495 mgf_digest: DigestAlgorithm::Sha256,
1496 salt_len,
1497 };
1498 assert!(
1499 KeyTrustPolicy::check_certificate_signature_algorithm(method, Some(&allowed))
1500 .is_err()
1501 );
1502 assert!(
1503 KeyTrustPolicy::check_certificate_signature_algorithm(
1504 method,
1505 Some(&CertificateSignatureAlgorithms::AllSupported)
1506 )
1507 .is_ok()
1508 );
1509 }
1510 let pq = X509SignatureAlgorithm::PostQuantum(PqAlgorithm::MlDsa44);
1511 assert!(KeyTrustPolicy::check_certificate_signature_algorithm(pq, None).is_err());
1512 assert!(
1513 KeyTrustPolicy::check_certificate_signature_algorithm(
1514 pq,
1515 Some(&CertificateSignatureAlgorithms::AllSupported)
1516 )
1517 .is_ok()
1518 );
1519 }
1520
1521 #[test]
1522 fn resource_policy_cannot_exceed_implementation_ceiling() {
1523 let policy = ResourcePolicy {
1524 max_xml_nodes: 100_001,
1525 ..ResourcePolicy::default()
1526 };
1527 assert!(matches!(
1528 policy.validate(),
1529 Err(PolicyViolation::ResourceLimit {
1530 resource: resource_name::XML_NODES,
1531 maximum: 100_000,
1532 actual: 100_001,
1533 })
1534 ));
1535 }
1536
1537 #[test]
1538 fn xml_parse_work_policy_cannot_exceed_implementation_ceiling() {
1539 let policy = ResourcePolicy {
1540 max_xml_parse_work_bytes: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING
1541 .saturating_add(1),
1542 ..ResourcePolicy::default()
1543 };
1544
1545 assert_eq!(
1546 policy.validate(),
1547 Err(PolicyViolation::ResourceLimit {
1548 resource: resource_name::XML_PARSE_WORK_BYTES,
1549 maximum: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
1550 actual: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING.saturating_add(1),
1551 })
1552 );
1553 }
1554
1555 #[test]
1556 fn every_resource_policy_field_obeys_its_hard_ceiling() {
1557 type Case = (&'static str, usize, fn(&mut ResourcePolicy) -> &mut usize);
1561 let cases: &[Case] = &[
1562 (
1563 resource_name::XML_NODES,
1564 crate::hard_limits::XML_DOCUMENT_NODE_CEILING as usize,
1565 |p| &mut p.max_xml_nodes,
1566 ),
1567 (
1568 resource_name::XML_DEPTH,
1569 crate::hard_limits::XML_DOCUMENT_DEPTH_CEILING,
1570 |p| &mut p.max_xml_depth,
1571 ),
1572 (
1573 resource_name::XML_NAMESPACE_BINDINGS,
1574 crate::hard_limits::XML_NAMESPACE_BINDING_CEILING,
1575 |p| &mut p.max_xml_namespace_bindings,
1576 ),
1577 (
1578 resource_name::SIGNATURE_REFERENCES,
1579 crate::hard_limits::SIGNATURE_REFERENCE_CEILING,
1580 |p| &mut p.max_references,
1581 ),
1582 (
1583 resource_name::VERIFICATION_SIGNATURES,
1584 crate::hard_limits::VERIFICATION_SIGNATURE_CEILING,
1585 |p| &mut p.max_signatures,
1586 ),
1587 (
1588 resource_name::REFERENCE_TRANSFORMS,
1589 crate::hard_limits::REFERENCE_TRANSFORM_CEILING,
1590 |p| &mut p.max_transforms_per_reference,
1591 ),
1592 (
1593 resource_name::XML_BASE_COMPONENTS,
1594 crate::hard_limits::XML_BASE_COMPONENT_CEILING,
1595 |p| &mut p.max_xml_base_components,
1596 ),
1597 (
1598 resource_name::XML_BASE_RESOLUTION_BYTES,
1599 crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING,
1600 |p| &mut p.max_xml_base_resolution_bytes,
1601 ),
1602 (
1603 resource_name::CANONICALIZED_BYTES,
1604 crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING,
1605 |p| &mut p.max_canonicalized_bytes,
1606 ),
1607 (
1608 resource_name::EXTERNAL_RESOURCE_BYTES,
1609 crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING,
1610 |p| &mut p.max_external_resource_bytes,
1611 ),
1612 (
1613 resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
1614 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING,
1615 |p| &mut p.max_external_resource_total_bytes,
1616 ),
1617 (
1618 resource_name::ENCRYPTION_PLAINTEXT_BYTES,
1619 crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING,
1620 |p| &mut p.max_encryption_plaintext_bytes,
1621 ),
1622 (
1623 resource_name::XML_DOCUMENT,
1624 crate::hard_limits::XML_DOCUMENT_BYTE_CEILING,
1625 |p| &mut p.max_xml_document_bytes,
1626 ),
1627 (
1628 resource_name::XML_PARSE_WORK_BYTES,
1629 crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
1630 |p| &mut p.max_xml_parse_work_bytes,
1631 ),
1632 (
1633 resource_name::ENCRYPTION_RECIPIENTS,
1634 crate::hard_limits::ENCRYPTION_RECIPIENT_CEILING,
1635 |p| &mut p.max_encryption_recipients,
1636 ),
1637 (
1638 resource_name::ENCRYPTION_METADATA_BYTES,
1639 crate::hard_limits::ENCRYPTION_METADATA_BYTE_CEILING,
1640 |p| &mut p.max_encryption_metadata_bytes,
1641 ),
1642 (
1643 resource_name::KEY_CANDIDATES,
1644 crate::hard_limits::KEY_CANDIDATE_CEILING,
1645 |p| &mut p.max_key_candidates,
1646 ),
1647 (
1648 resource_name::KEY_IMPORT_KDF_WORK,
1649 crate::hard_limits::KEY_IMPORT_KDF_WORK_CEILING as usize,
1650 |p| &mut p.max_key_import_kdf_work,
1651 ),
1652 (
1653 resource_name::KEY_IMPORT_KDF_MEMORY,
1654 crate::hard_limits::KEY_IMPORT_KDF_MEMORY_CEILING,
1655 |p| &mut p.max_key_import_kdf_memory_bytes,
1656 ),
1657 (
1658 resource_name::KEY_INFO_REFERENCE_DEPTH,
1659 crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING,
1660 |p| &mut p.max_key_info_reference_depth,
1661 ),
1662 (
1663 resource_name::BASE64_TRANSFORM_INPUT_BYTES,
1664 crate::hard_limits::BASE64_TRANSFORM_INPUT_BYTE_CEILING,
1665 |p| &mut p.max_base64_transform_input_bytes,
1666 ),
1667 (
1668 resource_name::BASE64_TRANSFORM_OUTPUT_BYTES,
1669 crate::hard_limits::BASE64_TRANSFORM_OUTPUT_BYTE_CEILING,
1670 |p| &mut p.max_base64_transform_output_bytes,
1671 ),
1672 (
1673 resource_name::XPATH_EXPRESSIONS,
1674 crate::hard_limits::XPATH_EXPRESSION_COUNT_CEILING,
1675 |p| &mut p.max_xpath_expressions,
1676 ),
1677 (
1678 resource_name::XPATH_EXPRESSION_BYTES,
1679 crate::hard_limits::XPATH_EXPRESSION_BYTE_CEILING,
1680 |p| &mut p.max_xpath_expression_bytes,
1681 ),
1682 (
1683 resource_name::XPATH_EXPRESSION_COMPLEXITY,
1684 crate::hard_limits::XPATH_EXPRESSION_COMPLEXITY_CEILING,
1685 |p| &mut p.max_xpath_expression_complexity,
1686 ),
1687 (
1688 resource_name::XPATH_CONTEXT_EVALUATIONS,
1689 crate::hard_limits::XPATH_CONTEXT_EVALUATION_CEILING,
1690 |p| &mut p.max_xpath_context_evaluations,
1691 ),
1692 (
1693 resource_name::XPATH_EVALUATION_WORK,
1694 crate::hard_limits::XPATH_EVALUATION_WORK_CEILING,
1695 |p| &mut p.max_xpath_evaluation_work,
1696 ),
1697 (
1698 resource_name::XPATH_MIRROR_STRING_BYTES,
1699 crate::hard_limits::XPATH_MIRROR_STRING_BYTE_CEILING,
1700 |p| &mut p.max_xpath_mirror_string_bytes,
1701 ),
1702 (
1703 resource_name::XPATH_STRING_WORK_BYTES,
1704 crate::hard_limits::XPATH_STRING_WORK_BYTE_CEILING,
1705 |p| &mut p.max_xpath_string_work_bytes,
1706 ),
1707 (
1708 resource_name::XPATH_NAMESPACE_BINDINGS,
1709 crate::hard_limits::XPATH_NAMESPACE_BINDING_CEILING,
1710 |p| &mut p.max_xpath_namespace_bindings,
1711 ),
1712 (
1713 resource_name::XPATH_NAMESPACE_BYTES,
1714 crate::hard_limits::XPATH_NAMESPACE_BYTE_CEILING,
1715 |p| &mut p.max_xpath_namespace_bytes,
1716 ),
1717 (
1718 resource_name::XPATH_FILTERS,
1719 crate::hard_limits::XPATH_FILTER_COUNT_CEILING,
1720 |p| &mut p.max_xpath_filters,
1721 ),
1722 (
1723 resource_name::NODE_SET_FILTER_WORK,
1724 crate::hard_limits::NODE_SET_FILTER_WORK_CEILING,
1725 |p| &mut p.max_node_set_filter_work,
1726 ),
1727 (
1728 resource_name::NODE_SET_ENTRIES,
1729 crate::hard_limits::NODE_SET_ENTRY_CEILING,
1730 |p| &mut p.max_node_set_entries,
1731 ),
1732 (
1733 resource_name::NODE_SET_OWNED_STRING_BYTES,
1734 crate::hard_limits::NODE_SET_OWNED_STRING_BYTE_CEILING,
1735 |p| &mut p.max_node_set_owned_string_bytes,
1736 ),
1737 (
1738 resource_name::NODE_SET_CUMULATIVE_OWNED_STRING_BYTES,
1739 crate::hard_limits::NODE_SET_CUMULATIVE_OWNED_STRING_BYTE_CEILING,
1740 |p| &mut p.max_node_set_cumulative_owned_string_bytes,
1741 ),
1742 ];
1743
1744 for &(resource, ceiling, field) in cases {
1745 let mut policy = ResourcePolicy::default();
1746 let actual = ceiling.saturating_add(1);
1747 *field(&mut policy) = actual;
1748 assert_eq!(
1749 policy.validate(),
1750 Err(PolicyViolation::ResourceLimit {
1751 resource,
1752 maximum: ceiling,
1753 actual,
1754 }),
1755 "wrong hard-ceiling validation for {resource}",
1756 );
1757 }
1758 }
1759
1760 #[test]
1761 fn resource_policy_accepts_zero_as_a_deny_all_ceiling() {
1762 let policy = ResourcePolicy {
1765 max_xml_nodes: 0,
1766 max_xml_depth: 0,
1767 max_xml_namespace_bindings: 0,
1768 max_references: 0,
1769 max_signatures: 0,
1770 max_transforms_per_reference: 0,
1771 max_xml_base_components: 0,
1772 max_xml_base_resolution_bytes: 0,
1773 max_canonicalized_bytes: 0,
1774 max_external_resource_bytes: 0,
1775 max_external_resource_total_bytes: 0,
1776 max_encryption_plaintext_bytes: 0,
1777 max_xml_document_bytes: 0,
1778 max_xml_parse_work_bytes: 0,
1779 max_encryption_recipients: 0,
1780 max_encryption_metadata_bytes: 0,
1781 max_key_candidates: 0,
1782 max_key_import_kdf_work: 0,
1783 max_key_import_kdf_memory_bytes: 0,
1784 max_key_info_reference_depth: 0,
1785 max_base64_transform_input_bytes: 0,
1786 max_base64_transform_output_bytes: 0,
1787 max_xpath_expressions: 0,
1788 max_xpath_expression_bytes: 0,
1789 max_xpath_expression_complexity: 0,
1790 max_xpath_context_evaluations: 0,
1791 max_xpath_evaluation_work: 0,
1792 max_xpath_mirror_string_bytes: 0,
1793 max_xpath_string_work_bytes: 0,
1794 max_xpath_namespace_bindings: 0,
1795 max_xpath_namespace_bytes: 0,
1796 max_xpath_filters: 0,
1797 max_node_set_filter_work: 0,
1798 max_node_set_entries: 0,
1799 max_node_set_owned_string_bytes: 0,
1800 max_node_set_cumulative_owned_string_bytes: 0,
1801 };
1802
1803 assert_eq!(policy.validate(), Ok(()));
1804 }
1805
1806 #[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
1807 #[test]
1808 fn node_set_policy_has_headroom_above_its_default() {
1809 let mut policy = ResourcePolicy::default();
1810 assert_eq!(policy.max_node_set_entries, 262_144);
1811 assert_eq!(policy.validate(), Ok(()));
1812
1813 policy.max_node_set_entries = 524_288;
1814 assert_eq!(policy.validate(), Ok(()));
1815
1816 policy.max_node_set_entries = 524_289;
1817 assert_eq!(
1818 policy.validate(),
1819 Err(PolicyViolation::ResourceLimit {
1820 resource: resource_name::NODE_SET_ENTRIES,
1821 maximum: 524_288,
1822 actual: 524_289,
1823 })
1824 );
1825 }
1826
1827 #[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
1828 #[test]
1829 fn rsa_key_policy_enforces_structure_range_and_explicit_relaxation() {
1830 let secure = RsaKeyPolicy::default();
1831 assert!(matches!(
1832 secure.validate_components("test", &[0x80; 128], &[1, 0, 1]),
1833 Err(PolicyViolation::KeySize {
1834 minimum_bits: 2048,
1835 maximum_bits: 8192,
1836 actual_bits: 1024,
1837 ..
1838 })
1839 ));
1840 let mut short_2048_width = [0_u8; 256];
1841 short_2048_width[0] = 1;
1842 assert!(matches!(
1843 secure.validate_components("test", &short_2048_width, &[1, 0, 1]),
1844 Err(PolicyViolation::KeySize {
1845 minimum_bits: 2048,
1846 actual_bits: 2041,
1847 ..
1848 })
1849 ));
1850 assert!(matches!(
1851 secure.validate_components("test", &[1; 1025], &[1, 0, 1]),
1852 Err(PolicyViolation::KeySize {
1853 actual_bits: 8193,
1854 ..
1855 })
1856 ));
1857 assert!(matches!(
1858 secure.validate_components("test", &[0x80; 256], &[2]),
1859 Err(PolicyViolation::InvalidKeyMaterial { .. })
1860 ));
1861 assert_eq!(
1862 secure.validate_components("test", &[0x80; 256], &[0x80, 0, 0, 1]),
1863 Ok(256),
1864 "normalized RSA components encode the exponent as unsigned bytes"
1865 );
1866
1867 let compatibility = RsaKeyPolicy {
1868 minimum_modulus_bits: 1024,
1869 };
1870 assert_eq!(
1871 compatibility.validate_components("test", &[0x80; 128], &[1, 0, 1]),
1872 Ok(128)
1873 );
1874 assert!(
1875 RsaKeyPolicy {
1876 minimum_modulus_bits: 2047,
1877 }
1878 .validate()
1879 .is_err()
1880 );
1881 }
1882
1883 #[cfg(feature = "xmldsig")]
1884 #[test]
1885 fn mandatory_x509_limits_report_the_nonzero_requirement() {
1886 let policy = KeyTrustPolicy {
1889 max_x509_chain_depth: 0,
1890 ..KeyTrustPolicy::default()
1891 };
1892
1893 let error = policy.validate().expect_err("zero depth must be rejected");
1894 assert!(
1895 error.to_string().contains("must be nonzero"),
1896 "unexpected lower-bound diagnostic: {error}"
1897 );
1898 }
1899
1900 #[cfg(feature = "xmldsig")]
1901 #[test]
1902 fn custom_extended_key_purposes_require_valid_oid_arcs() {
1903 let mut policy = KeyTrustPolicy::default();
1906 policy
1907 .allowed_extended_key_usages
1908 .insert(ExtendedKeyPurpose::Other(vec![1, 40, 7]));
1909
1910 assert!(matches!(
1911 policy.validate(),
1912 Err(PolicyViolation::KeyTrust {
1913 reason: "custom extended key purposes must contain valid OID arcs",
1914 })
1915 ));
1916 }
1917
1918 #[cfg(feature = "xmldsig")]
1919 #[test]
1920 fn crl_checking_requires_x509_chain_validation() {
1921 let policy = KeyTrustPolicy {
1924 check_crls: true,
1925 ..KeyTrustPolicy::default()
1926 };
1927
1928 assert!(matches!(
1929 policy.validate(),
1930 Err(PolicyViolation::KeyTrust {
1931 reason: "CRL checking requires X.509 chain validation"
1932 })
1933 ));
1934 }
1935
1936 #[cfg(feature = "xmldsig")]
1937 #[test]
1938 fn legacy_signature_algorithms_require_independent_policy_opt_ins() {
1939 let legacy = [
1940 SignatureAlgorithm::RsaSha1,
1941 SignatureAlgorithm::DsaSha1,
1942 SignatureAlgorithm::HmacSha1,
1943 SignatureAlgorithm::EcdsaSha1,
1944 ];
1945 let mut policy = VerificationPolicy::default();
1946
1947 for algorithm in legacy {
1948 assert!(matches!(
1949 policy.check_signature_algorithm(algorithm),
1950 Err(PolicyViolation::Algorithm { .. })
1951 ));
1952 policy
1953 .key_trust
1954 .allowed_legacy_signature_algorithms
1955 .insert(algorithm);
1956 assert_eq!(policy.check_signature_algorithm(algorithm), Ok(()));
1957 policy
1958 .key_trust
1959 .allowed_legacy_signature_algorithms
1960 .remove(&algorithm);
1961 }
1962 }
1963
1964 #[cfg(feature = "xmldsig")]
1965 #[test]
1966 fn dsa_key_policy_enforces_configured_minimum_and_hard_ceiling() {
1967 let policy = DsaKeyPolicy::default();
1968
1969 assert!(matches!(
1970 policy.validate_modulus_bits(1024),
1971 Err(PolicyViolation::KeySize {
1972 key_type: "DSA",
1973 minimum_bits: 2048,
1974 maximum_bits: 3072,
1975 actual_bits: 1024,
1976 ..
1977 })
1978 ));
1979 assert_eq!(policy.validate_modulus_bits(2048), Ok(()));
1980 assert!(matches!(
1981 policy.validate_modulus_bits(4096),
1982 Err(PolicyViolation::KeySize {
1983 key_type: "DSA",
1984 minimum_bits: 2048,
1985 maximum_bits: 3072,
1986 actual_bits: 4096,
1987 ..
1988 })
1989 ));
1990 }
1991
1992 #[cfg(feature = "xmldsig")]
1993 #[test]
1994 fn hmac_output_policy_cannot_weaken_the_xmldsig_floor() {
1995 let compatibility = HmacPolicy {
1998 minimum_key_bits: 40,
1999 minimum_output_bits: 40,
2000 };
2001
2002 assert_eq!(
2003 compatibility.validate_output(SignatureAlgorithm::HmacSha1, 80),
2004 Ok(())
2005 );
2006 assert!(matches!(
2007 compatibility.validate_output(SignatureAlgorithm::HmacSha1, 72),
2008 Err(PolicyViolation::HmacOutputLength { minimum: 80, .. })
2009 ));
2010 assert!(matches!(
2011 compatibility.validate_output(SignatureAlgorithm::HmacSha256, 120),
2012 Err(PolicyViolation::HmacOutputLength { minimum: 128, .. })
2013 ));
2014 }
2015
2016 #[cfg(feature = "xmldsig")]
2017 #[test]
2018 fn documented_xmldsig_limits_match_hard_limits() {
2019 let docs = include_str!("../docs/xmldsig.md");
2022 let mib = 1024 * 1024;
2023 assert!(docs.contains(&format!(
2024 "Individual resources are limited to {} MiB",
2025 crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING / mib
2026 )));
2027 assert!(docs.contains(&format!(
2028 "complete map to {} MiB",
2029 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING / mib
2030 )));
2031 assert!(docs.contains(&format!(
2032 "ceilings are {} components and {} MiB per operation",
2033 crate::hard_limits::XML_BASE_COMPONENT_CEILING,
2034 crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING / mib
2035 )));
2036 }
2037}