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