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 NODE_SET_ENTRIES: &str = "node-set entries";
115 pub const NODE_SET_OWNED_STRING_BYTES: &str = "node-set owned string bytes";
116 pub const NODE_SET_CUMULATIVE_OWNED_STRING_BYTES: &str =
117 "cumulative node-set owned string bytes";
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
122#[non_exhaustive]
123pub enum PolicyViolation {
124 #[error("policy compilation failed: {reason}")]
126 Configuration {
127 reason: &'static str,
129 },
130 #[cfg(feature = "xmldsig")]
132 #[error("Manifest reference processing is disabled by policy")]
133 ManifestProcessingDisabled,
134 #[cfg(feature = "xmldsig")]
136 #[error("{profile:?} signature profile rejects document: {reason}")]
137 SignatureProfile {
138 profile: SignatureConstraints,
140 reason: &'static str,
142 },
143 #[error("policy authorization is outside its validity interval")]
145 AuthorizationExpired,
146 #[error("{operation} policy rejects algorithm {algorithm}")]
148 Algorithm {
149 operation: &'static str,
151 algorithm: String,
153 },
154 #[cfg(feature = "xmldsig")]
156 #[error("HMAC output length must be between {minimum} and {maximum} bits: got {actual}")]
157 HmacOutputLength {
158 minimum: usize,
160 maximum: usize,
162 actual: usize,
164 },
165 #[error("{resource} exceeds policy maximum {maximum}: got {actual}")]
167 ResourceLimit {
168 resource: &'static str,
170 maximum: usize,
172 actual: usize,
174 },
175 #[error("{resource} exceeds policy maximum {maximum}")]
177 ResourceLimitExceeded {
178 resource: &'static str,
180 maximum: usize,
182 },
183 #[error("key import KDF iterations must be between 1 and {maximum}")]
185 KdfIterationsOutsideLimit {
186 maximum: usize,
188 },
189 #[error("{resource} has invalid policy limit {actual}: {requirement}")]
191 InvalidResourceLimit {
192 resource: &'static str,
194 requirement: &'static str,
196 actual: usize,
198 },
199 #[error("key/trust policy rejected the operation: {reason}")]
201 KeyTrust {
202 reason: &'static str,
204 },
205 #[error("XML input policy rejected the operation: {reason}")]
207 XmlInput {
208 reason: &'static str,
210 },
211 #[error("{operation} URI policy rejected the operation: {reason}")]
213 Uri {
214 operation: &'static str,
216 reason: &'static str,
218 },
219 #[error(
221 "{operation} policy requires {key_type} keys between {minimum_bits} and {maximum_bits} bits: got {actual_bits}"
222 )]
223 KeySize {
224 operation: &'static str,
226 key_type: &'static str,
228 minimum_bits: usize,
230 maximum_bits: usize,
232 actual_bits: usize,
234 },
235 #[error("{operation} policy rejects invalid {key_type} key material: {reason}")]
237 InvalidKeyMaterial {
238 operation: &'static str,
240 key_type: &'static str,
242 reason: &'static str,
244 },
245}
246
247#[cfg(feature = "xmldsig")]
249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250pub struct HmacPolicy {
251 pub minimum_key_bits: usize,
253 pub minimum_output_bits: usize,
255}
256
257#[cfg(feature = "xmldsig")]
258impl Default for HmacPolicy {
259 fn default() -> Self {
260 Self {
261 minimum_key_bits: 128,
262 minimum_output_bits: 128,
263 }
264 }
265}
266
267#[cfg(feature = "xmldsig")]
268impl HmacPolicy {
269 pub(crate) fn validate(self) -> Result<(), PolicyViolation> {
270 if self.minimum_key_bits == 0 || self.minimum_output_bits == 0 {
271 return Err(PolicyViolation::KeyTrust {
272 reason: "HMAC minimum key and output lengths must be nonzero",
273 });
274 }
275 Ok(())
276 }
277
278 pub(crate) fn validate_key_bytes(self, key_bytes: usize) -> Result<(), PolicyViolation> {
279 self.validate_key_bits(key_bytes.saturating_mul(8))
280 }
281
282 pub(crate) fn validate_key_bits(self, key_bits: usize) -> Result<(), PolicyViolation> {
283 if key_bits < self.minimum_key_bits {
284 return Err(PolicyViolation::InvalidKeyMaterial {
285 operation: "HMAC",
286 key_type: "symmetric",
287 reason: "secret is shorter than the configured minimum",
288 });
289 }
290 Ok(())
291 }
292
293 pub(crate) fn validate_output(
294 self,
295 algorithm: SignatureAlgorithm,
296 selected_bits: usize,
297 ) -> Result<(), PolicyViolation> {
298 let maximum = algorithm
299 .hmac_output_bits()
300 .ok_or_else(|| PolicyViolation::Algorithm {
301 operation: "HMAC",
302 algorithm: algorithm.uri().to_owned(),
303 })?;
304 let minimum = self.minimum_output_bits.max(80).max(maximum / 2);
309 if selected_bits < minimum || selected_bits > maximum {
310 return Err(PolicyViolation::HmacOutputLength {
311 minimum,
312 maximum,
313 actual: selected_bits,
314 });
315 }
316 Ok(())
317 }
318}
319
320#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
322#[derive(Debug, Clone, Copy, PartialEq, Eq)]
323pub struct RsaKeyPolicy {
324 pub minimum_modulus_bits: usize,
326}
327
328#[cfg(feature = "xmldsig")]
330#[derive(Debug, Clone, Copy, PartialEq, Eq)]
331pub struct DsaKeyPolicy {
332 pub minimum_modulus_bits: usize,
334}
335
336#[cfg(feature = "xmldsig")]
337impl Default for DsaKeyPolicy {
338 fn default() -> Self {
339 Self {
340 minimum_modulus_bits: 2048,
341 }
342 }
343}
344
345#[cfg(feature = "xmldsig")]
346impl DsaKeyPolicy {
347 pub fn validate(&self) -> Result<(), PolicyViolation> {
349 validate_modulus_minimum(
350 "minimum DSA modulus bits",
351 self.minimum_modulus_bits,
352 64,
353 "minimum must be a nonzero multiple of 64 bits",
354 crate::hard_limits::DSA_MODULUS_BIT_CEILING,
355 )
356 }
357
358 pub(crate) fn validate_modulus_bits(&self, actual_bits: usize) -> Result<(), PolicyViolation> {
359 self.validate()?;
360 if !(self.minimum_modulus_bits..=crate::hard_limits::DSA_MODULUS_BIT_CEILING)
361 .contains(&actual_bits)
362 {
363 return Err(PolicyViolation::KeySize {
364 operation: "verification",
365 key_type: "DSA",
366 minimum_bits: self.minimum_modulus_bits,
367 maximum_bits: crate::hard_limits::DSA_MODULUS_BIT_CEILING,
368 actual_bits,
369 });
370 }
371 Ok(())
372 }
373}
374
375#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
376impl Default for RsaKeyPolicy {
377 fn default() -> Self {
378 Self {
379 minimum_modulus_bits: 2048,
380 }
381 }
382}
383
384#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
385impl RsaKeyPolicy {
386 pub fn validate(&self) -> Result<(), PolicyViolation> {
388 validate_modulus_minimum(
389 "minimum RSA modulus bits",
390 self.minimum_modulus_bits,
391 8,
392 "minimum must be a nonzero whole-byte width",
393 crate::hard_limits::RSA_MODULUS_BIT_CEILING,
394 )
395 }
396
397 pub(crate) fn validate_components(
398 &self,
399 operation: &'static str,
400 modulus: &[u8],
401 exponent: &[u8],
402 ) -> Result<usize, PolicyViolation> {
403 self.validate()?;
404 let modulus = modulus
405 .iter()
406 .position(|byte| *byte != 0)
407 .map(|start| &modulus[start..])
408 .ok_or(PolicyViolation::InvalidKeyMaterial {
409 operation,
410 key_type: "RSA",
411 reason: "modulus is zero",
412 })?;
413 let modulus_bits = modulus
414 .len()
415 .checked_mul(8)
416 .and_then(|width| width.checked_sub(modulus[0].leading_zeros() as usize))
417 .ok_or(PolicyViolation::InvalidKeyMaterial {
418 operation,
419 key_type: "RSA",
420 reason: "modulus width overflows",
421 })?;
422 let exponent = if exponent.is_empty() || exponent.len() > 8 {
423 None
424 } else {
425 let mut bytes = [0_u8; 8];
426 bytes[8 - exponent.len()..].copy_from_slice(exponent);
427 Some(u64::from_be_bytes(bytes))
428 };
429 self.validate_public_metadata(operation, modulus_bits, exponent)
430 }
431
432 pub(crate) fn validate_public_metadata(
433 &self,
434 operation: &'static str,
435 modulus_bits: usize,
436 exponent: Option<u64>,
437 ) -> Result<usize, PolicyViolation> {
438 self.validate()?;
439 if modulus_bits == 0 {
440 return Err(PolicyViolation::InvalidKeyMaterial {
441 operation,
442 key_type: "RSA",
443 reason: "modulus is zero",
444 });
445 }
446 if !(self.minimum_modulus_bits..=crate::hard_limits::RSA_MODULUS_BIT_CEILING)
447 .contains(&modulus_bits)
448 {
449 return Err(PolicyViolation::KeySize {
450 operation,
451 key_type: "RSA",
452 minimum_bits: self.minimum_modulus_bits,
453 maximum_bits: crate::hard_limits::RSA_MODULUS_BIT_CEILING,
454 actual_bits: modulus_bits,
455 });
456 }
457 let exponent = exponent.ok_or(PolicyViolation::InvalidKeyMaterial {
458 operation,
459 key_type: "RSA",
460 reason: "public exponent has invalid encoding",
461 })?;
462 if !(3..=((1_u64 << 33) - 1)).contains(&exponent) || exponent % 2 == 0 {
463 return Err(PolicyViolation::InvalidKeyMaterial {
464 operation,
465 key_type: "RSA",
466 reason: "public exponent is outside the supported odd range",
467 });
468 }
469 Ok(modulus_bits.div_ceil(8))
470 }
471}
472
473#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
474fn validate_modulus_minimum(
475 resource: &'static str,
476 minimum_bits: usize,
477 alignment_bits: usize,
478 requirement: &'static str,
479 ceiling: usize,
480) -> Result<(), PolicyViolation> {
481 if minimum_bits == 0 || !minimum_bits.is_multiple_of(alignment_bits) {
482 return Err(PolicyViolation::InvalidResourceLimit {
483 resource,
484 requirement,
485 actual: minimum_bits,
486 });
487 }
488 ResourcePolicy::within(resource, minimum_bits, ceiling)
489}
490
491#[derive(Debug, Clone, PartialEq, Eq)]
493pub struct ResourcePolicy {
494 pub max_xml_nodes: usize,
496 pub max_xml_depth: usize,
498 pub max_xml_namespace_bindings: usize,
501 pub max_references: usize,
503 pub max_signatures: usize,
505 pub max_transforms_per_reference: usize,
507 pub max_xml_base_components: usize,
509 pub max_xml_base_resolution_bytes: usize,
511 pub max_canonicalized_bytes: usize,
513 pub max_external_resource_bytes: usize,
515 pub max_external_resource_total_bytes: usize,
517 pub max_encryption_plaintext_bytes: usize,
519 pub max_xml_document_bytes: usize,
521 pub max_xml_parse_work_bytes: usize,
523 pub max_encryption_recipients: usize,
525 pub max_encryption_metadata_bytes: usize,
527 pub max_key_candidates: usize,
530 pub max_x509_work: usize,
533 pub max_x509_workspace_bytes: usize,
536 pub max_key_import_kdf_work: usize,
539 pub max_key_import_kdf_memory_bytes: usize,
541 pub max_key_info_reference_depth: usize,
543 pub max_base64_transform_input_bytes: usize,
545 pub max_base64_transform_output_bytes: usize,
547 pub max_xpath_expressions: usize,
549 pub max_xpath_expression_bytes: usize,
551 pub max_xpath_expression_complexity: usize,
553 pub max_xpath_context_evaluations: usize,
555 pub max_xpath_evaluation_work: usize,
557 pub max_xpath_mirror_string_bytes: usize,
559 pub max_xpath_string_work_bytes: usize,
561 pub max_xpath_namespace_bindings: usize,
563 pub max_xpath_namespace_bytes: usize,
565 pub max_xpath_filters: usize,
567 pub max_node_set_filter_work: usize,
569 pub max_node_set_entries: usize,
573 pub max_node_set_owned_string_bytes: usize,
575 pub max_node_set_cumulative_owned_string_bytes: usize,
577 pub max_opc_parameter_bytes: usize,
579 pub max_opc_workspace_bytes: usize,
581}
582
583impl Default for ResourcePolicy {
584 fn default() -> Self {
585 Self {
586 max_xml_nodes: crate::hard_limits::XML_DOCUMENT_NODE_CEILING as usize,
587 max_xml_depth: crate::hard_limits::XML_DOCUMENT_DEPTH_CEILING,
588 max_xml_namespace_bindings: crate::hard_limits::XML_NAMESPACE_BINDING_CEILING,
589 max_references: crate::hard_limits::SIGNATURE_REFERENCE_CEILING,
590 max_signatures: crate::hard_limits::VERIFICATION_SIGNATURE_CEILING,
591 max_transforms_per_reference: crate::hard_limits::REFERENCE_TRANSFORM_CEILING,
592 max_xml_base_components: crate::hard_limits::XML_BASE_COMPONENT_CEILING,
593 max_xml_base_resolution_bytes: crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING,
594 max_canonicalized_bytes: crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING,
595 max_external_resource_bytes: crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING,
596 max_external_resource_total_bytes:
597 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING,
598 max_encryption_plaintext_bytes: crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING,
599 max_xml_document_bytes: crate::hard_limits::XML_DOCUMENT_BYTE_CEILING,
600 max_xml_parse_work_bytes: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
601 max_encryption_recipients: crate::hard_limits::ENCRYPTION_RECIPIENT_CEILING,
602 max_encryption_metadata_bytes: crate::hard_limits::ENCRYPTION_METADATA_BYTE_CEILING,
603 max_key_candidates: crate::hard_limits::KEY_CANDIDATE_CEILING,
604 max_x509_work: crate::hard_limits::X509_WORK_CEILING,
605 max_x509_workspace_bytes: crate::hard_limits::X509_WORKSPACE_BYTE_CEILING,
606 max_key_import_kdf_work: crate::hard_limits::KEY_IMPORT_KDF_WORK_CEILING as usize,
607 max_key_import_kdf_memory_bytes: crate::hard_limits::KEY_IMPORT_KDF_MEMORY_CEILING,
608 max_key_info_reference_depth: crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING,
609 max_base64_transform_input_bytes:
610 crate::hard_limits::BASE64_TRANSFORM_INPUT_BYTE_CEILING,
611 max_base64_transform_output_bytes:
612 crate::hard_limits::BASE64_TRANSFORM_OUTPUT_BYTE_CEILING,
613 max_xpath_expressions: crate::hard_limits::XPATH_EXPRESSION_COUNT_CEILING,
614 max_xpath_expression_bytes: crate::hard_limits::XPATH_EXPRESSION_BYTE_CEILING,
615 max_xpath_expression_complexity:
616 crate::hard_limits::XPATH_EXPRESSION_COMPLEXITY_CEILING,
617 max_xpath_context_evaluations: crate::hard_limits::XPATH_CONTEXT_EVALUATION_CEILING,
618 max_xpath_evaluation_work: crate::hard_limits::XPATH_EVALUATION_WORK_CEILING,
619 max_xpath_mirror_string_bytes: crate::hard_limits::XPATH_MIRROR_STRING_BYTE_CEILING,
620 max_xpath_string_work_bytes: crate::hard_limits::XPATH_STRING_WORK_BYTE_CEILING,
621 max_xpath_namespace_bindings: crate::hard_limits::XPATH_NAMESPACE_BINDING_CEILING,
622 max_xpath_namespace_bytes: crate::hard_limits::XPATH_NAMESPACE_BYTE_CEILING,
623 max_xpath_filters: crate::hard_limits::XPATH_FILTER_COUNT_CEILING,
624 max_node_set_filter_work: crate::hard_limits::NODE_SET_FILTER_WORK_CEILING,
625 max_node_set_entries: 262_144,
626 max_node_set_owned_string_bytes: crate::hard_limits::NODE_SET_OWNED_STRING_BYTE_CEILING,
627 max_node_set_cumulative_owned_string_bytes:
628 crate::hard_limits::NODE_SET_CUMULATIVE_OWNED_STRING_BYTE_CEILING,
629 max_opc_workspace_bytes: crate::hard_limits::OPC_WORKSPACE_BYTE_CEILING,
630 max_opc_parameter_bytes: crate::hard_limits::OPC_PARAMETER_BYTE_CEILING,
631 }
632 }
633}
634
635impl ResourcePolicy {
636 pub fn validate(&self) -> Result<(), PolicyViolation> {
638 Self::within(
639 resource_name::XML_NODES,
640 self.max_xml_nodes,
641 crate::hard_limits::XML_DOCUMENT_NODE_CEILING as usize,
642 )?;
643 Self::within(
644 resource_name::XML_DEPTH,
645 self.max_xml_depth,
646 crate::hard_limits::XML_DOCUMENT_DEPTH_CEILING,
647 )?;
648 Self::within(
649 resource_name::XML_NAMESPACE_BINDINGS,
650 self.max_xml_namespace_bindings,
651 crate::hard_limits::XML_NAMESPACE_BINDING_CEILING,
652 )?;
653 Self::within(
654 resource_name::CANONICALIZED_BYTES,
655 self.max_canonicalized_bytes,
656 crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING,
657 )?;
658 Self::within(
659 resource_name::SIGNATURE_REFERENCES,
660 self.max_references,
661 crate::hard_limits::SIGNATURE_REFERENCE_CEILING,
662 )?;
663 Self::within(
664 resource_name::VERIFICATION_SIGNATURES,
665 self.max_signatures,
666 crate::hard_limits::VERIFICATION_SIGNATURE_CEILING,
667 )?;
668 Self::within(
669 resource_name::REFERENCE_TRANSFORMS,
670 self.max_transforms_per_reference,
671 crate::hard_limits::REFERENCE_TRANSFORM_CEILING,
672 )?;
673 Self::within(
674 resource_name::XML_BASE_COMPONENTS,
675 self.max_xml_base_components,
676 crate::hard_limits::XML_BASE_COMPONENT_CEILING,
677 )?;
678 Self::within(
679 resource_name::XML_BASE_RESOLUTION_BYTES,
680 self.max_xml_base_resolution_bytes,
681 crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING,
682 )?;
683 Self::within(
684 resource_name::XML_DOCUMENT,
685 self.max_xml_document_bytes,
686 crate::hard_limits::XML_DOCUMENT_BYTE_CEILING,
687 )?;
688 Self::within(
689 resource_name::XML_PARSE_WORK_BYTES,
690 self.max_xml_parse_work_bytes,
691 crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
692 )?;
693 Self::within(
694 resource_name::EXTERNAL_RESOURCE_BYTES,
695 self.max_external_resource_bytes,
696 crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING,
697 )?;
698 Self::within(
699 resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
700 self.max_external_resource_total_bytes,
701 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING,
702 )?;
703 Self::within(
704 resource_name::ENCRYPTION_PLAINTEXT_BYTES,
705 self.max_encryption_plaintext_bytes,
706 crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING,
707 )?;
708 Self::within(
709 resource_name::ENCRYPTION_RECIPIENTS,
710 self.max_encryption_recipients,
711 crate::hard_limits::ENCRYPTION_RECIPIENT_CEILING,
712 )?;
713 Self::within(
714 resource_name::ENCRYPTION_METADATA_BYTES,
715 self.max_encryption_metadata_bytes,
716 crate::hard_limits::ENCRYPTION_METADATA_BYTE_CEILING,
717 )?;
718 for (resource, selected, ceiling) in [
719 (
720 resource_name::X509_WORK,
721 self.max_x509_work,
722 crate::hard_limits::X509_WORK_CEILING,
723 ),
724 (
725 resource_name::X509_WORKSPACE_BYTES,
726 self.max_x509_workspace_bytes,
727 crate::hard_limits::X509_WORKSPACE_BYTE_CEILING,
728 ),
729 (
730 resource_name::KEY_CANDIDATES,
731 self.max_key_candidates,
732 crate::hard_limits::KEY_CANDIDATE_CEILING,
733 ),
734 (
735 resource_name::KEY_IMPORT_KDF_WORK,
736 self.max_key_import_kdf_work,
737 crate::hard_limits::KEY_IMPORT_KDF_WORK_CEILING as usize,
738 ),
739 (
740 resource_name::KEY_IMPORT_KDF_MEMORY,
741 self.max_key_import_kdf_memory_bytes,
742 crate::hard_limits::KEY_IMPORT_KDF_MEMORY_CEILING,
743 ),
744 (
745 resource_name::KEY_INFO_REFERENCE_DEPTH,
746 self.max_key_info_reference_depth,
747 crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING,
748 ),
749 (
750 resource_name::BASE64_TRANSFORM_INPUT_BYTES,
751 self.max_base64_transform_input_bytes,
752 crate::hard_limits::BASE64_TRANSFORM_INPUT_BYTE_CEILING,
753 ),
754 (
755 resource_name::BASE64_TRANSFORM_OUTPUT_BYTES,
756 self.max_base64_transform_output_bytes,
757 crate::hard_limits::BASE64_TRANSFORM_OUTPUT_BYTE_CEILING,
758 ),
759 (
760 resource_name::XPATH_EXPRESSIONS,
761 self.max_xpath_expressions,
762 crate::hard_limits::XPATH_EXPRESSION_COUNT_CEILING,
763 ),
764 (
765 resource_name::XPATH_EXPRESSION_BYTES,
766 self.max_xpath_expression_bytes,
767 crate::hard_limits::XPATH_EXPRESSION_BYTE_CEILING,
768 ),
769 (
770 resource_name::XPATH_EXPRESSION_COMPLEXITY,
771 self.max_xpath_expression_complexity,
772 crate::hard_limits::XPATH_EXPRESSION_COMPLEXITY_CEILING,
773 ),
774 (
775 resource_name::XPATH_CONTEXT_EVALUATIONS,
776 self.max_xpath_context_evaluations,
777 crate::hard_limits::XPATH_CONTEXT_EVALUATION_CEILING,
778 ),
779 (
780 resource_name::XPATH_EVALUATION_WORK,
781 self.max_xpath_evaluation_work,
782 crate::hard_limits::XPATH_EVALUATION_WORK_CEILING,
783 ),
784 (
785 resource_name::XPATH_MIRROR_STRING_BYTES,
786 self.max_xpath_mirror_string_bytes,
787 crate::hard_limits::XPATH_MIRROR_STRING_BYTE_CEILING,
788 ),
789 (
790 resource_name::XPATH_STRING_WORK_BYTES,
791 self.max_xpath_string_work_bytes,
792 crate::hard_limits::XPATH_STRING_WORK_BYTE_CEILING,
793 ),
794 (
795 resource_name::XPATH_NAMESPACE_BINDINGS,
796 self.max_xpath_namespace_bindings,
797 crate::hard_limits::XPATH_NAMESPACE_BINDING_CEILING,
798 ),
799 (
800 resource_name::XPATH_NAMESPACE_BYTES,
801 self.max_xpath_namespace_bytes,
802 crate::hard_limits::XPATH_NAMESPACE_BYTE_CEILING,
803 ),
804 (
805 resource_name::XPATH_FILTERS,
806 self.max_xpath_filters,
807 crate::hard_limits::XPATH_FILTER_COUNT_CEILING,
808 ),
809 (
810 resource_name::NODE_SET_FILTER_WORK,
811 self.max_node_set_filter_work,
812 crate::hard_limits::NODE_SET_FILTER_WORK_CEILING,
813 ),
814 (
815 resource_name::NODE_SET_ENTRIES,
816 self.max_node_set_entries,
817 crate::hard_limits::NODE_SET_ENTRY_CEILING,
818 ),
819 (
820 resource_name::NODE_SET_OWNED_STRING_BYTES,
821 self.max_node_set_owned_string_bytes,
822 crate::hard_limits::NODE_SET_OWNED_STRING_BYTE_CEILING,
823 ),
824 (
825 resource_name::NODE_SET_CUMULATIVE_OWNED_STRING_BYTES,
826 self.max_node_set_cumulative_owned_string_bytes,
827 crate::hard_limits::NODE_SET_CUMULATIVE_OWNED_STRING_BYTE_CEILING,
828 ),
829 ] {
830 Self::within(resource, selected, ceiling)?;
831 }
832 Self::within(
833 resource_name::OPC_WORKSPACE_BYTES,
834 self.max_opc_workspace_bytes,
835 crate::hard_limits::OPC_WORKSPACE_BYTE_CEILING,
836 )?;
837 Self::within(
838 resource_name::OPC_PARAMETER_BYTES,
839 self.max_opc_parameter_bytes,
840 crate::hard_limits::OPC_PARAMETER_BYTE_CEILING,
841 )?;
842 Ok(())
843 }
844
845 pub(crate) fn validate_xml_document_len(&self, actual: usize) -> Result<(), PolicyViolation> {
846 if actual > self.max_xml_document_bytes {
847 return Err(PolicyViolation::ResourceLimit {
848 resource: resource_name::XML_DOCUMENT,
849 maximum: self.max_xml_document_bytes,
850 actual,
851 });
852 }
853 Ok(())
854 }
855
856 #[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
857 pub fn validate_key_candidates(&self, actual: usize) -> Result<(), PolicyViolation> {
859 if actual > self.max_key_candidates {
860 return Err(PolicyViolation::ResourceLimit {
861 resource: resource_name::KEY_CANDIDATES,
862 maximum: self.max_key_candidates,
863 actual,
864 });
865 }
866 Ok(())
867 }
868
869 #[cfg(feature = "xmldsig")]
871 pub fn validate_key_info_reference_depth(&self, actual: usize) -> Result<(), PolicyViolation> {
872 if actual > self.max_key_info_reference_depth {
873 return Err(PolicyViolation::ResourceLimit {
874 resource: resource_name::KEY_INFO_REFERENCE_DEPTH,
875 maximum: self.max_key_info_reference_depth,
876 actual,
877 });
878 }
879 Ok(())
880 }
881
882 pub(crate) fn effective_xml_nodes(&self) -> u32 {
883 u32::try_from(self.max_xml_nodes)
884 .unwrap_or(crate::hard_limits::XML_DOCUMENT_NODE_CEILING)
885 .min(crate::hard_limits::XML_DOCUMENT_NODE_CEILING)
886 }
887
888 #[cfg(feature = "xmldsig")]
889 pub(crate) fn effective_canonicalized_bytes(&self) -> usize {
890 self.max_canonicalized_bytes
891 .min(crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING)
892 }
893
894 #[cfg(feature = "xmldsig")]
895 pub(crate) fn effective_xml_base_components(&self) -> usize {
896 self.max_xml_base_components
897 .min(crate::hard_limits::XML_BASE_COMPONENT_CEILING)
898 }
899
900 #[cfg(feature = "xmldsig")]
901 pub(crate) fn effective_xml_base_resolution_bytes(&self) -> usize {
902 self.max_xml_base_resolution_bytes
903 .min(crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING)
904 }
905
906 fn within(
907 resource: &'static str,
908 selected: usize,
909 ceiling: usize,
910 ) -> Result<(), PolicyViolation> {
911 if selected > ceiling {
912 return Err(PolicyViolation::ResourceLimit {
913 resource,
914 maximum: ceiling,
915 actual: selected,
916 });
917 }
918 Ok(())
919 }
920
921 #[cfg(feature = "xmldsig")]
922 fn nonzero_within(
923 resource: &'static str,
924 selected: usize,
925 ceiling: usize,
926 ) -> Result<(), PolicyViolation> {
927 if selected == 0 {
928 return Err(PolicyViolation::InvalidResourceLimit {
929 resource,
930 requirement: "limit must be nonzero",
931 actual: selected,
932 });
933 }
934 Self::within(resource, selected, ceiling)
935 }
936}
937
938#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
940pub struct XmlInputPolicy {
941 pub allow_internal_dtd: bool,
943}
944
945#[cfg(feature = "xmldsig")]
949#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
950pub enum OpcRelationshipEdition {
951 Ecma2012,
953 #[default]
955 Ecma2021,
956}
957
958#[cfg(feature = "xmldsig")]
960#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
961pub enum SameDocumentIdSemantics {
962 #[default]
964 Specification,
965 XmlSecBarename,
971 XmlSecVisa3d,
975}
976
977#[cfg(feature = "xmldsig")]
979#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
980pub enum EcdsaSignatureValueEncoding {
981 #[default]
983 XmlDsig,
984 XmlSecAsn1Der,
986}
987
988#[cfg(feature = "xmldsig")]
990#[derive(Debug, Clone, Default, PartialEq, Eq)]
991pub struct TransformPolicy {
992 pub allowed_algorithms: Option<HashSet<String>>,
994 pub xpath_here_semantics: XPathHereSemantics,
996 pub same_document_id_semantics: SameDocumentIdSemantics,
998 pub opc_relationship_edition: OpcRelationshipEdition,
1000}
1001
1002#[cfg(feature = "xmldsig")]
1004#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1005pub struct UriPolicy {
1006 pub references: UriTypeSet,
1008 pub retrieval_methods: UriTypeSet,
1010 pub key_info_references: UriTypeSet,
1012}
1013
1014#[cfg(feature = "xmldsig")]
1015impl Default for UriPolicy {
1016 fn default() -> Self {
1017 Self {
1018 references: UriTypeSet::SAME_DOCUMENT,
1019 retrieval_methods: UriTypeSet::SAME_DOCUMENT,
1020 key_info_references: UriTypeSet::SAME_DOCUMENT,
1021 }
1022 }
1023}
1024
1025#[cfg(feature = "xmldsig")]
1027#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1028pub struct KeySourcePolicy {
1029 pub preset_key: bool,
1031 pub key_name: bool,
1033 pub key_value: bool,
1035 pub der_encoded_key_value: bool,
1037 pub x509_data: bool,
1039 pub key_info_reference: bool,
1041}
1042
1043#[cfg(feature = "xmldsig")]
1044impl Default for KeySourcePolicy {
1045 fn default() -> Self {
1046 Self {
1047 preset_key: true,
1048 key_name: true,
1049 key_value: true,
1050 der_encoded_key_value: true,
1051 x509_data: true,
1052 key_info_reference: true,
1053 }
1054 }
1055}
1056
1057#[cfg(feature = "xmldsig")]
1059#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1060#[non_exhaustive]
1061pub enum ExtendedKeyPurpose {
1062 ServerAuth,
1064 ClientAuth,
1066 CodeSigning,
1068 EmailProtection,
1070 TimeStamping,
1072 OcspSigning,
1074 Other(Vec<u64>),
1076}
1077
1078#[cfg(feature = "xmldsig")]
1080#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1081pub enum VerificationTrustMode {
1082 #[default]
1084 RequireTrustedKey,
1085 CryptographicOnly,
1087}
1088
1089#[cfg(feature = "xmldsig")]
1091#[derive(Debug, Clone, PartialEq, Eq)]
1092pub struct KeyTrustPolicy {
1093 pub mode: VerificationTrustMode,
1095 pub verify_x509_chains: bool,
1097 pub max_x509_chain_depth: usize,
1099 pub max_x509_candidate_paths: usize,
1101 pub allowed_legacy_signature_algorithms: HashSet<SignatureAlgorithm>,
1103 pub parameterized_rsa_pss: RsaPssPermission,
1105 pub certificate_signature_algorithms: Option<CertificateSignatureAlgorithms>,
1110 pub rsa_keys: RsaKeyPolicy,
1112 pub dsa_keys: DsaKeyPolicy,
1114 pub allowed_extended_key_usages: HashSet<ExtendedKeyPurpose>,
1120 pub check_crls: bool,
1123 pub verification_time: Option<SystemTime>,
1125}
1126
1127#[cfg(feature = "xmldsig")]
1128impl Default for KeyTrustPolicy {
1129 fn default() -> Self {
1130 Self {
1131 mode: VerificationTrustMode::RequireTrustedKey,
1132 verify_x509_chains: false,
1133 max_x509_chain_depth: crate::hard_limits::X509_CHAIN_DEPTH_CEILING,
1134 max_x509_candidate_paths: crate::hard_limits::X509_CANDIDATE_PATH_CEILING,
1135 allowed_legacy_signature_algorithms: HashSet::new(),
1136 parameterized_rsa_pss: RsaPssPermission::ExactOnly,
1137 certificate_signature_algorithms: None,
1138 rsa_keys: RsaKeyPolicy::default(),
1139 dsa_keys: DsaKeyPolicy::default(),
1140 allowed_extended_key_usages: HashSet::new(),
1141 check_crls: false,
1142 verification_time: None,
1143 }
1144 }
1145}
1146
1147#[cfg(feature = "xmldsig")]
1148#[derive(Debug, Clone, PartialEq, Eq)]
1149pub enum CertificateSignatureAlgorithms {
1151 AllSupported,
1154 Allowlist(HashSet<crate::provider::X509SignatureAlgorithm>),
1156}
1157
1158#[cfg(feature = "xmldsig")]
1159impl KeyTrustPolicy {
1160 pub(crate) fn check_certificate_signature_algorithm(
1161 algorithm: crate::provider::X509SignatureAlgorithm,
1162 allowed: Option<&CertificateSignatureAlgorithms>,
1163 ) -> Result<(), PolicyViolation> {
1164 let permitted = match allowed {
1168 Some(CertificateSignatureAlgorithms::AllSupported) => true,
1169 Some(CertificateSignatureAlgorithms::Allowlist(allowed)) => {
1170 allowed.contains(&algorithm)
1171 }
1172 None => !matches!(
1173 algorithm,
1174 crate::provider::X509SignatureAlgorithm::PostQuantum(_)
1175 ),
1176 };
1177 if !permitted {
1178 return Err(PolicyViolation::Algorithm {
1179 operation: "certificate/CRL verification",
1180 algorithm: algorithm
1181 .oid()
1182 .map_or_else(|| format!("{algorithm:?}"), str::to_owned),
1183 });
1184 }
1185 Ok(())
1186 }
1187
1188 pub(crate) fn validate(&self) -> Result<(), PolicyViolation> {
1189 if self.check_crls && !self.verify_x509_chains {
1190 return Err(PolicyViolation::KeyTrust {
1191 reason: "CRL checking requires X.509 chain validation",
1192 });
1193 }
1194 if self
1195 .allowed_extended_key_usages
1196 .iter()
1197 .any(|purpose| match purpose {
1198 ExtendedKeyPurpose::Other(arcs) => {
1199 arcs.len() < 2 || arcs[0] > 2 || (arcs[0] < 2 && arcs[1] > 39)
1200 }
1201 _ => false,
1202 })
1203 {
1204 return Err(PolicyViolation::KeyTrust {
1205 reason: "custom extended key purposes must contain valid OID arcs",
1206 });
1207 }
1208 self.rsa_keys.validate()?;
1209 self.dsa_keys.validate()?;
1210 ResourcePolicy::nonzero_within(
1211 "X.509 chain depth",
1212 self.max_x509_chain_depth,
1213 crate::hard_limits::X509_CHAIN_DEPTH_CEILING,
1214 )?;
1215 ResourcePolicy::nonzero_within(
1216 "X.509 candidate paths",
1217 self.max_x509_candidate_paths,
1218 crate::hard_limits::X509_CANDIDATE_PATH_CEILING,
1219 )
1220 }
1221}
1222
1223#[cfg(feature = "xmldsig")]
1226#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1227pub enum ManifestProcessing {
1228 #[default]
1230 Ignore,
1231 Process,
1233}
1234
1235#[cfg(feature = "xmldsig")]
1237#[derive(Debug, Clone, Default)]
1238pub struct VerificationPolicy {
1239 pub validity: Option<PolicyValidity>,
1241 pub signature_constraints: SignatureConstraints,
1243 pub key_establishment: KeyEstablishmentPolicy,
1245 pub parameterized_rsa_pss: RsaPssPermission,
1248 pub signature_algorithms: Option<HashSet<SignatureAlgorithm>>,
1252 pub digest_algorithms: Option<HashSet<DigestAlgorithm>>,
1254 pub hmac: HmacPolicy,
1256 pub ecdsa_signature_value_encoding: EcdsaSignatureValueEncoding,
1258 pub key_trust: KeyTrustPolicy,
1260 pub key_sources: KeySourcePolicy,
1262 pub uris: UriPolicy,
1264 pub transforms: TransformPolicy,
1266 pub manifest_processing: ManifestProcessing,
1268 pub xml: XmlInputPolicy,
1270 pub resources: ResourcePolicy,
1272}
1273
1274#[cfg(feature = "xmldsig")]
1275impl VerificationPolicy {
1276 pub(crate) fn check_digest_algorithm(
1277 &self,
1278 algorithm: DigestAlgorithm,
1279 ) -> Result<(), PolicyViolation> {
1280 if self
1281 .digest_algorithms
1282 .as_ref()
1283 .map_or(!algorithm.requires_explicit_permission(), |allowed| {
1284 allowed.contains(&algorithm)
1285 })
1286 {
1287 Ok(())
1288 } else {
1289 Err(PolicyViolation::Algorithm {
1290 operation: "verification",
1291 algorithm: algorithm.uri().to_owned(),
1292 })
1293 }
1294 }
1295 pub fn validate(&self) -> Result<(), PolicyViolation> {
1297 if let Some(validity) = &self.validity {
1298 validity.validate_at(SystemTime::now())?;
1299 }
1300 if self.signature_constraints != SignatureConstraints::General
1301 && self.key_trust.mode == VerificationTrustMode::CryptographicOnly
1302 {
1303 return Err(PolicyViolation::Configuration {
1304 reason: "target coverage requires trusted-key verification",
1305 });
1306 }
1307 self.resources.validate()?;
1308 self.key_establishment.validate()?;
1309 self.key_trust.validate()?;
1310 self.hmac.validate()
1311 }
1312
1313 pub fn check_signature_algorithm(
1315 &self,
1316 algorithm: SignatureAlgorithm,
1317 ) -> Result<(), PolicyViolation> {
1318 if (matches!(algorithm, SignatureAlgorithm::PostQuantum(_))
1319 || algorithm.requires_explicit_permission())
1320 && !self
1321 .signature_algorithms
1322 .as_ref()
1323 .is_some_and(|allowed| allowed.contains(&algorithm))
1324 {
1325 return Err(PolicyViolation::Algorithm {
1326 operation: "verification",
1327 algorithm: algorithm.uri().to_owned(),
1328 });
1329 }
1330 if (algorithm.uses_legacy_pss_hash()
1331 || matches!(
1332 algorithm,
1333 SignatureAlgorithm::RsaSha1
1334 | SignatureAlgorithm::DsaSha1
1335 | SignatureAlgorithm::HmacSha1
1336 | SignatureAlgorithm::EcdsaSha1
1337 ))
1338 && !self
1339 .key_trust
1340 .allowed_legacy_signature_algorithms
1341 .contains(&algorithm)
1342 && !self.key_trust.parameterized_rsa_pss.permits(algorithm)
1343 {
1344 return Err(PolicyViolation::Algorithm {
1345 operation: "verification",
1346 algorithm: algorithm.uri().to_string(),
1347 });
1348 }
1349 if self
1350 .signature_algorithms
1351 .as_ref()
1352 .is_some_and(|allowed| !allowed.contains(&algorithm))
1353 && !self.parameterized_rsa_pss.permits(algorithm)
1354 {
1355 return Err(PolicyViolation::Algorithm {
1356 operation: "verification",
1357 algorithm: algorithm.uri().to_string(),
1358 });
1359 }
1360 Ok(())
1361 }
1362}
1363
1364#[cfg(feature = "xmldsig")]
1366#[derive(Debug, Clone, Default)]
1367pub struct SigningPolicy {
1368 pub validity: Option<PolicyValidity>,
1370 pub signature_constraints: SignatureConstraints,
1372 pub key_establishment: KeyEstablishmentPolicy,
1374 pub parameterized_rsa_pss: RsaPssPermission,
1376 pub signature_algorithms: Option<HashSet<SignatureAlgorithm>>,
1379 pub digest_algorithms: Option<HashSet<DigestAlgorithm>>,
1381 pub hmac: HmacPolicy,
1383 pub ecdsa_signature_value_encoding: EcdsaSignatureValueEncoding,
1385 pub rsa_keys: RsaKeyPolicy,
1387 pub dsa_keys: DsaKeyPolicy,
1389 pub uris: UriPolicy,
1392 pub transforms: TransformPolicy,
1394 pub manifest_processing: ManifestProcessing,
1396 pub xml: XmlInputPolicy,
1398 pub resources: ResourcePolicy,
1400}
1401
1402#[cfg(feature = "xmldsig")]
1403impl SigningPolicy {
1404 pub fn validate(&self) -> Result<(), PolicyViolation> {
1406 if let Some(validity) = &self.validity {
1407 validity.validate_at(SystemTime::now())?;
1408 }
1409 self.resources.validate()?;
1410 self.key_establishment.validate()?;
1411 self.rsa_keys.validate()?;
1412 self.dsa_keys.validate()?;
1413 self.hmac.validate()
1414 }
1415
1416 pub(crate) fn check_signature_algorithm(
1417 &self,
1418 algorithm: SignatureAlgorithm,
1419 ) -> Result<(), PolicyViolation> {
1420 if self.parameterized_rsa_pss.permits(algorithm) {
1421 return Ok(());
1422 }
1423 check_signing_algorithm(
1424 self.signature_algorithms.as_ref(),
1425 algorithm,
1426 algorithm.signing_allowed(),
1427 algorithm.uri(),
1428 )
1429 }
1430
1431 pub(crate) fn check_digest_algorithm(
1432 &self,
1433 algorithm: DigestAlgorithm,
1434 ) -> Result<(), PolicyViolation> {
1435 check_signing_algorithm(
1436 self.digest_algorithms.as_ref(),
1437 algorithm,
1438 algorithm.signing_allowed(),
1439 algorithm.uri(),
1440 )
1441 }
1442}
1443
1444#[cfg(feature = "xmldsig")]
1445fn check_signing_algorithm<T: Eq + std::hash::Hash>(
1446 allowlist: Option<&HashSet<T>>,
1447 algorithm: T,
1448 default_allowed: bool,
1449 uri: &str,
1450) -> Result<(), PolicyViolation> {
1451 if allowlist.map_or(default_allowed, |allowed| allowed.contains(&algorithm)) {
1452 Ok(())
1453 } else {
1454 Err(PolicyViolation::Algorithm {
1455 operation: "signing",
1456 algorithm: uri.to_owned(),
1457 })
1458 }
1459}
1460
1461#[cfg(feature = "xmlenc")]
1463pub(crate) fn check_content_algorithm(
1464 allowlist: Option<&HashSet<DataEncryptionAlgorithm>>,
1465 algorithm: DataEncryptionAlgorithm,
1466 operation: &'static str,
1467) -> Result<(), PolicyViolation> {
1468 if allowlist.map_or(!algorithm.requires_explicit_permission(), |allowed| {
1469 allowed.contains(&algorithm)
1470 }) {
1471 Ok(())
1472 } else {
1473 Err(PolicyViolation::Algorithm {
1474 operation,
1475 algorithm: algorithm.uri().to_owned(),
1476 })
1477 }
1478}
1479
1480#[cfg(feature = "xmlenc")]
1482#[derive(Debug, Clone, Default)]
1483pub struct EncryptionPolicy {
1484 pub validity: Option<PolicyValidity>,
1486 pub data_algorithms: Option<HashSet<DataEncryptionAlgorithm>>,
1488 pub key_transport_algorithms: Option<HashSet<KeyTransportAlgorithm>>,
1490 pub key_wrap_algorithms: Option<HashSet<KeyWrapAlgorithm>>,
1492 pub oaep_digests: Option<HashSet<OaepDigestAlgorithm>>,
1494 pub rsa_keys: RsaKeyPolicy,
1496 pub key_establishment: KeyEstablishmentPolicy,
1498 pub xml: XmlInputPolicy,
1500 pub resources: ResourcePolicy,
1502}
1503
1504#[cfg(feature = "xmlenc")]
1505impl EncryptionPolicy {
1506 pub fn validate(&self) -> Result<(), PolicyViolation> {
1508 if let Some(validity) = &self.validity {
1509 validity.validate_at(SystemTime::now())?;
1510 }
1511 self.resources.validate()?;
1512 self.key_establishment.validate()?;
1513 self.rsa_keys.validate()
1514 }
1515}
1516
1517#[cfg(feature = "xmlenc")]
1519#[derive(Debug, Clone, Default)]
1520pub struct DecryptionPolicy {
1521 pub validity: Option<PolicyValidity>,
1523 pub uris: UriPolicy,
1525 pub transforms: TransformPolicy,
1527 pub data_algorithms: Option<HashSet<DataEncryptionAlgorithm>>,
1529 pub key_transport_algorithms: Option<HashSet<KeyTransportAlgorithm>>,
1531 pub key_wrap_algorithms: Option<HashSet<KeyWrapAlgorithm>>,
1533 pub oaep_digests: Option<HashSet<OaepDigestAlgorithm>>,
1535 pub rsa_keys: RsaKeyPolicy,
1538 pub key_establishment: KeyEstablishmentPolicy,
1540 pub xml: XmlInputPolicy,
1542 pub resources: ResourcePolicy,
1544}
1545
1546#[cfg(feature = "xmlenc")]
1547impl DecryptionPolicy {
1548 pub fn validate(&self) -> Result<(), PolicyViolation> {
1550 if let Some(validity) = &self.validity {
1551 validity.validate_at(SystemTime::now())?;
1552 }
1553 self.resources.validate()?;
1554 self.key_establishment.validate()?;
1555 self.rsa_keys.validate()
1556 }
1557}
1558
1559#[cfg(test)]
1560mod tests {
1561 use super::*;
1562
1563 #[cfg(all(feature = "legacy-algorithms", feature = "xmldsig"))]
1564 #[test]
1565 fn legacy_digest_permission_is_explicit_and_operation_local() {
1566 for algorithm in [DigestAlgorithm::Md5, DigestAlgorithm::Ripemd160] {
1569 assert!(
1570 VerificationPolicy::default()
1571 .check_digest_algorithm(algorithm)
1572 .is_err()
1573 );
1574 assert!(
1575 SigningPolicy::default()
1576 .check_digest_algorithm(algorithm)
1577 .is_err()
1578 );
1579 let verification = VerificationPolicy {
1580 digest_algorithms: Some(HashSet::from([algorithm])),
1581 ..VerificationPolicy::default()
1582 };
1583 let signing = SigningPolicy {
1584 digest_algorithms: Some(HashSet::from([algorithm])),
1585 ..SigningPolicy::default()
1586 };
1587 assert!(verification.check_digest_algorithm(algorithm).is_ok());
1588 assert!(signing.check_digest_algorithm(algorithm).is_ok());
1589 assert!(
1590 verification
1591 .check_digest_algorithm(DigestAlgorithm::Sha256)
1592 .is_err()
1593 );
1594 assert!(
1595 signing
1596 .check_digest_algorithm(DigestAlgorithm::Sha256)
1597 .is_err()
1598 );
1599 }
1600 }
1601
1602 #[cfg(feature = "xmldsig")]
1603 #[test]
1604 fn certificate_permissions_keep_exact_pss_and_compatibility_distinct() {
1605 use crate::provider::X509SignatureAlgorithm;
1606 use crate::xmldsig::{DigestAlgorithm, PqAlgorithm};
1607 let exact = X509SignatureAlgorithm::RsaPss {
1610 digest: DigestAlgorithm::Sha256,
1611 mgf_digest: DigestAlgorithm::Sha256,
1612 salt_len: 32,
1613 };
1614 let allowed = CertificateSignatureAlgorithms::Allowlist(HashSet::from([exact]));
1615 assert!(
1616 KeyTrustPolicy::check_certificate_signature_algorithm(exact, Some(&allowed)).is_ok()
1617 );
1618 for salt_len in [0, 17, 64, 255] {
1619 let method = X509SignatureAlgorithm::RsaPss {
1620 digest: DigestAlgorithm::Sha256,
1621 mgf_digest: DigestAlgorithm::Sha256,
1622 salt_len,
1623 };
1624 assert!(
1625 KeyTrustPolicy::check_certificate_signature_algorithm(method, Some(&allowed))
1626 .is_err()
1627 );
1628 assert!(
1629 KeyTrustPolicy::check_certificate_signature_algorithm(
1630 method,
1631 Some(&CertificateSignatureAlgorithms::AllSupported)
1632 )
1633 .is_ok()
1634 );
1635 }
1636 let pq = X509SignatureAlgorithm::PostQuantum(PqAlgorithm::MlDsa44);
1637 assert!(KeyTrustPolicy::check_certificate_signature_algorithm(pq, None).is_err());
1638 assert!(
1639 KeyTrustPolicy::check_certificate_signature_algorithm(
1640 pq,
1641 Some(&CertificateSignatureAlgorithms::AllSupported)
1642 )
1643 .is_ok()
1644 );
1645 }
1646
1647 #[test]
1648 fn resource_policy_cannot_exceed_implementation_ceiling() {
1649 let policy = ResourcePolicy {
1650 max_xml_nodes: 100_001,
1651 ..ResourcePolicy::default()
1652 };
1653 assert!(matches!(
1654 policy.validate(),
1655 Err(PolicyViolation::ResourceLimit {
1656 resource: resource_name::XML_NODES,
1657 maximum: 100_000,
1658 actual: 100_001,
1659 })
1660 ));
1661 }
1662
1663 #[test]
1664 fn xml_parse_work_policy_cannot_exceed_implementation_ceiling() {
1665 let policy = ResourcePolicy {
1666 max_xml_parse_work_bytes: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING
1667 .saturating_add(1),
1668 ..ResourcePolicy::default()
1669 };
1670
1671 assert_eq!(
1672 policy.validate(),
1673 Err(PolicyViolation::ResourceLimit {
1674 resource: resource_name::XML_PARSE_WORK_BYTES,
1675 maximum: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
1676 actual: crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING.saturating_add(1),
1677 })
1678 );
1679 }
1680
1681 #[test]
1682 fn every_resource_policy_field_obeys_its_hard_ceiling() {
1683 type Case = (&'static str, usize, fn(&mut ResourcePolicy) -> &mut usize);
1687 let cases: &[Case] = &[
1688 (
1689 resource_name::X509_WORK,
1690 crate::hard_limits::X509_WORK_CEILING,
1691 |p| &mut p.max_x509_work,
1692 ),
1693 (
1694 resource_name::X509_WORKSPACE_BYTES,
1695 crate::hard_limits::X509_WORKSPACE_BYTE_CEILING,
1696 |p| &mut p.max_x509_workspace_bytes,
1697 ),
1698 (
1699 resource_name::XML_NODES,
1700 crate::hard_limits::XML_DOCUMENT_NODE_CEILING as usize,
1701 |p| &mut p.max_xml_nodes,
1702 ),
1703 (
1704 resource_name::XML_DEPTH,
1705 crate::hard_limits::XML_DOCUMENT_DEPTH_CEILING,
1706 |p| &mut p.max_xml_depth,
1707 ),
1708 (
1709 resource_name::XML_NAMESPACE_BINDINGS,
1710 crate::hard_limits::XML_NAMESPACE_BINDING_CEILING,
1711 |p| &mut p.max_xml_namespace_bindings,
1712 ),
1713 (
1714 resource_name::SIGNATURE_REFERENCES,
1715 crate::hard_limits::SIGNATURE_REFERENCE_CEILING,
1716 |p| &mut p.max_references,
1717 ),
1718 (
1719 resource_name::VERIFICATION_SIGNATURES,
1720 crate::hard_limits::VERIFICATION_SIGNATURE_CEILING,
1721 |p| &mut p.max_signatures,
1722 ),
1723 (
1724 resource_name::REFERENCE_TRANSFORMS,
1725 crate::hard_limits::REFERENCE_TRANSFORM_CEILING,
1726 |p| &mut p.max_transforms_per_reference,
1727 ),
1728 (
1729 resource_name::XML_BASE_COMPONENTS,
1730 crate::hard_limits::XML_BASE_COMPONENT_CEILING,
1731 |p| &mut p.max_xml_base_components,
1732 ),
1733 (
1734 resource_name::XML_BASE_RESOLUTION_BYTES,
1735 crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING,
1736 |p| &mut p.max_xml_base_resolution_bytes,
1737 ),
1738 (
1739 resource_name::CANONICALIZED_BYTES,
1740 crate::hard_limits::CANONICALIZED_SIGNATURE_DATA_BYTE_CEILING,
1741 |p| &mut p.max_canonicalized_bytes,
1742 ),
1743 (
1744 resource_name::EXTERNAL_RESOURCE_BYTES,
1745 crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING,
1746 |p| &mut p.max_external_resource_bytes,
1747 ),
1748 (
1749 resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
1750 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING,
1751 |p| &mut p.max_external_resource_total_bytes,
1752 ),
1753 (
1754 resource_name::ENCRYPTION_PLAINTEXT_BYTES,
1755 crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING,
1756 |p| &mut p.max_encryption_plaintext_bytes,
1757 ),
1758 (
1759 resource_name::XML_DOCUMENT,
1760 crate::hard_limits::XML_DOCUMENT_BYTE_CEILING,
1761 |p| &mut p.max_xml_document_bytes,
1762 ),
1763 (
1764 resource_name::XML_PARSE_WORK_BYTES,
1765 crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING,
1766 |p| &mut p.max_xml_parse_work_bytes,
1767 ),
1768 (
1769 resource_name::ENCRYPTION_RECIPIENTS,
1770 crate::hard_limits::ENCRYPTION_RECIPIENT_CEILING,
1771 |p| &mut p.max_encryption_recipients,
1772 ),
1773 (
1774 resource_name::ENCRYPTION_METADATA_BYTES,
1775 crate::hard_limits::ENCRYPTION_METADATA_BYTE_CEILING,
1776 |p| &mut p.max_encryption_metadata_bytes,
1777 ),
1778 (
1779 resource_name::KEY_CANDIDATES,
1780 crate::hard_limits::KEY_CANDIDATE_CEILING,
1781 |p| &mut p.max_key_candidates,
1782 ),
1783 (
1784 resource_name::KEY_IMPORT_KDF_WORK,
1785 crate::hard_limits::KEY_IMPORT_KDF_WORK_CEILING as usize,
1786 |p| &mut p.max_key_import_kdf_work,
1787 ),
1788 (
1789 resource_name::KEY_IMPORT_KDF_MEMORY,
1790 crate::hard_limits::KEY_IMPORT_KDF_MEMORY_CEILING,
1791 |p| &mut p.max_key_import_kdf_memory_bytes,
1792 ),
1793 (
1794 resource_name::KEY_INFO_REFERENCE_DEPTH,
1795 crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING,
1796 |p| &mut p.max_key_info_reference_depth,
1797 ),
1798 (
1799 resource_name::BASE64_TRANSFORM_INPUT_BYTES,
1800 crate::hard_limits::BASE64_TRANSFORM_INPUT_BYTE_CEILING,
1801 |p| &mut p.max_base64_transform_input_bytes,
1802 ),
1803 (
1804 resource_name::BASE64_TRANSFORM_OUTPUT_BYTES,
1805 crate::hard_limits::BASE64_TRANSFORM_OUTPUT_BYTE_CEILING,
1806 |p| &mut p.max_base64_transform_output_bytes,
1807 ),
1808 (
1809 resource_name::XPATH_EXPRESSIONS,
1810 crate::hard_limits::XPATH_EXPRESSION_COUNT_CEILING,
1811 |p| &mut p.max_xpath_expressions,
1812 ),
1813 (
1814 resource_name::XPATH_EXPRESSION_BYTES,
1815 crate::hard_limits::XPATH_EXPRESSION_BYTE_CEILING,
1816 |p| &mut p.max_xpath_expression_bytes,
1817 ),
1818 (
1819 resource_name::XPATH_EXPRESSION_COMPLEXITY,
1820 crate::hard_limits::XPATH_EXPRESSION_COMPLEXITY_CEILING,
1821 |p| &mut p.max_xpath_expression_complexity,
1822 ),
1823 (
1824 resource_name::XPATH_CONTEXT_EVALUATIONS,
1825 crate::hard_limits::XPATH_CONTEXT_EVALUATION_CEILING,
1826 |p| &mut p.max_xpath_context_evaluations,
1827 ),
1828 (
1829 resource_name::XPATH_EVALUATION_WORK,
1830 crate::hard_limits::XPATH_EVALUATION_WORK_CEILING,
1831 |p| &mut p.max_xpath_evaluation_work,
1832 ),
1833 (
1834 resource_name::XPATH_MIRROR_STRING_BYTES,
1835 crate::hard_limits::XPATH_MIRROR_STRING_BYTE_CEILING,
1836 |p| &mut p.max_xpath_mirror_string_bytes,
1837 ),
1838 (
1839 resource_name::XPATH_STRING_WORK_BYTES,
1840 crate::hard_limits::XPATH_STRING_WORK_BYTE_CEILING,
1841 |p| &mut p.max_xpath_string_work_bytes,
1842 ),
1843 (
1844 resource_name::XPATH_NAMESPACE_BINDINGS,
1845 crate::hard_limits::XPATH_NAMESPACE_BINDING_CEILING,
1846 |p| &mut p.max_xpath_namespace_bindings,
1847 ),
1848 (
1849 resource_name::XPATH_NAMESPACE_BYTES,
1850 crate::hard_limits::XPATH_NAMESPACE_BYTE_CEILING,
1851 |p| &mut p.max_xpath_namespace_bytes,
1852 ),
1853 (
1854 resource_name::XPATH_FILTERS,
1855 crate::hard_limits::XPATH_FILTER_COUNT_CEILING,
1856 |p| &mut p.max_xpath_filters,
1857 ),
1858 (
1859 resource_name::NODE_SET_FILTER_WORK,
1860 crate::hard_limits::NODE_SET_FILTER_WORK_CEILING,
1861 |p| &mut p.max_node_set_filter_work,
1862 ),
1863 (
1864 resource_name::OPC_PARAMETER_BYTES,
1865 crate::hard_limits::OPC_PARAMETER_BYTE_CEILING,
1866 |p| &mut p.max_opc_parameter_bytes,
1867 ),
1868 (
1869 resource_name::OPC_WORKSPACE_BYTES,
1870 crate::hard_limits::OPC_WORKSPACE_BYTE_CEILING,
1871 |p| &mut p.max_opc_workspace_bytes,
1872 ),
1873 (
1874 resource_name::NODE_SET_ENTRIES,
1875 crate::hard_limits::NODE_SET_ENTRY_CEILING,
1876 |p| &mut p.max_node_set_entries,
1877 ),
1878 (
1879 resource_name::NODE_SET_OWNED_STRING_BYTES,
1880 crate::hard_limits::NODE_SET_OWNED_STRING_BYTE_CEILING,
1881 |p| &mut p.max_node_set_owned_string_bytes,
1882 ),
1883 (
1884 resource_name::NODE_SET_CUMULATIVE_OWNED_STRING_BYTES,
1885 crate::hard_limits::NODE_SET_CUMULATIVE_OWNED_STRING_BYTE_CEILING,
1886 |p| &mut p.max_node_set_cumulative_owned_string_bytes,
1887 ),
1888 ];
1889
1890 for &(resource, ceiling, field) in cases {
1891 let mut policy = ResourcePolicy::default();
1892 let actual = ceiling.saturating_add(1);
1893 *field(&mut policy) = actual;
1894 assert_eq!(
1895 policy.validate(),
1896 Err(PolicyViolation::ResourceLimit {
1897 resource,
1898 maximum: ceiling,
1899 actual,
1900 }),
1901 "wrong hard-ceiling validation for {resource}",
1902 );
1903 }
1904 }
1905
1906 #[test]
1907 fn resource_policy_accepts_zero_as_a_deny_all_ceiling() {
1908 let policy = ResourcePolicy {
1911 max_xml_nodes: 0,
1912 max_xml_depth: 0,
1913 max_xml_namespace_bindings: 0,
1914 max_references: 0,
1915 max_signatures: 0,
1916 max_transforms_per_reference: 0,
1917 max_xml_base_components: 0,
1918 max_xml_base_resolution_bytes: 0,
1919 max_canonicalized_bytes: 0,
1920 max_external_resource_bytes: 0,
1921 max_external_resource_total_bytes: 0,
1922 max_encryption_plaintext_bytes: 0,
1923 max_xml_document_bytes: 0,
1924 max_xml_parse_work_bytes: 0,
1925 max_encryption_recipients: 0,
1926 max_encryption_metadata_bytes: 0,
1927 max_key_candidates: 0,
1928 max_x509_work: 0,
1929 max_x509_workspace_bytes: 0,
1930 max_key_import_kdf_work: 0,
1931 max_key_import_kdf_memory_bytes: 0,
1932 max_key_info_reference_depth: 0,
1933 max_base64_transform_input_bytes: 0,
1934 max_base64_transform_output_bytes: 0,
1935 max_xpath_expressions: 0,
1936 max_xpath_expression_bytes: 0,
1937 max_xpath_expression_complexity: 0,
1938 max_xpath_context_evaluations: 0,
1939 max_xpath_evaluation_work: 0,
1940 max_xpath_mirror_string_bytes: 0,
1941 max_xpath_string_work_bytes: 0,
1942 max_xpath_namespace_bindings: 0,
1943 max_xpath_namespace_bytes: 0,
1944 max_xpath_filters: 0,
1945 max_node_set_filter_work: 0,
1946 max_node_set_entries: 0,
1947 max_node_set_owned_string_bytes: 0,
1948 max_node_set_cumulative_owned_string_bytes: 0,
1949 max_opc_workspace_bytes: 0,
1950 max_opc_parameter_bytes: 0,
1951 };
1952
1953 assert_eq!(policy.validate(), Ok(()));
1954 }
1955
1956 #[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
1957 #[test]
1958 fn node_set_policy_has_headroom_above_its_default() {
1959 let mut policy = ResourcePolicy::default();
1960 assert_eq!(policy.max_node_set_entries, 262_144);
1961 assert_eq!(policy.validate(), Ok(()));
1962
1963 policy.max_node_set_entries = 524_288;
1964 assert_eq!(policy.validate(), Ok(()));
1965
1966 policy.max_node_set_entries = 524_289;
1967 assert_eq!(
1968 policy.validate(),
1969 Err(PolicyViolation::ResourceLimit {
1970 resource: resource_name::NODE_SET_ENTRIES,
1971 maximum: 524_288,
1972 actual: 524_289,
1973 })
1974 );
1975 }
1976
1977 #[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
1978 #[test]
1979 fn rsa_key_policy_enforces_structure_range_and_explicit_relaxation() {
1980 let secure = RsaKeyPolicy::default();
1981 assert!(matches!(
1982 secure.validate_components("test", &[0x80; 128], &[1, 0, 1]),
1983 Err(PolicyViolation::KeySize {
1984 minimum_bits: 2048,
1985 maximum_bits: 8192,
1986 actual_bits: 1024,
1987 ..
1988 })
1989 ));
1990 let mut short_2048_width = [0_u8; 256];
1991 short_2048_width[0] = 1;
1992 assert!(matches!(
1993 secure.validate_components("test", &short_2048_width, &[1, 0, 1]),
1994 Err(PolicyViolation::KeySize {
1995 minimum_bits: 2048,
1996 actual_bits: 2041,
1997 ..
1998 })
1999 ));
2000 assert!(matches!(
2001 secure.validate_components("test", &[1; 1025], &[1, 0, 1]),
2002 Err(PolicyViolation::KeySize {
2003 actual_bits: 8193,
2004 ..
2005 })
2006 ));
2007 assert!(matches!(
2008 secure.validate_components("test", &[0x80; 256], &[2]),
2009 Err(PolicyViolation::InvalidKeyMaterial { .. })
2010 ));
2011 assert_eq!(
2012 secure.validate_components("test", &[0x80; 256], &[0x80, 0, 0, 1]),
2013 Ok(256),
2014 "normalized RSA components encode the exponent as unsigned bytes"
2015 );
2016
2017 let compatibility = RsaKeyPolicy {
2018 minimum_modulus_bits: 1024,
2019 };
2020 assert_eq!(
2021 compatibility.validate_components("test", &[0x80; 128], &[1, 0, 1]),
2022 Ok(128)
2023 );
2024 assert!(
2025 RsaKeyPolicy {
2026 minimum_modulus_bits: 2047,
2027 }
2028 .validate()
2029 .is_err()
2030 );
2031 }
2032
2033 #[cfg(feature = "xmldsig")]
2034 #[test]
2035 fn mandatory_x509_limits_report_the_nonzero_requirement() {
2036 let policy = KeyTrustPolicy {
2039 max_x509_chain_depth: 0,
2040 ..KeyTrustPolicy::default()
2041 };
2042
2043 let error = policy.validate().expect_err("zero depth must be rejected");
2044 assert!(
2045 error.to_string().contains("must be nonzero"),
2046 "unexpected lower-bound diagnostic: {error}"
2047 );
2048 }
2049
2050 #[cfg(feature = "xmldsig")]
2051 #[test]
2052 fn custom_extended_key_purposes_require_valid_oid_arcs() {
2053 let mut policy = KeyTrustPolicy::default();
2056 policy
2057 .allowed_extended_key_usages
2058 .insert(ExtendedKeyPurpose::Other(vec![1, 40, 7]));
2059
2060 assert!(matches!(
2061 policy.validate(),
2062 Err(PolicyViolation::KeyTrust {
2063 reason: "custom extended key purposes must contain valid OID arcs",
2064 })
2065 ));
2066 }
2067
2068 #[cfg(feature = "xmldsig")]
2069 #[test]
2070 fn crl_checking_requires_x509_chain_validation() {
2071 let policy = KeyTrustPolicy {
2074 check_crls: true,
2075 ..KeyTrustPolicy::default()
2076 };
2077
2078 assert!(matches!(
2079 policy.validate(),
2080 Err(PolicyViolation::KeyTrust {
2081 reason: "CRL checking requires X.509 chain validation"
2082 })
2083 ));
2084 }
2085
2086 #[cfg(feature = "xmldsig")]
2087 #[test]
2088 fn legacy_signature_algorithms_require_independent_policy_opt_ins() {
2089 let legacy = [
2090 SignatureAlgorithm::RsaSha1,
2091 SignatureAlgorithm::DsaSha1,
2092 SignatureAlgorithm::HmacSha1,
2093 SignatureAlgorithm::EcdsaSha1,
2094 ];
2095 let mut policy = VerificationPolicy::default();
2096
2097 for algorithm in legacy {
2098 assert!(matches!(
2099 policy.check_signature_algorithm(algorithm),
2100 Err(PolicyViolation::Algorithm { .. })
2101 ));
2102 policy
2103 .key_trust
2104 .allowed_legacy_signature_algorithms
2105 .insert(algorithm);
2106 assert_eq!(policy.check_signature_algorithm(algorithm), Ok(()));
2107 policy
2108 .key_trust
2109 .allowed_legacy_signature_algorithms
2110 .remove(&algorithm);
2111 }
2112 }
2113
2114 #[cfg(feature = "xmldsig")]
2115 #[test]
2116 fn dsa_key_policy_enforces_configured_minimum_and_hard_ceiling() {
2117 let policy = DsaKeyPolicy::default();
2118
2119 assert!(matches!(
2120 policy.validate_modulus_bits(1024),
2121 Err(PolicyViolation::KeySize {
2122 key_type: "DSA",
2123 minimum_bits: 2048,
2124 maximum_bits: 3072,
2125 actual_bits: 1024,
2126 ..
2127 })
2128 ));
2129 assert_eq!(policy.validate_modulus_bits(2048), Ok(()));
2130 assert!(matches!(
2131 policy.validate_modulus_bits(4096),
2132 Err(PolicyViolation::KeySize {
2133 key_type: "DSA",
2134 minimum_bits: 2048,
2135 maximum_bits: 3072,
2136 actual_bits: 4096,
2137 ..
2138 })
2139 ));
2140 }
2141
2142 #[cfg(feature = "xmldsig")]
2143 #[test]
2144 fn hmac_output_policy_cannot_weaken_the_xmldsig_floor() {
2145 let compatibility = HmacPolicy {
2148 minimum_key_bits: 40,
2149 minimum_output_bits: 40,
2150 };
2151
2152 assert_eq!(
2153 compatibility.validate_output(SignatureAlgorithm::HmacSha1, 80),
2154 Ok(())
2155 );
2156 assert!(matches!(
2157 compatibility.validate_output(SignatureAlgorithm::HmacSha1, 72),
2158 Err(PolicyViolation::HmacOutputLength { minimum: 80, .. })
2159 ));
2160 assert!(matches!(
2161 compatibility.validate_output(SignatureAlgorithm::HmacSha256, 120),
2162 Err(PolicyViolation::HmacOutputLength { minimum: 128, .. })
2163 ));
2164 }
2165
2166 #[cfg(feature = "xmldsig")]
2167 #[test]
2168 fn documented_xmldsig_limits_match_hard_limits() {
2169 let docs = include_str!("../docs/xmldsig.md");
2172 let mib = 1024 * 1024;
2173 assert!(docs.contains(&format!(
2174 "Individual resources are limited to {} MiB",
2175 crate::hard_limits::EXTERNAL_RESOURCE_BYTE_CEILING / mib
2176 )));
2177 assert!(docs.contains(&format!(
2178 "complete map to {} MiB",
2179 crate::hard_limits::EXTERNAL_RESOURCE_TOTAL_BYTE_CEILING / mib
2180 )));
2181 assert!(docs.contains(&format!(
2182 "ceilings are {} components and {} MiB per operation",
2183 crate::hard_limits::XML_BASE_COMPONENT_CEILING,
2184 crate::hard_limits::XML_BASE_RESOLUTION_BYTE_CEILING / mib
2185 )));
2186 }
2187}