1use std::{borrow::Cow, fmt, sync::Arc};
4
5#[cfg(test)]
6use crate::xml::dom::Document;
7#[cfg(test)]
8use base64::Engine as _;
9
10use crate::document::{DocumentParseSettings, XmlDocument, XmlParseWorkBudget};
11use crate::operation::{
12 OperationExecutionContext, OperationNodeId, OperationNodeKind, OperationPlanError,
13 OperationStage,
14};
15use rsa::RsaPrivateKey;
16
17use super::parse::validate_encrypted_data_metadata;
18use super::types::{MAX_CIPHER_VALUE_BASE64_LEN, XMLENC_NS, validate_ciphertext_framing};
19use super::{
20 DataEncryptionAlgorithm, DecryptedContent, EncryptedData, EncryptedDataType, EncryptedKey,
21 KeyTransportAlgorithm, KeyWrapAlgorithm, OaepDigestAlgorithm, RsaOaepParameters, XmlEncError,
22 map_document_error,
23};
24
25#[cfg(test)]
26use super::parse_encrypted_data;
27
28#[derive(Debug)]
33pub struct KeyCandidateBudget {
34 maximum: usize,
35 remaining: usize,
36 pub(super) key_establishment: super::key_establishment_budget::KeyEstablishmentUsage,
37}
38
39impl KeyCandidateBudget {
40 pub fn for_operation() -> Self {
42 Self::with_limit(crate::hard_limits::KEY_CANDIDATE_CEILING)
43 }
44
45 pub fn with_limit(maximum: usize) -> Self {
47 Self {
48 maximum,
49 remaining: maximum,
50 key_establishment: Default::default(),
51 }
52 }
53
54 pub const fn remaining(&self) -> usize {
56 self.remaining
57 }
58
59 pub fn derive_key(
64 &mut self,
65 provider: &dyn crate::provider::CryptoProvider,
66 policy: &crate::policy::DecryptionPolicy,
67 parameters: &crate::provider::KdfParameters<'_>,
68 secret: &[u8],
69 ) -> Result<zeroize::Zeroizing<Vec<u8>>, XmlEncError> {
70 self.key_establishment
71 .derive_key(&policy.key_establishment, provider, parameters, secret)
72 }
73
74 pub fn agree_and_derive(
78 &mut self,
79 provider: &dyn crate::provider::CryptoProvider,
80 policy: &crate::policy::DecryptionPolicy,
81 key: &dyn crate::provider::KeyAgreementKey,
82 agreement: &crate::provider::KeyAgreementParameters<'_>,
83 parameters: &crate::provider::KdfParameters<'_>,
84 ) -> Result<zeroize::Zeroizing<Vec<u8>>, XmlEncError> {
85 self.consume(2)?;
86 self.key_establishment.agree_and_derive(
87 &policy.key_establishment,
88 provider,
89 key,
90 agreement,
91 parameters,
92 )
93 }
94
95 pub fn consume(&mut self, count: usize) -> Result<(), XmlEncError> {
97 self.require_available(count)?;
98 self.remaining -= count;
99 Ok(())
100 }
101
102 pub(super) fn require_available(&self, count: usize) -> Result<(), XmlEncError> {
103 if count > self.remaining {
104 return Err(crate::policy::PolicyViolation::ResourceLimit {
105 resource: crate::policy::resource_name::KEY_CANDIDATES,
106 maximum: self.maximum,
107 actual: self
108 .maximum
109 .saturating_sub(self.remaining)
110 .saturating_add(count),
111 }
112 .into());
113 }
114 Ok(())
115 }
116
117 fn account_returned_candidates(
118 &mut self,
119 remaining_before: usize,
120 returned: usize,
121 ) -> Result<(), XmlEncError> {
122 let resolver_charged = remaining_before.saturating_sub(self.remaining);
123 self.consume(returned.saturating_sub(resolver_charged))
124 }
125}
126
127pub enum KeyEncryptionKeySource<'a> {
131 Encapsulation(&'a crate::key_establishment::EncapsulationMechanism),
133 Direct,
135 Encrypted(&'a EncryptedKey),
137 Agreement(&'a super::AgreementMethod),
139 Derived(&'a super::DerivedKey),
141}
142
143pub trait DecryptionKeyResolver {
145 fn resolve_encapsulation_content_keys_with_policy(
148 &self,
149 _provider: &dyn crate::provider::CryptoProvider,
150 _algorithm: DataEncryptionAlgorithm,
151 _descriptor: &crate::key_establishment::EncapsulationMechanism,
152 _policy: &crate::policy::DecryptionPolicy,
153 _budget: &mut KeyCandidateBudget,
154 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
155 Err(XmlEncError::KeyNotFound)
156 }
157 fn resolve_key_encryption_keys_with_policy(
160 &self,
161 _provider: &dyn crate::provider::CryptoProvider,
162 _algorithm: KeyWrapAlgorithm,
163 _source: KeyEncryptionKeySource<'_>,
164 _policy: &crate::policy::DecryptionPolicy,
165 _budget: &mut KeyCandidateBudget,
166 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
167 Err(XmlEncError::KeyNotFound)
168 }
169 fn resolve_agreement_content_keys_with_policy(
172 &self,
173 _provider: &dyn crate::provider::CryptoProvider,
174 _algorithm: DataEncryptionAlgorithm,
175 _descriptor: &super::AgreementMethod,
176 _policy: &crate::policy::DecryptionPolicy,
177 _budget: &mut KeyCandidateBudget,
178 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
179 Err(XmlEncError::KeyNotFound)
180 }
181 fn resolve_derived_content_keys_with_policy(
185 &self,
186 _provider: &dyn crate::provider::CryptoProvider,
187 _algorithm: DataEncryptionAlgorithm,
188 _descriptor: &super::DerivedKey,
189 _policy: &crate::policy::DecryptionPolicy,
190 _budget: &mut KeyCandidateBudget,
191 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
192 Err(XmlEncError::KeyNotFound)
193 }
194 fn resolve_content_keys_with_policy(
198 &self,
199 provider: &dyn crate::provider::CryptoProvider,
200 algorithm: DataEncryptionAlgorithm,
201 encrypted_key: Option<&EncryptedKey>,
202 policy: &crate::policy::DecryptionPolicy,
203 budget: &mut KeyCandidateBudget,
204 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
205 self.resolve_key_candidates_with_policy(provider, algorithm, encrypted_key, policy, budget)
206 .map(|keys| {
207 keys.into_iter()
208 .map(crate::provider::RecoveredContentKey::confirmed)
209 .collect()
210 })
211 }
212 fn resolve_key_candidates_with_policy(
220 &self,
221 provider: &dyn crate::provider::CryptoProvider,
222 algorithm: DataEncryptionAlgorithm,
223 encrypted_key: Option<&EncryptedKey>,
224 policy: &crate::policy::DecryptionPolicy,
225 budget: &mut KeyCandidateBudget,
226 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
227 policy.validate()?;
228 if encrypted_key.is_some() {
229 return Err(XmlEncError::KeyNotFound);
230 }
231 self.resolve_key_candidates(provider, algorithm, None, budget)
232 }
233
234 fn resolve_key(
236 &self,
237 provider: &dyn crate::provider::CryptoProvider,
238 algorithm: DataEncryptionAlgorithm,
239 encrypted_key: Option<&EncryptedKey>,
240 ) -> Result<Vec<u8>, XmlEncError>;
241
242 fn resolve_key_candidates(
254 &self,
255 provider: &dyn crate::provider::CryptoProvider,
256 algorithm: DataEncryptionAlgorithm,
257 encrypted_key: Option<&EncryptedKey>,
258 budget: &mut KeyCandidateBudget,
259 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
260 budget.consume(1)?;
261 self.resolve_key(provider, algorithm, encrypted_key)
262 .map(|key| vec![key])
263 }
264}
265
266pub struct DerivedKeyDecryptor<'a> {
270 method: &'a super::KeyDerivationMethod,
271 input: DerivedKeyInput<'a>,
272 purpose: DerivedKeyPurpose,
273 master_key_name: Option<&'a str>,
274}
275
276pub enum DerivedKeyInput<'a> {
279 Secret(&'a [u8]),
281 Agreement {
283 key: &'a dyn crate::provider::KeyAgreementKey,
285 parameters: crate::provider::KeyAgreementParameters<'a>,
287 },
288}
289
290enum DerivedKeyPurpose {
291 Content(DataEncryptionAlgorithm),
292 Wrapping(KeyWrapAlgorithm),
293}
294
295impl<'a> DerivedKeyDecryptor<'a> {
296 pub fn content(
299 method: &'a super::KeyDerivationMethod,
300 input: DerivedKeyInput<'a>,
301 algorithm: DataEncryptionAlgorithm,
302 ) -> Self {
303 Self {
304 method,
305 input,
306 purpose: DerivedKeyPurpose::Content(algorithm),
307 master_key_name: None,
308 }
309 }
310
311 pub fn wrapping(
314 method: &'a super::KeyDerivationMethod,
315 input: DerivedKeyInput<'a>,
316 algorithm: KeyWrapAlgorithm,
317 ) -> Self {
318 Self {
319 method,
320 input,
321 purpose: DerivedKeyPurpose::Wrapping(algorithm),
322 master_key_name: None,
323 }
324 }
325
326 pub fn master_key_name(mut self, name: &'a str) -> Self {
329 self.master_key_name = Some(name);
330 self
331 }
332
333 fn derive(
334 &self,
335 provider: &dyn crate::provider::CryptoProvider,
336 width: usize,
337 policy: &crate::policy::DecryptionPolicy,
338 budget: &mut KeyCandidateBudget,
339 ) -> Result<zeroize::Zeroizing<Vec<u8>>, XmlEncError> {
340 let parameters = self.method.parameters(width)?;
341 match &self.input {
342 DerivedKeyInput::Secret(secret) => {
343 budget.consume(1)?;
344 budget.derive_key(provider, policy, ¶meters, secret)
345 }
346 DerivedKeyInput::Agreement {
347 key,
348 parameters: agreement,
349 } => budget.agree_and_derive(provider, policy, *key, agreement, ¶meters),
350 }
351 }
352}
353
354impl fmt::Debug for DerivedKeyDecryptor<'_> {
355 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
356 formatter
357 .debug_struct("DerivedKeyDecryptor")
358 .finish_non_exhaustive()
359 }
360}
361
362impl DecryptionKeyResolver for DerivedKeyDecryptor<'_> {
363 fn resolve_key_encryption_keys_with_policy(
364 &self,
365 provider: &dyn crate::provider::CryptoProvider,
366 algorithm: KeyWrapAlgorithm,
367 source: KeyEncryptionKeySource<'_>,
368 policy: &crate::policy::DecryptionPolicy,
369 budget: &mut KeyCandidateBudget,
370 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
371 if !matches!(self.purpose, DerivedKeyPurpose::Wrapping(expected) if expected == algorithm) {
372 return Err(XmlEncError::KeyNotFound);
373 }
374 match source {
375 KeyEncryptionKeySource::Derived(descriptor)
376 if descriptor
377 .method
378 .as_ref()
379 .is_none_or(|method| method == self.method)
380 && descriptor
381 .master_key_name
382 .as_deref()
383 .is_none_or(|name| Some(name) == self.master_key_name) => {}
384 KeyEncryptionKeySource::Agreement(descriptor) => {
385 let DerivedKeyInput::Agreement { parameters, .. } = &self.input else {
386 return Err(XmlEncError::KeyNotFound);
387 };
388 if descriptor.algorithm.uri() != parameters.algorithm
389 || descriptor
390 .method
391 .as_ref()
392 .is_some_and(|method| method != self.method)
393 || descriptor.originator.is_some()
394 || descriptor.recipient.is_some()
395 || !descriptor.nonce.is_empty()
396 || descriptor.legacy_digest.is_some()
397 {
398 return Err(XmlEncError::KeyNotFound);
399 }
400 }
401 _ => return Err(XmlEncError::KeyNotFound),
402 }
403 budget.require_available(match self.input {
404 DerivedKeyInput::Secret(_) => 2,
405 DerivedKeyInput::Agreement { .. } => 3,
406 })?;
407 let mut key = self.derive(provider, algorithm.key_len(), policy, budget)?;
408 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
409 core::mem::take(&mut *key),
410 )])
411 }
412 fn resolve_agreement_content_keys_with_policy(
413 &self,
414 provider: &dyn crate::provider::CryptoProvider,
415 algorithm: DataEncryptionAlgorithm,
416 descriptor: &super::AgreementMethod,
417 policy: &crate::policy::DecryptionPolicy,
418 budget: &mut KeyCandidateBudget,
419 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
420 let DerivedKeyInput::Agreement { parameters, .. } = &self.input else {
421 return Err(XmlEncError::KeyNotFound);
422 };
423 if descriptor.algorithm.uri() != parameters.algorithm
426 || descriptor
427 .method
428 .as_ref()
429 .is_some_and(|method| method != self.method)
430 || descriptor.originator.is_some()
431 || descriptor.recipient.is_some()
432 || !descriptor.nonce.is_empty()
433 || descriptor.legacy_digest.is_some()
434 {
435 return Err(XmlEncError::KeyNotFound);
436 }
437 self.resolve_content_keys_with_policy(provider, algorithm, None, policy, budget)
438 }
439 fn resolve_derived_content_keys_with_policy(
440 &self,
441 provider: &dyn crate::provider::CryptoProvider,
442 algorithm: DataEncryptionAlgorithm,
443 descriptor: &super::DerivedKey,
444 policy: &crate::policy::DecryptionPolicy,
445 budget: &mut KeyCandidateBudget,
446 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
447 if descriptor
452 .method
453 .as_ref()
454 .is_some_and(|method| method != self.method)
455 || descriptor
456 .master_key_name
457 .as_deref()
458 .is_some_and(|name| Some(name) != self.master_key_name)
459 {
460 return Err(XmlEncError::KeyNotFound);
461 }
462 self.resolve_content_keys_with_policy(provider, algorithm, None, policy, budget)
463 }
464 fn resolve_key(
465 &self,
466 _provider: &dyn crate::provider::CryptoProvider,
467 _algorithm: DataEncryptionAlgorithm,
468 _encrypted_key: Option<&EncryptedKey>,
469 ) -> Result<Vec<u8>, XmlEncError> {
470 Err(XmlEncError::KeyNotFound)
472 }
473
474 fn resolve_content_keys_with_policy(
475 &self,
476 provider: &dyn crate::provider::CryptoProvider,
477 algorithm: DataEncryptionAlgorithm,
478 encrypted_key: Option<&EncryptedKey>,
479 policy: &crate::policy::DecryptionPolicy,
480 budget: &mut KeyCandidateBudget,
481 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
482 policy.validate()?;
483 match (&self.purpose, encrypted_key) {
484 (DerivedKeyPurpose::Content(_), Some(_)) | (DerivedKeyPurpose::Wrapping(_), None) => {
485 return Err(XmlEncError::KeyNotFound);
486 }
487 (DerivedKeyPurpose::Content(expected), None) if *expected != algorithm => {
488 return Err(XmlEncError::KeyNotFound);
489 }
490 _ => {}
491 }
492 let wrap = match encrypted_key {
493 None => None,
494 Some(encrypted_key) => {
495 validate_encrypted_key_policy(encrypted_key, policy)?;
496 encrypted_key.encryption_method.validate_structure()?;
497 Some(KeyWrapAlgorithm::from_uri(
498 &encrypted_key.encryption_method.algorithm,
499 )?)
500 }
501 };
502 if let (DerivedKeyPurpose::Wrapping(expected), Some(actual)) = (&self.purpose, wrap)
503 && *expected != actual
504 {
505 return Err(XmlEncError::KeyNotFound);
506 }
507 let width = match wrap {
508 Some(wrap) => wrap.key_len(),
509 None => algorithm.key_len(),
510 };
511 let derivation_attempts = match self.input {
515 DerivedKeyInput::Secret(_) => 1,
516 DerivedKeyInput::Agreement { .. } => 2,
517 };
518 let required = if wrap.is_some() {
519 derivation_attempts + 1
520 } else {
521 derivation_attempts
522 };
523 budget.require_available(required)?;
524 let mut key = self.derive(provider, width, policy, budget)?;
525 if let Some(wrap) = wrap {
526 return KekDecryptor::borrowed_with_kind(&key, wrap.key_kind())
529 .resolve_content_keys_with_policy(
530 provider,
531 algorithm,
532 encrypted_key,
533 policy,
534 budget,
535 );
536 }
537 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
538 core::mem::take(&mut *key),
539 )])
540 }
541}
542
543pub struct OpaqueContentKeyResolver {
545 key: std::sync::Arc<dyn crate::provider::ContentDecryptionKey>,
546}
547
548impl OpaqueContentKeyResolver {
549 pub fn new(key: std::sync::Arc<dyn crate::provider::ContentDecryptionKey>) -> Self {
551 Self { key }
552 }
553}
554
555impl DecryptionKeyResolver for OpaqueContentKeyResolver {
556 fn resolve_key(
557 &self,
558 _provider: &dyn crate::provider::CryptoProvider,
559 _algorithm: DataEncryptionAlgorithm,
560 _encrypted_key: Option<&EncryptedKey>,
561 ) -> Result<Vec<u8>, XmlEncError> {
562 Err(crate::provider::ProviderError::KeyNotExportable.into())
563 }
564 fn resolve_content_keys_with_policy(
565 &self,
566 provider: &dyn crate::provider::CryptoProvider,
567 algorithm: DataEncryptionAlgorithm,
568 encrypted_key: Option<&EncryptedKey>,
569 policy: &crate::policy::DecryptionPolicy,
570 budget: &mut KeyCandidateBudget,
571 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
572 policy.validate()?;
573 if encrypted_key.is_some() {
574 return Err(XmlEncError::KeyNotFound);
575 }
576 budget.consume(1)?;
577 provider.require_capability(crate::provider::ProviderCapability::Decrypt(algorithm))?;
578 validate_content_key_len(algorithm, self.key.key_len())?;
579 Ok(vec![crate::provider::RecoveredContentKey::opaque(
580 self.key.clone(),
581 )])
582 }
583}
584
585pub struct OpaqueKekDecryptor {
587 key: std::sync::Arc<dyn crate::provider::KeyUnwrappingKey>,
588}
589
590impl OpaqueKekDecryptor {
591 pub fn new(key: std::sync::Arc<dyn crate::provider::KeyUnwrappingKey>) -> Self {
593 Self { key }
594 }
595}
596
597impl DecryptionKeyResolver for OpaqueKekDecryptor {
598 fn resolve_key(
599 &self,
600 _provider: &dyn crate::provider::CryptoProvider,
601 _algorithm: DataEncryptionAlgorithm,
602 _encrypted_key: Option<&EncryptedKey>,
603 ) -> Result<Vec<u8>, XmlEncError> {
604 Err(crate::provider::ProviderError::KeyNotExportable.into())
605 }
606 fn resolve_content_keys_with_policy(
607 &self,
608 provider: &dyn crate::provider::CryptoProvider,
609 algorithm: DataEncryptionAlgorithm,
610 encrypted_key: Option<&EncryptedKey>,
611 policy: &crate::policy::DecryptionPolicy,
612 budget: &mut KeyCandidateBudget,
613 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
614 policy.validate()?;
615 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
616 validate_encrypted_key_policy(encrypted_key, policy)?;
617 encrypted_key.encryption_method.validate_structure()?;
618 let wrap = KeyWrapAlgorithm::from_uri(&encrypted_key.encryption_method.algorithm)?;
619 provider.require_capability(crate::provider::ProviderCapability::KeyUnwrap(wrap))?;
620 budget.consume(1)?;
621 let wrapped = encrypted_key.cipher_data.octets()?;
622 let expected = wrap
623 .wrapped_len(algorithm.key_len())
624 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(wrap.uri().into()))?;
625 if wrapped.len() != expected {
626 return Err(XmlEncError::InvalidWrappedKeyLength {
627 expected,
628 actual: wrapped.len(),
629 });
630 }
631 let key = provider.unwrap_content_key(self.key.as_ref(), wrap, algorithm, &wrapped)?;
632 validate_content_key_len(algorithm, key.key_len())?;
633 Ok(vec![key])
634 }
635}
636
637#[derive(Debug, Clone, Copy, Default)]
639pub struct DocumentDecryptionOptions<'a> {
640 pub encrypted_data_id: Option<&'a str>,
642}
643
644pub struct DecryptContext<'a> {
646 resolver: &'a dyn DecryptionKeyResolver,
647 policy: crate::policy::DecryptionPolicy,
648 provider: &'a dyn crate::provider::CryptoProvider,
649 xml_backend: crate::XmlBackend,
650 id_attributes: &'a [crate::IdAttributeRegistration],
651 external_resources: Option<&'a std::collections::HashMap<String, Vec<u8>>>,
652}
653
654struct DecryptionPlanNodes {
655 document: OperationNodeId,
656 ciphertext: OperationNodeId,
657 key: OperationNodeId,
658 crypto: OperationNodeId,
659 evidence: OperationNodeId,
660 mutation: Option<OperationNodeId>,
661}
662
663struct DecryptionOperationBudgets {
664 key_candidates: std::cell::RefCell<KeyCandidateBudget>,
665 xml_parse: XmlParseWorkBudget,
666}
667
668struct DecryptionReferenceGate<'a> {
669 operation:
670 &'a OperationExecutionContext<crate::policy::DecryptionPolicy, DecryptionOperationBudgets>,
671 document: OperationNodeId,
672 ciphertext: OperationNodeId,
673}
674
675impl super::cipher_reference::ReferenceOperationGate for DecryptionReferenceGate<'_> {
676 fn run_resource(
677 &self,
678 identity: &crate::operation::OperationResourceIdentity,
679 action: &mut dyn FnMut() -> Result<Vec<u8>, XmlEncError>,
680 ) -> Result<Vec<u8>, XmlEncError> {
681 self.operation
682 .run_discovered_resource(self.document, self.ciphertext, identity, action)
683 }
684}
685
686impl DecryptionOperationBudgets {
687 fn from_policy(policy: &crate::policy::DecryptionPolicy) -> Self {
688 Self {
689 key_candidates: std::cell::RefCell::new(KeyCandidateBudget::with_limit(
690 policy.resources.max_key_candidates,
691 )),
692 xml_parse: XmlParseWorkBudget::from_resources(&policy.resources),
693 }
694 }
695}
696
697fn compile_decryption_plan(
698 operation: &mut OperationExecutionContext<
699 crate::policy::DecryptionPolicy,
700 DecryptionOperationBudgets,
701 >,
702 mutation: bool,
703) -> Result<DecryptionPlanNodes, XmlEncError> {
704 let document = operation.add_node(OperationNodeKind::Document, OperationStage::Parse, None);
705 let ciphertext =
706 operation.add_node(OperationNodeKind::Ciphertext, OperationStage::Resolve, None);
707 operation
708 .add_dependency(ciphertext, document)
709 .map_err(map_decryption_plan_error)?;
710 let key = operation.add_node(
711 OperationNodeKind::Key { index: 0 },
712 OperationStage::Resolve,
713 None,
714 );
715 operation
716 .add_dependency(key, ciphertext)
717 .map_err(map_decryption_plan_error)?;
718 let crypto = operation.add_node(OperationNodeKind::Crypto, OperationStage::Crypto, None);
719 operation
720 .add_dependency(crypto, key)
721 .map_err(map_decryption_plan_error)?;
722 let evidence = operation.add_node(OperationNodeKind::Evidence, OperationStage::Evidence, None);
723 operation
724 .add_dependency(evidence, crypto)
725 .map_err(map_decryption_plan_error)?;
726 let mutation = mutation.then(|| {
727 let node = operation.add_node(OperationNodeKind::Mutation, OperationStage::Mutation, None);
728 operation
729 .add_dependency(node, evidence)
730 .expect("evidence-to-mutation stage order is fixed");
731 node
732 });
733 operation.compile().map_err(map_decryption_plan_error)?;
734 Ok(DecryptionPlanNodes {
735 document,
736 ciphertext,
737 key,
738 crypto,
739 evidence,
740 mutation,
741 })
742}
743
744struct ProcessedDecryption<T> {
745 output: T,
746 operation:
747 OperationExecutionContext<crate::policy::DecryptionPolicy, DecryptionOperationBudgets>,
748 mutation: Option<OperationNodeId>,
749}
750
751#[derive(Clone, Copy)]
752enum DecryptionInput<'a> {
753 Xml {
754 source: &'a str,
755 node_start: Option<usize>,
756 },
757 Parsed(&'a EncryptedData),
758}
759
760fn cipher_reference_node<'doc, 'input>(
761 node: crate::xml::dom::Node<'doc, 'input>,
762) -> Result<crate::xml::dom::Node<'doc, 'input>, XmlEncError> {
763 node.children()
764 .find(|child| child.has_tag_name((XMLENC_NS, "CipherData")))
765 .and_then(|child| {
766 child
767 .children()
768 .find(|node| node.has_tag_name((XMLENC_NS, "CipherReference")))
769 })
770 .ok_or(XmlEncError::MissingRequired("CipherReference"))
771}
772
773fn selected_encrypted_node<'doc, 'input>(
774 document: &'doc crate::xml::dom::Document<'input>,
775 start: Option<usize>,
776) -> Result<crate::xml::dom::Node<'doc, 'input>, XmlEncError> {
777 match start {
778 Some(start) => document
779 .descendants()
780 .find(|node| node.is_element() && node.range().start == start)
781 .ok_or_else(|| XmlEncError::OperationPlan("selected encrypted node is absent".into())),
782 None => Ok(document.root_element()),
783 }
784}
785
786fn map_decryption_plan_error(error: OperationPlanError) -> XmlEncError {
787 XmlEncError::from(error)
788}
789
790impl<'a> DecryptContext<'a> {
791 pub fn new(resolver: &'a dyn DecryptionKeyResolver) -> Self {
793 Self {
794 resolver,
795 policy: crate::policy::DecryptionPolicy::default(),
796 provider: crate::provider::default_provider(),
797 xml_backend: crate::XmlBackend::default(),
798 id_attributes: &[],
799 external_resources: None,
800 }
801 }
802
803 pub fn policy(mut self, policy: crate::policy::DecryptionPolicy) -> Self {
805 self.policy = policy;
806 self
807 }
808
809 pub fn provider(mut self, provider: &'a dyn crate::provider::CryptoProvider) -> Self {
811 self.provider = provider;
812 self
813 }
814
815 pub fn xml_backend(mut self, backend: crate::XmlBackend) -> Self {
817 self.xml_backend = backend;
818 self
819 }
820
821 fn document_parse_settings(&self) -> DocumentParseSettings {
822 DocumentParseSettings::from_policy(&self.policy.xml, &self.policy.resources)
823 .with_backend(self.xml_backend)
824 }
825
826 pub fn id_attributes(mut self, registrations: &'a [crate::IdAttributeRegistration]) -> Self {
828 self.id_attributes = registrations;
829 self
830 }
831
832 pub fn external_resources(
834 mut self,
835 resources: &'a std::collections::HashMap<String, Vec<u8>>,
836 ) -> Self {
837 self.external_resources = Some(resources);
838 self
839 }
840
841 pub fn decrypt(&self, xml: &str) -> Result<DecryptedContent, XmlEncError> {
843 let budgets = DecryptionOperationBudgets::from_policy(&self.policy);
844 self.process_decryption_input(
845 DecryptionInput::Xml {
846 source: xml,
847 node_start: None,
848 },
849 None,
850 false,
851 budgets,
852 |content, _| Ok(content),
853 )
854 .map(|processed| processed.output)
855 }
856
857 pub fn decrypt_data(&self, encrypted: &EncryptedData) -> Result<DecryptedContent, XmlEncError> {
859 self.process_decryption_candidates(
860 encrypted,
861 None,
862 false,
863 DecryptionOperationBudgets::from_policy(&self.policy),
864 |content, _| Ok(content),
865 )
866 .map(|processed| processed.output)
867 }
868
869 fn process_decryption_candidates<T>(
870 &self,
871 encrypted: &EncryptedData,
872 document_binding: Option<(crate::DocumentIdentity, u64)>,
873 mutates_document: bool,
874 budgets: DecryptionOperationBudgets,
875 accept: impl FnMut(DecryptedContent, &XmlParseWorkBudget) -> Result<T, XmlEncError>,
876 ) -> Result<ProcessedDecryption<T>, XmlEncError> {
877 self.process_decryption_input(
878 DecryptionInput::Parsed(encrypted),
879 document_binding,
880 mutates_document,
881 budgets,
882 accept,
883 )
884 }
885
886 fn process_decryption_input<T>(
887 &self,
888 input: DecryptionInput<'_>,
889 document_binding: Option<(crate::DocumentIdentity, u64)>,
890 mutates_document: bool,
891 budgets: DecryptionOperationBudgets,
892 mut accept: impl FnMut(DecryptedContent, &XmlParseWorkBudget) -> Result<T, XmlEncError>,
893 ) -> Result<ProcessedDecryption<T>, XmlEncError> {
894 let mut operation =
895 OperationExecutionContext::new(self.policy.clone(), budgets, document_binding);
896 operation.policy().resources.validate()?;
897 let plan_nodes = compile_decryption_plan(&mut operation, mutates_document)?;
898 let source_document = operation.run(plan_nodes.document, || match input {
899 DecryptionInput::Xml { source, node_start } => {
900 let settings = self.document_parse_settings();
901 let document = crate::document::parse_borrowed_with_settings_and_budget(
902 source,
903 settings,
904 Some(&operation.budgets().xml_parse),
905 )
906 .map_err(|error| map_document_error(error, settings))?;
907 Ok::<_, XmlEncError>(Some((document, node_start)))
908 }
909 DecryptionInput::Parsed(_) => Ok(None),
910 })?;
911 let reference_gate = DecryptionReferenceGate {
912 operation: &operation,
913 document: plan_nodes.document,
914 ciphertext: plan_nodes.ciphertext,
915 };
916 let reference_context = super::CipherReferenceContext::new(
917 operation.policy(),
918 self.external_resources,
919 self.xml_backend,
920 self.id_attributes,
921 )?
922 .with_operation(&reference_gate);
923 let parsed = operation.run(plan_nodes.ciphertext, || {
924 let (mut encrypted, origins) = match input {
928 DecryptionInput::Xml { .. } => {
929 let (document, node_start) =
930 source_document.as_ref().expect("XML input parsed once");
931 let selected = match *node_start {
932 Some(start) => document
933 .descendants()
934 .find(|node| node.is_element() && node.range().start == start)
935 .ok_or_else(|| {
936 XmlEncError::OperationPlan(
937 "selected encrypted node changed during policy parsing".into(),
938 )
939 })?,
940 None => document.root_element(),
941 };
942 let (encrypted, origins) =
943 super::parse::parse_encrypted_data_node_with_origins(
944 selected,
945 operation.policy().into(),
946 false,
947 self.id_attributes,
948 self.provider,
949 Some(&reference_context),
950 Some(&operation.budgets().xml_parse),
951 )?;
952 (Cow::Owned(encrypted), origins)
953 }
954 DecryptionInput::Parsed(encrypted) => (Cow::Borrowed(encrypted), Vec::new()),
955 };
956 validate_encrypted_data_metadata(&encrypted, operation.policy())?;
957 encrypted.encryption_method.validate_structure()?;
958 validate_recipient_count(
959 encrypted.encrypted_keys.len(),
960 operation.policy().resources.max_encryption_recipients,
961 )?;
962 let algorithm =
963 DataEncryptionAlgorithm::from_uri(&encrypted.encryption_method.algorithm)?;
964 crate::policy::check_content_algorithm(
965 operation.policy().data_algorithms.as_ref(),
966 algorithm,
967 "decryption",
968 )?;
969 self.provider
970 .require_capability(crate::provider::ProviderCapability::Decrypt(algorithm))?;
971 validate_typed_cipher_values(
972 &encrypted,
973 algorithm,
974 operation.policy().resources.max_encryption_plaintext_bytes,
975 operation.policy().resources.max_xml_document_bytes,
976 )?;
977 if matches!(
978 algorithm,
979 DataEncryptionAlgorithm::ChaCha20 | DataEncryptionAlgorithm::ChaCha20Poly1305
980 ) && encrypted
981 .encryption_method
982 .chacha
983 .as_ref()
984 .and_then(|parameters| parameters.nonce)
985 .is_none()
986 {
987 return Err(XmlEncError::MissingRequired("ChaCha Nonce"));
988 }
989 let bound_references = source_document
990 .as_ref()
991 .map(|(document, _)| reference_context.bind_document(document));
992 let ciphertext = match (&encrypted.cipher_data, source_document.as_ref()) {
993 (super::CipherData::Reference { uri, transforms }, Some((document, start))) => {
994 let source = selected_encrypted_node(document, *start)?;
995 bound_references
996 .as_ref()
997 .expect("XML source has a bound reference context")
998 .resolve_parsed(
999 cipher_reference_node(source)?,
1000 uri,
1001 transforms,
1002 &operation.budgets().xml_parse,
1003 )?
1004 }
1005 _ => encrypted.cipher_data.octets()?.into_owned(),
1006 };
1007 if let Some((document, _)) = source_document.as_ref() {
1008 let mut origins = origins.iter();
1009 resolve_nested_cipher_references(
1010 &mut encrypted.to_mut().encrypted_keys,
1011 document,
1012 &mut origins,
1013 bound_references
1014 .as_ref()
1015 .expect("XML source has a bound reference context"),
1016 &operation.budgets().xml_parse,
1017 )?;
1018 if origins.next().is_some() {
1019 return Err(XmlEncError::OperationPlan(
1020 "unused encrypted key origin".into(),
1021 ));
1022 }
1023 }
1024 validate_typed_cipher_values(
1025 &encrypted,
1026 algorithm,
1027 operation.policy().resources.max_encryption_plaintext_bytes,
1028 operation.policy().resources.max_xml_document_bytes,
1029 )?;
1030 validate_content_framing_before_resolution(
1031 algorithm,
1032 ciphertext.len(),
1033 &encrypted.encrypted_keys,
1034 operation.policy(),
1035 )?;
1036 validate_possible_plaintext_len(
1037 algorithm,
1038 ciphertext.len(),
1039 operation.policy().resources.max_encryption_plaintext_bytes,
1040 )?;
1041 Ok::<_, XmlEncError>((encrypted, algorithm, ciphertext, origins, bound_references))
1042 });
1043 let (encrypted, algorithm, ciphertext, origins, bound_references) = parsed?;
1044 let keys = operation.run(plan_nodes.key, || {
1045 let document = match (source_document.as_ref(), bound_references.as_ref()) {
1046 (Some((document, start)), Some(bound)) => Some(KeySourceDocument {
1047 references: bound,
1048 target: selected_encrypted_node(document, *start)?.id(),
1049 origins: &origins,
1050 }),
1051 _ => None,
1052 };
1053 resolve_content_key_candidates(
1054 self.provider,
1055 algorithm,
1056 &encrypted,
1057 self.resolver,
1058 operation.policy(),
1059 &mut operation.budgets().key_candidates.borrow_mut(),
1060 document,
1061 )
1062 })?;
1063 let keys = compatible_decryption_key_candidates(algorithm, keys)?;
1064 validate_decryption_key_candidates(algorithm, keys.len())?;
1065 let output = operation.run(plan_nodes.crypto, || {
1066 let mut last_error = None;
1067 for key in keys {
1068 let attempt = (|| {
1069 validate_content_key_len(algorithm, key.key_len())?;
1070 let result = key.decrypt(
1071 self.provider,
1072 algorithm,
1073 &ciphertext,
1074 encrypted
1075 .encryption_method
1076 .chacha
1077 .as_ref()
1078 .map(super::ChaChaParameters::borrowed),
1079 );
1080 if !key.valid() {
1085 if let Ok(mut plaintext) = result {
1086 zeroize::Zeroize::zeroize(&mut plaintext);
1087 }
1088 return Err(if algorithm.cbc_block_len().is_some() {
1089 XmlEncError::InvalidPadding
1090 } else {
1091 XmlEncError::AeadAuthenticationFailed
1092 });
1093 }
1094 let plaintext = result.map_err(|error| {
1095 map_data_decryption_error(algorithm, ciphertext.len(), error)
1096 })?;
1097 validate_provider_plaintext_len(algorithm, ciphertext.len(), plaintext.len())?;
1098 validate_plaintext_len(
1099 plaintext.len(),
1100 operation.policy().resources.max_encryption_plaintext_bytes,
1101 )?;
1102 let content = match encrypted.encrypted_type.as_ref() {
1103 Some(EncryptedDataType::Element | EncryptedDataType::Content) => {
1104 DecryptedContent::Xml(String::from_utf8(plaintext)?)
1105 }
1106 Some(EncryptedDataType::Other(_)) | None => {
1107 DecryptedContent::Bytes(plaintext)
1108 }
1109 };
1110 accept(content, &operation.budgets().xml_parse)
1111 })();
1112 match attempt {
1113 Ok(output) => return Ok(output),
1114 Err(error) => last_error = Some(error),
1115 }
1116 }
1117 Err(last_error.unwrap_or(XmlEncError::KeyNotFound))
1118 })?;
1119 let output = operation.run(plan_nodes.evidence, || Ok::<_, XmlEncError>(output))?;
1120 Ok(ProcessedDecryption {
1121 output,
1122 operation,
1123 mutation: plan_nodes.mutation,
1124 })
1125 }
1126
1127 pub fn decrypt_document(
1129 &self,
1130 xml: &str,
1131 encrypted_data_id: Option<&str>,
1132 ) -> Result<String, XmlEncError> {
1133 decrypt_document_with_context(
1134 xml,
1135 DocumentEncryptedDataSelector::EncryptedDataId(encrypted_data_id),
1136 self,
1137 )
1138 }
1139
1140 pub fn decrypt_owned_document(
1142 &self,
1143 document: &mut XmlDocument,
1144 encrypted_data_id: Option<&str>,
1145 ) -> Result<(), XmlEncError> {
1146 let budgets = DecryptionOperationBudgets::from_policy(&self.policy);
1147 decrypt_owned_document_with_context(
1148 document,
1149 DocumentEncryptedDataSelector::EncryptedDataId(encrypted_data_id),
1150 self,
1151 budgets,
1152 )
1153 }
1154
1155 pub fn decrypt_document_from_start_node(
1158 &self,
1159 xml: &str,
1160 start_node_id: Option<&str>,
1161 ) -> Result<String, XmlEncError> {
1162 decrypt_document_with_context(
1163 xml,
1164 DocumentEncryptedDataSelector::UniqueBelowStartNode(start_node_id),
1165 self,
1166 )
1167 }
1168
1169 pub fn decrypt_first_document_from_start_node(
1172 &self,
1173 xml: &str,
1174 start_node_id: Option<&str>,
1175 ) -> Result<String, XmlEncError> {
1176 decrypt_document_with_context(
1177 xml,
1178 DocumentEncryptedDataSelector::FirstBelowStartNode(start_node_id),
1179 self,
1180 )
1181 }
1182}
1183
1184#[derive(Clone)]
1186pub struct SymmetricKeyDecryptor {
1187 key: Vec<u8>,
1188 kind: crate::key_manager::SymmetricKeyKind,
1189}
1190
1191impl fmt::Debug for SymmetricKeyDecryptor {
1192 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1193 formatter
1194 .debug_struct("SymmetricKeyDecryptor")
1195 .field("key", &"[REDACTED]")
1196 .finish()
1197 }
1198}
1199
1200impl SymmetricKeyDecryptor {
1201 pub fn new(key: impl Into<Vec<u8>>) -> Self {
1203 Self::with_kind(key, crate::key_manager::SymmetricKeyKind::Aes)
1204 }
1205
1206 pub fn with_kind(key: impl Into<Vec<u8>>, kind: crate::key_manager::SymmetricKeyKind) -> Self {
1210 Self {
1211 key: key.into(),
1212 kind,
1213 }
1214 }
1215}
1216
1217impl DecryptionKeyResolver for SymmetricKeyDecryptor {
1218 fn resolve_content_keys_with_policy(
1219 &self,
1220 provider: &dyn crate::provider::CryptoProvider,
1221 algorithm: DataEncryptionAlgorithm,
1222 encrypted_key: Option<&EncryptedKey>,
1223 policy: &crate::policy::DecryptionPolicy,
1224 budget: &mut KeyCandidateBudget,
1225 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1226 policy.validate()?;
1227 if encrypted_key.is_some() {
1228 return Err(XmlEncError::KeyNotFound);
1229 }
1230 budget.consume(1)?;
1231 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
1232 self.resolve_key(provider, algorithm, None)?,
1233 )])
1234 }
1235
1236 fn resolve_key(
1237 &self,
1238 _provider: &dyn crate::provider::CryptoProvider,
1239 algorithm: DataEncryptionAlgorithm,
1240 _encrypted_key: Option<&EncryptedKey>,
1241 ) -> Result<Vec<u8>, XmlEncError> {
1242 if self.kind != algorithm.key_kind() {
1243 return Err(XmlEncError::KeyNotFound);
1244 }
1245 validate_key_len(algorithm, &self.key)?;
1246 Ok(self.key.clone())
1247 }
1248}
1249
1250#[derive(Clone)]
1252pub struct PrivateKeyDecryptor {
1253 key: Arc<dyn crate::provider::KeyRecoveryKey>,
1254}
1255
1256#[derive(Clone)]
1258pub struct KekDecryptor<'a> {
1259 kek: std::borrow::Cow<'a, [u8]>,
1260 kind: crate::key_manager::SymmetricKeyKind,
1261}
1262
1263impl fmt::Debug for KekDecryptor<'_> {
1264 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1265 formatter
1266 .debug_struct("KekDecryptor")
1267 .field("kek", &"[REDACTED]")
1268 .field("kind", &self.kind)
1269 .finish()
1270 }
1271}
1272
1273impl KekDecryptor<'static> {
1274 pub fn new(kek: impl Into<Vec<u8>>) -> Self {
1276 Self::with_kind(kek, crate::key_manager::SymmetricKeyKind::Aes)
1277 }
1278
1279 pub fn with_kind(kek: impl Into<Vec<u8>>, kind: crate::key_manager::SymmetricKeyKind) -> Self {
1281 Self {
1282 kek: std::borrow::Cow::Owned(kek.into()),
1283 kind,
1284 }
1285 }
1286}
1287
1288impl<'a> KekDecryptor<'a> {
1289 pub fn borrowed(kek: &'a [u8]) -> Self {
1291 Self::borrowed_with_kind(kek, crate::key_manager::SymmetricKeyKind::Aes)
1292 }
1293
1294 pub fn borrowed_with_kind(kek: &'a [u8], kind: crate::key_manager::SymmetricKeyKind) -> Self {
1296 Self {
1297 kek: std::borrow::Cow::Borrowed(kek),
1298 kind,
1299 }
1300 }
1301}
1302
1303impl DecryptionKeyResolver for KekDecryptor<'_> {
1304 fn resolve_key_encryption_keys_with_policy(
1305 &self,
1306 provider: &dyn crate::provider::CryptoProvider,
1307 algorithm: KeyWrapAlgorithm,
1308 source: KeyEncryptionKeySource<'_>,
1309 policy: &crate::policy::DecryptionPolicy,
1310 budget: &mut KeyCandidateBudget,
1311 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1312 match source {
1313 KeyEncryptionKeySource::Direct => {
1314 if self.kind != algorithm.key_kind() || self.kek.len() != algorithm.key_len() {
1315 return Err(XmlEncError::KeyNotFound);
1316 }
1317 budget.consume(1)?;
1318 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
1319 self.kek.to_vec(),
1320 )])
1321 }
1322 KeyEncryptionKeySource::Encrypted(key) => {
1323 validate_encrypted_key_policy(key, policy)?;
1324 budget.consume(2)?;
1325 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
1326 self.unwrap(provider, algorithm.key_len(), key)?,
1327 )])
1328 }
1329 _ => Err(XmlEncError::KeyNotFound),
1330 }
1331 }
1332 fn resolve_content_keys_with_policy(
1333 &self,
1334 provider: &dyn crate::provider::CryptoProvider,
1335 algorithm: DataEncryptionAlgorithm,
1336 encrypted_key: Option<&EncryptedKey>,
1337 policy: &crate::policy::DecryptionPolicy,
1338 budget: &mut KeyCandidateBudget,
1339 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1340 policy.validate()?;
1341 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
1342 validate_encrypted_key_policy(encrypted_key, policy)?;
1343 budget.consume(1)?;
1344 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
1345 self.resolve_key(provider, algorithm, Some(encrypted_key))?,
1346 )])
1347 }
1348
1349 fn resolve_key_candidates_with_policy(
1350 &self,
1351 provider: &dyn crate::provider::CryptoProvider,
1352 algorithm: DataEncryptionAlgorithm,
1353 encrypted_key: Option<&EncryptedKey>,
1354 policy: &crate::policy::DecryptionPolicy,
1355 budget: &mut KeyCandidateBudget,
1356 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1357 self.resolve_content_keys_with_policy(provider, algorithm, encrypted_key, policy, budget)?
1358 .into_iter()
1359 .map(|key| key.into_key().map_err(XmlEncError::from))
1360 .collect()
1361 }
1362
1363 fn resolve_key(
1364 &self,
1365 provider: &dyn crate::provider::CryptoProvider,
1366 algorithm: DataEncryptionAlgorithm,
1367 encrypted_key: Option<&EncryptedKey>,
1368 ) -> Result<Vec<u8>, XmlEncError> {
1369 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
1370 let key = self.unwrap(provider, algorithm.key_len(), encrypted_key)?;
1371 validate_key_len(algorithm, &key)?;
1372 Ok(key)
1373 }
1374}
1375
1376impl KekDecryptor<'_> {
1377 fn unwrap(
1378 &self,
1379 provider: &dyn crate::provider::CryptoProvider,
1380 output_len: usize,
1381 encrypted_key: &EncryptedKey,
1382 ) -> Result<Vec<u8>, XmlEncError> {
1383 encrypted_key.encryption_method.validate_structure()?;
1384 let wrap_algorithm =
1385 KeyWrapAlgorithm::from_uri(&encrypted_key.encryption_method.algorithm)?;
1386 if self.kind != wrap_algorithm.key_kind() {
1390 return Err(XmlEncError::KeyNotFound);
1391 }
1392 let wrapped = encrypted_key.cipher_data.octets()?;
1393 let expected_kek_len = wrap_algorithm.key_len();
1394 if self.kek.len() != expected_kek_len {
1395 return Err(XmlEncError::InvalidKekSize {
1396 algorithm: wrap_algorithm,
1397 expected: expected_kek_len,
1398 actual: self.kek.len(),
1399 });
1400 }
1401 let expected_wrapped_len = wrap_algorithm
1402 .wrapped_len(output_len)
1403 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(wrap_algorithm.uri().into()))?;
1404 if wrapped.len() != expected_wrapped_len {
1405 return Err(XmlEncError::InvalidWrappedKeyLength {
1406 expected: expected_wrapped_len,
1407 actual: wrapped.len(),
1408 });
1409 }
1410 provider.require_capability(crate::provider::ProviderCapability::KeyUnwrap(
1411 wrap_algorithm,
1412 ))?;
1413 let key = provider
1414 .unwrap_key(wrap_algorithm, &self.kek, &wrapped)
1415 .map_err(|error| match error {
1416 crate::provider::ProviderError::InvalidKeySize { expected, actual } => {
1417 XmlEncError::InvalidKekSize {
1418 algorithm: wrap_algorithm,
1419 expected,
1420 actual,
1421 }
1422 }
1423 crate::provider::ProviderError::AuthenticationFailed
1424 | crate::provider::ProviderError::InvalidInput(
1425 crate::provider::ProviderInputError::AesKeyWrapFraming,
1426 ) => XmlEncError::KeyWrapIntegrity,
1427 error => XmlEncError::Provider(error),
1428 })?;
1429 if key.len() != output_len {
1430 return Err(XmlEncError::InvalidWrappedKeyLength {
1431 expected: output_len,
1432 actual: key.len(),
1433 });
1434 }
1435 Ok(key)
1436 }
1437}
1438
1439impl PrivateKeyDecryptor {
1440 pub fn new(key: RsaPrivateKey) -> Self {
1442 Self::provider_key(Arc::new(crate::provider::RustCryptoRsaPrivateKey::new(key)))
1443 }
1444
1445 pub fn provider_key(key: Arc<dyn crate::provider::KeyRecoveryKey>) -> Self {
1447 Self { key }
1448 }
1449}
1450
1451impl DecryptionKeyResolver for PrivateKeyDecryptor {
1452 fn resolve_key_encryption_keys_with_policy(
1453 &self,
1454 provider: &dyn crate::provider::CryptoProvider,
1455 algorithm: KeyWrapAlgorithm,
1456 source: KeyEncryptionKeySource<'_>,
1457 policy: &crate::policy::DecryptionPolicy,
1458 budget: &mut KeyCandidateBudget,
1459 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1460 let KeyEncryptionKeySource::Encrypted(key) = source else {
1461 return Err(XmlEncError::KeyNotFound);
1462 };
1463 validate_encrypted_key_policy(key, policy)?;
1464 let width = policy.rsa_keys.validate_public_metadata(
1465 "decryption",
1466 self.key.rsa_modulus_bits(),
1467 self.key.rsa_public_exponent(),
1468 )?;
1469 let ciphertext = key.cipher_data.octets()?;
1470 if width != self.key.ciphertext_len() || ciphertext.len() != width {
1471 return Err(XmlEncError::InvalidWrappedKeyLength {
1472 expected: width,
1473 actual: ciphertext.len(),
1474 });
1475 }
1476 let transport = KeyTransportAlgorithm::from_uri(&key.encryption_method.algorithm)?;
1477 budget.consume(2)?;
1478 #[cfg(feature = "legacy-algorithms")]
1479 if transport == KeyTransportAlgorithm::RsaPkcs1v15 {
1480 provider.require_capability(crate::provider::ProviderCapability::Pkcs1v15Recovery)?;
1481 return Ok(vec![provider.recover_pkcs1v15(
1482 self.key.as_ref(),
1483 &ciphertext,
1484 algorithm.key_len(),
1485 )?]);
1486 }
1487 let method = &key.encryption_method;
1488 let parameters = RsaOaepParameters {
1489 algorithm: transport,
1490 digest: parse_oaep_digest(method.oaep_digest.as_deref())?,
1491 mgf_digest: parse_oaep_mgf_digest(method.mgf_algorithm.as_deref())?,
1492 label: method.oaep_params.clone().unwrap_or_default(),
1493 };
1494 provider.require_capability(crate::provider::ProviderCapability::KeyRecovery(
1495 ¶meters,
1496 ))?;
1497 let key = zeroize::Zeroizing::new(provider.recover_key(
1498 self.key.as_ref(),
1499 ¶meters,
1500 &ciphertext,
1501 )?);
1502 if key.len() != algorithm.key_len() {
1503 return Err(XmlEncError::InvalidKekSize {
1504 algorithm,
1505 expected: algorithm.key_len(),
1506 actual: key.len(),
1507 });
1508 }
1509 let mut key = key;
1510 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
1511 core::mem::take(&mut *key),
1512 )])
1513 }
1514 fn resolve_content_keys_with_policy(
1515 &self,
1516 provider: &dyn crate::provider::CryptoProvider,
1517 algorithm: DataEncryptionAlgorithm,
1518 encrypted_key: Option<&EncryptedKey>,
1519 policy: &crate::policy::DecryptionPolicy,
1520 budget: &mut KeyCandidateBudget,
1521 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1522 policy.validate()?;
1523 budget.consume(1)?;
1524 self.resolve_key_with_policy(provider, algorithm, encrypted_key, policy)
1525 .map(|key| vec![key])
1526 }
1527 fn resolve_key_candidates_with_policy(
1528 &self,
1529 provider: &dyn crate::provider::CryptoProvider,
1530 algorithm: DataEncryptionAlgorithm,
1531 encrypted_key: Option<&EncryptedKey>,
1532 policy: &crate::policy::DecryptionPolicy,
1533 budget: &mut KeyCandidateBudget,
1534 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1535 policy.validate()?;
1536 budget.consume(1)?;
1537 self.resolve_key_with_policy(provider, algorithm, encrypted_key, policy)
1538 .and_then(|key| key.into_key().map_err(XmlEncError::Provider))
1539 .map(|key| vec![key])
1540 }
1541
1542 fn resolve_key(
1543 &self,
1544 provider: &dyn crate::provider::CryptoProvider,
1545 algorithm: DataEncryptionAlgorithm,
1546 encrypted_key: Option<&EncryptedKey>,
1547 ) -> Result<Vec<u8>, XmlEncError> {
1548 self.resolve_key_with_policy(
1549 provider,
1550 algorithm,
1551 encrypted_key,
1552 &crate::policy::DecryptionPolicy::default(),
1553 )
1554 .and_then(|key| key.into_key().map_err(XmlEncError::Provider))
1555 }
1556}
1557
1558impl PrivateKeyDecryptor {
1559 pub fn resolve_key_with_policy(
1562 &self,
1563 provider: &dyn crate::provider::CryptoProvider,
1564 algorithm: DataEncryptionAlgorithm,
1565 encrypted_key: Option<&EncryptedKey>,
1566 policy: &crate::policy::DecryptionPolicy,
1567 ) -> Result<crate::provider::RecoveredContentKey, XmlEncError> {
1568 policy.validate()?;
1569 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
1570 validate_encrypted_key_policy(encrypted_key, policy)?;
1571 let width = policy.rsa_keys.validate_public_metadata(
1572 "decryption",
1573 self.key.rsa_modulus_bits(),
1574 self.key.rsa_public_exponent(),
1575 )?;
1576 if width != self.key.ciphertext_len() {
1577 return Err(crate::policy::PolicyViolation::InvalidKeyMaterial {
1578 operation: "decryption",
1579 key_type: "RSA",
1580 reason: "ciphertext width disagrees with modulus",
1581 }
1582 .into());
1583 }
1584 encrypted_key.encryption_method.validate_structure()?;
1585 let wrapped = encrypted_key.cipher_data.octets()?;
1586 let label = encrypted_key
1587 .encryption_method
1588 .oaep_params
1589 .clone()
1590 .unwrap_or_default();
1591 let transport =
1592 KeyTransportAlgorithm::from_uri(&encrypted_key.encryption_method.algorithm)?;
1593 let key = match transport {
1594 #[cfg(feature = "legacy-algorithms")]
1595 KeyTransportAlgorithm::RsaPkcs1v15 => {
1596 if wrapped.len() != self.key.ciphertext_len() {
1597 return Err(XmlEncError::InvalidWrappedKeyLength {
1598 expected: self.key.ciphertext_len(),
1599 actual: wrapped.len(),
1600 });
1601 }
1602 provider
1603 .require_capability(crate::provider::ProviderCapability::Pkcs1v15Recovery)?;
1604 provider
1609 .recover_pkcs1v15(self.key.as_ref(), &wrapped, algorithm.key_len())
1610 .map_err(XmlEncError::Provider)
1611 }
1612 KeyTransportAlgorithm::RsaOaepMgf1p => self.decrypt_oaep_mgf1p(
1613 provider,
1614 encrypted_key.encryption_method.oaep_digest.as_deref(),
1615 encrypted_key.encryption_method.mgf_algorithm.as_deref(),
1616 label,
1617 &wrapped,
1618 algorithm,
1619 ),
1620 KeyTransportAlgorithm::RsaOaep11 => self.decrypt_oaep11(
1621 provider,
1622 encrypted_key.encryption_method.oaep_digest.as_deref(),
1623 encrypted_key.encryption_method.mgf_algorithm.as_deref(),
1624 label,
1625 &wrapped,
1626 algorithm,
1627 ),
1628 }?;
1629 validate_content_key_len(algorithm, key.key_len())?;
1630 Ok(key)
1631 }
1632}
1633
1634impl PrivateKeyDecryptor {
1635 fn decrypt_oaep_mgf1p(
1636 &self,
1637 provider: &dyn crate::provider::CryptoProvider,
1638 digest: Option<&str>,
1639 mgf: Option<&str>,
1640 label: Vec<u8>,
1641 wrapped: &[u8],
1642 algorithm: DataEncryptionAlgorithm,
1643 ) -> Result<crate::provider::RecoveredContentKey, XmlEncError> {
1644 let parameters = RsaOaepParameters {
1645 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p,
1646 digest: parse_oaep_digest(digest)?,
1647 mgf_digest: parse_oaep_mgf_digest(mgf)?,
1648 label,
1649 };
1650 recover_rsa_oaep(provider, self.key.as_ref(), ¶meters, wrapped, algorithm)
1651 }
1652
1653 fn decrypt_oaep11(
1654 &self,
1655 provider: &dyn crate::provider::CryptoProvider,
1656 digest: Option<&str>,
1657 mgf: Option<&str>,
1658 label: Vec<u8>,
1659 wrapped: &[u8],
1660 algorithm: DataEncryptionAlgorithm,
1661 ) -> Result<crate::provider::RecoveredContentKey, XmlEncError> {
1662 let parameters = RsaOaepParameters {
1663 algorithm: KeyTransportAlgorithm::RsaOaep11,
1664 digest: parse_oaep_digest(digest)?,
1665 mgf_digest: parse_oaep_mgf_digest(mgf)?,
1666 label,
1667 };
1668 recover_rsa_oaep(provider, self.key.as_ref(), ¶meters, wrapped, algorithm)
1669 }
1670}
1671
1672fn parse_oaep_digest(uri: Option<&str>) -> Result<OaepDigestAlgorithm, XmlEncError> {
1673 let uri = uri.unwrap_or("http://www.w3.org/2000/09/xmldsig#sha1");
1674 OaepDigestAlgorithm::from_uri(uri)
1675 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(uri.to_owned()))
1676}
1677
1678fn parse_oaep_mgf_digest(uri: Option<&str>) -> Result<OaepDigestAlgorithm, XmlEncError> {
1679 let uri = uri.unwrap_or("http://www.w3.org/2009/xmlenc11#mgf1sha1");
1680 OaepDigestAlgorithm::from_mgf_uri(uri)
1681 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(uri.to_owned()))
1682}
1683
1684fn recover_rsa_oaep(
1685 provider: &dyn crate::provider::CryptoProvider,
1686 key: &dyn crate::provider::KeyRecoveryKey,
1687 parameters: &RsaOaepParameters,
1688 wrapped: &[u8],
1689 algorithm: DataEncryptionAlgorithm,
1690) -> Result<crate::provider::RecoveredContentKey, XmlEncError> {
1691 let expected = key.ciphertext_len();
1692 if wrapped.len() != expected {
1693 return Err(XmlEncError::InvalidWrappedKeyLength {
1694 expected,
1695 actual: wrapped.len(),
1696 });
1697 }
1698 provider.require_capability(crate::provider::ProviderCapability::KeyRecovery(parameters))?;
1699 provider
1700 .recover_content_key(key, parameters, algorithm, wrapped)
1701 .map_err(|error| match error {
1702 crate::provider::ProviderError::Random(message) => XmlEncError::Rng(message),
1703 error @ (crate::provider::ProviderError::AuthenticationFailed
1704 | crate::provider::ProviderError::InvalidInput(_)) => {
1705 XmlEncError::Rsa(error.to_string())
1706 }
1707 error => XmlEncError::Provider(error),
1708 })
1709}
1710
1711pub fn decrypt(
1713 xml: &str,
1714 resolver: &dyn DecryptionKeyResolver,
1715) -> Result<DecryptedContent, XmlEncError> {
1716 DecryptContext::new(resolver).decrypt(xml)
1717}
1718
1719pub fn decrypt_document(
1726 xml: &str,
1727 encrypted_data_id: Option<&str>,
1728 resolver: &dyn DecryptionKeyResolver,
1729) -> Result<String, XmlEncError> {
1730 decrypt_document_with_options(
1731 xml,
1732 DocumentDecryptionOptions { encrypted_data_id },
1733 resolver,
1734 )
1735}
1736
1737pub fn decrypt_document_with_options(
1742 xml: &str,
1743 options: DocumentDecryptionOptions<'_>,
1744 resolver: &dyn DecryptionKeyResolver,
1745) -> Result<String, XmlEncError> {
1746 DecryptContext::new(resolver).decrypt_document(xml, options.encrypted_data_id)
1747}
1748
1749#[derive(Clone, Copy)]
1750enum DocumentEncryptedDataSelector<'a> {
1751 EncryptedDataId(Option<&'a str>),
1752 UniqueBelowStartNode(Option<&'a str>),
1753 FirstBelowStartNode(Option<&'a str>),
1754}
1755
1756fn decrypt_document_with_context(
1757 xml: &str,
1758 selector: DocumentEncryptedDataSelector<'_>,
1759 context: &DecryptContext<'_>,
1760) -> Result<String, XmlEncError> {
1761 context.policy.resources.validate()?;
1762 validate_encryption_document_len(xml.len(), &context.policy)?;
1763 let budgets = DecryptionOperationBudgets::from_policy(&context.policy);
1764 let settings = context.document_parse_settings();
1765 let mut document =
1766 XmlDocument::parse_with_settings_and_budget(xml.to_owned(), settings, &budgets.xml_parse)
1767 .map_err(|error| map_document_error(error, settings))?;
1768 decrypt_owned_document_with_context(&mut document, selector, context, budgets)?;
1769 Ok(document.into_xml())
1770}
1771
1772fn decrypt_owned_document_with_context(
1773 document: &mut XmlDocument,
1774 selector: DocumentEncryptedDataSelector<'_>,
1775 context: &DecryptContext<'_>,
1776 budgets: DecryptionOperationBudgets,
1777) -> Result<(), XmlEncError> {
1778 context.policy.resources.validate()?;
1779 document.validate_operation_policy(&context.policy.xml, &context.policy.resources)?;
1780 let (target, target_len, target_start, replacement_type) = document.with_view(|view| {
1781 let start = match selector {
1782 DocumentEncryptedDataSelector::UniqueBelowStartNode(Some(id))
1783 | DocumentEncryptedDataSelector::FirstBelowStartNode(Some(id)) => view
1784 .node_for_id(id, context.id_attributes)
1785 .ok_or_else(|| XmlEncError::SelectedNodeUnavailable { id: id.to_owned() })
1786 .and_then(|identity| view.resolve_node(identity).map_err(XmlEncError::from))?,
1787 DocumentEncryptedDataSelector::UniqueBelowStartNode(None)
1788 | DocumentEncryptedDataSelector::FirstBelowStartNode(None)
1789 | DocumentEncryptedDataSelector::EncryptedDataId(_) => view.document().root(),
1790 };
1791 let encrypted_data_id = match selector {
1792 DocumentEncryptedDataSelector::EncryptedDataId(id) => id,
1793 DocumentEncryptedDataSelector::UniqueBelowStartNode(_)
1794 | DocumentEncryptedDataSelector::FirstBelowStartNode(_) => None,
1795 };
1796 let mut matches = start.descendants().filter(|node| {
1797 node.has_tag_name((XMLENC_NS, "EncryptedData"))
1798 && encrypted_data_id.is_none_or(|id| node.attribute("Id") == Some(id))
1799 });
1800 let selected = matches.next().ok_or(XmlEncError::EncryptedDataNotFound)?;
1801 if matches!(
1802 selector,
1803 DocumentEncryptedDataSelector::EncryptedDataId(_)
1804 | DocumentEncryptedDataSelector::UniqueBelowStartNode(_)
1805 ) && matches.next().is_some()
1806 {
1807 return Err(XmlEncError::AmbiguousEncryptedData);
1808 }
1809 Ok::<_, XmlEncError>((
1810 view.node_identity(selected),
1811 selected.range().len(),
1812 selected.range().start,
1813 match selected.attribute("Type") {
1814 Some("http://www.w3.org/2001/04/xmlenc#Element") => {
1815 Some(EncryptedDataType::Element)
1816 }
1817 Some("http://www.w3.org/2001/04/xmlenc#Content") => {
1818 Some(EncryptedDataType::Content)
1819 }
1820 _ => None,
1821 },
1822 ))
1823 })?;
1824 let document_binding = Some((document.identity(), document.generation()));
1825 let mut processed = context.process_decryption_input(
1826 DecryptionInput::Xml {
1827 source: document.as_xml(),
1828 node_start: Some(target_start),
1829 },
1830 document_binding,
1831 true,
1832 budgets,
1833 |candidate, operation_parse_budget| {
1834 let DecryptedContent::Xml(plaintext) = candidate else {
1835 return Err(XmlEncError::ReplacementRequiresXml);
1836 };
1837 validate_encryption_document_len(
1838 document
1839 .as_xml()
1840 .len()
1841 .saturating_sub(target_len)
1842 .saturating_add(plaintext.len()),
1843 &context.policy,
1844 )?;
1845 let settings = context.document_parse_settings();
1846 match replacement_type.as_ref() {
1847 Some(EncryptedDataType::Element) => document
1848 .prepare_element_replacement_with_budget(
1849 target,
1850 &plaintext,
1851 settings,
1852 operation_parse_budget,
1853 )
1854 .map_err(|error| map_document_error(error, settings)),
1855 Some(EncryptedDataType::Content) => document
1856 .prepare_node_fragment_replacement_with_budget(
1857 target,
1858 &plaintext,
1859 settings,
1860 operation_parse_budget,
1861 )
1862 .map_err(|error| map_document_error(error, settings)),
1863 Some(EncryptedDataType::Other(_)) | None => {
1864 Err(XmlEncError::ReplacementRequiresXml)
1865 }
1866 }
1867 },
1868 )?;
1869 let mutation = processed.mutation.ok_or_else(|| {
1870 XmlEncError::OperationPlan("decryption mutation node is unavailable".into())
1871 })?;
1872 processed
1873 .operation
1874 .run_document_transition(mutation, document, |document, _| {
1875 document
1876 .commit_prepared(processed.output)
1877 .map_err(|error| map_document_error(error, context.document_parse_settings()))
1878 })
1879}
1880
1881fn validate_encryption_document_len(
1882 actual: usize,
1883 policy: &crate::policy::DecryptionPolicy,
1884) -> Result<(), XmlEncError> {
1885 policy.resources.validate_xml_document_len(actual)?;
1886 Ok(())
1887}
1888
1889fn validate_recipient_count(actual: usize, maximum: usize) -> Result<(), XmlEncError> {
1890 if actual > maximum {
1891 return Err(crate::policy::PolicyViolation::ResourceLimit {
1892 resource: crate::policy::resource_name::ENCRYPTION_RECIPIENTS,
1893 maximum,
1894 actual,
1895 }
1896 .into());
1897 }
1898 Ok(())
1899}
1900
1901pub fn decrypt_data(
1903 encrypted: &EncryptedData,
1904 resolver: &dyn DecryptionKeyResolver,
1905) -> Result<DecryptedContent, XmlEncError> {
1906 DecryptContext::new(resolver).decrypt_data(encrypted)
1907}
1908
1909fn resolve_content_key_candidates(
1910 provider: &dyn crate::provider::CryptoProvider,
1911 algorithm: DataEncryptionAlgorithm,
1912 encrypted: &EncryptedData,
1913 resolver: &dyn DecryptionKeyResolver,
1914 policy: &crate::policy::DecryptionPolicy,
1915 budget: &mut KeyCandidateBudget,
1916 mut document: Option<KeySourceDocument<'_, '_>>,
1917) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1918 let mut last_error = None;
1919 let mut candidates = if encrypted.derived_keys.is_empty()
1920 && encrypted.agreement_methods.is_empty()
1921 && encrypted.encapsulation_methods.is_empty()
1922 {
1923 match resolve_candidates_with_budget(resolver, provider, algorithm, None, policy, budget) {
1924 Ok(keys) => keys,
1925 Err(error) => {
1926 record_candidate_source_error_or_fail_operation(error, &mut last_error)?;
1927 Vec::new()
1928 }
1929 }
1930 } else {
1931 Vec::new()
1932 };
1933 for descriptor in &encrypted.encapsulation_methods {
1934 policy
1935 .key_establishment
1936 .check_encapsulation(descriptor.algorithm)?;
1937 let remaining_before = budget.remaining();
1938 budget.require_available(1)?;
1939 match resolver.resolve_encapsulation_content_keys_with_policy(
1940 provider, algorithm, descriptor, policy, budget,
1941 ) {
1942 Ok(keys) => {
1943 budget.account_returned_candidates(remaining_before, keys.len())?;
1944 candidates.extend(keys);
1945 }
1946 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
1947 }
1948 }
1949 for descriptor in &encrypted.agreement_methods {
1950 let remaining_before = budget.remaining();
1951 match resolver.resolve_agreement_content_keys_with_policy(
1952 provider, algorithm, descriptor, policy, budget,
1953 ) {
1954 Ok(keys) => {
1955 budget.account_returned_candidates(remaining_before, keys.len())?;
1956 candidates.extend(keys);
1957 }
1958 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
1959 }
1960 }
1961 for descriptor in &encrypted.derived_keys {
1962 if !reference_list_applies_to_target(
1963 descriptor.reference_list.as_ref(),
1964 ReferenceTarget::new(
1965 document,
1966 encrypted.id.as_deref(),
1967 policy.transforms.same_document_id_semantics,
1968 ),
1969 ReferenceKind::Data,
1970 ) {
1971 continue;
1972 }
1973 let remaining_before = budget.remaining();
1974 match resolver.resolve_derived_content_keys_with_policy(
1975 provider, algorithm, descriptor, policy, budget,
1976 ) {
1977 Ok(keys) => {
1978 budget.account_returned_candidates(remaining_before, keys.len())?;
1979 candidates.extend(keys);
1980 }
1981 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
1982 }
1983 }
1984 for encrypted_key in &encrypted.encrypted_keys {
1985 let source = KeySourceView {
1986 key: encrypted_key,
1987 document: take_key_document(&mut document, encrypted_key)?,
1988 };
1989 if !encrypted_key_applies_to_data(
1990 encrypted_key,
1991 encrypted,
1992 ReferenceTarget::new(
1993 document,
1994 encrypted.id.as_deref(),
1995 policy.transforms.same_document_id_semantics,
1996 ),
1997 ) {
1998 continue;
1999 }
2000 if let Err(error) = validate_encrypted_key_policy(encrypted_key, policy) {
2001 last_error = Some(error);
2002 continue;
2003 }
2004 let resolution = if encrypted_key.sources.is_empty() {
2005 resolve_candidates_with_budget(
2006 resolver,
2007 provider,
2008 algorithm,
2009 Some(encrypted_key),
2010 policy,
2011 budget,
2012 )
2013 } else {
2014 resolve_nested_key(
2015 provider,
2016 source,
2017 algorithm.key_len(),
2018 resolver,
2019 policy,
2020 budget,
2021 1,
2022 )
2023 };
2024 match resolution {
2025 Ok(keys) => candidates.extend(keys),
2026 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
2027 }
2028 }
2029 if candidates.is_empty() {
2030 Err(last_error.unwrap_or(XmlEncError::KeyNotFound))
2031 } else {
2032 Ok(candidates)
2033 }
2034}
2035
2036pub(super) fn resolve_nested_cipher_references<'a>(
2037 keys: &mut [EncryptedKey],
2038 document: &crate::XmlDomDocument<'_>,
2039 origins: &mut core::slice::Iter<'_, Option<crate::NodeId>>,
2040 context: &super::cipher_reference::BoundCipherReferenceContext<'a, 'a>,
2041 parse: &XmlParseWorkBudget,
2042) -> Result<(), XmlEncError> {
2043 for key in keys {
2046 let origin = origins
2047 .next()
2048 .ok_or_else(|| XmlEncError::OperationPlan("missing encrypted key origin".into()))?;
2049 if let super::CipherData::Reference { uri, transforms } = &key.cipher_data {
2050 let node = origin.and_then(|id| document.get_node(id)).ok_or_else(|| {
2051 XmlEncError::OperationPlan("unresolved foreign encrypted key origin".into())
2052 })?;
2053 key.cipher_data = super::CipherData::Bytes(context.resolve_parsed(
2054 cipher_reference_node(node)?,
2055 uri,
2056 transforms,
2057 parse,
2058 )?);
2059 }
2060 resolve_nested_cipher_references(
2061 &mut key.sources.encrypted_keys,
2062 document,
2063 origins,
2064 context,
2065 parse,
2066 )?;
2067 }
2068 Ok(())
2069}
2070
2071fn resolve_nested_key(
2072 provider: &dyn crate::provider::CryptoProvider,
2073 source: KeySourceView<'_, '_>,
2074 output_len: usize,
2075 resolver: &dyn DecryptionKeyResolver,
2076 policy: &crate::policy::DecryptionPolicy,
2077 budget: &mut KeyCandidateBudget,
2078 depth: usize,
2079) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
2080 let key = source.key;
2081 let mut document = source.document;
2082 let target = ReferenceTarget::new(
2083 document,
2084 key.id.as_deref(),
2085 policy.transforms.same_document_id_semantics,
2086 );
2087 policy.resources.validate_key_info_reference_depth(depth)?;
2088 validate_encrypted_key_policy(key, policy)?;
2089 let wrap = KeyWrapAlgorithm::from_uri(&key.encryption_method.algorithm)?;
2090 provider.require_capability(crate::provider::ProviderCapability::KeyUnwrap(wrap))?;
2091 let ciphertext = key.cipher_data.octets()?;
2092 let expected = wrap
2093 .wrapped_len(output_len)
2094 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(wrap.uri().into()))?;
2095 if ciphertext.len() != expected {
2096 return Err(XmlEncError::InvalidWrappedKeyLength {
2097 expected,
2098 actual: ciphertext.len(),
2099 });
2100 }
2101 let mut keys = Vec::new();
2102 let mut last_error = None;
2103 let mut resolve_source = |source| -> Result<(), XmlEncError> {
2104 budget.require_available(1)?;
2105 let remaining = budget.remaining();
2106 match resolver
2107 .resolve_key_encryption_keys_with_policy(provider, wrap, source, policy, budget)
2108 {
2109 Ok(resolved) => {
2110 budget.account_returned_candidates(remaining, resolved.len())?;
2111 keys.extend(resolved);
2112 Ok(())
2113 }
2114 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error),
2115 }
2116 };
2117 for mechanism in &key.sources.encapsulation_methods {
2118 resolve_source(KeyEncryptionKeySource::Encapsulation(mechanism))?;
2119 }
2120 for agreement in &key.sources.agreement_methods {
2121 resolve_source(KeyEncryptionKeySource::Agreement(agreement))?;
2122 }
2123 for derived in &key.sources.derived_keys {
2124 if !reference_list_applies_to_target(
2125 derived.reference_list.as_ref(),
2126 target,
2127 ReferenceKind::Key,
2128 ) {
2129 continue;
2130 }
2131 resolve_source(KeyEncryptionKeySource::Derived(derived))?;
2132 }
2133 for nested in &key.sources.encrypted_keys {
2134 let source = KeySourceView {
2135 key: nested,
2136 document: take_key_document(&mut document, nested)?,
2137 };
2138 if !reference_list_applies_to_target(
2139 nested.reference_list.as_ref(),
2140 target,
2141 ReferenceKind::Key,
2142 ) {
2143 continue;
2144 }
2145 let resolved = if nested.sources.is_empty() {
2146 budget.require_available(3)?;
2147 let remaining = budget.remaining();
2148 resolver
2149 .resolve_key_encryption_keys_with_policy(
2150 provider,
2151 wrap,
2152 KeyEncryptionKeySource::Encrypted(nested),
2153 policy,
2154 budget,
2155 )
2156 .and_then(|resolved| {
2157 budget.account_returned_candidates(remaining, resolved.len())?;
2158 Ok(resolved)
2159 })
2160 } else {
2161 resolve_nested_key(
2162 provider,
2163 source,
2164 wrap.key_len(),
2165 resolver,
2166 policy,
2167 budget,
2168 depth + 1,
2169 )
2170 };
2171 match resolved {
2172 Ok(resolved) => keys.extend(resolved),
2173 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
2174 }
2175 }
2176 let mut outputs = Vec::new();
2177 for kek in keys {
2181 if kek.key_len() != wrap.key_len() {
2182 last_error = Some(XmlEncError::InvalidKekSize {
2183 algorithm: wrap,
2184 expected: wrap.key_len(),
2185 actual: kek.key_len(),
2186 });
2187 continue;
2188 }
2189 budget.consume(1)?;
2190 match kek.unwrap_nested(provider, wrap, &ciphertext, output_len) {
2191 Ok(result) if result.key_len() == output_len => outputs.push(result),
2192 Ok(result) => {
2193 last_error = Some(XmlEncError::InvalidWrappedKeyLength {
2194 expected: output_len,
2195 actual: result.key_len(),
2196 })
2197 }
2198 Err(error) => last_error = Some(XmlEncError::Provider(error)),
2199 }
2200 }
2201 if outputs.is_empty() {
2202 Err(last_error.unwrap_or(XmlEncError::KeyNotFound))
2203 } else {
2204 Ok(outputs)
2205 }
2206}
2207
2208#[derive(Clone, Copy)]
2209struct KeySourceDocument<'a, 'doc> {
2210 references: &'a super::cipher_reference::BoundCipherReferenceContext<'doc, 'doc>,
2211 target: crate::NodeId,
2212 origins: &'a [Option<crate::NodeId>],
2213}
2214
2215struct KeySourceView<'a, 'doc> {
2216 key: &'a EncryptedKey,
2217 document: Option<KeySourceDocument<'a, 'doc>>,
2218}
2219
2220fn key_origin_count(key: &EncryptedKey) -> usize {
2221 1 + key
2224 .sources
2225 .encrypted_keys
2226 .iter()
2227 .map(key_origin_count)
2228 .sum::<usize>()
2229}
2230
2231fn take_key_document<'a, 'doc>(
2232 document: &mut Option<KeySourceDocument<'a, 'doc>>,
2233 key: &EncryptedKey,
2234) -> Result<Option<KeySourceDocument<'a, 'doc>>, XmlEncError> {
2235 let Some(parent) = document else {
2236 return Ok(None);
2237 };
2238 let count = key_origin_count(key);
2239 if count > parent.origins.len() {
2240 return Err(XmlEncError::OperationPlan(
2241 "missing key association origin".into(),
2242 ));
2243 }
2244 let (subtree, remaining) = parent.origins.split_at(count);
2245 parent.origins = remaining;
2246 Ok(subtree[0].map(|target| KeySourceDocument {
2247 references: parent.references,
2248 target,
2249 origins: &subtree[1..],
2250 }))
2251}
2252
2253#[derive(Clone, Copy)]
2254enum ReferenceTarget<'a, 'doc> {
2255 Document {
2256 references: &'a super::cipher_reference::BoundCipherReferenceContext<'doc, 'doc>,
2257 node: crate::NodeId,
2258 },
2259 TypedId {
2260 id: Option<&'a str>,
2261 semantics: crate::policy::SameDocumentIdSemantics,
2262 },
2263}
2264
2265impl<'a, 'doc> ReferenceTarget<'a, 'doc> {
2266 fn new(
2267 document: Option<KeySourceDocument<'a, 'doc>>,
2268 id: Option<&'a str>,
2269 semantics: crate::policy::SameDocumentIdSemantics,
2270 ) -> Self {
2271 match document {
2272 Some(source) => Self::Document {
2273 references: source.references,
2274 node: source.target,
2275 },
2276 None => Self::TypedId { id, semantics },
2277 }
2278 }
2279
2280 fn matches(self, uri: &str) -> bool {
2281 match self {
2287 Self::Document { references, node } => references
2288 .resolver()
2289 .same_document_reference_targets(uri, node),
2290 Self::TypedId { id, semantics } => {
2291 id.is_some_and(|id| reference_targets_id(uri, id, semantics))
2292 }
2293 }
2294 }
2295}
2296
2297enum ReferenceKind {
2298 Data,
2299 Key,
2300}
2301
2302fn record_candidate_source_error_or_fail_operation(
2303 error: XmlEncError,
2304 last_error: &mut Option<XmlEncError>,
2305) -> Result<(), XmlEncError> {
2306 if matches!(
2310 &error,
2311 XmlEncError::Policy(_)
2312 | XmlEncError::KeyEstablishment(
2313 crate::key_establishment::KeyEstablishmentError::Policy(_)
2314 )
2315 ) {
2316 return Err(error);
2317 }
2318 *last_error = Some(error);
2319 Ok(())
2320}
2321
2322fn resolve_candidates_with_budget(
2323 resolver: &dyn DecryptionKeyResolver,
2324 provider: &dyn crate::provider::CryptoProvider,
2325 algorithm: DataEncryptionAlgorithm,
2326 encrypted_key: Option<&EncryptedKey>,
2327 policy: &crate::policy::DecryptionPolicy,
2328 budget: &mut KeyCandidateBudget,
2329) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
2330 let remaining_before = budget.remaining();
2331 let keys = resolver.resolve_content_keys_with_policy(
2332 provider,
2333 algorithm,
2334 encrypted_key,
2335 policy,
2336 budget,
2337 )?;
2338 budget.account_returned_candidates(remaining_before, keys.len())?;
2339 Ok(keys)
2340}
2341
2342fn encrypted_key_applies_to_data(
2343 encrypted_key: &EncryptedKey,
2344 encrypted_data: &EncryptedData,
2345 target: ReferenceTarget<'_, '_>,
2346) -> bool {
2347 if !reference_list_applies_to_target(
2351 encrypted_key.reference_list.as_ref(),
2352 target,
2353 ReferenceKind::Data,
2354 ) {
2355 return false;
2356 }
2357 if let (Some(carried), Some(expected)) = (
2358 encrypted_key.carried_key_name.as_deref(),
2359 encrypted_data.key_name.as_deref(),
2360 ) && carried != expected
2361 {
2362 return false;
2363 }
2364 true
2365}
2366
2367pub(super) struct EncapsulationRecipient {
2368 key_path: [usize; crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING],
2369 depth: usize,
2370 method: usize,
2371}
2372
2373impl EncapsulationRecipient {
2374 pub(super) fn mechanism<'a>(
2375 &self,
2376 data: &'a EncryptedData,
2377 ) -> &'a crate::key_establishment::EncapsulationMechanism {
2378 let mut methods = &data.encapsulation_methods;
2379 let mut keys = &data.encrypted_keys;
2380 for index in &self.key_path[..self.depth] {
2381 let sources = &keys[*index].sources;
2382 methods = &sources.encapsulation_methods;
2383 keys = &sources.encrypted_keys;
2384 }
2385 &methods[self.method]
2386 }
2387}
2388
2389pub(super) fn select_encapsulation_recipient<'doc>(
2390 data: &EncryptedData,
2391 references: &super::cipher_reference::BoundCipherReferenceContext<'doc, 'doc>,
2392 target: crate::NodeId,
2393 origins: &[Option<crate::NodeId>],
2394 policy: &crate::policy::DecryptionPolicy,
2395) -> Result<Option<EncapsulationRecipient>, XmlEncError> {
2396 struct Selection<'a> {
2397 data: &'a EncryptedData,
2398 semantics: crate::policy::SameDocumentIdSemantics,
2399 path: [usize; crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING],
2400 depth: usize,
2401 selected: Option<EncapsulationRecipient>,
2402 }
2403 fn visit(
2404 methods: &[crate::key_establishment::EncapsulationMechanism],
2405 keys: &[EncryptedKey],
2406 mut document: Option<KeySourceDocument<'_, '_>>,
2407 parent: Option<&EncryptedKey>,
2408 selection: &mut Selection<'_>,
2409 ) -> Result<(), XmlEncError> {
2410 for method in 0..methods.len() {
2411 if selection.selected.is_some() {
2412 return Err(XmlEncError::InvalidStructure(
2413 "multiple KEM recipients require explicit application key resolution".into(),
2414 ));
2415 }
2416 selection.selected = Some(EncapsulationRecipient {
2417 key_path: selection.path,
2418 depth: selection.depth,
2419 method,
2420 });
2421 }
2422 for (index, key) in keys.iter().enumerate() {
2423 let source = take_key_document(&mut document, key)?;
2424 let target = ReferenceTarget::new(
2425 document,
2426 match parent {
2427 Some(parent) => parent.id.as_deref(),
2428 None => selection.data.id.as_deref(),
2429 },
2430 selection.semantics,
2431 );
2432 let applies = match parent {
2436 None => encrypted_key_applies_to_data(key, selection.data, target),
2437 Some(_) => reference_list_applies_to_target(
2438 key.reference_list.as_ref(),
2439 target,
2440 ReferenceKind::Key,
2441 ),
2442 };
2443 if !applies {
2444 continue;
2445 }
2446 debug_assert!(selection.depth < selection.path.len());
2449 selection.path[selection.depth] = index;
2450 selection.depth += 1;
2451 visit(
2452 &key.sources.encapsulation_methods,
2453 &key.sources.encrypted_keys,
2454 source,
2455 Some(key),
2456 selection,
2457 )?;
2458 selection.depth -= 1;
2459 }
2460 Ok(())
2461 }
2462 let mut selection = Selection {
2463 data,
2464 semantics: policy.transforms.same_document_id_semantics,
2465 path: [0; crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING],
2466 depth: 0,
2467 selected: None,
2468 };
2469 visit(
2470 &data.encapsulation_methods,
2471 &data.encrypted_keys,
2472 Some(KeySourceDocument {
2473 references,
2474 target,
2475 origins,
2476 }),
2477 None,
2478 &mut selection,
2479 )?;
2480 Ok(selection.selected)
2481}
2482
2483fn reference_list_applies_to_target(
2484 references: Option<&super::ReferenceList>,
2485 target: ReferenceTarget<'_, '_>,
2486 kind: ReferenceKind,
2487) -> bool {
2488 let Some(references) = references else {
2489 return true;
2490 };
2491 let uris = match kind {
2492 ReferenceKind::Data => &references.data_references,
2493 ReferenceKind::Key => &references.key_references,
2494 };
2495 if uris.is_empty() {
2496 return true;
2497 }
2498 for uri in uris {
2499 if target.matches(uri) {
2500 return true;
2501 }
2502 }
2503 false
2504}
2505
2506fn reference_targets_id(
2507 uri: &str,
2508 id: &str,
2509 semantics: crate::policy::SameDocumentIdSemantics,
2510) -> bool {
2511 let Some(fragment) = uri.strip_prefix('#') else {
2512 return false;
2513 };
2514 let Ok(fragment) =
2518 crate::xmldsig::uri::UriReferenceResolver::normalize_fragment(fragment, None, semantics)
2519 else {
2520 return false;
2521 };
2522 crate::xmldsig::uri::UriReferenceResolver::id_fragment(&fragment, semantics)
2523 .is_some_and(|(target, _)| target == id)
2524}
2525
2526fn compatible_decryption_key_candidates(
2527 algorithm: DataEncryptionAlgorithm,
2528 mut keys: Vec<crate::provider::RecoveredContentKey>,
2529) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
2530 let mut accepted = 0;
2531 let mut last_error = None;
2532 for index in 0..keys.len() {
2533 let key = &keys[index];
2534 match validate_content_key_len(algorithm, key.key_len()) {
2535 Ok(())
2536 if !keys[..accepted]
2537 .iter()
2538 .any(|existing| existing.same_candidate(key)) =>
2539 {
2540 keys.swap(accepted, index);
2544 accepted += 1;
2545 }
2546 Ok(()) => {}
2547 Err(error) => last_error = Some(error),
2548 }
2549 }
2550 keys.truncate(accepted);
2551 if keys.is_empty() {
2552 return Err(last_error.unwrap_or(XmlEncError::KeyNotFound));
2553 }
2554 Ok(keys)
2555}
2556
2557fn validate_decryption_key_candidates(
2558 algorithm: DataEncryptionAlgorithm,
2559 actual: usize,
2560) -> Result<(), XmlEncError> {
2561 let maximum = crate::hard_limits::KEY_CANDIDATE_CEILING;
2562 if actual > maximum {
2563 return Err(crate::policy::PolicyViolation::ResourceLimit {
2564 resource: crate::policy::resource_name::KEY_CANDIDATES,
2565 maximum,
2566 actual,
2567 }
2568 .into());
2569 }
2570 if actual > 1 && !algorithm.is_authenticated() {
2571 return Err(XmlEncError::AmbiguousKeyCandidates { algorithm, actual });
2572 }
2573 Ok(())
2574}
2575
2576fn validate_encrypted_key_policy(
2577 encrypted_key: &EncryptedKey,
2578 policy: &crate::policy::DecryptionPolicy,
2579) -> Result<(), XmlEncError> {
2580 validate_key_encryption_method_policy(&encrypted_key.encryption_method, policy)
2581}
2582
2583pub(super) fn validate_key_encryption_method_policy(
2584 method: &super::EncryptionMethod,
2585 policy: &crate::policy::DecryptionPolicy,
2586) -> Result<(), XmlEncError> {
2587 method.validate_structure()?;
2588 let uri = &method.algorithm;
2589 if let Ok(transport) = KeyTransportAlgorithm::from_uri(uri) {
2590 if policy
2591 .key_transport_algorithms
2592 .as_ref()
2593 .map_or(transport.requires_explicit_permission(), |allowed| {
2594 !allowed.contains(&transport)
2595 })
2596 {
2597 return Err(crate::policy::PolicyViolation::Algorithm {
2598 operation: "decryption",
2599 algorithm: uri.clone(),
2600 }
2601 .into());
2602 }
2603 #[cfg(feature = "legacy-algorithms")]
2604 if transport == KeyTransportAlgorithm::RsaPkcs1v15 {
2605 return Ok(());
2606 }
2607 let digest = parse_oaep_digest(method.oaep_digest.as_deref())?;
2608 let mgf_digest = parse_oaep_mgf_digest(method.mgf_algorithm.as_deref())?;
2609 let selected_algorithms = [
2610 (
2611 digest,
2612 method.oaep_digest.as_deref().unwrap_or(digest.uri()),
2613 ),
2614 (
2615 mgf_digest,
2616 method
2617 .mgf_algorithm
2618 .as_deref()
2619 .unwrap_or(mgf_digest.mgf_uri().ok_or_else(|| {
2620 XmlEncError::InvalidStructure(
2621 "OAEP mask digest has no XML Encryption identifier".into(),
2622 )
2623 })?),
2624 ),
2625 ];
2626 for (selected, wire_uri) in selected_algorithms {
2627 if selected.requires_explicit_permission()
2628 && !policy
2629 .oaep_digests
2630 .as_ref()
2631 .is_some_and(|allowed| allowed.contains(&selected))
2632 {
2633 return Err(crate::policy::PolicyViolation::Algorithm {
2634 operation: "decryption",
2635 algorithm: wire_uri.to_owned(),
2636 }
2637 .into());
2638 }
2639 if policy
2640 .oaep_digests
2641 .as_ref()
2642 .is_some_and(|allowed| !allowed.contains(&selected))
2643 {
2644 return Err(crate::policy::PolicyViolation::Algorithm {
2645 operation: "decryption",
2646 algorithm: wire_uri.to_owned(),
2647 }
2648 .into());
2649 }
2650 }
2651 } else {
2652 let wrap = KeyWrapAlgorithm::from_uri(uri)?;
2653 if policy
2654 .key_wrap_algorithms
2655 .as_ref()
2656 .map_or(wrap.requires_explicit_permission(), |allowed| {
2657 !allowed.contains(&wrap)
2658 })
2659 {
2660 return Err(crate::policy::PolicyViolation::Algorithm {
2661 operation: "decryption",
2662 algorithm: uri.clone(),
2663 }
2664 .into());
2665 }
2666 }
2667 Ok(())
2668}
2669
2670fn validate_content_framing_before_resolution(
2671 algorithm: DataEncryptionAlgorithm,
2672 ciphertext_len: usize,
2673 encrypted_keys: &[EncryptedKey],
2674 policy: &crate::policy::DecryptionPolicy,
2675) -> Result<(), XmlEncError> {
2676 let Err(framing_error) = validate_ciphertext_framing(algorithm, ciphertext_len) else {
2677 return Ok(());
2678 };
2679
2680 if !encrypted_keys.is_empty() {
2685 let mut last_key_error = None;
2686 for encrypted_key in encrypted_keys {
2687 match validate_encrypted_key_policy(encrypted_key, policy) {
2688 Ok(()) => return Err(framing_error),
2689 Err(error) => last_key_error = Some(error),
2690 }
2691 }
2692 if let Some(error) = last_key_error {
2693 return Err(error);
2694 }
2695 }
2696 Err(framing_error)
2697}
2698
2699fn validate_typed_cipher_values(
2700 encrypted: &EncryptedData,
2701 algorithm: DataEncryptionAlgorithm,
2702 maximum_plaintext: usize,
2703 maximum_cipher_values: usize,
2704) -> Result<(), XmlEncError> {
2705 let maximum_ciphertext = algorithm
2706 .ciphertext_len_for_plaintext(maximum_plaintext)
2707 .ok_or(XmlEncError::InvalidEncryptionConfig(
2708 "ciphertext size overflow".into(),
2709 ))?;
2710 let projected = validate_cipher_data_len(&encrypted.cipher_data, maximum_ciphertext)?;
2711 if projected > maximum_ciphertext {
2712 return Err(crate::policy::PolicyViolation::ResourceLimit {
2713 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
2714 maximum: maximum_plaintext,
2715 actual: projected.saturating_sub(algorithm.minimum_ciphertext_len()),
2716 }
2717 .into());
2718 }
2719
2720 let mut aggregate_encoded = cipher_data_storage_len(&encrypted.cipher_data);
2721 if aggregate_encoded > maximum_cipher_values {
2722 return Err(crate::policy::PolicyViolation::ResourceLimit {
2723 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
2724 maximum: maximum_cipher_values,
2725 actual: aggregate_encoded,
2726 }
2727 .into());
2728 }
2729
2730 let maximum_wrapped_key = projected_decoded_len_for_encoded_len(MAX_CIPHER_VALUE_BASE64_LEN);
2731 validate_encapsulation_cipher_values(
2732 &encrypted.encapsulation_methods,
2733 maximum_cipher_values,
2734 &mut aggregate_encoded,
2735 )?;
2736 validate_wrapped_cipher_values(
2737 &encrypted.encrypted_keys,
2738 maximum_wrapped_key,
2739 maximum_cipher_values,
2740 &mut aggregate_encoded,
2741 )
2742}
2743
2744fn validate_wrapped_cipher_values(
2745 keys: &[EncryptedKey],
2746 maximum_wrapped_key: usize,
2747 maximum_cipher_values: usize,
2748 aggregate_encoded: &mut usize,
2749) -> Result<(), XmlEncError> {
2750 for encrypted_key in keys {
2751 validate_cipher_data_len(&encrypted_key.cipher_data, maximum_wrapped_key)?;
2752 *aggregate_encoded =
2753 aggregate_encoded.saturating_add(cipher_data_storage_len(&encrypted_key.cipher_data));
2754 if *aggregate_encoded > maximum_cipher_values {
2755 return Err(crate::policy::PolicyViolation::ResourceLimit {
2756 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
2757 maximum: maximum_cipher_values,
2758 actual: *aggregate_encoded,
2759 }
2760 .into());
2761 }
2762 validate_encapsulation_cipher_values(
2763 &encrypted_key.sources.encapsulation_methods,
2764 maximum_cipher_values,
2765 aggregate_encoded,
2766 )?;
2767 validate_wrapped_cipher_values(
2768 &encrypted_key.sources.encrypted_keys,
2769 maximum_wrapped_key,
2770 maximum_cipher_values,
2771 aggregate_encoded,
2772 )?;
2773 }
2774 Ok(())
2775}
2776
2777fn validate_encapsulation_cipher_values(
2778 methods: &[crate::key_establishment::EncapsulationMechanism],
2779 maximum: usize,
2780 aggregate: &mut usize,
2781) -> Result<(), XmlEncError> {
2782 for method in methods {
2783 let encoded = method.ciphertext.len().div_ceil(3).checked_mul(4);
2786 let total = encoded.and_then(|encoded| aggregate.checked_add(encoded));
2787 let total = total.ok_or(crate::policy::PolicyViolation::ResourceLimitExceeded {
2788 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
2789 maximum,
2790 })?;
2791 if total > maximum {
2792 return Err(crate::policy::PolicyViolation::ResourceLimit {
2793 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
2794 maximum,
2795 actual: total,
2796 }
2797 .into());
2798 }
2799 *aggregate = total;
2800 }
2801 Ok(())
2802}
2803
2804fn cipher_data_storage_len(cipher: &super::CipherData) -> usize {
2805 match cipher {
2806 super::CipherData::Value { value } => value.len(),
2807 super::CipherData::Bytes(bytes) => bytes.len(),
2808 super::CipherData::Reference { .. } => 0,
2809 }
2810}
2811
2812fn validate_cipher_data_len(
2813 cipher: &super::CipherData,
2814 maximum: usize,
2815) -> Result<usize, XmlEncError> {
2816 match cipher {
2817 super::CipherData::Value { value } => validate_cipher_value_len(value, maximum),
2818 super::CipherData::Bytes(bytes) if bytes.len() <= maximum => Ok(bytes.len()),
2819 super::CipherData::Bytes(bytes) => Err(crate::policy::PolicyViolation::ResourceLimit {
2820 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
2821 maximum,
2822 actual: bytes.len(),
2823 }
2824 .into()),
2825 super::CipherData::Reference { .. } => Ok(0),
2826 }
2827}
2828
2829fn validate_cipher_value_len(value: &str, maximum_decoded: usize) -> Result<usize, XmlEncError> {
2830 if value.len() > MAX_CIPHER_VALUE_BASE64_LEN {
2831 return Err(XmlEncError::InvalidStructure(format!(
2832 "CipherValue exceeds {MAX_CIPHER_VALUE_BASE64_LEN}-byte limit"
2833 )));
2834 }
2835 Ok(projected_decoded_len(value).min(maximum_decoded.saturating_add(1)))
2836}
2837
2838fn projected_decoded_len(value: &str) -> usize {
2839 let padding = value
2840 .as_bytes()
2841 .iter()
2842 .rev()
2843 .take(2)
2844 .take_while(|byte| **byte == b'=')
2845 .count();
2846 projected_decoded_len_for_encoded_len(value.len()).saturating_sub(padding)
2847}
2848
2849fn projected_decoded_len_for_encoded_len(encoded_len: usize) -> usize {
2850 encoded_len
2851 .checked_add(3)
2852 .map(|length| length / 4)
2853 .and_then(|quanta| quanta.checked_mul(3))
2854 .unwrap_or(usize::MAX)
2855}
2856
2857pub(crate) fn validate_key_len(
2858 algorithm: DataEncryptionAlgorithm,
2859 key: &[u8],
2860) -> Result<(), XmlEncError> {
2861 validate_content_key_len(algorithm, key.len())
2862}
2863
2864fn validate_content_key_len(
2865 algorithm: DataEncryptionAlgorithm,
2866 actual: usize,
2867) -> Result<(), XmlEncError> {
2868 if actual == algorithm.key_len() {
2869 Ok(())
2870 } else {
2871 Err(XmlEncError::InvalidKeySize {
2872 algorithm,
2873 expected: algorithm.key_len(),
2874 actual,
2875 })
2876 }
2877}
2878
2879fn validate_possible_plaintext_len(
2880 algorithm: DataEncryptionAlgorithm,
2881 ciphertext_len: usize,
2882 maximum: usize,
2883) -> Result<(), XmlEncError> {
2884 let framing = algorithm.minimum_ciphertext_len();
2887 validate_plaintext_len(ciphertext_len.saturating_sub(framing), maximum)
2888}
2889
2890fn validate_provider_plaintext_len(
2891 algorithm: DataEncryptionAlgorithm,
2892 ciphertext_len: usize,
2893 plaintext_len: usize,
2894) -> Result<(), XmlEncError> {
2895 use crate::provider::{ProviderError, ProviderOperation};
2896
2897 match algorithm.cbc_block_len() {
2898 None => {
2899 let expected = ciphertext_len - algorithm.minimum_ciphertext_len();
2900 if plaintext_len != expected {
2901 return Err(ProviderError::InvalidOutputSize {
2902 operation: ProviderOperation::Decrypt,
2903 expected,
2904 actual: plaintext_len,
2905 }
2906 .into());
2907 }
2908 }
2909 Some(block) => {
2910 let padded_len = ciphertext_len - block;
2911 let minimum = padded_len - block;
2912 let maximum = padded_len - 1;
2913 if !(minimum..=maximum).contains(&plaintext_len) {
2914 return Err(ProviderError::InvalidOutputSizeRange {
2915 operation: ProviderOperation::Decrypt,
2916 minimum,
2917 maximum,
2918 actual: plaintext_len,
2919 }
2920 .into());
2921 }
2922 }
2923 }
2924 Ok(())
2925}
2926
2927fn validate_plaintext_len(actual: usize, maximum: usize) -> Result<(), XmlEncError> {
2928 if actual <= maximum {
2929 Ok(())
2930 } else {
2931 Err(crate::policy::PolicyViolation::ResourceLimit {
2932 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
2933 maximum,
2934 actual,
2935 }
2936 .into())
2937 }
2938}
2939
2940fn map_data_decryption_error(
2941 algorithm: DataEncryptionAlgorithm,
2942 ciphertext_len: usize,
2943 error: crate::provider::ProviderError,
2944) -> XmlEncError {
2945 use crate::provider::ProviderError;
2946
2947 match error {
2948 ProviderError::AuthenticationFailed if algorithm.is_authenticated() => {
2949 XmlEncError::AeadAuthenticationFailed
2950 }
2951 ProviderError::InvalidInput(crate::provider::ProviderInputError::AesGcmFraming)
2952 if algorithm.cbc_block_len().is_none() =>
2953 {
2954 XmlEncError::DataTooShort {
2955 algorithm: "AES-GCM",
2956 minimum: 28,
2957 actual: ciphertext_len,
2958 }
2959 }
2960 error @ ProviderError::InvalidInput(crate::provider::ProviderInputError::AesCbcFraming)
2961 if algorithm.cbc_block_len().is_some() =>
2962 {
2963 match super::types::validate_ciphertext_framing(algorithm, ciphertext_len) {
2964 Err(error) => error,
2965 Ok(()) => XmlEncError::Provider(error),
2966 }
2967 }
2968 ProviderError::InvalidInput(crate::provider::ProviderInputError::AesCbcCiphertext)
2969 if algorithm.cbc_block_len().is_some() =>
2970 {
2971 XmlEncError::InvalidPadding
2972 }
2973 error => XmlEncError::Provider(error),
2974 }
2975}
2976
2977#[cfg(test)]
2978mod tests {
2979 #[test]
2980 fn raw_chacha_cannot_select_keys_or_confirm_kem_decapsulation() {
2981 let algorithm = super::DataEncryptionAlgorithm::ChaCha20;
2983 assert!(matches!(
2984 super::validate_decryption_key_candidates(algorithm, 2),
2985 Err(super::XmlEncError::AmbiguousKeyCandidates { actual: 2, .. })
2986 ));
2987 assert!(
2988 crate::policy::KeyEstablishmentPolicy::default()
2989 .check_kem_content(algorithm)
2990 .is_err()
2991 );
2992 let externally_authenticated = crate::policy::KeyEstablishmentPolicy {
2993 kem_content_authentication:
2994 crate::policy::KemContentAuthentication::ExternalAuthenticated,
2995 ..Default::default()
2996 };
2997 assert!(
2998 externally_authenticated
2999 .check_kem_content(algorithm)
3000 .is_ok()
3001 );
3002 for authenticated in [
3003 super::DataEncryptionAlgorithm::Aes128Gcm,
3004 super::DataEncryptionAlgorithm::ChaCha20Poly1305,
3005 ] {
3006 assert!(super::validate_decryption_key_candidates(authenticated, 2).is_ok());
3007 assert!(
3008 crate::policy::KeyEstablishmentPolicy::default()
3009 .check_kem_content(authenticated)
3010 .is_ok()
3011 );
3012 }
3013 }
3014
3015 #[test]
3016 fn raw_chacha_public_decryption_rejects_distinct_candidate_keys() {
3017 let algorithm = DataEncryptionAlgorithm::ChaCha20;
3020 let encrypted = super::super::EncryptedDataBuilder::new(algorithm)
3021 .policy(crate::policy::EncryptionPolicy {
3022 data_algorithms: Some([algorithm].into()),
3023 ..Default::default()
3024 })
3025 .chacha_parameters(super::super::ChaChaParameters {
3026 nonce: Some([1; 12]),
3027 counter: Some(1_u32.to_le_bytes()),
3028 aad: None,
3029 })
3030 .direct_key([7; 32])
3031 .encrypt_binary(b"payload")
3032 .expect("explicitly permitted raw ChaCha encryption");
3033 let resolver = CandidateResolver {
3034 keys: vec![vec![1; 32], vec![7; 32]],
3035 };
3036 assert!(matches!(
3037 DecryptContext::new(&resolver)
3038 .policy(crate::policy::DecryptionPolicy {
3039 data_algorithms: Some([algorithm].into()),
3040 ..Default::default()
3041 })
3042 .decrypt(&encrypted.encrypted_data_xml),
3043 Err(XmlEncError::AmbiguousKeyCandidates { actual: 2, .. })
3044 ));
3045 }
3046
3047 #[test]
3048 fn typed_reference_association_obeys_id_semantics() {
3049 use crate::policy::SameDocumentIdSemantics as Semantics;
3052 for semantics in [
3053 Semantics::Specification,
3054 Semantics::XmlSecBarename,
3055 Semantics::XmlSecVisa3d,
3056 ] {
3057 assert!(!super::reference_targets_id("#", "", semantics));
3058 }
3059 assert!(super::reference_targets_id(
3060 "#tar%67et",
3061 "target",
3062 Semantics::Specification
3063 ));
3064 assert!(super::reference_targets_id(
3065 "#xpointer(id('target'))",
3066 "target",
3067 Semantics::Specification
3068 ));
3069 assert!(!super::reference_targets_id(
3070 "#123",
3071 "123",
3072 Semantics::Specification
3073 ));
3074 assert!(super::reference_targets_id(
3075 "#123",
3076 "123",
3077 Semantics::XmlSecVisa3d
3078 ));
3079 assert!(!super::reference_targets_id(
3080 "#xpointer(id('a' or 'b'))",
3081 "a' or 'b",
3082 Semantics::Specification
3083 ));
3084 }
3085 #[cfg(feature = "legacy-algorithms")]
3086 #[test]
3087 fn legacy_cbc_rejects_ambiguous_candidates_before_decryption() {
3088 for algorithm in [
3091 super::DataEncryptionAlgorithm::TripleDesCbc,
3092 super::DataEncryptionAlgorithm::Aes192Cbc,
3093 ] {
3094 assert!(matches!(
3095 super::validate_decryption_key_candidates(algorithm, 2),
3096 Err(super::XmlEncError::AmbiguousKeyCandidates { actual: 2, .. })
3097 ));
3098 }
3099 }
3100 use std::cell::{Cell, RefCell};
3101 use std::sync::atomic::{AtomicUsize, Ordering};
3102
3103 use crate::rsa_encoding::RsaPrivateKeyEncoding as _;
3104 use aes_gcm::{
3105 Aes128Gcm,
3106 aead::{AeadInOut, KeyInit},
3107 };
3108 use aes_kw::KwAes128;
3109 use base64::engine::general_purpose::STANDARD;
3110 use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
3111 use rsa::{Oaep, RsaPublicKey};
3112 use sha1::Sha1;
3113 use sha2::{Sha256, Sha384};
3114
3115 use super::*;
3116 use crate::xmlenc::{CipherData, EncryptionMethod};
3117
3118 struct RecipientKeyResolver {
3119 recipient: &'static str,
3120 key: Vec<u8>,
3121 }
3122
3123 struct CountingResolver {
3124 candidate_calls: Cell<usize>,
3125 key: Vec<u8>,
3126 }
3127
3128 struct AllCallsResolver {
3129 calls: Cell<usize>,
3130 key: Vec<u8>,
3131 }
3132
3133 struct CandidateResolver {
3134 keys: Vec<Vec<u8>>,
3135 }
3136
3137 #[test]
3138 fn policy_unaware_recipient_resolver_is_not_dispatched() {
3139 struct LegacyRecipient {
3143 recipient_calls: Cell<usize>,
3144 key: Vec<u8>,
3145 }
3146 impl DecryptionKeyResolver for LegacyRecipient {
3147 fn resolve_key(
3148 &self,
3149 _provider: &dyn crate::provider::CryptoProvider,
3150 _algorithm: DataEncryptionAlgorithm,
3151 encrypted_key: Option<&EncryptedKey>,
3152 ) -> Result<Vec<u8>, XmlEncError> {
3153 if encrypted_key.is_none() {
3154 return Err(XmlEncError::KeyNotFound);
3155 }
3156 self.recipient_calls.set(self.recipient_calls.get() + 1);
3157 Ok(self.key.clone())
3158 }
3159 }
3160 let resolver = LegacyRecipient {
3161 recipient_calls: Cell::new(0),
3162 key: vec![0x63; 16],
3163 };
3164 let encrypted = encrypted_data_with_recipients(
3165 &resolver.key,
3166 vec![associated_encrypted_key("recipient", None, None)],
3167 None,
3168 );
3169 assert!(matches!(
3170 DecryptContext::new(&resolver).decrypt_data(&encrypted),
3171 Err(XmlEncError::KeyNotFound)
3172 ));
3173 assert_eq!(resolver.recipient_calls.get(), 0);
3174
3175 let direct = SymmetricKeyDecryptor::new(resolver.key.clone());
3176 let mut budget = KeyCandidateBudget::with_limit(1);
3177 assert!(matches!(
3178 resolver.resolve_key_candidates_with_policy(
3179 crate::provider::default_provider(),
3180 DataEncryptionAlgorithm::Aes128Gcm,
3181 encrypted.encrypted_keys.first(),
3182 &crate::policy::DecryptionPolicy::default(),
3183 &mut budget,
3184 ),
3185 Err(XmlEncError::KeyNotFound)
3186 ));
3187 assert_eq!(budget.remaining(), 1, "no recipient work was performed");
3188 assert_eq!(
3189 DecryptContext::new(&direct)
3190 .decrypt_data(&encrypted)
3191 .expect("direct AES does not require RSA metadata"),
3192 DecryptedContent::Bytes(b"payload".to_vec())
3193 );
3194 }
3195
3196 #[test]
3197 fn document_decryption_initial_parse_uses_the_policy_work_budget() {
3198 let xml = "<root/>";
3201 let policy = crate::policy::DecryptionPolicy {
3202 resources: crate::policy::ResourcePolicy {
3203 max_xml_parse_work_bytes: 0,
3204 ..crate::policy::ResourcePolicy::default()
3205 },
3206 ..crate::policy::DecryptionPolicy::default()
3207 };
3208 let resolver = SymmetricKeyDecryptor::new([0_u8; 16]);
3209
3210 let error = DecryptContext::new(&resolver)
3211 .policy(policy)
3212 .decrypt_document(xml, None)
3213 .expect_err("a zero parse-work budget must reject the input parse");
3214
3215 assert!(matches!(
3216 error,
3217 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3218 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
3219 maximum: 0,
3220 actual,
3221 }) if actual == xml.len()
3222 ));
3223 }
3224
3225 struct AggregateRecipientResolver {
3226 attempts: Cell<usize>,
3227 key: Vec<u8>,
3228 }
3229
3230 struct AssociationRecordingResolver {
3231 visited: RefCell<Vec<String>>,
3232 key: Vec<u8>,
3233 }
3234
3235 struct OrderedRecipientResolver {
3236 wrong: Vec<u8>,
3237 correct: Vec<u8>,
3238 }
3239
3240 struct DirectAndRecipientResolver {
3241 direct: Vec<u8>,
3242 recipient: Vec<u8>,
3243 }
3244
3245 struct FailingDirectResolver {
3246 recipient: Vec<u8>,
3247 }
3248
3249 struct PolicyRejectingDirectResolver {
3250 recipient: Vec<u8>,
3251 }
3252
3253 struct MislabelledExhaustionResolver {
3254 direct: Vec<u8>,
3255 }
3256
3257 impl DecryptionKeyResolver for DirectAndRecipientResolver {
3258 fn resolve_key_candidates_with_policy(
3259 &self,
3260 provider: &dyn crate::provider::CryptoProvider,
3261 algorithm: DataEncryptionAlgorithm,
3262 encrypted_key: Option<&EncryptedKey>,
3263 policy: &crate::policy::DecryptionPolicy,
3264 budget: &mut KeyCandidateBudget,
3265 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3266 policy.validate()?;
3268 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
3269 }
3270
3271 fn resolve_key(
3272 &self,
3273 _provider: &dyn crate::provider::CryptoProvider,
3274 _algorithm: DataEncryptionAlgorithm,
3275 encrypted_key: Option<&EncryptedKey>,
3276 ) -> Result<Vec<u8>, XmlEncError> {
3277 Ok(if encrypted_key.is_some() {
3278 self.recipient.clone()
3279 } else {
3280 self.direct.clone()
3281 })
3282 }
3283 }
3284
3285 impl DecryptionKeyResolver for FailingDirectResolver {
3286 fn resolve_key_candidates_with_policy(
3287 &self,
3288 provider: &dyn crate::provider::CryptoProvider,
3289 algorithm: DataEncryptionAlgorithm,
3290 encrypted_key: Option<&EncryptedKey>,
3291 policy: &crate::policy::DecryptionPolicy,
3292 budget: &mut KeyCandidateBudget,
3293 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3294 policy.validate()?;
3296 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
3297 }
3298
3299 fn resolve_key(
3300 &self,
3301 _provider: &dyn crate::provider::CryptoProvider,
3302 algorithm: DataEncryptionAlgorithm,
3303 encrypted_key: Option<&EncryptedKey>,
3304 ) -> Result<Vec<u8>, XmlEncError> {
3305 if encrypted_key.is_some() {
3306 Ok(self.recipient.clone())
3307 } else {
3308 Err(XmlEncError::InvalidKeySize {
3309 algorithm,
3310 expected: 16,
3311 actual: 8,
3312 })
3313 }
3314 }
3315 }
3316
3317 impl DecryptionKeyResolver for PolicyRejectingDirectResolver {
3318 fn resolve_key(
3319 &self,
3320 _provider: &dyn crate::provider::CryptoProvider,
3321 _algorithm: DataEncryptionAlgorithm,
3322 encrypted_key: Option<&EncryptedKey>,
3323 ) -> Result<Vec<u8>, XmlEncError> {
3324 if encrypted_key.is_some() {
3325 Ok(self.recipient.clone())
3326 } else {
3327 Err(crate::policy::PolicyViolation::KeyTrust {
3328 reason: "test resolver rejected the operation",
3329 }
3330 .into())
3331 }
3332 }
3333 }
3334
3335 impl DecryptionKeyResolver for MislabelledExhaustionResolver {
3336 fn resolve_key(
3337 &self,
3338 _provider: &dyn crate::provider::CryptoProvider,
3339 _algorithm: DataEncryptionAlgorithm,
3340 _encrypted_key: Option<&EncryptedKey>,
3341 ) -> Result<Vec<u8>, XmlEncError> {
3342 Err(XmlEncError::KeyNotFound)
3343 }
3344
3345 fn resolve_key_candidates_with_policy(
3346 &self,
3347 _provider: &dyn crate::provider::CryptoProvider,
3348 _algorithm: DataEncryptionAlgorithm,
3349 encrypted_key: Option<&EncryptedKey>,
3350 policy: &crate::policy::DecryptionPolicy,
3351 budget: &mut KeyCandidateBudget,
3352 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3353 policy.validate()?;
3355 if encrypted_key.is_none() {
3356 budget.consume(1)?;
3357 return Ok(vec![self.direct.clone()]);
3358 }
3359 budget.consume(budget.remaining().saturating_add(1))?;
3360 unreachable!("candidate budget exhaustion must return first")
3361 }
3362 }
3363
3364 impl DecryptionKeyResolver for OrderedRecipientResolver {
3365 fn resolve_key(
3366 &self,
3367 _provider: &dyn crate::provider::CryptoProvider,
3368 _algorithm: DataEncryptionAlgorithm,
3369 _encrypted_key: Option<&EncryptedKey>,
3370 ) -> Result<Vec<u8>, XmlEncError> {
3371 Err(XmlEncError::KeyNotFound)
3372 }
3373
3374 fn resolve_key_candidates_with_policy(
3375 &self,
3376 _provider: &dyn crate::provider::CryptoProvider,
3377 _algorithm: DataEncryptionAlgorithm,
3378 encrypted_key: Option<&EncryptedKey>,
3379 policy: &crate::policy::DecryptionPolicy,
3380 budget: &mut KeyCandidateBudget,
3381 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3382 policy.validate()?;
3384 budget.consume(1)?;
3385 match encrypted_key.and_then(|key| key.id.as_deref()) {
3386 Some("first") => Ok(vec![self.wrong.clone()]),
3387 Some("second") => Ok(vec![self.correct.clone()]),
3388 _ => Err(XmlEncError::KeyNotFound),
3389 }
3390 }
3391 }
3392
3393 impl DecryptionKeyResolver for CandidateResolver {
3394 fn resolve_key(
3395 &self,
3396 _provider: &dyn crate::provider::CryptoProvider,
3397 _algorithm: DataEncryptionAlgorithm,
3398 _encrypted_key: Option<&EncryptedKey>,
3399 ) -> Result<Vec<u8>, XmlEncError> {
3400 Err(XmlEncError::KeyNotFound)
3401 }
3402
3403 fn resolve_key_candidates(
3404 &self,
3405 _provider: &dyn crate::provider::CryptoProvider,
3406 _algorithm: DataEncryptionAlgorithm,
3407 encrypted_key: Option<&EncryptedKey>,
3408 budget: &mut KeyCandidateBudget,
3409 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3410 if encrypted_key.is_none() {
3411 budget.consume(self.keys.len())?;
3412 Ok(self.keys.clone())
3413 } else {
3414 Err(XmlEncError::KeyNotFound)
3415 }
3416 }
3417 }
3418
3419 impl DecryptionKeyResolver for AggregateRecipientResolver {
3420 fn resolve_key(
3421 &self,
3422 _provider: &dyn crate::provider::CryptoProvider,
3423 _algorithm: DataEncryptionAlgorithm,
3424 _encrypted_key: Option<&EncryptedKey>,
3425 ) -> Result<Vec<u8>, XmlEncError> {
3426 Err(XmlEncError::KeyNotFound)
3427 }
3428
3429 fn resolve_key_candidates_with_policy(
3430 &self,
3431 _provider: &dyn crate::provider::CryptoProvider,
3432 _algorithm: DataEncryptionAlgorithm,
3433 encrypted_key: Option<&EncryptedKey>,
3434 policy: &crate::policy::DecryptionPolicy,
3435 budget: &mut KeyCandidateBudget,
3436 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3437 policy.validate()?;
3439 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
3440 let attempts = budget.remaining();
3441 if attempts == 0 {
3442 budget.consume(1)?;
3443 }
3444 budget.consume(attempts)?;
3445 self.attempts.set(self.attempts.get() + attempts);
3446 if encrypted_key.id.as_deref() == Some("first") {
3447 Err(XmlEncError::KeyNotFound)
3448 } else {
3449 Ok(vec![self.key.clone()])
3450 }
3451 }
3452 }
3453
3454 impl DecryptionKeyResolver for AssociationRecordingResolver {
3455 fn resolve_key(
3456 &self,
3457 _provider: &dyn crate::provider::CryptoProvider,
3458 _algorithm: DataEncryptionAlgorithm,
3459 _encrypted_key: Option<&EncryptedKey>,
3460 ) -> Result<Vec<u8>, XmlEncError> {
3461 Err(XmlEncError::KeyNotFound)
3462 }
3463
3464 fn resolve_key_candidates_with_policy(
3465 &self,
3466 _provider: &dyn crate::provider::CryptoProvider,
3467 _algorithm: DataEncryptionAlgorithm,
3468 encrypted_key: Option<&EncryptedKey>,
3469 policy: &crate::policy::DecryptionPolicy,
3470 budget: &mut KeyCandidateBudget,
3471 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3472 policy.validate()?;
3474 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
3475 budget.consume(1)?;
3476 self.visited
3477 .borrow_mut()
3478 .push(encrypted_key.id.clone().unwrap_or_default());
3479 Ok(vec![self.key.clone()])
3480 }
3481 }
3482
3483 fn associated_encrypted_key(
3484 id: &str,
3485 data_reference: Option<&str>,
3486 carried_key_name: Option<&str>,
3487 ) -> EncryptedKey {
3488 EncryptedKey {
3489 id: Some(id.into()),
3490 sources: Default::default(),
3491 recipient: None,
3492 key_name: None,
3493 encryption_method: EncryptionMethod {
3494 chacha: None,
3495 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
3496 key_size_bits: None,
3497 oaep_digest: None,
3498 mgf_algorithm: None,
3499 oaep_params: None,
3500 },
3501 cipher_data: CipherData::Value {
3502 value: STANDARD.encode([0_u8; 256]),
3503 },
3504 reference_list: data_reference.map(|uri| crate::xmlenc::ReferenceList {
3505 data_references: vec![uri.into()],
3506 key_references: Vec::new(),
3507 }),
3508 carried_key_name: carried_key_name.map(str::to_owned),
3509 }
3510 }
3511
3512 fn encrypted_data_with_recipients(
3513 key: &[u8],
3514 encrypted_keys: Vec<EncryptedKey>,
3515 key_name: Option<&str>,
3516 ) -> EncryptedData {
3517 EncryptedData {
3518 encapsulation_methods: Vec::new(),
3519 id: Some("target".into()),
3520 derived_keys: Vec::new(),
3521 agreement_methods: Vec::new(),
3522 encrypted_type: None,
3523 encryption_method: EncryptionMethod {
3524 chacha: None,
3525 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3526 key_size_bits: None,
3527 oaep_digest: None,
3528 mgf_algorithm: None,
3529 oaep_params: None,
3530 },
3531 key_name: key_name.map(str::to_owned),
3532 encrypted_keys,
3533 cipher_data: CipherData::Value {
3534 value: STANDARD.encode(
3535 crate::provider::default_provider()
3536 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, key, b"payload")
3537 .expect("test encryption must succeed"),
3538 ),
3539 },
3540 }
3541 }
3542
3543 #[derive(Debug, Default)]
3544 struct PermissiveUnwrapProvider {
3545 decrypt_calls: AtomicUsize,
3546 unwrap_calls: AtomicUsize,
3547 recover_calls: AtomicUsize,
3548 plaintext: Vec<u8>,
3549 candidate_plaintexts: Vec<Vec<u8>>,
3550 }
3551
3552 struct OpaqueRecoveryKey;
3553
3554 impl crate::provider::KeyRecoveryKey for OpaqueRecoveryKey {
3555 fn rsa_modulus_bits(&self) -> usize {
3556 2048
3557 }
3558 fn rsa_public_exponent(&self) -> Option<u64> {
3559 Some(65537)
3560 }
3561 fn ciphertext_len(&self) -> usize {
3562 256
3563 }
3564
3565 fn recover_with_provider(
3566 &self,
3567 _provider: &dyn crate::provider::CryptoProvider,
3568 _parameters: &RsaOaepParameters,
3569 _ciphertext: &[u8],
3570 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3571 panic!("custom provider must own recovery for its opaque key")
3572 }
3573 }
3574
3575 impl crate::provider::CryptoProvider for PermissiveUnwrapProvider {
3576 #[cfg(feature = "legacy-algorithms")]
3577 fn recover_pkcs1v15(
3578 &self,
3579 key: &dyn crate::provider::KeyRecoveryKey,
3580 ciphertext: &[u8],
3581 key_len: usize,
3582 ) -> Result<crate::provider::RecoveredContentKey, crate::provider::ProviderError> {
3583 key.recover_pkcs1v15(self, ciphertext, key_len)
3584 }
3585 fn name(&self) -> &'static str {
3586 "permissive-unwrap-test"
3587 }
3588
3589 fn supports(&self, capability: crate::provider::ProviderCapability<'_>) -> bool {
3590 crate::provider::CryptoProvider::supports(
3591 &crate::provider::RustCryptoProvider,
3592 capability,
3593 )
3594 }
3595
3596 fn fill_random(&self, output: &mut [u8]) -> Result<(), crate::provider::ProviderError> {
3597 crate::provider::CryptoProvider::fill_random(
3598 &crate::provider::RustCryptoProvider,
3599 output,
3600 )
3601 }
3602
3603 fn derive_key(
3604 &self,
3605 parameters: &crate::provider::KdfParameters<'_>,
3606 secret: &[u8],
3607 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3608 crate::provider::RustCryptoProvider.derive_key(parameters, secret)
3609 }
3610
3611 #[cfg(feature = "xmldsig")]
3612 fn digest(
3613 &self,
3614 algorithm: crate::xmldsig::DigestAlgorithm,
3615 data: &[u8],
3616 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3617 crate::provider::CryptoProvider::digest(
3618 &crate::provider::RustCryptoProvider,
3619 algorithm,
3620 data,
3621 )
3622 }
3623
3624 #[cfg(feature = "xmldsig")]
3625 fn sign(
3626 &self,
3627 key: &dyn crate::xmldsig::SigningKey,
3628 algorithm: crate::xmldsig::SignatureAlgorithm,
3629 data: &[u8],
3630 ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
3631 crate::provider::CryptoProvider::sign(
3632 &crate::provider::RustCryptoProvider,
3633 key,
3634 algorithm,
3635 data,
3636 )
3637 }
3638
3639 #[cfg(feature = "xmldsig")]
3640 fn verify(
3641 &self,
3642 key: &dyn crate::xmldsig::VerifyingKey,
3643 algorithm: crate::xmldsig::SignatureAlgorithm,
3644 data: &[u8],
3645 signature: &[u8],
3646 ) -> Result<bool, crate::xmldsig::DsigError> {
3647 crate::provider::CryptoProvider::verify(
3648 &crate::provider::RustCryptoProvider,
3649 key,
3650 algorithm,
3651 data,
3652 signature,
3653 )
3654 }
3655
3656 fn encrypt_data(
3657 &self,
3658 algorithm: DataEncryptionAlgorithm,
3659 key: &[u8],
3660 plaintext: &[u8],
3661 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3662 crate::provider::CryptoProvider::encrypt_data(
3663 &crate::provider::RustCryptoProvider,
3664 algorithm,
3665 key,
3666 plaintext,
3667 )
3668 }
3669
3670 fn decrypt_data(
3671 &self,
3672 _algorithm: DataEncryptionAlgorithm,
3673 _key: &[u8],
3674 _ciphertext: &[u8],
3675 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3676 let index = self.decrypt_calls.fetch_add(1, Ordering::Relaxed);
3677 Ok(self
3678 .candidate_plaintexts
3679 .get(index)
3680 .unwrap_or(&self.plaintext)
3681 .clone())
3682 }
3683
3684 fn wrap_key(
3685 &self,
3686 algorithm: KeyWrapAlgorithm,
3687 kek: &[u8],
3688 key: &[u8],
3689 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3690 crate::provider::CryptoProvider::wrap_key(
3691 &crate::provider::RustCryptoProvider,
3692 algorithm,
3693 kek,
3694 key,
3695 )
3696 }
3697
3698 fn unwrap_key(
3699 &self,
3700 _algorithm: KeyWrapAlgorithm,
3701 _kek: &[u8],
3702 _wrapped: &[u8],
3703 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3704 self.unwrap_calls.fetch_add(1, Ordering::Relaxed);
3705 Ok(vec![0_u8; 16])
3706 }
3707
3708 fn transport_key(
3709 &self,
3710 key: &dyn crate::provider::KeyTransportKey,
3711 parameters: &RsaOaepParameters,
3712 plaintext: &[u8],
3713 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3714 crate::provider::CryptoProvider::transport_key(
3715 &crate::provider::RustCryptoProvider,
3716 key,
3717 parameters,
3718 plaintext,
3719 )
3720 }
3721
3722 fn recover_key(
3723 &self,
3724 _key: &dyn crate::provider::KeyRecoveryKey,
3725 _parameters: &RsaOaepParameters,
3726 _ciphertext: &[u8],
3727 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3728 self.recover_calls.fetch_add(1, Ordering::Relaxed);
3729 Ok(vec![0_u8; 16])
3730 }
3731 }
3732
3733 impl DecryptionKeyResolver for CountingResolver {
3734 fn resolve_encapsulation_content_keys_with_policy(
3735 &self,
3736 _: &dyn crate::provider::CryptoProvider,
3737 _: DataEncryptionAlgorithm,
3738 _: &crate::key_establishment::EncapsulationMechanism,
3739 _: &crate::policy::DecryptionPolicy,
3740 _: &mut KeyCandidateBudget,
3741 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
3742 self.candidate_calls.set(self.candidate_calls.get() + 1);
3743 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
3744 self.key.clone(),
3745 )])
3746 }
3747
3748 fn resolve_key_candidates_with_policy(
3749 &self,
3750 provider: &dyn crate::provider::CryptoProvider,
3751 algorithm: DataEncryptionAlgorithm,
3752 encrypted_key: Option<&EncryptedKey>,
3753 policy: &crate::policy::DecryptionPolicy,
3754 budget: &mut KeyCandidateBudget,
3755 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3756 policy.validate()?;
3758 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
3759 }
3760
3761 fn resolve_key(
3762 &self,
3763 _provider: &dyn crate::provider::CryptoProvider,
3764 _algorithm: DataEncryptionAlgorithm,
3765 encrypted_key: Option<&EncryptedKey>,
3766 ) -> Result<Vec<u8>, XmlEncError> {
3767 if encrypted_key.is_some() {
3768 self.candidate_calls.set(self.candidate_calls.get() + 1);
3769 Ok(self.key.clone())
3770 } else {
3771 Err(XmlEncError::KeyNotFound)
3772 }
3773 }
3774 }
3775
3776 impl DecryptionKeyResolver for AllCallsResolver {
3777 fn resolve_key(
3778 &self,
3779 _provider: &dyn crate::provider::CryptoProvider,
3780 _algorithm: DataEncryptionAlgorithm,
3781 _encrypted_key: Option<&EncryptedKey>,
3782 ) -> Result<Vec<u8>, XmlEncError> {
3783 self.calls.set(self.calls.get() + 1);
3784 Ok(self.key.clone())
3785 }
3786 }
3787
3788 impl DecryptionKeyResolver for RecipientKeyResolver {
3789 fn resolve_key_candidates_with_policy(
3790 &self,
3791 provider: &dyn crate::provider::CryptoProvider,
3792 algorithm: DataEncryptionAlgorithm,
3793 encrypted_key: Option<&EncryptedKey>,
3794 policy: &crate::policy::DecryptionPolicy,
3795 budget: &mut KeyCandidateBudget,
3796 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3797 policy.validate()?;
3799 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
3800 }
3801
3802 fn resolve_key(
3803 &self,
3804 _provider: &dyn crate::provider::CryptoProvider,
3805 _algorithm: DataEncryptionAlgorithm,
3806 encrypted_key: Option<&EncryptedKey>,
3807 ) -> Result<Vec<u8>, XmlEncError> {
3808 if encrypted_key.and_then(|key| key.recipient.as_deref()) == Some(self.recipient) {
3809 Ok(self.key.clone())
3810 } else {
3811 Err(XmlEncError::KeyNotFound)
3812 }
3813 }
3814 }
3815
3816 #[test]
3817 fn decrypts_gcm_and_rejects_tampering() {
3818 let key = [7_u8; 16];
3820 let nonce = [9_u8; 12];
3821 let mut ciphertext = b"<Assertion>trusted</Assertion>".to_vec();
3822 Aes128Gcm::new_from_slice(&key)
3823 .expect("fixed key length")
3824 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
3825 .expect("test encryption must succeed");
3826 let mut wire = nonce.to_vec();
3827 wire.extend_from_slice(&ciphertext);
3828 let xml = format!(
3829 "<xenc:EncryptedData xmlns:xenc=\"http://www.w3.org/2001/04/xmlenc#\" Type=\"http://www.w3.org/2001/04/xmlenc#Element\"><xenc:EncryptionMethod Algorithm=\"http://www.w3.org/2009/xmlenc11#aes128-gcm\"/><xenc:CipherData><xenc:CipherValue>{}</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData>",
3830 STANDARD.encode(&wire)
3831 );
3832 let decrypted = decrypt(&xml, &SymmetricKeyDecryptor::new(key))
3833 .expect("valid AES-GCM XML must decrypt");
3834 assert_eq!(
3835 decrypted,
3836 DecryptedContent::Xml("<Assertion>trusted</Assertion>".into())
3837 );
3838 let last = wire.len() - 1;
3839 wire[last] ^= 1;
3840 let tampered = xml.replace(&STANDARD.encode(ciphertext), &STANDARD.encode(&wire[12..]));
3841 assert!(matches!(
3842 decrypt(&tampered, &SymmetricKeyDecryptor::new(key)),
3843 Err(XmlEncError::AeadAuthenticationFailed)
3844 ));
3845 }
3846
3847 #[test]
3848 fn candidate_keys_retry_only_authenticated_decryption() {
3849 let key = [7_u8; 16];
3852 let nonce = [9_u8; 12];
3853 let mut ciphertext = b"candidate plaintext".to_vec();
3854 Aes128Gcm::new_from_slice(&key)
3855 .expect("fixed key length")
3856 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
3857 .expect("test encryption must succeed");
3858 let mut wire = nonce.to_vec();
3859 wire.extend_from_slice(&ciphertext);
3860 let encrypted = EncryptedData {
3861 encapsulation_methods: Vec::new(),
3862 id: None,
3863 derived_keys: Vec::new(),
3864 agreement_methods: Vec::new(),
3865 encrypted_type: None,
3866 encryption_method: EncryptionMethod {
3867 chacha: None,
3868 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3869 key_size_bits: None,
3870 oaep_digest: None,
3871 mgf_algorithm: None,
3872 oaep_params: None,
3873 },
3874 key_name: None,
3875 encrypted_keys: Vec::new(),
3876 cipher_data: CipherData::Value {
3877 value: STANDARD.encode(wire),
3878 },
3879 };
3880 let resolver = CandidateResolver {
3881 keys: vec![vec![1_u8; 16], key.to_vec()],
3882 };
3883
3884 let decrypted = DecryptContext::new(&resolver)
3885 .decrypt_data(&encrypted)
3886 .expect("a later authenticated candidate must decrypt");
3887
3888 assert_eq!(
3889 decrypted,
3890 DecryptedContent::Bytes(b"candidate plaintext".to_vec())
3891 );
3892 }
3893
3894 #[test]
3895 fn standalone_decryption_stops_after_first_successful_candidate() {
3896 let provider = PermissiveUnwrapProvider {
3899 plaintext: b"accepted".to_vec(),
3900 ..PermissiveUnwrapProvider::default()
3901 };
3902 let resolver = CandidateResolver {
3903 keys: vec![vec![1_u8; 16], vec![2_u8; 16], vec![3_u8; 16]],
3904 };
3905 let encrypted = EncryptedData {
3906 encapsulation_methods: Vec::new(),
3907 id: None,
3908 derived_keys: Vec::new(),
3909 agreement_methods: Vec::new(),
3910 encrypted_type: None,
3911 encryption_method: EncryptionMethod {
3912 chacha: None,
3913 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3914 key_size_bits: None,
3915 oaep_digest: None,
3916 mgf_algorithm: None,
3917 oaep_params: None,
3918 },
3919 key_name: None,
3920 encrypted_keys: Vec::new(),
3921 cipher_data: CipherData::Value {
3922 value: STANDARD.encode([0_u8; 36]),
3923 },
3924 };
3925
3926 let result = DecryptContext::new(&resolver)
3927 .provider(&provider)
3928 .decrypt_data(&encrypted)
3929 .expect("the first successful candidate must be returned");
3930
3931 assert_eq!(result, DecryptedContent::Bytes(b"accepted".to_vec()));
3932 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 1);
3933 }
3934
3935 #[test]
3936 fn document_decryption_discards_rejected_plaintext_before_next_candidate() {
3937 let provider = PermissiveUnwrapProvider {
3940 candidate_plaintexts: vec![b"<bad".to_vec(), b"<x/>".to_vec(), b"<u/>".to_vec()],
3941 ..PermissiveUnwrapProvider::default()
3942 };
3943 let resolver = CandidateResolver {
3944 keys: vec![vec![1_u8; 16], vec![2_u8; 16], vec![3_u8; 16]],
3945 };
3946 let encrypted = format!(
3947 "<xenc:EncryptedData xmlns:xenc=\"{XMLENC_NS}\" Type=\"{XMLENC_NS}Element\"><xenc:EncryptionMethod Algorithm=\"{}\"/><xenc:CipherData><xenc:CipherValue>{}</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData>",
3948 DataEncryptionAlgorithm::Aes128Gcm.uri(),
3949 STANDARD.encode([0_u8; 32]),
3950 );
3951
3952 let result = DecryptContext::new(&resolver)
3953 .provider(&provider)
3954 .decrypt_document(&encrypted, None)
3955 .expect("a later candidate with valid replacement XML must succeed");
3956
3957 assert_eq!(result, "<x/>");
3958 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 2);
3959 }
3960
3961 #[test]
3962 fn cbc_rejects_multiple_unordered_key_candidates() {
3963 let encrypted = EncryptedData {
3966 encapsulation_methods: Vec::new(),
3967 id: None,
3968 derived_keys: Vec::new(),
3969 agreement_methods: Vec::new(),
3970 encrypted_type: None,
3971 encryption_method: EncryptionMethod {
3972 chacha: None,
3973 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
3974 key_size_bits: None,
3975 oaep_digest: None,
3976 mgf_algorithm: None,
3977 oaep_params: None,
3978 },
3979 key_name: None,
3980 encrypted_keys: Vec::new(),
3981 cipher_data: CipherData::Value {
3982 value: STANDARD.encode(
3983 crate::provider::default_provider()
3984 .encrypt_data(
3985 DataEncryptionAlgorithm::Aes128Cbc,
3986 &[7_u8; 16],
3987 b"opaque plaintext",
3988 )
3989 .expect("test encryption must succeed"),
3990 ),
3991 },
3992 };
3993 let resolver = CandidateResolver {
3994 keys: vec![vec![1_u8; 16], vec![7_u8; 16]],
3995 };
3996
3997 let error = DecryptContext::new(&resolver)
3998 .decrypt_data(&encrypted)
3999 .expect_err("unauthenticated CBC must not guess among candidate keys");
4000
4001 assert!(matches!(
4002 error,
4003 XmlEncError::AmbiguousKeyCandidates {
4004 algorithm: DataEncryptionAlgorithm::Aes128Cbc,
4005 actual: 2,
4006 }
4007 ));
4008 }
4009
4010 #[test]
4011 fn cbc_accepts_duplicate_copies_of_one_key_identity() {
4012 let key = vec![0x27_u8; 16];
4015 let encrypted = EncryptedData {
4016 encapsulation_methods: Vec::new(),
4017 id: None,
4018 derived_keys: Vec::new(),
4019 agreement_methods: Vec::new(),
4020 encrypted_type: None,
4021 encryption_method: EncryptionMethod {
4022 chacha: None,
4023 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
4024 key_size_bits: None,
4025 oaep_digest: None,
4026 mgf_algorithm: None,
4027 oaep_params: None,
4028 },
4029 key_name: None,
4030 encrypted_keys: Vec::new(),
4031 cipher_data: CipherData::Value {
4032 value: STANDARD.encode(
4033 crate::provider::default_provider()
4034 .encrypt_data(
4035 DataEncryptionAlgorithm::Aes128Cbc,
4036 &key,
4037 b"duplicate identity",
4038 )
4039 .expect("test encryption must succeed"),
4040 ),
4041 },
4042 };
4043 let resolver = CandidateResolver {
4044 keys: vec![key.clone(), key],
4045 };
4046
4047 assert_eq!(
4048 DecryptContext::new(&resolver)
4049 .decrypt_data(&encrypted)
4050 .expect("one distinct CBC key identity must decrypt"),
4051 DecryptedContent::Bytes(b"duplicate identity".to_vec())
4052 );
4053 }
4054
4055 #[test]
4056 fn candidate_keys_are_bounded_before_cryptographic_processing() {
4057 let encrypted = EncryptedData {
4060 encapsulation_methods: Vec::new(),
4061 id: None,
4062 derived_keys: Vec::new(),
4063 agreement_methods: Vec::new(),
4064 encrypted_type: None,
4065 encryption_method: EncryptionMethod {
4066 chacha: None,
4067 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
4068 key_size_bits: None,
4069 oaep_digest: None,
4070 mgf_algorithm: None,
4071 oaep_params: None,
4072 },
4073 key_name: None,
4074 encrypted_keys: Vec::new(),
4075 cipher_data: CipherData::Value {
4076 value: STANDARD.encode(vec![0_u8; 28]),
4077 },
4078 };
4079 let actual = crate::hard_limits::KEY_CANDIDATE_CEILING + 1;
4080 let resolver = CandidateResolver {
4081 keys: vec![vec![0_u8; 16]; actual],
4082 };
4083
4084 let error = DecryptContext::new(&resolver)
4085 .decrypt_data(&encrypted)
4086 .expect_err("oversized candidate sets must fail before decryption");
4087
4088 assert!(matches!(
4089 error,
4090 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4091 resource: crate::policy::resource_name::KEY_CANDIDATES,
4092 maximum: crate::hard_limits::KEY_CANDIDATE_CEILING,
4093 actual: observed,
4094 }) if observed == actual
4095 ));
4096 }
4097
4098 #[test]
4099 fn operation_policy_controls_candidate_budget() {
4100 let encrypted = EncryptedData {
4103 encapsulation_methods: Vec::new(),
4104 id: None,
4105 derived_keys: Vec::new(),
4106 agreement_methods: Vec::new(),
4107 encrypted_type: None,
4108 encryption_method: EncryptionMethod {
4109 chacha: None,
4110 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
4111 key_size_bits: None,
4112 oaep_digest: None,
4113 mgf_algorithm: None,
4114 oaep_params: None,
4115 },
4116 key_name: None,
4117 encrypted_keys: Vec::new(),
4118 cipher_data: CipherData::Value {
4119 value: STANDARD.encode(vec![0_u8; 28]),
4120 },
4121 };
4122 let resolver = CandidateResolver {
4123 keys: vec![vec![0_u8; 16]; 3],
4124 };
4125 let mut policy = crate::policy::DecryptionPolicy::default();
4126 policy.resources.max_key_candidates = 2;
4127
4128 let error = DecryptContext::new(&resolver)
4129 .policy(policy)
4130 .decrypt_data(&encrypted)
4131 .expect_err("candidate accounting must use the operation policy ceiling");
4132
4133 assert!(matches!(
4134 error,
4135 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4136 resource: crate::policy::resource_name::KEY_CANDIDATES,
4137 maximum: 2,
4138 actual: 3,
4139 })
4140 ));
4141 }
4142
4143 #[test]
4144 fn candidate_work_ceiling_is_shared_across_recipients() {
4145 let key = vec![0x39_u8; 16];
4148 let resolver = AggregateRecipientResolver {
4149 attempts: Cell::new(0),
4150 key: key.clone(),
4151 };
4152 let encrypted = encrypted_data_with_recipients(
4153 &key,
4154 vec![
4155 associated_encrypted_key("first", None, None),
4156 associated_encrypted_key("second", None, None),
4157 ],
4158 None,
4159 );
4160
4161 DecryptContext::new(&resolver)
4162 .decrypt_data(&encrypted)
4163 .expect_err("a second recipient must not receive a fresh candidate allowance");
4164 assert_eq!(
4165 resolver.attempts.get(),
4166 crate::hard_limits::KEY_CANDIDATE_CEILING
4167 );
4168 }
4169
4170 #[test]
4171 fn candidate_budget_exhaustion_is_fatal_after_a_key_was_found() {
4172 let key = vec![0x49_u8; 16];
4175 let resolver = MislabelledExhaustionResolver {
4176 direct: key.clone(),
4177 };
4178 let encrypted = encrypted_data_with_recipients(
4179 &key,
4180 vec![associated_encrypted_key("recipient", None, None)],
4181 None,
4182 );
4183
4184 let error = DecryptContext::new(&resolver)
4185 .decrypt_data(&encrypted)
4186 .expect_err("candidate exhaustion must override an earlier usable key");
4187
4188 assert!(matches!(
4189 error,
4190 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4191 resource: "key candidates",
4192 maximum: crate::hard_limits::KEY_CANDIDATE_CEILING,
4193 actual,
4194 }) if actual == crate::hard_limits::KEY_CANDIDATE_CEILING + 1
4195 ));
4196 }
4197
4198 #[test]
4199 fn authenticated_decryption_continues_after_wrong_unwrapped_recipient_key() {
4200 let correct = vec![0x53_u8; 16];
4203 let encrypted = encrypted_data_with_recipients(
4204 &correct,
4205 vec![
4206 associated_encrypted_key("first", None, None),
4207 associated_encrypted_key("second", None, None),
4208 ],
4209 None,
4210 );
4211 let resolver = OrderedRecipientResolver {
4212 wrong: vec![0x11_u8; 16],
4213 correct,
4214 };
4215
4216 let plaintext = DecryptContext::new(&resolver)
4217 .decrypt_data(&encrypted)
4218 .expect("the second recipient key must authenticate");
4219 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
4220 }
4221
4222 #[test]
4223 fn authenticated_decryption_continues_from_direct_key_to_recipient() {
4224 let correct = vec![0x63_u8; 16];
4227 let encrypted = encrypted_data_with_recipients(
4228 &correct,
4229 vec![associated_encrypted_key("recipient", None, None)],
4230 None,
4231 );
4232 let resolver = DirectAndRecipientResolver {
4233 direct: vec![0x19_u8; 16],
4234 recipient: correct,
4235 };
4236
4237 let plaintext = DecryptContext::new(&resolver)
4238 .decrypt_data(&encrypted)
4239 .expect("the embedded recipient must remain available after a direct candidate");
4240
4241 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
4242 }
4243
4244 #[test]
4245 fn authenticated_decryption_continues_after_direct_lookup_error() {
4246 let correct = vec![0x64_u8; 16];
4249 let encrypted = encrypted_data_with_recipients(
4250 &correct,
4251 vec![associated_encrypted_key("recipient", None, None)],
4252 None,
4253 );
4254 let resolver = FailingDirectResolver { recipient: correct };
4255
4256 let plaintext = DecryptContext::new(&resolver)
4257 .decrypt_data(&encrypted)
4258 .expect("recipient lookup must follow a candidate-local direct error");
4259
4260 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
4261 }
4262
4263 #[test]
4264 fn resolver_policy_rejection_stops_before_later_recipient() {
4265 let correct = vec![0x65_u8; 16];
4268 let encrypted = encrypted_data_with_recipients(
4269 &correct,
4270 vec![associated_encrypted_key("recipient", None, None)],
4271 None,
4272 );
4273 let resolver = PolicyRejectingDirectResolver { recipient: correct };
4274
4275 let error = DecryptContext::new(&resolver)
4276 .decrypt_data(&encrypted)
4277 .expect_err("operation policy rejection must be fatal");
4278
4279 assert!(matches!(
4280 error,
4281 XmlEncError::Policy(crate::policy::PolicyViolation::KeyTrust {
4282 reason: "test resolver rejected the operation",
4283 })
4284 ));
4285 }
4286
4287 #[test]
4288 fn cbc_rejects_distinct_direct_and_recipient_candidates() {
4289 let recipient = vec![0x73_u8; 16];
4292 let mut encrypted = encrypted_data_with_recipients(
4293 &recipient,
4294 vec![associated_encrypted_key("recipient", None, None)],
4295 None,
4296 );
4297 encrypted.encryption_method.algorithm = DataEncryptionAlgorithm::Aes128Cbc.uri().into();
4298 *encrypted
4299 .cipher_data
4300 .inline_value_mut()
4301 .expect("inline ciphertext") = STANDARD.encode(
4302 crate::provider::default_provider()
4303 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &recipient, b"payload")
4304 .expect("test encryption must succeed"),
4305 );
4306 let resolver = DirectAndRecipientResolver {
4307 direct: vec![0x29_u8; 16],
4308 recipient,
4309 };
4310
4311 let error = DecryptContext::new(&resolver)
4312 .decrypt_data(&encrypted)
4313 .expect_err("CBC must not guess between direct and recipient keys");
4314
4315 assert!(matches!(
4316 error,
4317 XmlEncError::AmbiguousKeyCandidates {
4318 algorithm: DataEncryptionAlgorithm::Aes128Cbc,
4319 actual: 2,
4320 }
4321 ));
4322 }
4323
4324 #[test]
4325 fn cbc_ambiguity_ignores_algorithm_incompatible_key_widths() {
4326 let key = vec![0x47_u8; 16];
4329 let ciphertext = crate::provider::default_provider()
4330 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &key, b"payload")
4331 .expect("test encryption must succeed");
4332 let encrypted = EncryptedData {
4333 encapsulation_methods: Vec::new(),
4334 id: None,
4335 derived_keys: Vec::new(),
4336 agreement_methods: Vec::new(),
4337 encrypted_type: None,
4338 encryption_method: EncryptionMethod {
4339 chacha: None,
4340 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
4341 key_size_bits: None,
4342 oaep_digest: None,
4343 mgf_algorithm: None,
4344 oaep_params: None,
4345 },
4346 key_name: None,
4347 encrypted_keys: Vec::new(),
4348 cipher_data: CipherData::Value {
4349 value: STANDARD.encode(ciphertext),
4350 },
4351 };
4352 let resolver = CandidateResolver {
4353 keys: vec![vec![0_u8; 32], key],
4354 };
4355
4356 assert_eq!(
4357 DecryptContext::new(&resolver)
4358 .decrypt_data(&encrypted)
4359 .expect("the sole width-compatible CBC key must be selected"),
4360 DecryptedContent::Bytes(b"payload".to_vec())
4361 );
4362 }
4363
4364 #[test]
4365 fn data_reference_selects_the_associated_encrypted_key() {
4366 let key = vec![0x51_u8; 16];
4369 let resolver = AssociationRecordingResolver {
4370 visited: RefCell::new(Vec::new()),
4371 key: key.clone(),
4372 };
4373 let encrypted = encrypted_data_with_recipients(
4374 &key,
4375 vec![
4376 associated_encrypted_key("unrelated", Some("#other"), None),
4377 associated_encrypted_key("matching", Some("#target"), None),
4378 ],
4379 None,
4380 );
4381
4382 DecryptContext::new(&resolver)
4383 .decrypt_data(&encrypted)
4384 .expect("the associated recipient must decrypt");
4385 assert_eq!(resolver.visited.into_inner(), ["matching"]);
4386 }
4387
4388 #[test]
4389 fn carried_key_name_selects_the_named_content_key() {
4390 let key = vec![0x52_u8; 16];
4393 let resolver = AssociationRecordingResolver {
4394 visited: RefCell::new(Vec::new()),
4395 key: key.clone(),
4396 };
4397 let encrypted = encrypted_data_with_recipients(
4398 &key,
4399 vec![
4400 associated_encrypted_key("unrelated", None, Some("other")),
4401 associated_encrypted_key("matching", None, Some("wanted")),
4402 ],
4403 Some("wanted"),
4404 );
4405
4406 DecryptContext::new(&resolver)
4407 .decrypt_data(&encrypted)
4408 .expect("the matching carried key name must decrypt");
4409 assert_eq!(resolver.visited.into_inner(), ["matching"]);
4410 }
4411
4412 #[test]
4413 fn contradictory_encrypted_key_associations_fail_closed() {
4414 let key = vec![0x53_u8; 16];
4417 let resolver = AssociationRecordingResolver {
4418 visited: RefCell::new(Vec::new()),
4419 key: key.clone(),
4420 };
4421 let encrypted = encrypted_data_with_recipients(
4422 &key,
4423 vec![associated_encrypted_key("unrelated", Some("#other"), None)],
4424 None,
4425 );
4426
4427 assert!(matches!(
4428 DecryptContext::new(&resolver).decrypt_data(&encrypted),
4429 Err(XmlEncError::KeyNotFound)
4430 ));
4431 assert!(resolver.visited.into_inner().is_empty());
4432 }
4433
4434 #[test]
4435 fn direct_symmetric_key_ignores_embedded_key_hints() {
4436 let key = [0x28_u8; 16];
4439 let unrelated = EncryptedKey {
4440 sources: Default::default(),
4441 id: None,
4442 recipient: Some("other-recipient".into()),
4443 key_name: None,
4444 encryption_method: super::super::EncryptionMethod {
4445 chacha: None,
4446 algorithm: "urn:unrelated:key-transport".into(),
4447 key_size_bits: None,
4448 oaep_digest: None,
4449 mgf_algorithm: None,
4450 oaep_params: None,
4451 },
4452 cipher_data: super::super::CipherData::Value {
4453 value: STANDARD.encode([0_u8; 24]),
4454 },
4455 reference_list: None,
4456 carried_key_name: None,
4457 };
4458
4459 assert_eq!(
4460 SymmetricKeyDecryptor::new(key)
4461 .resolve_key(
4462 crate::provider::default_provider(),
4463 DataEncryptionAlgorithm::Aes128Gcm,
4464 Some(&unrelated)
4465 )
4466 .expect("direct key must ignore unrelated embedded hints"),
4467 key
4468 );
4469 }
4470
4471 #[test]
4472 fn decrypts_with_the_matching_recipient_key() {
4473 let key = [0x29_u8; 16];
4476 let plaintext = "recipient-specific plaintext";
4477 let encrypted = encrypted_gcm_element("", plaintext, None, true, &key);
4478 let recipient_key = |recipient: &str, method: &str| {
4479 format!(
4480 "<xenc:EncryptedKey Recipient=\"{recipient}\"><xenc:EncryptionMethod Algorithm=\"{method}\">{}</xenc:EncryptionMethod><xenc:CipherData><xenc:CipherValue>YQ==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey>",
4481 if recipient == "alice" {
4482 "<ds:DigestMethod Algorithm=\"urn:unsupported:digest\"/>"
4483 } else {
4484 ""
4485 }
4486 )
4487 };
4488 let key_info = format!(
4489 "<ds:KeyInfo xmlns:ds=\"{}\">{}{}</ds:KeyInfo>",
4490 crate::xmlenc::types::XMLDSIG_NS,
4491 recipient_key("alice", KeyTransportAlgorithm::RsaOaep11.uri()),
4492 recipient_key("bob", KeyWrapAlgorithm::AesKw128.uri())
4493 );
4494 let xml = encrypted.replacen(
4495 "<xenc:CipherData>",
4496 &format!("{key_info}<xenc:CipherData>"),
4497 1,
4498 );
4499 let resolver = RecipientKeyResolver {
4500 recipient: "bob",
4501 key: key.to_vec(),
4502 };
4503
4504 assert_eq!(
4505 decrypt(&xml, &resolver).expect("second recipient key must be tried"),
4506 DecryptedContent::Bytes(plaintext.as_bytes().to_vec())
4507 );
4508 }
4509
4510 #[test]
4511 fn decryption_policy_bounds_recipients_before_key_resolution() {
4512 let key = [0x29_u8; 16];
4515 let encrypted = encrypted_gcm_element("", "bounded recipients", None, true, &key);
4516 let recipient_key = |recipient: &str| {
4517 format!(
4518 "<xenc:EncryptedKey Recipient=\"{recipient}\"><xenc:EncryptionMethod Algorithm=\"urn:test:key\"/><xenc:CipherData><xenc:CipherValue>YQ==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey>"
4519 )
4520 };
4521 let key_info = format!(
4522 "<ds:KeyInfo xmlns:ds=\"{}\">{}{}</ds:KeyInfo>",
4523 crate::xmlenc::types::XMLDSIG_NS,
4524 recipient_key("alice"),
4525 recipient_key("bob")
4526 );
4527 let xml = encrypted.replacen(
4528 "<xenc:CipherData>",
4529 &format!("{key_info}<xenc:CipherData>"),
4530 1,
4531 );
4532 let parsed = parse_encrypted_data(&xml).expect("default parser accepts two recipients");
4533 let policy = crate::policy::DecryptionPolicy {
4534 resources: crate::policy::ResourcePolicy {
4535 max_encryption_recipients: 1,
4536 ..crate::policy::ResourcePolicy::default()
4537 },
4538 ..crate::policy::DecryptionPolicy::default()
4539 };
4540 let resolver = SymmetricKeyDecryptor::new(key);
4541 let context = DecryptContext::new(&resolver).policy(policy);
4542
4543 for error in [
4544 context
4545 .decrypt(&xml)
4546 .expect_err("XML recipient collection must be bounded"),
4547 context
4548 .decrypt_data(&parsed)
4549 .expect_err("typed recipient collection must be bounded"),
4550 ] {
4551 assert!(matches!(
4552 error,
4553 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4554 resource: crate::policy::resource_name::ENCRYPTION_RECIPIENTS,
4555 maximum: 1,
4556 actual: 2,
4557 })
4558 ));
4559 }
4560 }
4561
4562 #[test]
4563 fn decrypts_session_key_wrapped_with_aes_kw() {
4564 let kek = [3_u8; 16];
4566 let session_key = [4_u8; 16];
4567 let mut wrapped = [0_u8; 24];
4568 KwAes128::new_from_slice(&kek)
4569 .expect("fixed KEK length")
4570 .wrap_key(&session_key, &mut wrapped)
4571 .expect("RFC 3394 test wrapping must succeed");
4572 let encrypted_key = EncryptedKey {
4573 id: None,
4574 sources: Default::default(),
4575 recipient: None,
4576 key_name: None,
4577 encryption_method: super::super::EncryptionMethod {
4578 chacha: None,
4579 algorithm: "http://www.w3.org/2001/04/xmlenc#kw-aes128".into(),
4580 key_size_bits: None,
4581 oaep_digest: None,
4582 mgf_algorithm: None,
4583 oaep_params: None,
4584 },
4585 cipher_data: super::super::CipherData::Value {
4586 value: STANDARD.encode(wrapped),
4587 },
4588 reference_list: None,
4589 carried_key_name: None,
4590 };
4591 let resolved = KekDecryptor::new(kek)
4592 .resolve_key(
4593 crate::provider::default_provider(),
4594 DataEncryptionAlgorithm::Aes128Gcm,
4595 Some(&encrypted_key),
4596 )
4597 .expect("wrapped session key must resolve");
4598 assert_eq!(resolved, session_key);
4599 }
4600
4601 #[test]
4602 fn rejects_invalid_kek_before_custom_provider_dispatch() {
4603 let encrypted_key = EncryptedKey {
4606 id: None,
4607 sources: Default::default(),
4608 recipient: None,
4609 key_name: None,
4610 encryption_method: super::super::EncryptionMethod {
4611 chacha: None,
4612 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
4613 key_size_bits: None,
4614 oaep_digest: None,
4615 mgf_algorithm: None,
4616 oaep_params: None,
4617 },
4618 cipher_data: super::super::CipherData::Value {
4619 value: STANDARD.encode([0_u8; 24]),
4620 },
4621 reference_list: None,
4622 carried_key_name: None,
4623 };
4624 let provider = PermissiveUnwrapProvider::default();
4625
4626 assert!(matches!(
4627 KekDecryptor::new([0_u8; 32]).resolve_key(
4628 &provider,
4629 DataEncryptionAlgorithm::Aes128Gcm,
4630 Some(&encrypted_key),
4631 ),
4632 Err(XmlEncError::InvalidKekSize {
4633 algorithm: KeyWrapAlgorithm::AesKw128,
4634 expected: 16,
4635 actual: 32,
4636 })
4637 ));
4638 assert_eq!(provider.unwrap_calls.load(Ordering::Relaxed), 0);
4639 }
4640
4641 #[test]
4642 fn rejects_content_ciphertext_framing_before_resolution_or_provider_dispatch() {
4643 for (algorithm, ciphertext_len) in [
4646 (DataEncryptionAlgorithm::Aes128Gcm, 27),
4647 (DataEncryptionAlgorithm::Aes128Cbc, 33),
4648 ] {
4649 let resolver = AllCallsResolver {
4650 calls: Cell::new(0),
4651 key: vec![0_u8; algorithm.key_len()],
4652 };
4653 let provider = PermissiveUnwrapProvider::default();
4654 let encrypted = EncryptedData {
4655 encapsulation_methods: Vec::new(),
4656 id: None,
4657 derived_keys: Vec::new(),
4658 agreement_methods: Vec::new(),
4659 encrypted_type: None,
4660 key_name: None,
4661 encryption_method: super::super::EncryptionMethod {
4662 chacha: None,
4663 algorithm: algorithm.uri().into(),
4664 key_size_bits: None,
4665 oaep_digest: None,
4666 mgf_algorithm: None,
4667 oaep_params: None,
4668 },
4669 encrypted_keys: Vec::new(),
4670 cipher_data: super::super::CipherData::Value {
4671 value: STANDARD.encode(vec![0_u8; ciphertext_len]),
4672 },
4673 };
4674
4675 assert!(
4676 DecryptContext::new(&resolver)
4677 .provider(&provider)
4678 .decrypt_data(&encrypted)
4679 .is_err()
4680 );
4681 assert_eq!(resolver.calls.get(), 0);
4682 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 0);
4683 }
4684 }
4685
4686 #[test]
4687 fn rejects_custom_provider_plaintext_outside_algorithm_bounds() {
4688 for (algorithm, ciphertext_len, plaintext_len) in [
4691 (DataEncryptionAlgorithm::Aes128Gcm, 32, 5),
4692 (DataEncryptionAlgorithm::Aes128Cbc, 32, 16),
4693 ] {
4694 let resolver = AllCallsResolver {
4695 calls: Cell::new(0),
4696 key: vec![0_u8; algorithm.key_len()],
4697 };
4698 let provider = PermissiveUnwrapProvider {
4699 plaintext: vec![0_u8; plaintext_len],
4700 ..PermissiveUnwrapProvider::default()
4701 };
4702 let encrypted = EncryptedData {
4703 encapsulation_methods: Vec::new(),
4704 id: None,
4705 derived_keys: Vec::new(),
4706 agreement_methods: Vec::new(),
4707 encrypted_type: None,
4708 key_name: None,
4709 encryption_method: super::super::EncryptionMethod {
4710 chacha: None,
4711 algorithm: algorithm.uri().into(),
4712 key_size_bits: None,
4713 oaep_digest: None,
4714 mgf_algorithm: None,
4715 oaep_params: None,
4716 },
4717 encrypted_keys: Vec::new(),
4718 cipher_data: super::super::CipherData::Value {
4719 value: STANDARD.encode(vec![0_u8; ciphertext_len]),
4720 },
4721 };
4722
4723 let error = DecryptContext::new(&resolver)
4724 .provider(&provider)
4725 .decrypt_data(&encrypted)
4726 .expect_err("impossible provider output length must fail");
4727 match algorithm {
4728 DataEncryptionAlgorithm::Aes128Gcm => assert!(matches!(
4729 error,
4730 XmlEncError::Provider(crate::provider::ProviderError::InvalidOutputSize {
4731 operation: crate::provider::ProviderOperation::Decrypt,
4732 expected: 4,
4733 actual: 5,
4734 })
4735 )),
4736 DataEncryptionAlgorithm::Aes128Cbc => assert!(matches!(
4737 error,
4738 XmlEncError::Provider(crate::provider::ProviderError::InvalidOutputSizeRange {
4739 operation: crate::provider::ProviderOperation::Decrypt,
4740 minimum: 0,
4741 maximum: 15,
4742 actual: 16,
4743 })
4744 )),
4745 _ => unreachable!("the regression table covers one GCM and one CBC algorithm"),
4746 }
4747 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 1);
4748 }
4749 }
4750
4751 #[test]
4752 fn rejects_malformed_aes_kw_before_custom_provider_dispatch() {
4753 let provider = PermissiveUnwrapProvider::default();
4756 for actual in [0, 23, 25] {
4757 let encrypted_key = EncryptedKey {
4758 sources: Default::default(),
4759 id: None,
4760 recipient: None,
4761 key_name: None,
4762 encryption_method: super::super::EncryptionMethod {
4763 chacha: None,
4764 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
4765 key_size_bits: None,
4766 oaep_digest: None,
4767 mgf_algorithm: None,
4768 oaep_params: None,
4769 },
4770 cipher_data: super::super::CipherData::Value {
4771 value: STANDARD.encode(vec![0_u8; actual]),
4772 },
4773 reference_list: None,
4774 carried_key_name: None,
4775 };
4776 assert!(matches!(
4777 KekDecryptor::new([0_u8; 16]).resolve_key(
4778 &provider,
4779 DataEncryptionAlgorithm::Aes128Gcm,
4780 Some(&encrypted_key),
4781 ),
4782 Err(XmlEncError::InvalidWrappedKeyLength {
4783 expected: 24,
4784 actual: output_len,
4785 }) if output_len == actual
4786 ));
4787 }
4788 assert_eq!(provider.unwrap_calls.load(Ordering::Relaxed), 0);
4789 }
4790
4791 #[test]
4792 fn rejects_malformed_rsa_oaep_before_custom_provider_dispatch() {
4793 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
4796 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
4797 ))
4798 .expect("RSA donor private key must parse");
4799 let provider = PermissiveUnwrapProvider::default();
4800 for actual in [0, 255, 257] {
4801 assert!(matches!(
4802 recover_rsa_oaep(
4803 &provider,
4804 &private_key,
4805 &RsaOaepParameters::default(),
4806 &vec![0_u8; actual],
4807 DataEncryptionAlgorithm::Aes128Gcm,
4808 ),
4809 Err(XmlEncError::InvalidWrappedKeyLength {
4810 expected: 256,
4811 actual: output_len,
4812 }) if output_len == actual
4813 ));
4814 }
4815 assert_eq!(provider.recover_calls.load(Ordering::Relaxed), 0);
4816 }
4817
4818 #[cfg(feature = "legacy-algorithms")]
4819 #[test]
4820 fn invalid_rsa_recovery_cannot_release_successful_cbc_plaintext() {
4821 let key = RsaPrivateKey::from_pkcs8_pem(include_str!(
4824 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
4825 ))
4826 .expect("valid RSA fixture");
4827 let resolver = PrivateKeyDecryptor::new(key);
4828 for algorithm in [
4829 DataEncryptionAlgorithm::Aes128Cbc,
4830 DataEncryptionAlgorithm::Aes192Cbc,
4831 DataEncryptionAlgorithm::Aes256Cbc,
4832 DataEncryptionAlgorithm::TripleDesCbc,
4833 DataEncryptionAlgorithm::Aes128Gcm,
4834 ] {
4835 let provider = PermissiveUnwrapProvider {
4836 plaintext: b"accepted CBC plaintext".to_vec(),
4837 ..PermissiveUnwrapProvider::default()
4838 };
4839 let mut encrypted = encrypted_data_with_recipients(
4840 &[0; 16],
4841 vec![associated_encrypted_key("recipient", None, None)],
4842 None,
4843 );
4844 encrypted.encryption_method.algorithm = algorithm.uri().into();
4845 *encrypted
4846 .cipher_data
4847 .inline_value_mut()
4848 .expect("inline ciphertext") =
4849 STANDARD.encode(vec![
4850 0;
4851 algorithm
4852 .ciphertext_len_for_plaintext(provider.plaintext.len())
4853 .expect("bounded test frame")
4854 ]);
4855 encrypted.encrypted_keys[0].encryption_method.algorithm =
4856 KeyTransportAlgorithm::RsaPkcs1v15.uri().into();
4857 *encrypted.encrypted_keys[0]
4858 .cipher_data
4859 .inline_value_mut()
4860 .expect("inline wrapped key") = STANDARD.encode([0; 256]);
4861 let result = DecryptContext::new(&resolver)
4862 .provider(&provider)
4863 .policy(crate::policy::DecryptionPolicy {
4864 data_algorithms: Some([algorithm].into()),
4865 key_transport_algorithms: Some([KeyTransportAlgorithm::RsaPkcs1v15].into()),
4866 ..crate::policy::DecryptionPolicy::default()
4867 })
4868 .decrypt_data(&encrypted);
4869 assert!(
4870 result.is_err(),
4871 "invalid RSA padding must never release fallback plaintext"
4872 );
4873 assert_eq!(
4874 provider.decrypt_calls.load(Ordering::Relaxed),
4875 1,
4876 "content decryption must run before rejecting the recovery"
4877 );
4878 }
4879 }
4880
4881 #[test]
4882 fn candidate_filter_reuses_storage_and_preserves_precedence() {
4883 let keys = [
4886 vec![0; 8],
4887 vec![2; 16],
4888 vec![3; 16],
4889 vec![2; 16],
4890 vec![4; 32],
4891 vec![5; 16],
4892 ]
4893 .into_iter()
4894 .map(crate::provider::RecoveredContentKey::confirmed)
4895 .collect::<Vec<_>>();
4896 let allocation = keys.as_ptr();
4897 let keys = compatible_decryption_key_candidates(DataEncryptionAlgorithm::Aes128Gcm, keys)
4898 .expect("compatible candidates survive");
4899 assert_eq!(keys.as_ptr(), allocation);
4900 assert_eq!(keys.len(), 3);
4901 for (key, expected) in keys.iter().zip([2, 3, 5]) {
4902 assert_eq!(key.bytes(), &[expected; 16]);
4903 }
4904 }
4905
4906 #[test]
4907 fn custom_provider_recovers_with_an_opaque_private_key() {
4908 let encrypted_key = EncryptedKey {
4911 id: None,
4912 sources: Default::default(),
4913 recipient: None,
4914 key_name: None,
4915 encryption_method: super::super::EncryptionMethod {
4916 chacha: None,
4917 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
4918 key_size_bits: None,
4919 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
4920 mgf_algorithm: Some(
4921 OaepDigestAlgorithm::Sha256
4922 .mgf_uri()
4923 .expect("MGF1-SHA256 URI")
4924 .into(),
4925 ),
4926 oaep_params: None,
4927 },
4928 cipher_data: super::super::CipherData::Value {
4929 value: STANDARD.encode(vec![0x5a; 256]),
4930 },
4931 reference_list: None,
4932 carried_key_name: None,
4933 };
4934 let provider = PermissiveUnwrapProvider::default();
4935 let decryptor = PrivateKeyDecryptor::provider_key(Arc::new(OpaqueRecoveryKey));
4936
4937 let key = decryptor
4938 .resolve_key(
4939 &provider,
4940 DataEncryptionAlgorithm::Aes128Gcm,
4941 Some(&encrypted_key),
4942 )
4943 .expect("custom provider must recover through its opaque private key");
4944
4945 assert_eq!(key, vec![0_u8; 16]);
4946 assert_eq!(provider.recover_calls.load(Ordering::Relaxed), 1);
4947 }
4948
4949 #[test]
4950 fn rejects_truncated_gcm_and_invalid_wrapped_key() {
4951 assert!(matches!(
4953 crate::provider::default_provider().decrypt_data(
4954 DataEncryptionAlgorithm::Aes128Gcm,
4955 &[0_u8; 16],
4956 &[0_u8; 27],
4957 ),
4958 Err(crate::provider::ProviderError::InvalidInput(
4959 crate::provider::ProviderInputError::AesGcmFraming
4960 ))
4961 ));
4962 let truncated = EncryptedData {
4963 encapsulation_methods: Vec::new(),
4964 id: None,
4965 derived_keys: Vec::new(),
4966 agreement_methods: Vec::new(),
4967 encrypted_type: None,
4968 key_name: None,
4969 encryption_method: super::super::EncryptionMethod {
4970 chacha: None,
4971 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
4972 key_size_bits: None,
4973 oaep_digest: None,
4974 mgf_algorithm: None,
4975 oaep_params: None,
4976 },
4977 encrypted_keys: Vec::new(),
4978 cipher_data: super::super::CipherData::Value {
4979 value: STANDARD.encode([0_u8; 27]),
4980 },
4981 };
4982 assert!(matches!(
4983 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16])).decrypt_data(&truncated),
4984 Err(XmlEncError::DataTooShort {
4985 algorithm: "AES-GCM",
4986 actual: 27,
4987 ..
4988 })
4989 ));
4990 let encrypted_key = EncryptedKey {
4991 id: None,
4992 sources: Default::default(),
4993 recipient: None,
4994 key_name: None,
4995 encryption_method: super::super::EncryptionMethod {
4996 chacha: None,
4997 algorithm: "http://www.w3.org/2001/04/xmlenc#kw-aes128".into(),
4998 key_size_bits: None,
4999 oaep_digest: None,
5000 mgf_algorithm: None,
5001 oaep_params: None,
5002 },
5003 cipher_data: super::super::CipherData::Value {
5004 value: STANDARD.encode([0_u8; 24]),
5005 },
5006 reference_list: None,
5007 carried_key_name: None,
5008 };
5009 assert!(matches!(
5010 KekDecryptor::new([0_u8; 16]).resolve_key(
5011 crate::provider::default_provider(),
5012 DataEncryptionAlgorithm::Aes128Gcm,
5013 Some(&encrypted_key)
5014 ),
5015 Err(XmlEncError::KeyWrapIntegrity)
5016 ));
5017 assert!(matches!(
5018 KekDecryptor::new([0_u8; 32]).resolve_key(
5019 crate::provider::default_provider(),
5020 DataEncryptionAlgorithm::Aes128Gcm,
5021 Some(&encrypted_key)
5022 ),
5023 Err(XmlEncError::InvalidKekSize {
5024 algorithm: KeyWrapAlgorithm::AesKw128,
5025 expected: 16,
5026 actual: 32
5027 })
5028 ));
5029 }
5030
5031 #[test]
5032 fn decrypts_oaep11_with_independent_digest_and_mgf() {
5033 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5035 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5036 ))
5037 .expect("RSA donor private key must parse");
5038 let public_key = RsaPublicKey::from(&private_key);
5039 let session_key = [6_u8; 16];
5040 let label = b"xmlenc-label".to_vec();
5041 let wrapped = public_key
5042 .encrypt(
5043 &mut ChaCha20Rng::from_seed([17_u8; 32]),
5044 Oaep::<Sha256, Sha384>::new_with_mgf_hash_and_label(label.clone()),
5045 &session_key,
5046 )
5047 .expect("OAEP test wrapping must succeed");
5048 let encrypted_key = EncryptedKey {
5049 id: Some("wrapped-key".into()),
5050 sources: Default::default(),
5051 recipient: Some("recipient-a".into()),
5052 key_name: None,
5053 encryption_method: super::super::EncryptionMethod {
5054 chacha: None,
5055 algorithm: "http://www.w3.org/2009/xmlenc11#rsa-oaep".into(),
5056 key_size_bits: None,
5057 oaep_digest: Some("http://www.w3.org/2001/04/xmlenc#sha256".into()),
5058 mgf_algorithm: Some("http://www.w3.org/2009/xmlenc11#mgf1sha384".into()),
5059 oaep_params: Some(label),
5060 },
5061 cipher_data: super::super::CipherData::Value {
5062 value: STANDARD.encode(wrapped),
5063 },
5064 reference_list: None,
5065 carried_key_name: None,
5066 };
5067 let resolved = PrivateKeyDecryptor::new(private_key)
5068 .resolve_key(
5069 crate::provider::default_provider(),
5070 DataEncryptionAlgorithm::Aes128Gcm,
5071 Some(&encrypted_key),
5072 )
5073 .expect("OAEP 1.1 wrapped key must resolve");
5074 assert_eq!(resolved, session_key);
5075 }
5076
5077 #[test]
5078 fn decrypts_legacy_oaep_uri_with_sha256_digest() {
5079 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5081 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5082 ))
5083 .expect("RSA donor private key must parse");
5084 let public_key = RsaPublicKey::from(&private_key);
5085 let session_key = [8_u8; 16];
5086 let wrapped = public_key
5087 .encrypt(
5088 &mut ChaCha20Rng::from_seed([19_u8; 32]),
5089 Oaep::<Sha256, Sha1>::new_with_mgf_hash(),
5090 &session_key,
5091 )
5092 .expect("legacy OAEP URI test wrapping must succeed");
5093 let encrypted_key = EncryptedKey {
5094 id: None,
5095 sources: Default::default(),
5096 recipient: None,
5097 key_name: None,
5098 encryption_method: super::super::EncryptionMethod {
5099 chacha: None,
5100 algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p".into(),
5101 key_size_bits: None,
5102 oaep_digest: Some("http://www.w3.org/2001/04/xmlenc#sha256".into()),
5103 mgf_algorithm: None,
5104 oaep_params: None,
5105 },
5106 cipher_data: super::super::CipherData::Value {
5107 value: STANDARD.encode(wrapped),
5108 },
5109 reference_list: None,
5110 carried_key_name: None,
5111 };
5112 let resolved = PrivateKeyDecryptor::new(private_key)
5113 .resolve_key(
5114 crate::provider::default_provider(),
5115 DataEncryptionAlgorithm::Aes128Gcm,
5116 Some(&encrypted_key),
5117 )
5118 .expect("legacy OAEP URI with SHA-256 must resolve");
5119 assert_eq!(resolved, session_key);
5120 }
5121
5122 #[test]
5123 fn decrypts_sha384_oaep_with_the_xmlenc_digest_uri() {
5124 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5127 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5128 ))
5129 .expect("RSA donor private key must parse");
5130 let public_key = RsaPublicKey::from(&private_key);
5131 let session_key = [9_u8; 16];
5132 let digest = "http://www.w3.org/2001/04/xmlenc#sha384";
5133
5134 for (algorithm, mgf_algorithm) in [
5135 ("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p", None),
5136 (
5137 "http://www.w3.org/2009/xmlenc11#rsa-oaep",
5138 Some("http://www.w3.org/2009/xmlenc11#mgf1sha1"),
5139 ),
5140 ] {
5141 let wrapped = public_key
5142 .encrypt(
5143 &mut ChaCha20Rng::from_seed([23_u8; 32]),
5144 Oaep::<Sha384, Sha1>::new_with_mgf_hash(),
5145 &session_key,
5146 )
5147 .expect("SHA-384 OAEP test wrapping must succeed");
5148 let encrypted_key = EncryptedKey {
5149 id: None,
5150 sources: Default::default(),
5151 recipient: None,
5152 key_name: None,
5153 encryption_method: super::super::EncryptionMethod {
5154 chacha: None,
5155 algorithm: algorithm.into(),
5156 key_size_bits: None,
5157 oaep_digest: Some(digest.into()),
5158 mgf_algorithm: mgf_algorithm.map(str::to_owned),
5159 oaep_params: None,
5160 },
5161 cipher_data: super::super::CipherData::Value {
5162 value: STANDARD.encode(wrapped),
5163 },
5164 reference_list: None,
5165 carried_key_name: None,
5166 };
5167 let resolved = PrivateKeyDecryptor::new(private_key.clone())
5168 .resolve_key(
5169 crate::provider::default_provider(),
5170 DataEncryptionAlgorithm::Aes128Gcm,
5171 Some(&encrypted_key),
5172 )
5173 .expect("official XMLENC SHA-384 URI must resolve");
5174 assert_eq!(resolved, session_key);
5175 }
5176 }
5177
5178 #[test]
5179 fn rejects_unknown_oaep_digest_and_mgf_as_unsupported() {
5180 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5182 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5183 ))
5184 .expect("RSA donor private key must parse");
5185 let decryptor = PrivateKeyDecryptor::new(private_key);
5186 let mut encrypted_key = EncryptedKey {
5187 sources: Default::default(),
5188 id: None,
5189 recipient: None,
5190 key_name: None,
5191 encryption_method: super::super::EncryptionMethod {
5192 chacha: None,
5193 algorithm: "http://www.w3.org/2009/xmlenc11#rsa-oaep".into(),
5194 key_size_bits: None,
5195 oaep_digest: Some("urn:unsupported:digest".into()),
5196 mgf_algorithm: Some("http://www.w3.org/2009/xmlenc11#mgf1sha1".into()),
5197 oaep_params: None,
5198 },
5199 cipher_data: super::super::CipherData::Value {
5200 value: STANDARD.encode([0_u8; 256]),
5201 },
5202 reference_list: None,
5203 carried_key_name: None,
5204 };
5205 assert!(matches!(
5206 decryptor.resolve_key(crate::provider::default_provider(), DataEncryptionAlgorithm::Aes128Gcm, Some(&encrypted_key)),
5207 Err(XmlEncError::UnsupportedAlgorithm(uri)) if uri == "urn:unsupported:digest"
5208 ));
5209
5210 encrypted_key.encryption_method.oaep_digest = None;
5211 encrypted_key.encryption_method.mgf_algorithm = Some("urn:unsupported:mgf".into());
5212 assert!(matches!(
5213 decryptor.resolve_key(crate::provider::default_provider(), DataEncryptionAlgorithm::Aes128Gcm, Some(&encrypted_key)),
5214 Err(XmlEncError::UnsupportedAlgorithm(uri)) if uri == "urn:unsupported:mgf"
5215 ));
5216 }
5217
5218 #[test]
5219 fn decryption_policy_enforces_oaep_digest_and_plaintext_limits() {
5220 let encrypted_key = EncryptedKey {
5223 id: None,
5224 recipient: Some("selected".into()),
5225 sources: Default::default(),
5226 key_name: None,
5227 encryption_method: super::super::EncryptionMethod {
5228 chacha: None,
5229 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p.uri().into(),
5230 key_size_bits: None,
5231 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
5232 mgf_algorithm: Some(
5233 OaepDigestAlgorithm::Sha384
5234 .mgf_uri()
5235 .expect("MGF1-SHA384 URI")
5236 .into(),
5237 ),
5238 oaep_params: None,
5239 },
5240 cipher_data: super::super::CipherData::Value {
5241 value: STANDARD.encode([0_u8; 256]),
5242 },
5243 reference_list: None,
5244 carried_key_name: None,
5245 };
5246 let encrypted = EncryptedData {
5247 encapsulation_methods: Vec::new(),
5248 id: None,
5249 derived_keys: Vec::new(),
5250 agreement_methods: Vec::new(),
5251 encrypted_type: None,
5252 key_name: None,
5253 encryption_method: super::super::EncryptionMethod {
5254 chacha: None,
5255 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5256 key_size_bits: None,
5257 oaep_digest: None,
5258 mgf_algorithm: None,
5259 oaep_params: None,
5260 },
5261 encrypted_keys: vec![encrypted_key],
5262 cipher_data: super::super::CipherData::Value {
5263 value: STANDARD.encode([0_u8; 28]),
5264 },
5265 };
5266 let policy = crate::policy::DecryptionPolicy {
5267 oaep_digests: Some(std::collections::HashSet::from([
5268 OaepDigestAlgorithm::Sha256,
5269 ])),
5270 ..crate::policy::DecryptionPolicy::default()
5271 };
5272 assert!(matches!(
5273 DecryptContext::new(&RecipientKeyResolver {
5274 recipient: "selected",
5275 key: vec![0_u8; 16],
5276 })
5277 .policy(policy)
5278 .decrypt_data(&encrypted),
5279 Err(XmlEncError::Policy(
5280 crate::policy::PolicyViolation::Algorithm { algorithm, .. }
5281 )) if algorithm == OaepDigestAlgorithm::Sha384.mgf_uri().expect("MGF1-SHA384 URI")
5282 ));
5283
5284 let ciphertext = crate::provider::default_provider()
5285 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &[0_u8; 16], b"four")
5286 .expect("test encryption must succeed");
5287 let bounded = EncryptedData {
5288 encrypted_keys: Vec::new(),
5289 cipher_data: super::super::CipherData::Value {
5290 value: STANDARD.encode(ciphertext),
5291 },
5292 ..encrypted
5293 };
5294 let policy = crate::policy::DecryptionPolicy {
5295 resources: crate::policy::ResourcePolicy {
5296 max_encryption_plaintext_bytes: 3,
5297 ..crate::policy::ResourcePolicy::default()
5298 },
5299 ..crate::policy::DecryptionPolicy::default()
5300 };
5301 assert!(matches!(
5302 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16]))
5303 .policy(policy)
5304 .decrypt_data(&bounded),
5305 Err(XmlEncError::Policy(
5306 crate::policy::PolicyViolation::ResourceLimit {
5307 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
5308 maximum: 3,
5309 actual: 4
5310 }
5311 ))
5312 ));
5313
5314 let cbc_ciphertext = crate::provider::default_provider()
5315 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &[0_u8; 16], b"four")
5316 .expect("test CBC encryption must succeed");
5317 let bounded_cbc = EncryptedData {
5318 encryption_method: super::super::EncryptionMethod {
5319 chacha: None,
5320 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
5321 key_size_bits: None,
5322 oaep_digest: None,
5323 mgf_algorithm: None,
5324 oaep_params: None,
5325 },
5326 encrypted_keys: Vec::new(),
5327 cipher_data: super::super::CipherData::Value {
5328 value: STANDARD.encode(cbc_ciphertext),
5329 },
5330 ..bounded
5331 };
5332 let policy = crate::policy::DecryptionPolicy {
5333 resources: crate::policy::ResourcePolicy {
5334 max_encryption_plaintext_bytes: 4,
5335 ..crate::policy::ResourcePolicy::default()
5336 },
5337 ..crate::policy::DecryptionPolicy::default()
5338 };
5339 assert_eq!(
5340 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16]))
5341 .policy(policy)
5342 .decrypt_data(&bounded_cbc)
5343 .expect("CBC plaintext at the configured limit must decrypt"),
5344 DecryptedContent::Bytes(b"four".to_vec())
5345 );
5346 }
5347
5348 #[test]
5349 fn typed_decryption_input_cannot_bypass_metadata_policy() {
5350 let ciphertext = crate::provider::default_provider()
5353 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &[0_u8; 16], b"data")
5354 .expect("test encryption must succeed");
5355 let encrypted = EncryptedData {
5356 encapsulation_methods: Vec::new(),
5357 id: Some("oversized".into()),
5358 derived_keys: Vec::new(),
5359 agreement_methods: Vec::new(),
5360 encrypted_type: None,
5361 key_name: None,
5362 encryption_method: super::super::EncryptionMethod {
5363 chacha: None,
5364 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5365 key_size_bits: None,
5366 oaep_digest: None,
5367 mgf_algorithm: None,
5368 oaep_params: None,
5369 },
5370 encrypted_keys: Vec::new(),
5371 cipher_data: super::super::CipherData::Value {
5372 value: STANDARD.encode(ciphertext),
5373 },
5374 };
5375 let policy = crate::policy::DecryptionPolicy {
5376 resources: crate::policy::ResourcePolicy {
5377 max_encryption_metadata_bytes: 8,
5378 ..crate::policy::ResourcePolicy::default()
5379 },
5380 ..crate::policy::DecryptionPolicy::default()
5381 };
5382
5383 let resolver = AllCallsResolver {
5384 calls: Cell::new(0),
5385 key: vec![0_u8; 16],
5386 };
5387 assert!(matches!(
5388 DecryptContext::new(&resolver)
5389 .policy(policy)
5390 .decrypt_data(&encrypted),
5391 Err(XmlEncError::Policy(
5392 crate::policy::PolicyViolation::ResourceLimit {
5393 resource: crate::policy::resource_name::ENCRYPTION_METADATA_BYTES,
5394 maximum: 8,
5395 actual: 9,
5396 }
5397 ))
5398 ));
5399 assert_eq!(
5400 resolver.calls.get(),
5401 0,
5402 "metadata preflight must gate resolver-controlled work"
5403 );
5404 }
5405
5406 #[test]
5407 fn typed_cipher_values_are_bounded_before_decode_or_resolution() {
5408 let key = [0x41_u8; 16];
5411 let ciphertext = crate::provider::default_provider()
5412 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5413 .expect("test encryption must succeed");
5414 let mut encrypted = EncryptedData {
5415 encapsulation_methods: Vec::new(),
5416 id: None,
5417 derived_keys: Vec::new(),
5418 agreement_methods: Vec::new(),
5419 encrypted_type: None,
5420 key_name: None,
5421 encryption_method: super::super::EncryptionMethod {
5422 chacha: None,
5423 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5424 key_size_bits: None,
5425 oaep_digest: None,
5426 mgf_algorithm: None,
5427 oaep_params: None,
5428 },
5429 encrypted_keys: Vec::new(),
5430 cipher_data: super::super::CipherData::Value {
5431 value: STANDARD.encode(ciphertext),
5432 },
5433 };
5434 let policy = crate::policy::DecryptionPolicy {
5435 resources: crate::policy::ResourcePolicy {
5436 max_encryption_plaintext_bytes: 4,
5437 ..crate::policy::ResourcePolicy::default()
5438 },
5439 ..crate::policy::DecryptionPolicy::default()
5440 };
5441 *encrypted
5442 .cipher_data
5443 .inline_value_mut()
5444 .expect("inline ciphertext") = "A".repeat(48);
5445 assert!(matches!(
5446 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
5447 .policy(policy)
5448 .decrypt_data(&encrypted),
5449 Err(XmlEncError::Policy(
5450 crate::policy::PolicyViolation::ResourceLimit {
5451 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
5452 ..
5453 }
5454 ))
5455 ));
5456
5457 *encrypted
5458 .cipher_data
5459 .inline_value_mut()
5460 .expect("inline ciphertext") = STANDARD.encode([0_u8; 28]);
5461 encrypted.encrypted_keys.push(EncryptedKey {
5462 sources: Default::default(),
5463 id: None,
5464 recipient: None,
5465 key_name: None,
5466 encryption_method: super::super::EncryptionMethod {
5467 chacha: None,
5468 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
5469 key_size_bits: None,
5470 oaep_digest: None,
5471 mgf_algorithm: None,
5472 oaep_params: None,
5473 },
5474 cipher_data: super::super::CipherData::Value {
5475 value: "A".repeat(MAX_CIPHER_VALUE_BASE64_LEN + 4),
5476 },
5477 reference_list: None,
5478 carried_key_name: None,
5479 });
5480 let resolver = CountingResolver {
5481 candidate_calls: Cell::new(0),
5482 key: key.to_vec(),
5483 };
5484 assert!(matches!(
5485 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5486 Err(XmlEncError::InvalidStructure(_))
5487 ));
5488 assert_eq!(resolver.candidate_calls.get(), 0);
5489
5490 *encrypted.encrypted_keys[0]
5491 .cipher_data
5492 .inline_value_mut()
5493 .expect("inline wrapped key") = "AAAA".into();
5494 let aggregate_encoded_len = encrypted
5495 .cipher_data
5496 .inline_value()
5497 .expect("inline ciphertext")
5498 .len()
5499 + encrypted.encrypted_keys[0]
5500 .cipher_data
5501 .inline_value()
5502 .expect("inline wrapped key")
5503 .len();
5504 let policy = crate::policy::DecryptionPolicy {
5505 resources: crate::policy::ResourcePolicy {
5506 max_encryption_plaintext_bytes: 4,
5507 max_xml_document_bytes: aggregate_encoded_len - 1,
5508 ..crate::policy::ResourcePolicy::default()
5509 },
5510 ..crate::policy::DecryptionPolicy::default()
5511 };
5512 let resolver = CountingResolver {
5513 candidate_calls: Cell::new(0),
5514 key: key.to_vec(),
5515 };
5516 assert!(matches!(
5517 DecryptContext::new(&resolver)
5518 .policy(policy)
5519 .decrypt_data(&encrypted),
5520 Err(XmlEncError::Policy(
5521 crate::policy::PolicyViolation::ResourceLimit {
5522 resource:
5523 crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
5524 maximum,
5525 actual,
5526 }
5527 )) if maximum == aggregate_encoded_len - 1 && actual == aggregate_encoded_len
5528 ));
5529 assert_eq!(resolver.candidate_calls.get(), 0);
5530 }
5531
5532 #[cfg(feature = "experimental-pq")]
5533 #[test]
5534 fn typed_kem_width_is_checked_before_any_resolver_dispatch() {
5535 let document = crate::XmlDomDocument::parse(include_str!(
5538 "../../tests/fixtures/xmlenc/aleksey-xmlenc-01/enc-aes128gcm-em-ml-kem-512.xml"
5539 ))
5540 .expect("valid donor XML");
5541 let node = document
5542 .descendants()
5543 .find(|node| node.has_tag_name((crate::xmlenc::types::XMLENC_NS, "EncryptedData")))
5544 .expect("donor EncryptedData");
5545 let mut policy = crate::policy::DecryptionPolicy::default();
5546 policy
5547 .key_establishment
5548 .encapsulation_algorithms
5549 .insert(crate::provider::KeyEncapsulationAlgorithm::MlKem512);
5550 let data = super::super::parse_encrypted_data_node_with_policy(node, &policy)
5551 .expect("valid donor encryption metadata");
5552 for width in [0, 767, 769] {
5553 for nested in [false, true] {
5554 let mut encrypted = data.clone();
5555 encrypted.encapsulation_methods[0]
5556 .ciphertext
5557 .resize(width, 0);
5558 if nested {
5559 encrypted.encrypted_keys.push(EncryptedKey {
5560 sources: super::super::EncryptionKeySources {
5561 encapsulation_methods: std::mem::take(
5562 &mut encrypted.encapsulation_methods,
5563 ),
5564 ..Default::default()
5565 },
5566 id: None,
5567 recipient: None,
5568 key_name: None,
5569 encryption_method: super::super::EncryptionMethod {
5570 chacha: None,
5571 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
5572 key_size_bits: None,
5573 oaep_digest: None,
5574 mgf_algorithm: None,
5575 oaep_params: None,
5576 },
5577 cipher_data: super::super::CipherData::Bytes(vec![0; 24]),
5578 reference_list: None,
5579 carried_key_name: None,
5580 });
5581 }
5582 let resolver = CountingResolver {
5583 candidate_calls: Cell::new(0),
5584 key: vec![0; 16],
5585 };
5586 let error = DecryptContext::new(&resolver)
5587 .policy(policy.clone())
5588 .decrypt_data(&encrypted)
5589 .expect_err("malformed typed KEM width rejected");
5590 assert!(
5591 error
5592 .to_string()
5593 .contains("ciphertext size does not match Algorithm"),
5594 "{error}"
5595 );
5596 assert_eq!(resolver.candidate_calls.get(), 0);
5597 }
5598 }
5599 let mut encrypted = data;
5601 encrypted.encryption_method.algorithm = DataEncryptionAlgorithm::Aes256Cbc.uri().into();
5602 encrypted.cipher_data = super::super::CipherData::Bytes(vec![0; 32]);
5603 let resolver = CountingResolver {
5604 candidate_calls: Cell::new(0),
5605 key: vec![0; 32],
5606 };
5607 assert!(matches!(
5608 DecryptContext::new(&resolver)
5609 .policy(policy)
5610 .decrypt_data(&encrypted),
5611 Err(XmlEncError::Policy(
5612 crate::policy::PolicyViolation::Algorithm {
5613 operation: "direct KEM content without external authentication",
5614 ..
5615 }
5616 ))
5617 ));
5618 assert_eq!(resolver.candidate_calls.get(), 0);
5619 }
5620
5621 #[cfg(feature = "experimental-pq")]
5622 #[test]
5623 fn typed_kem_ciphertext_shares_aggregate_cipher_value_budget() {
5624 let xml = include_str!(
5627 "../../tests/fixtures/xmlenc/aleksey-xmlenc-01/enc-aes128gcm-em-ml-kem-512.xml"
5628 );
5629 let document = crate::XmlDomDocument::parse(xml).expect("donor XML parses");
5630 let node = document
5631 .descendants()
5632 .find(|node| node.has_tag_name((crate::xmlenc::types::XMLENC_NS, "EncryptedData")))
5633 .expect("donor EncryptedData");
5634 let mut policy = crate::policy::DecryptionPolicy::default();
5635 policy
5636 .key_establishment
5637 .encapsulation_algorithms
5638 .insert(crate::provider::KeyEncapsulationAlgorithm::MlKem512);
5639 let mut encrypted = super::super::parse_encrypted_data_node_with_policy(node, &policy)
5640 .expect("permitted donor recipient");
5641 let content = cipher_data_storage_len(&encrypted.cipher_data);
5642 let kem_encoded = 4 * encrypted.encapsulation_methods[0]
5643 .ciphertext
5644 .len()
5645 .div_ceil(3);
5646 assert!(
5647 validate_typed_cipher_values(
5648 &encrypted,
5649 DataEncryptionAlgorithm::Aes128Gcm,
5650 usize::MAX / 2,
5651 content + kem_encoded
5652 )
5653 .is_ok()
5654 );
5655 for nested in [false, true] {
5656 if nested {
5657 encrypted.encrypted_keys.push(EncryptedKey {
5658 sources: super::super::EncryptionKeySources {
5659 encapsulation_methods: std::mem::take(&mut encrypted.encapsulation_methods),
5660 ..Default::default()
5661 },
5662 id: None,
5663 recipient: None,
5664 key_name: None,
5665 encryption_method: super::super::EncryptionMethod {
5666 chacha: None,
5667 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
5668 key_size_bits: None,
5669 oaep_digest: None,
5670 mgf_algorithm: None,
5671 oaep_params: None,
5672 },
5673 cipher_data: super::super::CipherData::Bytes(vec![0; 24]),
5674 reference_list: None,
5675 carried_key_name: None,
5676 });
5677 }
5678 let total = content + kem_encoded + if nested { 24 } else { 0 };
5679 policy.resources.max_xml_document_bytes = total - 1;
5680 let resolver = CountingResolver {
5681 candidate_calls: Cell::new(0),
5682 key: vec![0; 16],
5683 };
5684 assert!(
5685 matches!(DecryptContext::new(&resolver).policy(policy.clone()).decrypt_data(&encrypted), Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit { resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES, maximum, actual })) if maximum == total - 1 && actual == total)
5686 );
5687 assert_eq!(resolver.candidate_calls.get(), 0);
5688 }
5689 }
5690
5691 #[test]
5692 fn typed_legacy_oaep_accepts_explicit_mgf() {
5693 let key = [0x43_u8; 16];
5696 let ciphertext = crate::provider::default_provider()
5697 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5698 .expect("test encryption must succeed");
5699 let encrypted = EncryptedData {
5700 encapsulation_methods: Vec::new(),
5701 id: None,
5702 derived_keys: Vec::new(),
5703 agreement_methods: Vec::new(),
5704 encrypted_type: None,
5705 key_name: None,
5706 encryption_method: super::super::EncryptionMethod {
5707 chacha: None,
5708 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5709 key_size_bits: None,
5710 oaep_digest: None,
5711 mgf_algorithm: None,
5712 oaep_params: None,
5713 },
5714 encrypted_keys: vec![EncryptedKey {
5715 sources: Default::default(),
5716 id: None,
5717 recipient: None,
5718 key_name: None,
5719 encryption_method: super::super::EncryptionMethod {
5720 chacha: None,
5721 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p.uri().into(),
5722 key_size_bits: None,
5723 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
5724 mgf_algorithm: Some(
5725 OaepDigestAlgorithm::Sha384
5726 .mgf_uri()
5727 .expect("MGF1-SHA384 URI")
5728 .into(),
5729 ),
5730 oaep_params: None,
5731 },
5732 cipher_data: super::super::CipherData::Value {
5733 value: STANDARD.encode([0_u8; 256]),
5734 },
5735 reference_list: None,
5736 carried_key_name: None,
5737 }],
5738 cipher_data: super::super::CipherData::Value {
5739 value: STANDARD.encode(ciphertext),
5740 },
5741 };
5742 let resolver = CountingResolver {
5743 candidate_calls: Cell::new(0),
5744 key: key.to_vec(),
5745 };
5746
5747 assert_eq!(
5748 DecryptContext::new(&resolver)
5749 .decrypt_data(&encrypted)
5750 .expect("explicit legacy OAEP MGF must reach key resolution"),
5751 DecryptedContent::Bytes(b"data".to_vec())
5752 );
5753 assert_eq!(resolver.candidate_calls.get(), 1);
5754 }
5755
5756 #[test]
5757 fn typed_zero_key_size_is_rejected_before_key_resolution() {
5758 let key = [0x45_u8; 16];
5761 let ciphertext = crate::provider::default_provider()
5762 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5763 .expect("test encryption must succeed");
5764 let encrypted = EncryptedData {
5765 encapsulation_methods: Vec::new(),
5766 id: None,
5767 derived_keys: Vec::new(),
5768 agreement_methods: Vec::new(),
5769 encrypted_type: None,
5770 key_name: None,
5771 encryption_method: super::super::EncryptionMethod {
5772 chacha: None,
5773 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5774 key_size_bits: None,
5775 oaep_digest: None,
5776 mgf_algorithm: None,
5777 oaep_params: None,
5778 },
5779 encrypted_keys: vec![EncryptedKey {
5780 id: None,
5781 sources: Default::default(),
5782 recipient: None,
5783 key_name: None,
5784 encryption_method: super::super::EncryptionMethod {
5785 chacha: None,
5786 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
5787 key_size_bits: Some(0),
5788 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
5789 mgf_algorithm: Some(
5790 OaepDigestAlgorithm::Sha256
5791 .mgf_uri()
5792 .expect("MGF1-SHA256 URI")
5793 .into(),
5794 ),
5795 oaep_params: None,
5796 },
5797 cipher_data: super::super::CipherData::Value {
5798 value: STANDARD.encode([0_u8; 256]),
5799 },
5800 reference_list: None,
5801 carried_key_name: None,
5802 }],
5803 cipher_data: super::super::CipherData::Value {
5804 value: STANDARD.encode(ciphertext),
5805 },
5806 };
5807 let resolver = CountingResolver {
5808 candidate_calls: Cell::new(0),
5809 key: key.to_vec(),
5810 };
5811
5812 assert!(matches!(
5813 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5814 Err(XmlEncError::InvalidStructure(message))
5815 if message == "KeySize must be a positive integer"
5816 ));
5817 assert_eq!(resolver.candidate_calls.get(), 0);
5818 }
5819
5820 #[test]
5821 fn typed_content_method_is_validated_before_key_resolution() {
5822 let key = [0x44_u8; 16];
5825 let ciphertext = crate::provider::default_provider()
5826 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5827 .expect("test encryption must succeed");
5828 let encrypted = EncryptedData {
5829 encapsulation_methods: Vec::new(),
5830 id: None,
5831 derived_keys: Vec::new(),
5832 agreement_methods: Vec::new(),
5833 encrypted_type: None,
5834 key_name: None,
5835 encryption_method: super::super::EncryptionMethod {
5836 chacha: None,
5837 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5838 key_size_bits: Some(256),
5839 oaep_digest: None,
5840 mgf_algorithm: None,
5841 oaep_params: None,
5842 },
5843 encrypted_keys: Vec::new(),
5844 cipher_data: super::super::CipherData::Value {
5845 value: STANDARD.encode(ciphertext),
5846 },
5847 };
5848 let resolver = AllCallsResolver {
5849 calls: Cell::new(0),
5850 key: key.to_vec(),
5851 };
5852
5853 assert!(matches!(
5854 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5855 Err(XmlEncError::InvalidStructure(message))
5856 if message.contains("requires KeySize 128, got 256")
5857 ));
5858 assert_eq!(resolver.calls.get(), 0);
5859 }
5860
5861 #[test]
5862 fn unknown_encrypted_key_algorithm_never_reaches_resolver() {
5863 let key = [0x42_u8; 16];
5866 let ciphertext = crate::provider::default_provider()
5867 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5868 .expect("test encryption must succeed");
5869 let encrypted = EncryptedData {
5870 encapsulation_methods: Vec::new(),
5871 id: None,
5872 derived_keys: Vec::new(),
5873 agreement_methods: Vec::new(),
5874 encrypted_type: None,
5875 key_name: None,
5876 encryption_method: super::super::EncryptionMethod {
5877 chacha: None,
5878 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5879 key_size_bits: None,
5880 oaep_digest: None,
5881 mgf_algorithm: None,
5882 oaep_params: None,
5883 },
5884 encrypted_keys: vec![EncryptedKey {
5885 id: None,
5886 recipient: None,
5887 sources: Default::default(),
5888 key_name: None,
5889 encryption_method: super::super::EncryptionMethod {
5890 chacha: None,
5891 algorithm: "urn:example:unknown-key-algorithm".into(),
5892 key_size_bits: None,
5893 oaep_digest: None,
5894 mgf_algorithm: None,
5895 oaep_params: None,
5896 },
5897 cipher_data: super::super::CipherData::Value {
5898 value: STANDARD.encode([0_u8; 24]),
5899 },
5900 reference_list: None,
5901 carried_key_name: None,
5902 }],
5903 cipher_data: super::super::CipherData::Value {
5904 value: STANDARD.encode(ciphertext),
5905 },
5906 };
5907 let resolver = CountingResolver {
5908 candidate_calls: Cell::new(0),
5909 key: key.to_vec(),
5910 };
5911
5912 assert!(matches!(
5913 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5914 Err(XmlEncError::UnsupportedAlgorithm(_))
5915 ));
5916 assert_eq!(resolver.candidate_calls.get(), 0);
5917 }
5918
5919 #[test]
5920 fn cbc_padding_errors_do_not_expose_decrypted_octets() {
5921 let error = map_data_decryption_error(
5924 DataEncryptionAlgorithm::Aes128Cbc,
5925 32,
5926 crate::provider::ProviderError::InvalidInput(
5927 crate::provider::ProviderInputError::AesCbcCiphertext,
5928 ),
5929 );
5930
5931 assert_eq!(error.to_string(), "invalid XMLEnc padding");
5932 }
5933
5934 #[test]
5935 fn replaces_element_and_content_in_caller_owned_documents() {
5936 let key = [0x31_u8; 16];
5939 let element = encrypted_gcm_element(
5940 "http://www.w3.org/2001/04/xmlenc#Element",
5941 "<secret id=\"visible\">value</secret>",
5942 None,
5943 true,
5944 &key,
5945 );
5946 assert_eq!(
5947 decrypt_document(&element, None, &SymmetricKeyDecryptor::new(key))
5948 .expect("root Element replacement must succeed"),
5949 "<secret id=\"visible\">value</secret>"
5950 );
5951
5952 let content = encrypted_gcm_element(
5953 "http://www.w3.org/2001/04/xmlenc#Content",
5954 "before<child/>after",
5955 None,
5956 false,
5957 &key,
5958 );
5959 let document =
5960 format!("<root xmlns:xenc=\"{XMLENC_NS}\"><prefix/>{content}<suffix/></root>");
5961 let replaced = decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key))
5962 .expect("nested Content replacement must succeed");
5963 assert_eq!(
5964 replaced,
5965 format!(
5966 "<root xmlns:xenc=\"{XMLENC_NS}\"><prefix/>before<child/>after<suffix/></root>"
5967 )
5968 );
5969 }
5970
5971 #[test]
5972 fn accepts_whitespace_and_comments_around_element_plaintext() {
5973 let key = [0x34_u8; 16];
5976 let plaintext = "\n<!--before--><secret/><!--after-->\n";
5977 let encrypted = encrypted_gcm_element(
5978 "http://www.w3.org/2001/04/xmlenc#Element",
5979 plaintext,
5980 None,
5981 true,
5982 &key,
5983 );
5984
5985 assert_eq!(
5986 decrypt_document(&encrypted, None, &SymmetricKeyDecryptor::new(key))
5987 .expect("one element with boundary trivia must be accepted"),
5988 plaintext
5989 );
5990 }
5991
5992 #[test]
5993 fn decrypts_unknown_and_empty_type_hints_as_opaque_bytes() {
5994 let key = [0x35_u8; 16];
5997 let plaintext = "\0opaque\u{ff}bytes";
5998 let unknown = encrypted_gcm_element("urn:example:binary", plaintext, None, true, &key);
5999 let empty = encrypted_gcm_element("", plaintext, None, true, &key).replacen(
6000 "<xenc:EncryptedData",
6001 "<xenc:EncryptedData Type=\"\"",
6002 1,
6003 );
6004
6005 let parsed = parse_encrypted_data(&unknown).expect("unknown Type must remain parseable");
6006 assert_eq!(
6007 parsed.encrypted_type,
6008 Some(EncryptedDataType::Other("urn:example:binary".into()))
6009 );
6010 assert!(matches!(
6011 decrypt_document(&unknown, None, &SymmetricKeyDecryptor::new(key)),
6012 Err(XmlEncError::ReplacementRequiresXml)
6013 ));
6014
6015 for encrypted in [unknown, empty] {
6016 assert_eq!(
6017 decrypt(&encrypted, &SymmetricKeyDecryptor::new(key))
6018 .expect("opaque Type hints must not block decryption"),
6019 DecryptedContent::Bytes(plaintext.as_bytes().to_vec())
6020 );
6021 }
6022 }
6023
6024 #[test]
6025 fn selects_document_encrypted_data_by_id_and_rejects_ambiguity() {
6026 let key = [0x32_u8; 16];
6029 let first = encrypted_gcm_element(
6030 "http://www.w3.org/2001/04/xmlenc#Content",
6031 "first",
6032 Some("first"),
6033 false,
6034 &key,
6035 );
6036 let second = encrypted_gcm_element(
6037 "http://www.w3.org/2001/04/xmlenc#Content",
6038 "second",
6039 Some("second"),
6040 false,
6041 &key,
6042 );
6043 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{first}{second}</root>");
6044 let resolver = SymmetricKeyDecryptor::new(key);
6045 assert!(matches!(
6046 decrypt_document(&document, None, &resolver),
6047 Err(XmlEncError::AmbiguousEncryptedData)
6048 ));
6049 let replaced = decrypt_document(&document, Some("second"), &resolver)
6050 .expect("Id selection must choose exactly one encrypted region");
6051 assert!(replaced.contains("second"));
6052 assert!(replaced.contains("Id=\"first\""));
6053 assert!(matches!(
6054 decrypt_document(&document, Some("missing"), &resolver),
6055 Err(XmlEncError::EncryptedDataNotFound)
6056 ));
6057 }
6058
6059 #[test]
6060 fn selects_encrypted_data_below_a_unique_operation_start_node() {
6061 let key = [0x42_u8; 16];
6064 let first = encrypted_gcm_element(
6065 "http://www.w3.org/2001/04/xmlenc#Content",
6066 "first",
6067 None,
6068 false,
6069 &key,
6070 );
6071 let second = encrypted_gcm_element(
6072 "http://www.w3.org/2001/04/xmlenc#Content",
6073 "second",
6074 None,
6075 false,
6076 &key,
6077 );
6078 let document = format!(
6079 "<root xmlns:xenc=\"{XMLENC_NS}\"><scope Id=\"first\">{first}</scope><scope Id=\"second\">{second}</scope></root>"
6080 );
6081 let resolver = SymmetricKeyDecryptor::new(key);
6082 let context = DecryptContext::new(&resolver);
6083 let replaced = context
6084 .decrypt_document_from_start_node(&document, Some("second"))
6085 .expect("ancestor ID must select its encrypted descendant");
6086 assert!(replaced.contains("<scope Id=\"second\">second</scope>"));
6087 assert!(replaced.contains("<scope Id=\"first\"><xenc:EncryptedData"));
6088
6089 assert!(matches!(
6090 context.decrypt_document_from_start_node(&document, Some("missing")),
6091 Err(XmlEncError::SelectedNodeUnavailable { id }) if id == "missing"
6092 ));
6093 let duplicate = document.replace("Id=\"second\"", "Id=\"first\"");
6094 assert!(matches!(
6095 context.decrypt_document_from_start_node(&duplicate, Some("first")),
6096 Err(XmlEncError::SelectedNodeUnavailable { id }) if id == "first"
6097 ));
6098 let ambiguous = format!(
6099 "<root xmlns:xenc=\"{XMLENC_NS}\"><scope Id=\"selected\">{first}{second}</scope></root>"
6100 );
6101 assert!(matches!(
6102 context.decrypt_document_from_start_node(&ambiguous, Some("selected")),
6103 Err(XmlEncError::AmbiguousEncryptedData)
6104 ));
6105
6106 let first_replaced = context
6107 .decrypt_first_document_from_start_node(&ambiguous, Some("selected"))
6108 .expect("first-match selection must leave later encrypted descendants untouched");
6109 assert!(first_replaced.contains("<scope Id=\"selected\">first<xenc:EncryptedData"));
6110 let replaced_document =
6111 Document::parse(&first_replaced).expect("first-match output must remain valid XML");
6112 assert_eq!(
6113 replaced_document
6114 .descendants()
6115 .filter(|node| node.has_tag_name((XMLENC_NS, "EncryptedData")))
6116 .count(),
6117 1
6118 );
6119 }
6120
6121 #[test]
6122 fn rejects_non_xml_or_malformed_document_replacement_plaintext() {
6123 let key = [0x33_u8; 16];
6126 let binary = encrypted_gcm_element("", "binary", None, true, &key);
6127 assert!(matches!(
6128 decrypt_document(&binary, None, &SymmetricKeyDecryptor::new(key)),
6129 Err(XmlEncError::ReplacementRequiresXml)
6130 ));
6131
6132 let malformed = encrypted_gcm_element(
6133 "http://www.w3.org/2001/04/xmlenc#Element",
6134 "<unclosed>",
6135 None,
6136 true,
6137 &key,
6138 );
6139 assert!(matches!(
6140 decrypt_document(&malformed, None, &SymmetricKeyDecryptor::new(key)),
6141 Err(XmlEncError::XmlParse(_))
6142 ));
6143
6144 for invalid_element in ["text-only", "<first/><second/>"] {
6145 let encrypted = encrypted_gcm_element(
6146 "http://www.w3.org/2001/04/xmlenc#Element",
6147 invalid_element,
6148 None,
6149 false,
6150 &key,
6151 );
6152 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>");
6153 assert!(
6154 decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key)).is_err(),
6155 "Element plaintext must contain exactly one element: {invalid_element}"
6156 );
6157 }
6158
6159 let content = encrypted_gcm_element(
6160 "http://www.w3.org/2001/04/xmlenc#Content",
6161 "plaintext",
6162 None,
6163 false,
6164 &key,
6165 );
6166 let with_dtd = format!(
6167 "<!DOCTYPE root [<!ATTLIST root Id ID #IMPLIED>]><root xmlns:xenc=\"{XMLENC_NS}\">{content}</root>"
6168 );
6169 assert!(matches!(
6170 decrypt_document(&with_dtd, None, &SymmetricKeyDecryptor::new(key)),
6171 Err(XmlEncError::XmlParse(
6172 crate::xml::dom::ParseError::DtdDetected
6173 ))
6174 ));
6175 let mut policy = crate::policy::DecryptionPolicy::default();
6176 policy.xml.allow_internal_dtd = true;
6177 assert!(
6178 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6179 .policy(policy)
6180 .decrypt_document(&with_dtd, None)
6181 .expect("explicit internal-DTD opt-in must decrypt")
6182 .contains("plaintext")
6183 );
6184 }
6185
6186 #[test]
6187 fn rejects_plaintext_markup_that_crosses_the_encrypted_region() {
6188 let key = [0x36_u8; 16];
6192 let crossing_markup = "</parent><attacker/><parent>";
6193 for type_uri in [
6194 "http://www.w3.org/2001/04/xmlenc#Content",
6195 "http://www.w3.org/2001/04/xmlenc#Element",
6196 ] {
6197 let encrypted = encrypted_gcm_element(type_uri, crossing_markup, None, false, &key);
6198 let document =
6199 format!("<outer xmlns:xenc=\"{XMLENC_NS}\"><parent>{encrypted}</parent></outer>");
6200 assert!(
6201 decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key)).is_err(),
6202 "{type_uri} plaintext must not escape its replacement boundary"
6203 );
6204 }
6205 }
6206
6207 #[test]
6208 fn document_decryption_applies_byte_and_node_policy_before_parsing() {
6209 let key = [0x38_u8; 16];
6212 let encrypted = encrypted_gcm_element(
6213 "http://www.w3.org/2001/04/xmlenc#Content",
6214 "plaintext",
6215 None,
6216 false,
6217 &key,
6218 );
6219 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\"><a/>{encrypted}</root>");
6220 let byte_policy = crate::policy::DecryptionPolicy {
6221 resources: crate::policy::ResourcePolicy {
6222 max_xml_document_bytes: document.len() - 1,
6223 ..crate::policy::ResourcePolicy::default()
6224 },
6225 ..crate::policy::DecryptionPolicy::default()
6226 };
6227 assert!(matches!(
6228 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6229 .policy(byte_policy)
6230 .decrypt_document(&document, None),
6231 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
6232 resource: crate::policy::resource_name::XML_DOCUMENT,
6233 maximum,
6234 actual,
6235 })) if maximum == document.len() - 1 && actual == document.len()
6236 ));
6237
6238 let node_policy = crate::policy::DecryptionPolicy {
6239 resources: crate::policy::ResourcePolicy {
6240 max_xml_nodes: 3,
6241 ..crate::policy::ResourcePolicy::default()
6242 },
6243 ..crate::policy::DecryptionPolicy::default()
6244 };
6245 assert!(matches!(
6246 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6247 .policy(node_policy)
6248 .decrypt_document(&document, None),
6249 Err(XmlEncError::Policy(
6250 crate::policy::PolicyViolation::ResourceLimit {
6251 resource: crate::policy::resource_name::XML_NODES,
6252 maximum: 3,
6253 actual: 4,
6254 }
6255 ))
6256 ));
6257 }
6258
6259 #[test]
6260 fn decryption_entry_points_enforce_policy_depth() {
6261 let xml = "<root><child><leaf/></child></root>";
6264 let policy = crate::policy::DecryptionPolicy {
6265 resources: crate::policy::ResourcePolicy {
6266 max_xml_depth: 2,
6267 ..crate::policy::ResourcePolicy::default()
6268 },
6269 ..crate::policy::DecryptionPolicy::default()
6270 };
6271 let resolver = SymmetricKeyDecryptor::new([0_u8; 16]);
6272 let mut document = XmlDocument::parse(xml).expect("wide retained fixture must parse");
6273
6274 assert!(matches!(
6275 DecryptContext::new(&resolver)
6276 .policy(policy.clone())
6277 .decrypt_document(xml, None),
6278 Err(XmlEncError::Policy(
6279 crate::policy::PolicyViolation::ResourceLimit {
6280 resource: crate::policy::resource_name::XML_DEPTH,
6281 maximum: 2,
6282 actual: 3,
6283 }
6284 ))
6285 ));
6286 assert!(matches!(
6287 DecryptContext::new(&resolver)
6288 .policy(policy)
6289 .decrypt_owned_document(&mut document, None),
6290 Err(XmlEncError::Policy(
6291 crate::policy::PolicyViolation::ResourceLimit {
6292 resource: crate::policy::resource_name::XML_DEPTH,
6293 maximum: 2,
6294 actual: 3,
6295 }
6296 ))
6297 ));
6298 }
6299
6300 #[test]
6301 fn fragment_validation_does_not_charge_its_internal_wrapper_node() {
6302 let key = [0x39_u8; 16];
6305 let plaintext = "<item/>".repeat(20);
6306 let encrypted = encrypted_gcm_element(
6307 "http://www.w3.org/2001/04/xmlenc#Content",
6308 &plaintext,
6309 None,
6310 false,
6311 &key,
6312 );
6313 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>");
6314 let resolver = SymmetricKeyDecryptor::new(key);
6315 let expected = decrypt_document(&document, None, &resolver)
6316 .expect("unbounded setup decryption must succeed");
6317 let exact_output_nodes = Document::parse(&expected)
6318 .expect("decrypted output must parse")
6319 .descendants()
6320 .count();
6321 let policy = crate::policy::DecryptionPolicy {
6322 resources: crate::policy::ResourcePolicy {
6323 max_xml_nodes: exact_output_nodes,
6324 ..crate::policy::ResourcePolicy::default()
6325 },
6326 ..crate::policy::DecryptionPolicy::default()
6327 };
6328
6329 assert_eq!(
6330 DecryptContext::new(&resolver)
6331 .policy(policy)
6332 .decrypt_document(&document, None)
6333 .expect("temporary wrapper must not consume caller node budget"),
6334 expected
6335 );
6336 }
6337
6338 #[test]
6339 fn owned_decryption_rejects_projected_node_limit_atomically() {
6340 let key = [0x3a_u8; 16];
6343 let plaintext = "<item/>".repeat(64);
6344 let encrypted = encrypted_gcm_element(
6345 "http://www.w3.org/2001/04/xmlenc#Content",
6346 &plaintext,
6347 None,
6348 false,
6349 &key,
6350 );
6351 let mut document = XmlDocument::parse(format!(
6352 "<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>"
6353 ))
6354 .expect("owned encrypted fixture must parse");
6355 let input_nodes = document.with_view(|view| view.node_count());
6356 let before = document.as_xml().to_owned();
6357 let policy = crate::policy::DecryptionPolicy {
6358 resources: crate::policy::ResourcePolicy {
6359 max_xml_nodes: input_nodes,
6360 ..crate::policy::ResourcePolicy::default()
6361 },
6362 ..crate::policy::DecryptionPolicy::default()
6363 };
6364
6365 let error = DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6366 .policy(policy)
6367 .decrypt_owned_document(&mut document, None)
6368 .expect_err("expanded plaintext must exceed the operation node ceiling");
6369
6370 assert!(matches!(
6371 error,
6372 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
6373 resource: crate::policy::resource_name::XML_NODES,
6374 maximum,
6375 ..
6376 }) if maximum == input_nodes
6377 ));
6378 assert_eq!(document.as_xml(), before);
6379 assert_eq!(document.generation(), 0);
6380 }
6381
6382 #[test]
6383 fn owned_decryption_reports_decrypted_depth_as_policy() {
6384 let key = [0x3b_u8; 16];
6388 let plaintext = format!("{}value{}", "<nested>".repeat(32), "</nested>".repeat(32));
6389 let encrypted = encrypted_gcm_element(
6390 "http://www.w3.org/2001/04/xmlenc#Element",
6391 &plaintext,
6392 None,
6393 false,
6394 &key,
6395 );
6396 let mut document = XmlDocument::parse(format!(
6397 "<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>"
6398 ))
6399 .expect("encrypted fixture must parse");
6400 let input_depth = document.with_view(|view| view.max_depth());
6401 let before = document.as_xml().to_owned();
6402 let policy = crate::policy::DecryptionPolicy {
6403 resources: crate::policy::ResourcePolicy {
6404 max_xml_depth: input_depth,
6405 ..crate::policy::ResourcePolicy::default()
6406 },
6407 ..crate::policy::DecryptionPolicy::default()
6408 };
6409
6410 let error = DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6411 .policy(policy)
6412 .decrypt_owned_document(&mut document, None)
6413 .expect_err("deep plaintext must exceed the active depth policy");
6414
6415 assert!(matches!(
6416 error,
6417 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
6418 resource: crate::policy::resource_name::XML_DEPTH,
6419 maximum,
6420 actual,
6421 }) if maximum == input_depth && actual > maximum
6422 ));
6423 assert_eq!(document.as_xml(), before);
6424 assert_eq!(document.generation(), 0);
6425 }
6426
6427 #[test]
6428 fn validates_replacement_plaintext_in_its_namespace_context() {
6429 let key = [0x37_u8; 16];
6432 let encrypted = encrypted_gcm_element(
6433 "http://www.w3.org/2001/04/xmlenc#Content",
6434 "<shared:child/>",
6435 None,
6436 false,
6437 &key,
6438 );
6439 let document = format!(
6440 "<root xmlns:xenc=\"{XMLENC_NS}\" xmlns:shared=\"urn:shared\">{encrypted}</root>"
6441 );
6442 let decrypted = decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key))
6443 .expect("inherited namespace prefixes must remain valid");
6444 assert_eq!(
6445 decrypted,
6446 format!(
6447 "<root xmlns:xenc=\"{XMLENC_NS}\" xmlns:shared=\"urn:shared\"><shared:child/></root>"
6448 )
6449 );
6450 }
6451
6452 fn encrypted_gcm_element(
6453 type_uri: &str,
6454 plaintext: &str,
6455 id: Option<&str>,
6456 declare_namespace: bool,
6457 key: &[u8; 16],
6458 ) -> String {
6459 let nonce = [0x44_u8; 12];
6460 let mut ciphertext = plaintext.as_bytes().to_vec();
6461 Aes128Gcm::new_from_slice(key)
6462 .expect("fixed content key length")
6463 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
6464 .expect("test encryption must succeed");
6465 let mut wire = nonce.to_vec();
6466 wire.extend_from_slice(&ciphertext);
6467 let namespace = declare_namespace
6468 .then_some(format!(" xmlns:xenc=\"{XMLENC_NS}\""))
6469 .unwrap_or_default();
6470 let data_type = (!type_uri.is_empty())
6471 .then_some(format!(" Type=\"{type_uri}\""))
6472 .unwrap_or_default();
6473 let id = id
6474 .map(|value| format!(" Id=\"{value}\""))
6475 .unwrap_or_default();
6476 format!(
6477 "<xenc:EncryptedData{namespace}{data_type}{id}><xenc:EncryptionMethod Algorithm=\"http://www.w3.org/2009/xmlenc11#aes128-gcm\"/><xenc:CipherData><xenc:CipherValue>{}</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData>",
6478 STANDARD.encode(wire)
6479 )
6480 }
6481}