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 = "xmldsig")]
20mod key_establishment;
21#[cfg(feature = "xmldsig")]
22pub use key_establishment::{
23 KemContentAuthentication, KeyAgreementAlgorithm, KeyDerivationAlgorithm, KeyEstablishmentPolicy,
24};
25
26#[cfg(feature = "xmldsig")]
27mod profiles;
28#[cfg(feature = "xmldsig")]
29pub use profiles::{
30 CompiledPolicies, PolicyCompileContext, PolicyCompiler, PolicyProfile, PolicySet,
31 PolicyValidity, SignatureConstraints,
32};
33
34#[cfg(feature = "xmldsig")]
37#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
38pub enum RsaPssPermission {
39 #[default]
41 ExactOnly,
42 SecureSupported,
44 AllSupported,
46}
47
48#[cfg(feature = "xmldsig")]
49impl RsaPssPermission {
50 fn permits(self, algorithm: SignatureAlgorithm) -> bool {
51 matches!(algorithm, SignatureAlgorithm::RsaPss(_))
52 && (self == Self::AllSupported
53 || (self == Self::SecureSupported && algorithm.signing_allowed()))
54 }
55}
56
57pub(crate) mod resource_name {
62 pub const XML_NODES: &str = "XML nodes";
63 pub const XML_DEPTH: &str = "XML element depth";
64 pub const XML_NAMESPACE_BINDINGS: &str = "XML namespace bindings";
65 pub const SIGNATURE_REFERENCES: &str = "signature references";
66 #[cfg(feature = "xmlenc")]
67 pub const ENCRYPTION_REFERENCES: &str = "encryption key references";
68 pub const VERIFICATION_SIGNATURES: &str = "verification signatures";
69 pub const REFERENCE_TRANSFORMS: &str = "reference transforms";
70 pub const XML_BASE_COMPONENTS: &str = "XML Base components";
71 pub const XML_BASE_RESOLUTION_BYTES: &str = "XML Base resolution bytes";
72 pub const CANONICALIZED_BYTES: &str = "canonicalized bytes";
73 pub const EXTERNAL_RESOURCE_BYTES: &str = "external resource bytes";
74 pub const AGGREGATE_EXTERNAL_RESOURCE_BYTES: &str = "aggregate external resource bytes";
75 pub const ENCRYPTION_PLAINTEXT_BYTES: &str = "encryption plaintext bytes";
76 #[cfg(feature = "xmlenc")]
77 pub const AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES: &str =
78 "aggregate encryption CipherValue bytes";
79 pub const XML_DOCUMENT: &str = "XML document";
80 pub const XML_PARSE_WORK_BYTES: &str = "cumulative XML parse-work bytes";
81 pub const ENCRYPTION_RECIPIENTS: &str = "encryption recipients";
82 pub const ENCRYPTION_METADATA_BYTES: &str = "encryption metadata bytes";
83 pub const KEY_CANDIDATES: &str = "key candidates";
84 pub const KEY_IMPORT_KDF_WORK: &str = "key import KDF work";
85 pub const KEY_IMPORT_KDF_MEMORY: &str = "key import KDF memory bytes";
86 pub const X509_WORK: &str = "X.509 work";
87 pub const X509_WORKSPACE_BYTES: &str = "X.509 workspace bytes";
88 #[cfg(feature = "xmldsig")]
89 pub const KEY_ESTABLISHMENT_HASH_BLOCKS: &str = "key establishment hash blocks";
90 #[cfg(feature = "xmldsig")]
91 pub const KEY_ESTABLISHMENT_OWNED_BYTES: &str = "key establishment owned bytes";
92 #[cfg(feature = "xmldsig")]
93 pub const DH_MODULUS_BITS: &str = "DH modulus bits";
94 #[cfg(feature = "xmldsig")]
95 pub const DH_SUBGROUP_BITS: &str = "DH subgroup bits";
96 #[cfg(feature = "xmldsig")]
97 pub const KEY_ESTABLISHMENT_MODULAR_WORK: &str = "key establishment modular work";
98 pub const KEY_INFO_REFERENCE_DEPTH: &str = "KeyInfoReference depth";
99 pub const BASE64_TRANSFORM_INPUT_BYTES: &str = "Base64 transform input bytes";
100 pub const BASE64_TRANSFORM_OUTPUT_BYTES: &str = "Base64 transform output bytes";
101 pub const XPATH_EXPRESSIONS: &str = "XPath expressions";
102 pub const XPATH_EXPRESSION_BYTES: &str = "XPath expression bytes";
103 pub const XPATH_EXPRESSION_COMPLEXITY: &str = "XPath expression complexity";
104 pub const XPATH_CONTEXT_EVALUATIONS: &str = "XPath context evaluations";
105 pub const XPATH_EVALUATION_WORK: &str = "XPath evaluation work";
106 pub const XPATH_MIRROR_STRING_BYTES: &str = "XPath mirror string bytes";
107 pub const XPATH_STRING_WORK_BYTES: &str = "XPath string-processing work bytes";
108 pub const XPATH_NAMESPACE_BINDINGS: &str = "XPath namespace bindings";
109 pub const XPATH_NAMESPACE_BYTES: &str = "XPath namespace bytes";
110 pub const XPATH_FILTERS: &str = "XPath filters";
111 pub const NODE_SET_FILTER_WORK: &str = "node-set filter work";
112 pub const OPC_WORKSPACE_BYTES: &str = "OPC workspace bytes";
113 pub const OPC_PARAMETER_BYTES: &str = "OPC parameter bytes";
114 pub const XSLT_WORK: &str = "XSLT work";
115 pub const XSLT_WORKSPACE_BYTES: &str = "XSLT workspace bytes";
116 pub const XSLT_OUTPUT_BYTES: &str = "XSLT output bytes";
117 pub const XSLT_RECURSION_DEPTH: &str = "XSLT recursion depth";
118 pub const NODE_SET_ENTRIES: &str = "node-set entries";
119 pub const NODE_SET_OWNED_STRING_BYTES: &str = "node-set owned string bytes";
120 pub const NODE_SET_CUMULATIVE_OWNED_STRING_BYTES: &str =
121 "cumulative node-set owned string bytes";
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
126#[non_exhaustive]
127pub enum PolicyViolation {
128 #[error("policy compilation failed: {reason}")]
130 Configuration {
131 reason: &'static str,
133 },
134 #[cfg(feature = "xmldsig")]
136 #[error("Manifest reference processing is disabled by policy")]
137 ManifestProcessingDisabled,
138 #[cfg(feature = "xmldsig")]
140 #[error("{profile:?} signature profile rejects document: {reason}")]
141 SignatureProfile {
142 profile: SignatureConstraints,
144 reason: &'static str,
146 },
147 #[error("policy authorization is outside its validity interval")]
149 AuthorizationExpired,
150 #[error("{operation} policy rejects algorithm {algorithm}")]
152 Algorithm {
153 operation: &'static str,
155 algorithm: String,
157 },
158 #[cfg(feature = "xmldsig")]
160 #[error("HMAC output length must be between {minimum} and {maximum} bits: got {actual}")]
161 HmacOutputLength {
162 minimum: usize,
164 maximum: usize,
166 actual: usize,
168 },
169 #[error("{resource} exceeds policy maximum {maximum}: got {actual}")]
171 ResourceLimit {
172 resource: &'static str,
174 maximum: usize,
176 actual: usize,
178 },
179 #[error("{resource} exceeds policy maximum {maximum}")]
181 ResourceLimitExceeded {
182 resource: &'static str,
184 maximum: usize,
186 },
187 #[error("key import KDF iterations must be between 1 and {maximum}")]
189 KdfIterationsOutsideLimit {
190 maximum: usize,
192 },
193 #[error("{resource} has invalid policy limit {actual}: {requirement}")]
195 InvalidResourceLimit {
196 resource: &'static str,
198 requirement: &'static str,
200 actual: usize,
202 },
203 #[error("key/trust policy rejected the operation: {reason}")]
205 KeyTrust {
206 reason: &'static str,
208 },
209 #[error("XML input policy rejected the operation: {reason}")]
211 XmlInput {
212 reason: &'static str,
214 },
215 #[error("{operation} URI policy rejected the operation: {reason}")]
217 Uri {
218 operation: &'static str,
220 reason: &'static str,
222 },
223 #[error(
225 "{operation} policy requires {key_type} keys between {minimum_bits} and {maximum_bits} bits: got {actual_bits}"
226 )]
227 KeySize {
228 operation: &'static str,
230 key_type: &'static str,
232 minimum_bits: usize,
234 maximum_bits: usize,
236 actual_bits: usize,
238 },
239 #[error("{operation} policy rejects invalid {key_type} key material: {reason}")]
241 InvalidKeyMaterial {
242 operation: &'static str,
244 key_type: &'static str,
246 reason: &'static str,
248 },
249}
250
251#[cfg(feature = "xmldsig")]
253#[derive(Debug, Clone, Copy, PartialEq, Eq)]
254pub struct HmacPolicy {
255 pub minimum_key_bits: usize,
257 pub minimum_output_bits: usize,
259}
260
261#[cfg(feature = "xmldsig")]
262impl Default for HmacPolicy {
263 fn default() -> Self {
264 Self {
265 minimum_key_bits: 128,
266 minimum_output_bits: 128,
267 }
268 }
269}
270
271#[cfg(feature = "xmldsig")]
272impl HmacPolicy {
273 pub(crate) fn validate(self) -> Result<(), PolicyViolation> {
274 if self.minimum_key_bits == 0 || self.minimum_output_bits == 0 {
275 return Err(PolicyViolation::KeyTrust {
276 reason: "HMAC minimum key and output lengths must be nonzero",
277 });
278 }
279 Ok(())
280 }
281
282 pub(crate) fn validate_key_bytes(self, key_bytes: usize) -> Result<(), PolicyViolation> {
283 self.validate_key_bits(key_bytes.saturating_mul(8))
284 }
285
286 pub(crate) fn validate_key_bits(self, key_bits: usize) -> Result<(), PolicyViolation> {
287 if key_bits < self.minimum_key_bits {
288 return Err(PolicyViolation::InvalidKeyMaterial {
289 operation: "HMAC",
290 key_type: "symmetric",
291 reason: "secret is shorter than the configured minimum",
292 });
293 }
294 Ok(())
295 }
296
297 pub(crate) fn validate_output(
298 self,
299 algorithm: SignatureAlgorithm,
300 selected_bits: usize,
301 ) -> Result<(), PolicyViolation> {
302 let maximum = algorithm
303 .hmac_output_bits()
304 .ok_or_else(|| PolicyViolation::Algorithm {
305 operation: "HMAC",
306 algorithm: algorithm.uri().to_owned(),
307 })?;
308 let minimum = self.minimum_output_bits.max(80).max(maximum / 2);
313 if selected_bits < minimum || selected_bits > maximum {
314 return Err(PolicyViolation::HmacOutputLength {
315 minimum,
316 maximum,
317 actual: selected_bits,
318 });
319 }
320 Ok(())
321 }
322}
323
324#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
326#[derive(Debug, Clone, Copy, PartialEq, Eq)]
327pub struct RsaKeyPolicy {
328 pub minimum_modulus_bits: usize,
330}
331
332#[cfg(feature = "xmldsig")]
334#[derive(Debug, Clone, Copy, PartialEq, Eq)]
335pub struct DsaKeyPolicy {
336 pub minimum_modulus_bits: usize,
338}
339
340#[cfg(feature = "xmldsig")]
341impl Default for DsaKeyPolicy {
342 fn default() -> Self {
343 Self {
344 minimum_modulus_bits: 2048,
345 }
346 }
347}
348
349#[cfg(feature = "xmldsig")]
350impl DsaKeyPolicy {
351 pub fn validate(&self) -> Result<(), PolicyViolation> {
353 validate_modulus_minimum(
354 "minimum DSA modulus bits",
355 self.minimum_modulus_bits,
356 64,
357 "minimum must be a nonzero multiple of 64 bits",
358 crate::hard_limits::DSA_MODULUS_BIT_CEILING,
359 )
360 }
361
362 pub(crate) fn validate_modulus_bits(&self, actual_bits: usize) -> Result<(), PolicyViolation> {
363 self.validate()?;
364 if !(self.minimum_modulus_bits..=crate::hard_limits::DSA_MODULUS_BIT_CEILING)
365 .contains(&actual_bits)
366 {
367 return Err(PolicyViolation::KeySize {
368 operation: "verification",
369 key_type: "DSA",
370 minimum_bits: self.minimum_modulus_bits,
371 maximum_bits: crate::hard_limits::DSA_MODULUS_BIT_CEILING,
372 actual_bits,
373 });
374 }
375 Ok(())
376 }
377}
378
379#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
380impl Default for RsaKeyPolicy {
381 fn default() -> Self {
382 Self {
383 minimum_modulus_bits: 2048,
384 }
385 }
386}
387
388#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
389impl RsaKeyPolicy {
390 pub fn validate(&self) -> Result<(), PolicyViolation> {
392 validate_modulus_minimum(
393 "minimum RSA modulus bits",
394 self.minimum_modulus_bits,
395 8,
396 "minimum must be a nonzero whole-byte width",
397 crate::hard_limits::RSA_MODULUS_BIT_CEILING,
398 )
399 }
400
401 pub(crate) fn validate_components(
402 &self,
403 operation: &'static str,
404 modulus: &[u8],
405 exponent: &[u8],
406 ) -> Result<usize, PolicyViolation> {
407 self.validate()?;
408 let modulus = modulus
409 .iter()
410 .position(|byte| *byte != 0)
411 .map(|start| &modulus[start..])
412 .ok_or(PolicyViolation::InvalidKeyMaterial {
413 operation,
414 key_type: "RSA",
415 reason: "modulus is zero",
416 })?;
417 let modulus_bits = modulus
418 .len()
419 .checked_mul(8)
420 .and_then(|width| width.checked_sub(modulus[0].leading_zeros() as usize))
421 .ok_or(PolicyViolation::InvalidKeyMaterial {
422 operation,
423 key_type: "RSA",
424 reason: "modulus width overflows",
425 })?;
426 let exponent = if exponent.is_empty() || exponent.len() > 8 {
427 None
428 } else {
429 let mut bytes = [0_u8; 8];
430 bytes[8 - exponent.len()..].copy_from_slice(exponent);
431 Some(u64::from_be_bytes(bytes))
432 };
433 self.validate_public_metadata(operation, modulus_bits, exponent)
434 }
435
436 pub(crate) fn validate_public_metadata(
437 &self,
438 operation: &'static str,
439 modulus_bits: usize,
440 exponent: Option<u64>,
441 ) -> Result<usize, PolicyViolation> {
442 self.validate()?;
443 if modulus_bits == 0 {
444 return Err(PolicyViolation::InvalidKeyMaterial {
445 operation,
446 key_type: "RSA",
447 reason: "modulus is zero",
448 });
449 }
450 if !(self.minimum_modulus_bits..=crate::hard_limits::RSA_MODULUS_BIT_CEILING)
451 .contains(&modulus_bits)
452 {
453 return Err(PolicyViolation::KeySize {
454 operation,
455 key_type: "RSA",
456 minimum_bits: self.minimum_modulus_bits,
457 maximum_bits: crate::hard_limits::RSA_MODULUS_BIT_CEILING,
458 actual_bits: modulus_bits,
459 });
460 }
461 let exponent = exponent.ok_or(PolicyViolation::InvalidKeyMaterial {
462 operation,
463 key_type: "RSA",
464 reason: "public exponent has invalid encoding",
465 })?;
466 if !(3..=((1_u64 << 33) - 1)).contains(&exponent) || exponent % 2 == 0 {
467 return Err(PolicyViolation::InvalidKeyMaterial {
468 operation,
469 key_type: "RSA",
470 reason: "public exponent is outside the supported odd range",
471 });
472 }
473 Ok(modulus_bits.div_ceil(8))
474 }
475}
476
477#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
478fn validate_modulus_minimum(
479 resource: &'static str,
480 minimum_bits: usize,
481 alignment_bits: usize,
482 requirement: &'static str,
483 ceiling: usize,
484) -> Result<(), PolicyViolation> {
485 if minimum_bits == 0 || !minimum_bits.is_multiple_of(alignment_bits) {
486 return Err(PolicyViolation::InvalidResourceLimit {
487 resource,
488 requirement,
489 actual: minimum_bits,
490 });
491 }
492 ResourcePolicy::within(resource, minimum_bits, ceiling)
493}
494
495#[derive(Debug, Clone, PartialEq, Eq)]
497pub struct ResourcePolicy {
498 pub max_xml_nodes: usize,
500 pub max_xml_depth: usize,
502 pub max_xml_namespace_bindings: usize,
505 pub max_references: usize,
507 pub max_signatures: usize,
509 pub max_transforms_per_reference: usize,
511 pub max_xml_base_components: usize,
513 pub max_xml_base_resolution_bytes: usize,
515 pub max_canonicalized_bytes: usize,
517 pub max_external_resource_bytes: usize,
519 pub max_external_resource_total_bytes: usize,
521 pub max_encryption_plaintext_bytes: usize,
523 pub max_xml_document_bytes: usize,
525 pub max_xml_parse_work_bytes: usize,
527 pub max_encryption_recipients: usize,
529 pub max_encryption_metadata_bytes: usize,
531 pub max_key_candidates: usize,
534 pub max_x509_work: usize,
537 pub max_x509_workspace_bytes: usize,
540 pub max_key_import_kdf_work: usize,
543 pub max_key_import_kdf_memory_bytes: usize,
545 pub max_key_info_reference_depth: usize,
547 pub max_base64_transform_input_bytes: usize,
549 pub max_base64_transform_output_bytes: usize,
551 pub max_xpath_expressions: usize,
553 pub max_xpath_expression_bytes: usize,
555 pub max_xpath_expression_complexity: usize,
557 pub max_xpath_context_evaluations: usize,
559 pub max_xpath_evaluation_work: usize,
561 pub max_xpath_mirror_string_bytes: usize,
563 pub max_xpath_string_work_bytes: usize,
565 pub max_xpath_namespace_bindings: usize,
567 pub max_xpath_namespace_bytes: usize,
569 pub max_xpath_filters: usize,
571 pub max_node_set_filter_work: usize,
573 pub max_node_set_entries: usize,
577 pub max_node_set_owned_string_bytes: usize,
579 pub max_node_set_cumulative_owned_string_bytes: usize,
581 pub max_opc_parameter_bytes: usize,
583 pub max_opc_workspace_bytes: usize,
585 pub max_xslt_work: usize,
590 pub max_xslt_workspace_bytes: usize,
592 pub max_xslt_output_bytes: usize,
594 pub max_xslt_recursion_depth: usize,
596}
597
598impl Default for ResourcePolicy {
599 fn default() -> Self {
600 Self {
601 max_xml_nodes: crate::hard_limits::XML_DOCUMENT_NODE_CEILING as usize,
602 max_xml_depth: crate::hard_limits::XML_DOCUMENT_DEPTH_CEILING,
603 max_xml_namespace_bindings: crate::hard_limits::XML_NAMESPACE_BINDING_CEILING,
604 max_references: crate::hard_limits::SIGNATURE_REFERENCE_CEILING,
605 max_signatures: crate::hard_limits::VERIFICATION_SIGNATURE_CEILING,
606 max_transforms_per_reference: crate::hard_limits::REFERENCE_TRANSFORM_CEILING,
607 max_xml_base_components: crate::hard_limits::XML_BASE_COMPONENT_CEILING,
608 max_xml_base_resolution_bytes: crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING,
609 max_canonicalized_bytes: crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING,
610 max_external_resource_bytes: crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING,
611 max_external_resource_total_bytes:
612 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING,
613 max_encryption_plaintext_bytes: crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING,
614 max_xml_document_bytes: crate::hard_limits::XML_DOCUMENT_BYTE_CEILING,
615 max_xml_parse_work_bytes: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
616 max_encryption_recipients: crate::hard_limits::ENCRYPTION_RECIPIENT_CEILING,
617 max_encryption_metadata_bytes: crate::hard_limits::ENCRYPTION_METADATA_BYTE_CEILING,
618 max_key_candidates: crate::hard_limits::KEY_CANDIDATE_CEILING,
619 max_x509_work: crate::hard_limits::X509_WORK_CEILING,
620 max_x509_workspace_bytes: crate::hard_limits::X509_WORKSPACE_BYTE_CEILING,
621 max_key_import_kdf_work: crate::hard_limits::KEY_IMPORT_KDF_WORK_CEILING as usize,
622 max_key_import_kdf_memory_bytes: crate::hard_limits::KEY_IMPORT_KDF_MEMORY_CEILING,
623 max_key_info_reference_depth: crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING,
624 max_base64_transform_input_bytes:
625 crate::hard_limits::BASE64_TRANSFORM_INPUT_BYTE_CEILING,
626 max_base64_transform_output_bytes:
627 crate::hard_limits::BASE64_TRANSFORM_OUTPUT_BYTE_CEILING,
628 max_xpath_expressions: crate::hard_limits::XPATH_EXPRESSION_COUNT_CEILING,
629 max_xpath_expression_bytes: crate::hard_limits::XPATH_EXPRESSION_BYTE_CEILING,
630 max_xpath_expression_complexity:
631 crate::hard_limits::XPATH_EXPRESSION_COMPLEXITY_CEILING,
632 max_xpath_context_evaluations: crate::hard_limits::XPATH_CONTEXT_EVALUATION_CEILING,
633 max_xpath_evaluation_work: crate::hard_limits::XPATH_EVALUATION_WORK_CEILING,
634 max_xpath_mirror_string_bytes: crate::hard_limits::XPATH_MIRROR_STRING_BYTE_CEILING,
635 max_xpath_string_work_bytes: crate::hard_limits::XPATH_STRING_WORK_BYTE_CEILING,
636 max_xpath_namespace_bindings: crate::hard_limits::XPATH_NAMESPACE_BINDING_CEILING,
637 max_xpath_namespace_bytes: crate::hard_limits::XPATH_NAMESPACE_BYTE_CEILING,
638 max_xpath_filters: crate::hard_limits::XPATH_FILTER_COUNT_CEILING,
639 max_node_set_filter_work: crate::hard_limits::NODE_SET_FILTER_WORK_CEILING,
640 max_node_set_entries: 262_144,
641 max_node_set_owned_string_bytes: crate::hard_limits::NODE_SET_OWNED_STRING_BYTE_CEILING,
642 max_node_set_cumulative_owned_string_bytes:
643 crate::hard_limits::NODE_SET_CUMULATIVE_OWNED_STRING_BYTE_CEILING,
644 max_opc_workspace_bytes: crate::hard_limits::OPC_WORKSPACE_BYTE_CEILING,
645 max_opc_parameter_bytes: crate::hard_limits::OPC_PARAMETER_BYTE_CEILING,
646 max_xslt_work: crate::hard_limits::XSLT_WORK_CEILING,
647 max_xslt_workspace_bytes: crate::hard_limits::XSLT_WORKSPACE_BYTE_CEILING,
648 max_xslt_output_bytes: crate::hard_limits::XSLT_OUTPUT_BYTE_CEILING,
649 max_xslt_recursion_depth: crate::hard_limits::XSLT_RECURSION_DEPTH_CEILING,
650 }
651 }
652}
653
654impl ResourcePolicy {
655 pub fn validate(&self) -> Result<(), PolicyViolation> {
657 Self::within(
658 resource_name::XML_NODES,
659 self.max_xml_nodes,
660 crate::hard_limits::XML_DOCUMENT_NODE_CEILING as usize,
661 )?;
662 Self::within(
663 resource_name::XML_DEPTH,
664 self.max_xml_depth,
665 crate::hard_limits::XML_DOCUMENT_DEPTH_CEILING,
666 )?;
667 Self::within(
668 resource_name::XML_NAMESPACE_BINDINGS,
669 self.max_xml_namespace_bindings,
670 crate::hard_limits::XML_NAMESPACE_BINDING_CEILING,
671 )?;
672 Self::within(
673 resource_name::CANONICALIZED_BYTES,
674 self.max_canonicalized_bytes,
675 crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING,
676 )?;
677 Self::within(
678 resource_name::SIGNATURE_REFERENCES,
679 self.max_references,
680 crate::hard_limits::SIGNATURE_REFERENCE_CEILING,
681 )?;
682 Self::within(
683 resource_name::VERIFICATION_SIGNATURES,
684 self.max_signatures,
685 crate::hard_limits::VERIFICATION_SIGNATURE_CEILING,
686 )?;
687 Self::within(
688 resource_name::REFERENCE_TRANSFORMS,
689 self.max_transforms_per_reference,
690 crate::hard_limits::REFERENCE_TRANSFORM_CEILING,
691 )?;
692 Self::within(
693 resource_name::XML_BASE_COMPONENTS,
694 self.max_xml_base_components,
695 crate::hard_limits::XML_BASE_COMPONENT_CEILING,
696 )?;
697 Self::within(
698 resource_name::XML_BASE_RESOLUTION_BYTES,
699 self.max_xml_base_resolution_bytes,
700 crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING,
701 )?;
702 Self::within(
703 resource_name::XML_DOCUMENT,
704 self.max_xml_document_bytes,
705 crate::hard_limits::XML_DOCUMENT_BYTE_CEILING,
706 )?;
707 Self::within(
708 resource_name::XML_PARSE_WORK_BYTES,
709 self.max_xml_parse_work_bytes,
710 crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
711 )?;
712 Self::within(
713 resource_name::EXTERNAL_RESOURCE_BYTES,
714 self.max_external_resource_bytes,
715 crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING,
716 )?;
717 Self::within(
718 resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
719 self.max_external_resource_total_bytes,
720 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING,
721 )?;
722 Self::within(
723 resource_name::ENCRYPTION_PLAINTEXT_BYTES,
724 self.max_encryption_plaintext_bytes,
725 crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING,
726 )?;
727 Self::within(
728 resource_name::ENCRYPTION_RECIPIENTS,
729 self.max_encryption_recipients,
730 crate::hard_limits::ENCRYPTION_RECIPIENT_CEILING,
731 )?;
732 Self::within(
733 resource_name::ENCRYPTION_METADATA_BYTES,
734 self.max_encryption_metadata_bytes,
735 crate::hard_limits::ENCRYPTION_METADATA_BYTE_CEILING,
736 )?;
737 for (resource, selected, ceiling) in [
738 (
739 resource_name::X509_WORK,
740 self.max_x509_work,
741 crate::hard_limits::X509_WORK_CEILING,
742 ),
743 (
744 resource_name::X509_WORKSPACE_BYTES,
745 self.max_x509_workspace_bytes,
746 crate::hard_limits::X509_WORKSPACE_BYTE_CEILING,
747 ),
748 (
749 resource_name::KEY_CANDIDATES,
750 self.max_key_candidates,
751 crate::hard_limits::KEY_CANDIDATE_CEILING,
752 ),
753 (
754 resource_name::KEY_IMPORT_KDF_WORK,
755 self.max_key_import_kdf_work,
756 crate::hard_limits::KEY_IMPORT_KDF_WORK_CEILING as usize,
757 ),
758 (
759 resource_name::KEY_IMPORT_KDF_MEMORY,
760 self.max_key_import_kdf_memory_bytes,
761 crate::hard_limits::KEY_IMPORT_KDF_MEMORY_CEILING,
762 ),
763 (
764 resource_name::KEY_INFO_REFERENCE_DEPTH,
765 self.max_key_info_reference_depth,
766 crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING,
767 ),
768 (
769 resource_name::BASE64_TRANSFORM_INPUT_BYTES,
770 self.max_base64_transform_input_bytes,
771 crate::hard_limits::BASE64_TRANSFORM_INPUT_BYTE_CEILING,
772 ),
773 (
774 resource_name::BASE64_TRANSFORM_OUTPUT_BYTES,
775 self.max_base64_transform_output_bytes,
776 crate::hard_limits::BASE64_TRANSFORM_OUTPUT_BYTE_CEILING,
777 ),
778 (
779 resource_name::XPATH_EXPRESSIONS,
780 self.max_xpath_expressions,
781 crate::hard_limits::XPATH_EXPRESSION_COUNT_CEILING,
782 ),
783 (
784 resource_name::XPATH_EXPRESSION_BYTES,
785 self.max_xpath_expression_bytes,
786 crate::hard_limits::XPATH_EXPRESSION_BYTE_CEILING,
787 ),
788 (
789 resource_name::XPATH_EXPRESSION_COMPLEXITY,
790 self.max_xpath_expression_complexity,
791 crate::hard_limits::XPATH_EXPRESSION_COMPLEXITY_CEILING,
792 ),
793 (
794 resource_name::XPATH_CONTEXT_EVALUATIONS,
795 self.max_xpath_context_evaluations,
796 crate::hard_limits::XPATH_CONTEXT_EVALUATION_CEILING,
797 ),
798 (
799 resource_name::XPATH_EVALUATION_WORK,
800 self.max_xpath_evaluation_work,
801 crate::hard_limits::XPATH_EVALUATION_WORK_CEILING,
802 ),
803 (
804 resource_name::XPATH_MIRROR_STRING_BYTES,
805 self.max_xpath_mirror_string_bytes,
806 crate::hard_limits::XPATH_MIRROR_STRING_BYTE_CEILING,
807 ),
808 (
809 resource_name::XPATH_STRING_WORK_BYTES,
810 self.max_xpath_string_work_bytes,
811 crate::hard_limits::XPATH_STRING_WORK_BYTE_CEILING,
812 ),
813 (
814 resource_name::XPATH_NAMESPACE_BINDINGS,
815 self.max_xpath_namespace_bindings,
816 crate::hard_limits::XPATH_NAMESPACE_BINDING_CEILING,
817 ),
818 (
819 resource_name::XPATH_NAMESPACE_BYTES,
820 self.max_xpath_namespace_bytes,
821 crate::hard_limits::XPATH_NAMESPACE_BYTE_CEILING,
822 ),
823 (
824 resource_name::XPATH_FILTERS,
825 self.max_xpath_filters,
826 crate::hard_limits::XPATH_FILTER_COUNT_CEILING,
827 ),
828 (
829 resource_name::NODE_SET_FILTER_WORK,
830 self.max_node_set_filter_work,
831 crate::hard_limits::NODE_SET_FILTER_WORK_CEILING,
832 ),
833 (
834 resource_name::NODE_SET_ENTRIES,
835 self.max_node_set_entries,
836 crate::hard_limits::NODE_SET_ENTRY_CEILING,
837 ),
838 (
839 resource_name::NODE_SET_OWNED_STRING_BYTES,
840 self.max_node_set_owned_string_bytes,
841 crate::hard_limits::NODE_SET_OWNED_STRING_BYTE_CEILING,
842 ),
843 (
844 resource_name::NODE_SET_CUMULATIVE_OWNED_STRING_BYTES,
845 self.max_node_set_cumulative_owned_string_bytes,
846 crate::hard_limits::NODE_SET_CUMULATIVE_OWNED_STRING_BYTE_CEILING,
847 ),
848 ] {
849 Self::within(resource, selected, ceiling)?;
850 }
851 Self::within(
852 resource_name::OPC_WORKSPACE_BYTES,
853 self.max_opc_workspace_bytes,
854 crate::hard_limits::OPC_WORKSPACE_BYTE_CEILING,
855 )?;
856 Self::within(
857 resource_name::OPC_PARAMETER_BYTES,
858 self.max_opc_parameter_bytes,
859 crate::hard_limits::OPC_PARAMETER_BYTE_CEILING,
860 )?;
861 for (resource, selected, ceiling) in [
862 (
863 resource_name::XSLT_WORK,
864 self.max_xslt_work,
865 crate::hard_limits::XSLT_WORK_CEILING,
866 ),
867 (
868 resource_name::XSLT_WORKSPACE_BYTES,
869 self.max_xslt_workspace_bytes,
870 crate::hard_limits::XSLT_WORKSPACE_BYTE_CEILING,
871 ),
872 (
873 resource_name::XSLT_OUTPUT_BYTES,
874 self.max_xslt_output_bytes,
875 crate::hard_limits::XSLT_OUTPUT_BYTE_CEILING,
876 ),
877 (
878 resource_name::XSLT_RECURSION_DEPTH,
879 self.max_xslt_recursion_depth,
880 crate::hard_limits::XSLT_RECURSION_DEPTH_CEILING,
881 ),
882 ] {
883 Self::within(resource, selected, ceiling)?;
884 }
885 Ok(())
886 }
887
888 pub(crate) fn validate_xml_document_len(&self, actual: usize) -> Result<(), PolicyViolation> {
889 if actual > self.max_xml_document_bytes {
890 return Err(PolicyViolation::ResourceLimit {
891 resource: resource_name::XML_DOCUMENT,
892 maximum: self.max_xml_document_bytes,
893 actual,
894 });
895 }
896 Ok(())
897 }
898
899 #[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
900 pub fn validate_key_candidates(&self, actual: usize) -> Result<(), PolicyViolation> {
902 if actual > self.max_key_candidates {
903 return Err(PolicyViolation::ResourceLimit {
904 resource: resource_name::KEY_CANDIDATES,
905 maximum: self.max_key_candidates,
906 actual,
907 });
908 }
909 Ok(())
910 }
911
912 #[cfg(feature = "xmldsig")]
914 pub fn validate_key_info_reference_depth(&self, actual: usize) -> Result<(), PolicyViolation> {
915 if actual > self.max_key_info_reference_depth {
916 return Err(PolicyViolation::ResourceLimit {
917 resource: resource_name::KEY_INFO_REFERENCE_DEPTH,
918 maximum: self.max_key_info_reference_depth,
919 actual,
920 });
921 }
922 Ok(())
923 }
924
925 pub(crate) fn effective_xml_nodes(&self) -> u32 {
926 u32::try_from(self.max_xml_nodes)
927 .unwrap_or(crate::hard_limits::XML_DOCUMENT_NODE_CEILING)
928 .min(crate::hard_limits::XML_DOCUMENT_NODE_CEILING)
929 }
930
931 #[cfg(feature = "xmldsig")]
932 pub(crate) fn effective_canonicalized_bytes(&self) -> usize {
933 self.max_canonicalized_bytes
934 .min(crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING)
935 }
936
937 #[cfg(feature = "xmldsig")]
938 pub(crate) fn effective_xml_base_components(&self) -> usize {
939 self.max_xml_base_components
940 .min(crate::hard_limits::XML_BASE_COMPONENT_CEILING)
941 }
942
943 #[cfg(feature = "xmldsig")]
944 pub(crate) fn effective_xml_base_resolution_bytes(&self) -> usize {
945 self.max_xml_base_resolution_bytes
946 .min(crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING)
947 }
948
949 fn within(
950 resource: &'static str,
951 selected: usize,
952 ceiling: usize,
953 ) -> Result<(), PolicyViolation> {
954 if selected > ceiling {
955 return Err(PolicyViolation::ResourceLimit {
956 resource,
957 maximum: ceiling,
958 actual: selected,
959 });
960 }
961 Ok(())
962 }
963
964 #[cfg(feature = "xmldsig")]
965 fn nonzero_within(
966 resource: &'static str,
967 selected: usize,
968 ceiling: usize,
969 ) -> Result<(), PolicyViolation> {
970 if selected == 0 {
971 return Err(PolicyViolation::InvalidResourceLimit {
972 resource,
973 requirement: "limit must be nonzero",
974 actual: selected,
975 });
976 }
977 Self::within(resource, selected, ceiling)
978 }
979}
980
981#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
983pub struct XmlInputPolicy {
984 pub allow_internal_dtd: bool,
986}
987
988#[cfg(feature = "xmldsig")]
992#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
993pub enum OpcRelationshipEdition {
994 Ecma2012,
996 #[default]
998 Ecma2021,
999}
1000
1001#[cfg(feature = "xmldsig")]
1003#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1004pub enum SameDocumentIdSemantics {
1005 #[default]
1007 Specification,
1008 XmlSecBarename,
1014 XmlSecVisa3d,
1018}
1019
1020#[cfg(feature = "xmldsig")]
1022#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1023pub enum XsltStylesheetSemantics {
1024 #[default]
1030 Specification,
1031 Libxmlsec1,
1035}
1036
1037#[cfg(feature = "xmldsig")]
1039#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1040pub enum EcdsaSignatureValueEncoding {
1041 #[default]
1043 XmlDsig,
1044 XmlSecAsn1Der,
1046}
1047
1048#[cfg(feature = "xmldsig")]
1050#[derive(Debug, Clone, Default, PartialEq, Eq)]
1051pub struct TransformPolicy {
1052 pub allowed_algorithms: Option<HashSet<String>>,
1054 pub xpath_here_semantics: XPathHereSemantics,
1056 pub same_document_id_semantics: SameDocumentIdSemantics,
1058 pub opc_relationship_edition: OpcRelationshipEdition,
1060 pub xslt_stylesheet_semantics: XsltStylesheetSemantics,
1062}
1063
1064#[cfg(feature = "xmldsig")]
1066#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1067pub struct UriPolicy {
1068 pub references: UriTypeSet,
1070 pub retrieval_methods: UriTypeSet,
1072 pub key_info_references: UriTypeSet,
1074}
1075
1076#[cfg(feature = "xmldsig")]
1077impl Default for UriPolicy {
1078 fn default() -> Self {
1079 Self {
1080 references: UriTypeSet::SAME_DOCUMENT,
1081 retrieval_methods: UriTypeSet::SAME_DOCUMENT,
1082 key_info_references: UriTypeSet::SAME_DOCUMENT,
1083 }
1084 }
1085}
1086
1087#[cfg(feature = "xmldsig")]
1089#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1090pub struct KeySourcePolicy {
1091 pub preset_key: bool,
1093 pub key_name: bool,
1095 pub key_value: bool,
1097 pub der_encoded_key_value: bool,
1099 pub x509_data: bool,
1101 pub key_info_reference: bool,
1103}
1104
1105#[cfg(feature = "xmldsig")]
1106impl Default for KeySourcePolicy {
1107 fn default() -> Self {
1108 Self {
1109 preset_key: true,
1110 key_name: true,
1111 key_value: true,
1112 der_encoded_key_value: true,
1113 x509_data: true,
1114 key_info_reference: true,
1115 }
1116 }
1117}
1118
1119#[cfg(feature = "xmldsig")]
1121#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1122#[non_exhaustive]
1123pub enum ExtendedKeyPurpose {
1124 ServerAuth,
1126 ClientAuth,
1128 CodeSigning,
1130 EmailProtection,
1132 TimeStamping,
1134 OcspSigning,
1136 Other(Vec<u64>),
1138}
1139
1140#[cfg(feature = "xmldsig")]
1142#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1143pub enum VerificationTrustMode {
1144 #[default]
1146 RequireTrustedKey,
1147 CryptographicOnly,
1149}
1150
1151#[cfg(feature = "xmldsig")]
1153#[derive(Debug, Clone, PartialEq, Eq)]
1154pub struct KeyTrustPolicy {
1155 pub mode: VerificationTrustMode,
1157 pub verify_x509_chains: bool,
1159 pub max_x509_chain_depth: usize,
1161 pub max_x509_candidate_paths: usize,
1163 pub allowed_legacy_signature_algorithms: HashSet<SignatureAlgorithm>,
1165 pub parameterized_rsa_pss: RsaPssPermission,
1167 pub certificate_signature_algorithms: Option<CertificateSignatureAlgorithms>,
1172 pub rsa_keys: RsaKeyPolicy,
1174 pub dsa_keys: DsaKeyPolicy,
1176 pub allowed_extended_key_usages: HashSet<ExtendedKeyPurpose>,
1182 pub check_crls: bool,
1185 pub verification_time: Option<SystemTime>,
1187}
1188
1189#[cfg(feature = "xmldsig")]
1190impl Default for KeyTrustPolicy {
1191 fn default() -> Self {
1192 Self {
1193 mode: VerificationTrustMode::RequireTrustedKey,
1194 verify_x509_chains: false,
1195 max_x509_chain_depth: crate::hard_limits::X509_CHAIN_DEPTH_CEILING,
1196 max_x509_candidate_paths: crate::hard_limits::X509_CANDIDATE_PATH_CEILING,
1197 allowed_legacy_signature_algorithms: HashSet::new(),
1198 parameterized_rsa_pss: RsaPssPermission::ExactOnly,
1199 certificate_signature_algorithms: None,
1200 rsa_keys: RsaKeyPolicy::default(),
1201 dsa_keys: DsaKeyPolicy::default(),
1202 allowed_extended_key_usages: HashSet::new(),
1203 check_crls: false,
1204 verification_time: None,
1205 }
1206 }
1207}
1208
1209#[cfg(feature = "xmldsig")]
1210#[derive(Debug, Clone, PartialEq, Eq)]
1211pub enum CertificateSignatureAlgorithms {
1213 AllSupported,
1216 Allowlist(HashSet<crate::provider::X509SignatureAlgorithm>),
1218}
1219
1220#[cfg(feature = "xmldsig")]
1221impl KeyTrustPolicy {
1222 pub(crate) fn check_certificate_signature_algorithm(
1223 algorithm: crate::provider::X509SignatureAlgorithm,
1224 allowed: Option<&CertificateSignatureAlgorithms>,
1225 ) -> Result<(), PolicyViolation> {
1226 let permitted = match allowed {
1230 Some(CertificateSignatureAlgorithms::AllSupported) => true,
1231 Some(CertificateSignatureAlgorithms::Allowlist(allowed)) => {
1232 allowed.contains(&algorithm)
1233 }
1234 None => !matches!(
1235 algorithm,
1236 crate::provider::X509SignatureAlgorithm::PostQuantum(_)
1237 ),
1238 };
1239 if !permitted {
1240 return Err(PolicyViolation::Algorithm {
1241 operation: "certificate/CRL verification",
1242 algorithm: algorithm
1243 .oid()
1244 .map_or_else(|| format!("{algorithm:?}"), str::to_owned),
1245 });
1246 }
1247 Ok(())
1248 }
1249
1250 pub(crate) fn validate(&self) -> Result<(), PolicyViolation> {
1251 if self.check_crls && !self.verify_x509_chains {
1252 return Err(PolicyViolation::KeyTrust {
1253 reason: "CRL checking requires X.509 chain validation",
1254 });
1255 }
1256 if self
1257 .allowed_extended_key_usages
1258 .iter()
1259 .any(|purpose| match purpose {
1260 ExtendedKeyPurpose::Other(arcs) => {
1261 arcs.len() < 2 || arcs[0] > 2 || (arcs[0] < 2 && arcs[1] > 39)
1262 }
1263 _ => false,
1264 })
1265 {
1266 return Err(PolicyViolation::KeyTrust {
1267 reason: "custom extended key purposes must contain valid OID arcs",
1268 });
1269 }
1270 self.rsa_keys.validate()?;
1271 self.dsa_keys.validate()?;
1272 ResourcePolicy::nonzero_within(
1273 "X.509 chain depth",
1274 self.max_x509_chain_depth,
1275 crate::hard_limits::X509_CHAIN_DEPTH_CEILING,
1276 )?;
1277 ResourcePolicy::nonzero_within(
1278 "X.509 candidate paths",
1279 self.max_x509_candidate_paths,
1280 crate::hard_limits::X509_CANDIDATE_PATH_CEILING,
1281 )
1282 }
1283}
1284
1285#[cfg(feature = "xmldsig")]
1288#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1289pub enum ManifestProcessing {
1290 #[default]
1292 Ignore,
1293 Process,
1295}
1296
1297#[cfg(feature = "xmldsig")]
1299#[derive(Debug, Clone, Default)]
1300pub struct VerificationPolicy {
1301 pub validity: Option<PolicyValidity>,
1303 pub signature_constraints: SignatureConstraints,
1305 pub key_establishment: KeyEstablishmentPolicy,
1307 pub parameterized_rsa_pss: RsaPssPermission,
1310 pub signature_algorithms: Option<HashSet<SignatureAlgorithm>>,
1314 pub digest_algorithms: Option<HashSet<DigestAlgorithm>>,
1316 pub hmac: HmacPolicy,
1318 pub ecdsa_signature_value_encoding: EcdsaSignatureValueEncoding,
1320 pub key_trust: KeyTrustPolicy,
1322 pub key_sources: KeySourcePolicy,
1324 pub uris: UriPolicy,
1326 pub transforms: TransformPolicy,
1328 pub manifest_processing: ManifestProcessing,
1330 pub xml: XmlInputPolicy,
1332 pub resources: ResourcePolicy,
1334}
1335
1336#[cfg(feature = "xmldsig")]
1337impl VerificationPolicy {
1338 pub(crate) fn check_digest_algorithm(
1339 &self,
1340 algorithm: DigestAlgorithm,
1341 ) -> Result<(), PolicyViolation> {
1342 if self
1343 .digest_algorithms
1344 .as_ref()
1345 .map_or(!algorithm.requires_explicit_permission(), |allowed| {
1346 allowed.contains(&algorithm)
1347 })
1348 {
1349 Ok(())
1350 } else {
1351 Err(PolicyViolation::Algorithm {
1352 operation: "verification",
1353 algorithm: algorithm.uri().to_owned(),
1354 })
1355 }
1356 }
1357 pub fn validate(&self) -> Result<(), PolicyViolation> {
1359 if let Some(validity) = &self.validity {
1360 validity.validate_at(SystemTime::now())?;
1361 }
1362 if self.signature_constraints != SignatureConstraints::General
1363 && self.key_trust.mode == VerificationTrustMode::CryptographicOnly
1364 {
1365 return Err(PolicyViolation::Configuration {
1366 reason: "target coverage requires trusted-key verification",
1367 });
1368 }
1369 self.resources.validate()?;
1370 self.key_establishment.validate()?;
1371 self.key_trust.validate()?;
1372 self.hmac.validate()
1373 }
1374
1375 pub fn check_signature_algorithm(
1377 &self,
1378 algorithm: SignatureAlgorithm,
1379 ) -> Result<(), PolicyViolation> {
1380 if (matches!(algorithm, SignatureAlgorithm::PostQuantum(_))
1381 || algorithm.requires_explicit_permission())
1382 && !self
1383 .signature_algorithms
1384 .as_ref()
1385 .is_some_and(|allowed| allowed.contains(&algorithm))
1386 {
1387 return Err(PolicyViolation::Algorithm {
1388 operation: "verification",
1389 algorithm: algorithm.uri().to_owned(),
1390 });
1391 }
1392 if (algorithm.uses_legacy_pss_hash()
1393 || matches!(
1394 algorithm,
1395 SignatureAlgorithm::RsaSha1
1396 | SignatureAlgorithm::DsaSha1
1397 | SignatureAlgorithm::HmacSha1
1398 | SignatureAlgorithm::EcdsaSha1
1399 ))
1400 && !self
1401 .key_trust
1402 .allowed_legacy_signature_algorithms
1403 .contains(&algorithm)
1404 && !self.key_trust.parameterized_rsa_pss.permits(algorithm)
1405 {
1406 return Err(PolicyViolation::Algorithm {
1407 operation: "verification",
1408 algorithm: algorithm.uri().to_string(),
1409 });
1410 }
1411 if self
1412 .signature_algorithms
1413 .as_ref()
1414 .is_some_and(|allowed| !allowed.contains(&algorithm))
1415 && !self.parameterized_rsa_pss.permits(algorithm)
1416 {
1417 return Err(PolicyViolation::Algorithm {
1418 operation: "verification",
1419 algorithm: algorithm.uri().to_string(),
1420 });
1421 }
1422 Ok(())
1423 }
1424}
1425
1426#[cfg(feature = "xmldsig")]
1428#[derive(Debug, Clone, Default)]
1429pub struct SigningPolicy {
1430 pub validity: Option<PolicyValidity>,
1432 pub signature_constraints: SignatureConstraints,
1434 pub key_establishment: KeyEstablishmentPolicy,
1436 pub parameterized_rsa_pss: RsaPssPermission,
1438 pub signature_algorithms: Option<HashSet<SignatureAlgorithm>>,
1441 pub digest_algorithms: Option<HashSet<DigestAlgorithm>>,
1443 pub hmac: HmacPolicy,
1445 pub ecdsa_signature_value_encoding: EcdsaSignatureValueEncoding,
1447 pub rsa_keys: RsaKeyPolicy,
1449 pub dsa_keys: DsaKeyPolicy,
1451 pub uris: UriPolicy,
1454 pub transforms: TransformPolicy,
1456 pub manifest_processing: ManifestProcessing,
1458 pub xml: XmlInputPolicy,
1460 pub resources: ResourcePolicy,
1462}
1463
1464#[cfg(feature = "xmldsig")]
1465impl SigningPolicy {
1466 pub fn validate(&self) -> Result<(), PolicyViolation> {
1468 if let Some(validity) = &self.validity {
1469 validity.validate_at(SystemTime::now())?;
1470 }
1471 self.resources.validate()?;
1472 self.key_establishment.validate()?;
1473 self.rsa_keys.validate()?;
1474 self.dsa_keys.validate()?;
1475 self.hmac.validate()
1476 }
1477
1478 pub(crate) fn check_signature_algorithm(
1479 &self,
1480 algorithm: SignatureAlgorithm,
1481 ) -> Result<(), PolicyViolation> {
1482 if self.parameterized_rsa_pss.permits(algorithm) {
1483 return Ok(());
1484 }
1485 check_signing_algorithm(
1486 self.signature_algorithms.as_ref(),
1487 algorithm,
1488 algorithm.signing_allowed(),
1489 algorithm.uri(),
1490 )
1491 }
1492
1493 pub(crate) fn check_digest_algorithm(
1494 &self,
1495 algorithm: DigestAlgorithm,
1496 ) -> Result<(), PolicyViolation> {
1497 check_signing_algorithm(
1498 self.digest_algorithms.as_ref(),
1499 algorithm,
1500 algorithm.signing_allowed(),
1501 algorithm.uri(),
1502 )
1503 }
1504}
1505
1506#[cfg(feature = "xmldsig")]
1507fn check_signing_algorithm<T: Eq + std::hash::Hash>(
1508 allowlist: Option<&HashSet<T>>,
1509 algorithm: T,
1510 default_allowed: bool,
1511 uri: &str,
1512) -> Result<(), PolicyViolation> {
1513 if allowlist.map_or(default_allowed, |allowed| allowed.contains(&algorithm)) {
1514 Ok(())
1515 } else {
1516 Err(PolicyViolation::Algorithm {
1517 operation: "signing",
1518 algorithm: uri.to_owned(),
1519 })
1520 }
1521}
1522
1523#[cfg(feature = "xmlenc")]
1525pub(crate) fn check_content_algorithm(
1526 allowlist: Option<&HashSet<DataEncryptionAlgorithm>>,
1527 algorithm: DataEncryptionAlgorithm,
1528 operation: &'static str,
1529) -> Result<(), PolicyViolation> {
1530 if allowlist.map_or(!algorithm.requires_explicit_permission(), |allowed| {
1531 allowed.contains(&algorithm)
1532 }) {
1533 Ok(())
1534 } else {
1535 Err(PolicyViolation::Algorithm {
1536 operation,
1537 algorithm: algorithm.uri().to_owned(),
1538 })
1539 }
1540}
1541
1542#[cfg(feature = "xmlenc")]
1544#[derive(Debug, Clone, Default)]
1545pub struct EncryptionPolicy {
1546 pub validity: Option<PolicyValidity>,
1548 pub data_algorithms: Option<HashSet<DataEncryptionAlgorithm>>,
1550 pub key_transport_algorithms: Option<HashSet<KeyTransportAlgorithm>>,
1552 pub key_wrap_algorithms: Option<HashSet<KeyWrapAlgorithm>>,
1554 pub oaep_digests: Option<HashSet<OaepDigestAlgorithm>>,
1556 pub rsa_keys: RsaKeyPolicy,
1558 pub key_establishment: KeyEstablishmentPolicy,
1560 pub xml: XmlInputPolicy,
1562 pub resources: ResourcePolicy,
1564}
1565
1566#[cfg(feature = "xmlenc")]
1567impl EncryptionPolicy {
1568 pub fn validate(&self) -> Result<(), PolicyViolation> {
1570 if let Some(validity) = &self.validity {
1571 validity.validate_at(SystemTime::now())?;
1572 }
1573 self.resources.validate()?;
1574 self.key_establishment.validate()?;
1575 self.rsa_keys.validate()
1576 }
1577}
1578
1579#[cfg(feature = "xmlenc")]
1581#[derive(Debug, Clone, Default)]
1582pub struct DecryptionPolicy {
1583 pub validity: Option<PolicyValidity>,
1585 pub uris: UriPolicy,
1587 pub transforms: TransformPolicy,
1589 pub data_algorithms: Option<HashSet<DataEncryptionAlgorithm>>,
1591 pub key_transport_algorithms: Option<HashSet<KeyTransportAlgorithm>>,
1593 pub key_wrap_algorithms: Option<HashSet<KeyWrapAlgorithm>>,
1595 pub oaep_digests: Option<HashSet<OaepDigestAlgorithm>>,
1597 pub rsa_keys: RsaKeyPolicy,
1600 pub key_establishment: KeyEstablishmentPolicy,
1602 pub xml: XmlInputPolicy,
1604 pub resources: ResourcePolicy,
1606}
1607
1608#[cfg(feature = "xmlenc")]
1609impl DecryptionPolicy {
1610 pub fn validate(&self) -> Result<(), PolicyViolation> {
1612 if let Some(validity) = &self.validity {
1613 validity.validate_at(SystemTime::now())?;
1614 }
1615 self.resources.validate()?;
1616 self.key_establishment.validate()?;
1617 self.rsa_keys.validate()
1618 }
1619}
1620
1621#[cfg(test)]
1622mod tests {
1623 use super::*;
1624
1625 #[cfg(all(feature = "legacy-algorithms", feature = "xmldsig"))]
1626 #[test]
1627 fn legacy_digest_permission_is_explicit_and_operation_local() {
1628 for algorithm in [DigestAlgorithm::Md5, DigestAlgorithm::Ripemd160] {
1631 assert!(
1632 VerificationPolicy::default()
1633 .check_digest_algorithm(algorithm)
1634 .is_err()
1635 );
1636 assert!(
1637 SigningPolicy::default()
1638 .check_digest_algorithm(algorithm)
1639 .is_err()
1640 );
1641 let verification = VerificationPolicy {
1642 digest_algorithms: Some(HashSet::from([algorithm])),
1643 ..VerificationPolicy::default()
1644 };
1645 let signing = SigningPolicy {
1646 digest_algorithms: Some(HashSet::from([algorithm])),
1647 ..SigningPolicy::default()
1648 };
1649 assert!(verification.check_digest_algorithm(algorithm).is_ok());
1650 assert!(signing.check_digest_algorithm(algorithm).is_ok());
1651 assert!(
1652 verification
1653 .check_digest_algorithm(DigestAlgorithm::Sha256)
1654 .is_err()
1655 );
1656 assert!(
1657 signing
1658 .check_digest_algorithm(DigestAlgorithm::Sha256)
1659 .is_err()
1660 );
1661 }
1662 }
1663
1664 #[cfg(feature = "xmldsig")]
1665 #[test]
1666 fn certificate_permissions_keep_exact_pss_and_compatibility_distinct() {
1667 use crate::provider::X509SignatureAlgorithm;
1668 use crate::xmldsig::{DigestAlgorithm, PqAlgorithm};
1669 let exact = X509SignatureAlgorithm::RsaPss {
1672 digest: DigestAlgorithm::Sha256,
1673 mgf_digest: DigestAlgorithm::Sha256,
1674 salt_len: 32,
1675 };
1676 let allowed = CertificateSignatureAlgorithms::Allowlist(HashSet::from([exact]));
1677 assert!(
1678 KeyTrustPolicy::check_certificate_signature_algorithm(exact, Some(&allowed)).is_ok()
1679 );
1680 for salt_len in [0, 17, 64, 255] {
1681 let method = X509SignatureAlgorithm::RsaPss {
1682 digest: DigestAlgorithm::Sha256,
1683 mgf_digest: DigestAlgorithm::Sha256,
1684 salt_len,
1685 };
1686 assert!(
1687 KeyTrustPolicy::check_certificate_signature_algorithm(method, Some(&allowed))
1688 .is_err()
1689 );
1690 assert!(
1691 KeyTrustPolicy::check_certificate_signature_algorithm(
1692 method,
1693 Some(&CertificateSignatureAlgorithms::AllSupported)
1694 )
1695 .is_ok()
1696 );
1697 }
1698 let pq = X509SignatureAlgorithm::PostQuantum(PqAlgorithm::MlDsa44);
1699 assert!(KeyTrustPolicy::check_certificate_signature_algorithm(pq, None).is_err());
1700 assert!(
1701 KeyTrustPolicy::check_certificate_signature_algorithm(
1702 pq,
1703 Some(&CertificateSignatureAlgorithms::AllSupported)
1704 )
1705 .is_ok()
1706 );
1707 }
1708
1709 #[test]
1710 fn resource_policy_cannot_exceed_implementation_ceiling() {
1711 let policy = ResourcePolicy {
1712 max_xml_nodes: 100_001,
1713 ..ResourcePolicy::default()
1714 };
1715 assert!(matches!(
1716 policy.validate(),
1717 Err(PolicyViolation::ResourceLimit {
1718 resource: resource_name::XML_NODES,
1719 maximum: 100_000,
1720 actual: 100_001,
1721 })
1722 ));
1723 }
1724
1725 #[test]
1726 fn xml_parse_work_policy_cannot_exceed_implementation_ceiling() {
1727 let policy = ResourcePolicy {
1728 max_xml_parse_work_bytes: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING
1729 .saturating_add(1),
1730 ..ResourcePolicy::default()
1731 };
1732
1733 assert_eq!(
1734 policy.validate(),
1735 Err(PolicyViolation::ResourceLimit {
1736 resource: resource_name::XML_PARSE_WORK_BYTES,
1737 maximum: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
1738 actual: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING.saturating_add(1),
1739 })
1740 );
1741 }
1742
1743 #[test]
1744 fn every_resource_policy_field_obeys_its_hard_ceiling() {
1745 type Case = (&'static str, usize, fn(&mut ResourcePolicy) -> &mut usize);
1749 let cases: &[Case] = &[
1750 (
1751 resource_name::X509_WORK,
1752 crate::hard_limits::X509_WORK_CEILING,
1753 |p| &mut p.max_x509_work,
1754 ),
1755 (
1756 resource_name::X509_WORKSPACE_BYTES,
1757 crate::hard_limits::X509_WORKSPACE_BYTE_CEILING,
1758 |p| &mut p.max_x509_workspace_bytes,
1759 ),
1760 (
1761 resource_name::XML_NODES,
1762 crate::hard_limits::XML_DOCUMENT_NODE_CEILING as usize,
1763 |p| &mut p.max_xml_nodes,
1764 ),
1765 (
1766 resource_name::XML_DEPTH,
1767 crate::hard_limits::XML_DOCUMENT_DEPTH_CEILING,
1768 |p| &mut p.max_xml_depth,
1769 ),
1770 (
1771 resource_name::XML_NAMESPACE_BINDINGS,
1772 crate::hard_limits::XML_NAMESPACE_BINDING_CEILING,
1773 |p| &mut p.max_xml_namespace_bindings,
1774 ),
1775 (
1776 resource_name::SIGNATURE_REFERENCES,
1777 crate::hard_limits::SIGNATURE_REFERENCE_CEILING,
1778 |p| &mut p.max_references,
1779 ),
1780 (
1781 resource_name::VERIFICATION_SIGNATURES,
1782 crate::hard_limits::VERIFICATION_SIGNATURE_CEILING,
1783 |p| &mut p.max_signatures,
1784 ),
1785 (
1786 resource_name::REFERENCE_TRANSFORMS,
1787 crate::hard_limits::REFERENCE_TRANSFORM_CEILING,
1788 |p| &mut p.max_transforms_per_reference,
1789 ),
1790 (
1791 resource_name::XML_BASE_COMPONENTS,
1792 crate::hard_limits::XML_BASE_COMPONENT_CEILING,
1793 |p| &mut p.max_xml_base_components,
1794 ),
1795 (
1796 resource_name::XML_BASE_RESOLUTION_BYTES,
1797 crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING,
1798 |p| &mut p.max_xml_base_resolution_bytes,
1799 ),
1800 (
1801 resource_name::CANONICALIZED_BYTES,
1802 crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING,
1803 |p| &mut p.max_canonicalized_bytes,
1804 ),
1805 (
1806 resource_name::EXTERNAL_RESOURCE_BYTES,
1807 crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING,
1808 |p| &mut p.max_external_resource_bytes,
1809 ),
1810 (
1811 resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
1812 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING,
1813 |p| &mut p.max_external_resource_total_bytes,
1814 ),
1815 (
1816 resource_name::ENCRYPTION_PLAINTEXT_BYTES,
1817 crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING,
1818 |p| &mut p.max_encryption_plaintext_bytes,
1819 ),
1820 (
1821 resource_name::XML_DOCUMENT,
1822 crate::hard_limits::XML_DOCUMENT_BYTE_CEILING,
1823 |p| &mut p.max_xml_document_bytes,
1824 ),
1825 (
1826 resource_name::XML_PARSE_WORK_BYTES,
1827 crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
1828 |p| &mut p.max_xml_parse_work_bytes,
1829 ),
1830 (
1831 resource_name::ENCRYPTION_RECIPIENTS,
1832 crate::hard_limits::ENCRYPTION_RECIPIENT_CEILING,
1833 |p| &mut p.max_encryption_recipients,
1834 ),
1835 (
1836 resource_name::ENCRYPTION_METADATA_BYTES,
1837 crate::hard_limits::ENCRYPTION_METADATA_BYTE_CEILING,
1838 |p| &mut p.max_encryption_metadata_bytes,
1839 ),
1840 (
1841 resource_name::KEY_CANDIDATES,
1842 crate::hard_limits::KEY_CANDIDATE_CEILING,
1843 |p| &mut p.max_key_candidates,
1844 ),
1845 (
1846 resource_name::KEY_IMPORT_KDF_WORK,
1847 crate::hard_limits::KEY_IMPORT_KDF_WORK_CEILING as usize,
1848 |p| &mut p.max_key_import_kdf_work,
1849 ),
1850 (
1851 resource_name::KEY_IMPORT_KDF_MEMORY,
1852 crate::hard_limits::KEY_IMPORT_KDF_MEMORY_CEILING,
1853 |p| &mut p.max_key_import_kdf_memory_bytes,
1854 ),
1855 (
1856 resource_name::KEY_INFO_REFERENCE_DEPTH,
1857 crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING,
1858 |p| &mut p.max_key_info_reference_depth,
1859 ),
1860 (
1861 resource_name::BASE64_TRANSFORM_INPUT_BYTES,
1862 crate::hard_limits::BASE64_TRANSFORM_INPUT_BYTE_CEILING,
1863 |p| &mut p.max_base64_transform_input_bytes,
1864 ),
1865 (
1866 resource_name::BASE64_TRANSFORM_OUTPUT_BYTES,
1867 crate::hard_limits::BASE64_TRANSFORM_OUTPUT_BYTE_CEILING,
1868 |p| &mut p.max_base64_transform_output_bytes,
1869 ),
1870 (
1871 resource_name::XPATH_EXPRESSIONS,
1872 crate::hard_limits::XPATH_EXPRESSION_COUNT_CEILING,
1873 |p| &mut p.max_xpath_expressions,
1874 ),
1875 (
1876 resource_name::XPATH_EXPRESSION_BYTES,
1877 crate::hard_limits::XPATH_EXPRESSION_BYTE_CEILING,
1878 |p| &mut p.max_xpath_expression_bytes,
1879 ),
1880 (
1881 resource_name::XPATH_EXPRESSION_COMPLEXITY,
1882 crate::hard_limits::XPATH_EXPRESSION_COMPLEXITY_CEILING,
1883 |p| &mut p.max_xpath_expression_complexity,
1884 ),
1885 (
1886 resource_name::XPATH_CONTEXT_EVALUATIONS,
1887 crate::hard_limits::XPATH_CONTEXT_EVALUATION_CEILING,
1888 |p| &mut p.max_xpath_context_evaluations,
1889 ),
1890 (
1891 resource_name::XPATH_EVALUATION_WORK,
1892 crate::hard_limits::XPATH_EVALUATION_WORK_CEILING,
1893 |p| &mut p.max_xpath_evaluation_work,
1894 ),
1895 (
1896 resource_name::XPATH_MIRROR_STRING_BYTES,
1897 crate::hard_limits::XPATH_MIRROR_STRING_BYTE_CEILING,
1898 |p| &mut p.max_xpath_mirror_string_bytes,
1899 ),
1900 (
1901 resource_name::XPATH_STRING_WORK_BYTES,
1902 crate::hard_limits::XPATH_STRING_WORK_BYTE_CEILING,
1903 |p| &mut p.max_xpath_string_work_bytes,
1904 ),
1905 (
1906 resource_name::XPATH_NAMESPACE_BINDINGS,
1907 crate::hard_limits::XPATH_NAMESPACE_BINDING_CEILING,
1908 |p| &mut p.max_xpath_namespace_bindings,
1909 ),
1910 (
1911 resource_name::XPATH_NAMESPACE_BYTES,
1912 crate::hard_limits::XPATH_NAMESPACE_BYTE_CEILING,
1913 |p| &mut p.max_xpath_namespace_bytes,
1914 ),
1915 (
1916 resource_name::XPATH_FILTERS,
1917 crate::hard_limits::XPATH_FILTER_COUNT_CEILING,
1918 |p| &mut p.max_xpath_filters,
1919 ),
1920 (
1921 resource_name::NODE_SET_FILTER_WORK,
1922 crate::hard_limits::NODE_SET_FILTER_WORK_CEILING,
1923 |p| &mut p.max_node_set_filter_work,
1924 ),
1925 (
1926 resource_name::OPC_PARAMETER_BYTES,
1927 crate::hard_limits::OPC_PARAMETER_BYTE_CEILING,
1928 |p| &mut p.max_opc_parameter_bytes,
1929 ),
1930 (
1931 resource_name::OPC_WORKSPACE_BYTES,
1932 crate::hard_limits::OPC_WORKSPACE_BYTE_CEILING,
1933 |p| &mut p.max_opc_workspace_bytes,
1934 ),
1935 (
1936 resource_name::NODE_SET_ENTRIES,
1937 crate::hard_limits::NODE_SET_ENTRY_CEILING,
1938 |p| &mut p.max_node_set_entries,
1939 ),
1940 (
1941 resource_name::NODE_SET_OWNED_STRING_BYTES,
1942 crate::hard_limits::NODE_SET_OWNED_STRING_BYTE_CEILING,
1943 |p| &mut p.max_node_set_owned_string_bytes,
1944 ),
1945 (
1946 resource_name::NODE_SET_CUMULATIVE_OWNED_STRING_BYTES,
1947 crate::hard_limits::NODE_SET_CUMULATIVE_OWNED_STRING_BYTE_CEILING,
1948 |p| &mut p.max_node_set_cumulative_owned_string_bytes,
1949 ),
1950 (
1951 resource_name::XSLT_WORK,
1952 crate::hard_limits::XSLT_WORK_CEILING,
1953 |p| &mut p.max_xslt_work,
1954 ),
1955 (
1956 resource_name::XSLT_WORKSPACE_BYTES,
1957 crate::hard_limits::XSLT_WORKSPACE_BYTE_CEILING,
1958 |p| &mut p.max_xslt_workspace_bytes,
1959 ),
1960 (
1961 resource_name::XSLT_OUTPUT_BYTES,
1962 crate::hard_limits::XSLT_OUTPUT_BYTE_CEILING,
1963 |p| &mut p.max_xslt_output_bytes,
1964 ),
1965 (
1966 resource_name::XSLT_RECURSION_DEPTH,
1967 crate::hard_limits::XSLT_RECURSION_DEPTH_CEILING,
1968 |p| &mut p.max_xslt_recursion_depth,
1969 ),
1970 ];
1971
1972 for &(resource, ceiling, field) in cases {
1973 let mut policy = ResourcePolicy::default();
1974 let actual = ceiling.saturating_add(1);
1975 *field(&mut policy) = actual;
1976 assert_eq!(
1977 policy.validate(),
1978 Err(PolicyViolation::ResourceLimit {
1979 resource,
1980 maximum: ceiling,
1981 actual,
1982 }),
1983 "wrong hard-ceiling validation for {resource}",
1984 );
1985 }
1986 }
1987
1988 #[test]
1989 fn resource_policy_accepts_zero_as_a_deny_all_ceiling() {
1990 let policy = ResourcePolicy {
1993 max_xml_nodes: 0,
1994 max_xml_depth: 0,
1995 max_xml_namespace_bindings: 0,
1996 max_references: 0,
1997 max_signatures: 0,
1998 max_transforms_per_reference: 0,
1999 max_xml_base_components: 0,
2000 max_xml_base_resolution_bytes: 0,
2001 max_canonicalized_bytes: 0,
2002 max_external_resource_bytes: 0,
2003 max_external_resource_total_bytes: 0,
2004 max_encryption_plaintext_bytes: 0,
2005 max_xml_document_bytes: 0,
2006 max_xml_parse_work_bytes: 0,
2007 max_encryption_recipients: 0,
2008 max_encryption_metadata_bytes: 0,
2009 max_key_candidates: 0,
2010 max_x509_work: 0,
2011 max_x509_workspace_bytes: 0,
2012 max_key_import_kdf_work: 0,
2013 max_key_import_kdf_memory_bytes: 0,
2014 max_key_info_reference_depth: 0,
2015 max_base64_transform_input_bytes: 0,
2016 max_base64_transform_output_bytes: 0,
2017 max_xpath_expressions: 0,
2018 max_xpath_expression_bytes: 0,
2019 max_xpath_expression_complexity: 0,
2020 max_xpath_context_evaluations: 0,
2021 max_xpath_evaluation_work: 0,
2022 max_xpath_mirror_string_bytes: 0,
2023 max_xpath_string_work_bytes: 0,
2024 max_xpath_namespace_bindings: 0,
2025 max_xpath_namespace_bytes: 0,
2026 max_xpath_filters: 0,
2027 max_node_set_filter_work: 0,
2028 max_node_set_entries: 0,
2029 max_node_set_owned_string_bytes: 0,
2030 max_node_set_cumulative_owned_string_bytes: 0,
2031 max_opc_workspace_bytes: 0,
2032 max_opc_parameter_bytes: 0,
2033 max_xslt_work: 0,
2034 max_xslt_workspace_bytes: 0,
2035 max_xslt_output_bytes: 0,
2036 max_xslt_recursion_depth: 0,
2037 };
2038
2039 assert_eq!(policy.validate(), Ok(()));
2040 }
2041
2042 #[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
2043 #[test]
2044 fn node_set_policy_has_headroom_above_its_default() {
2045 let mut policy = ResourcePolicy::default();
2046 assert_eq!(policy.max_node_set_entries, 262_144);
2047 assert_eq!(policy.validate(), Ok(()));
2048
2049 policy.max_node_set_entries = 524_288;
2050 assert_eq!(policy.validate(), Ok(()));
2051
2052 policy.max_node_set_entries = 524_289;
2053 assert_eq!(
2054 policy.validate(),
2055 Err(PolicyViolation::ResourceLimit {
2056 resource: resource_name::NODE_SET_ENTRIES,
2057 maximum: 524_288,
2058 actual: 524_289,
2059 })
2060 );
2061 }
2062
2063 #[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
2064 #[test]
2065 fn rsa_key_policy_enforces_structure_range_and_explicit_relaxation() {
2066 let secure = RsaKeyPolicy::default();
2067 assert!(matches!(
2068 secure.validate_components("test", &[0x80; 128], &[1, 0, 1]),
2069 Err(PolicyViolation::KeySize {
2070 minimum_bits: 2048,
2071 maximum_bits: 8192,
2072 actual_bits: 1024,
2073 ..
2074 })
2075 ));
2076 let mut short_2048_width = [0_u8; 256];
2077 short_2048_width[0] = 1;
2078 assert!(matches!(
2079 secure.validate_components("test", &short_2048_width, &[1, 0, 1]),
2080 Err(PolicyViolation::KeySize {
2081 minimum_bits: 2048,
2082 actual_bits: 2041,
2083 ..
2084 })
2085 ));
2086 assert!(matches!(
2087 secure.validate_components("test", &[1; 1025], &[1, 0, 1]),
2088 Err(PolicyViolation::KeySize {
2089 actual_bits: 8193,
2090 ..
2091 })
2092 ));
2093 assert!(matches!(
2094 secure.validate_components("test", &[0x80; 256], &[2]),
2095 Err(PolicyViolation::InvalidKeyMaterial { .. })
2096 ));
2097 assert_eq!(
2098 secure.validate_components("test", &[0x80; 256], &[0x80, 0, 0, 1]),
2099 Ok(256),
2100 "normalized RSA components encode the exponent as unsigned bytes"
2101 );
2102
2103 let compatibility = RsaKeyPolicy {
2104 minimum_modulus_bits: 1024,
2105 };
2106 assert_eq!(
2107 compatibility.validate_components("test", &[0x80; 128], &[1, 0, 1]),
2108 Ok(128)
2109 );
2110 assert!(
2111 RsaKeyPolicy {
2112 minimum_modulus_bits: 2047,
2113 }
2114 .validate()
2115 .is_err()
2116 );
2117 }
2118
2119 #[cfg(feature = "xmldsig")]
2120 #[test]
2121 fn mandatory_x509_limits_report_the_nonzero_requirement() {
2122 let policy = KeyTrustPolicy {
2125 max_x509_chain_depth: 0,
2126 ..KeyTrustPolicy::default()
2127 };
2128
2129 let error = policy.validate().expect_err("zero depth must be rejected");
2130 assert!(
2131 error.to_string().contains("must be nonzero"),
2132 "unexpected lower-bound diagnostic: {error}"
2133 );
2134 }
2135
2136 #[cfg(feature = "xmldsig")]
2137 #[test]
2138 fn custom_extended_key_purposes_require_valid_oid_arcs() {
2139 let mut policy = KeyTrustPolicy::default();
2142 policy
2143 .allowed_extended_key_usages
2144 .insert(ExtendedKeyPurpose::Other(vec![1, 40, 7]));
2145
2146 assert!(matches!(
2147 policy.validate(),
2148 Err(PolicyViolation::KeyTrust {
2149 reason: "custom extended key purposes must contain valid OID arcs",
2150 })
2151 ));
2152 }
2153
2154 #[cfg(feature = "xmldsig")]
2155 #[test]
2156 fn crl_checking_requires_x509_chain_validation() {
2157 let policy = KeyTrustPolicy {
2160 check_crls: true,
2161 ..KeyTrustPolicy::default()
2162 };
2163
2164 assert!(matches!(
2165 policy.validate(),
2166 Err(PolicyViolation::KeyTrust {
2167 reason: "CRL checking requires X.509 chain validation"
2168 })
2169 ));
2170 }
2171
2172 #[cfg(feature = "xmldsig")]
2173 #[test]
2174 fn legacy_signature_algorithms_require_independent_policy_opt_ins() {
2175 let legacy = [
2176 SignatureAlgorithm::RsaSha1,
2177 SignatureAlgorithm::DsaSha1,
2178 SignatureAlgorithm::HmacSha1,
2179 SignatureAlgorithm::EcdsaSha1,
2180 ];
2181 let mut policy = VerificationPolicy::default();
2182
2183 for algorithm in legacy {
2184 assert!(matches!(
2185 policy.check_signature_algorithm(algorithm),
2186 Err(PolicyViolation::Algorithm { .. })
2187 ));
2188 policy
2189 .key_trust
2190 .allowed_legacy_signature_algorithms
2191 .insert(algorithm);
2192 assert_eq!(policy.check_signature_algorithm(algorithm), Ok(()));
2193 policy
2194 .key_trust
2195 .allowed_legacy_signature_algorithms
2196 .remove(&algorithm);
2197 }
2198 }
2199
2200 #[cfg(feature = "xmldsig")]
2201 #[test]
2202 fn dsa_key_policy_enforces_configured_minimum_and_hard_ceiling() {
2203 let policy = DsaKeyPolicy::default();
2204
2205 assert!(matches!(
2206 policy.validate_modulus_bits(1024),
2207 Err(PolicyViolation::KeySize {
2208 key_type: "DSA",
2209 minimum_bits: 2048,
2210 maximum_bits: 3072,
2211 actual_bits: 1024,
2212 ..
2213 })
2214 ));
2215 assert_eq!(policy.validate_modulus_bits(2048), Ok(()));
2216 assert!(matches!(
2217 policy.validate_modulus_bits(4096),
2218 Err(PolicyViolation::KeySize {
2219 key_type: "DSA",
2220 minimum_bits: 2048,
2221 maximum_bits: 3072,
2222 actual_bits: 4096,
2223 ..
2224 })
2225 ));
2226 }
2227
2228 #[cfg(feature = "xmldsig")]
2229 #[test]
2230 fn hmac_output_policy_cannot_weaken_the_xmldsig_floor() {
2231 let compatibility = HmacPolicy {
2234 minimum_key_bits: 40,
2235 minimum_output_bits: 40,
2236 };
2237
2238 assert_eq!(
2239 compatibility.validate_output(SignatureAlgorithm::HmacSha1, 80),
2240 Ok(())
2241 );
2242 assert!(matches!(
2243 compatibility.validate_output(SignatureAlgorithm::HmacSha1, 72),
2244 Err(PolicyViolation::HmacOutputLength { minimum: 80, .. })
2245 ));
2246 assert!(matches!(
2247 compatibility.validate_output(SignatureAlgorithm::HmacSha256, 120),
2248 Err(PolicyViolation::HmacOutputLength { minimum: 128, .. })
2249 ));
2250 }
2251
2252 #[cfg(feature = "xmldsig")]
2253 #[test]
2254 fn documented_xmldsig_limits_match_hard_limits() {
2255 let docs = include_str!("../docs/xmldsig.md");
2258 let mib = 1024 * 1024;
2259 assert!(docs.contains(&format!(
2260 "Individual resources are limited to {} MiB",
2261 crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING / mib
2262 )));
2263 assert!(docs.contains(&format!(
2264 "complete map to {} MiB",
2265 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING / mib
2266 )));
2267 assert!(docs.contains(&format!(
2268 "ceilings are {} components and {} MiB per operation",
2269 crate::hard_limits::XML_BASE_COMPONENT_CEILING,
2270 crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING / mib
2271 )));
2272 }
2273}