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 zeroize::Zeroize for SymmetricKeyDecryptor {
1192 fn zeroize(&mut self) {
1193 self.key.zeroize();
1194 }
1195}
1196
1197impl zeroize::ZeroizeOnDrop for SymmetricKeyDecryptor {}
1198
1199impl Drop for SymmetricKeyDecryptor {
1200 fn drop(&mut self) {
1201 zeroize::Zeroize::zeroize(self);
1202 }
1203}
1204
1205impl fmt::Debug for SymmetricKeyDecryptor {
1206 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1207 formatter
1208 .debug_struct("SymmetricKeyDecryptor")
1209 .field("key", &"[REDACTED]")
1210 .finish()
1211 }
1212}
1213
1214impl SymmetricKeyDecryptor {
1215 pub fn new(key: impl Into<Vec<u8>>) -> Self {
1217 Self::with_kind(key, crate::key_manager::SymmetricKeyKind::Aes)
1218 }
1219
1220 pub fn with_kind(key: impl Into<Vec<u8>>, kind: crate::key_manager::SymmetricKeyKind) -> Self {
1224 Self {
1225 key: key.into(),
1226 kind,
1227 }
1228 }
1229}
1230
1231impl DecryptionKeyResolver for SymmetricKeyDecryptor {
1232 fn resolve_content_keys_with_policy(
1233 &self,
1234 provider: &dyn crate::provider::CryptoProvider,
1235 algorithm: DataEncryptionAlgorithm,
1236 encrypted_key: Option<&EncryptedKey>,
1237 policy: &crate::policy::DecryptionPolicy,
1238 budget: &mut KeyCandidateBudget,
1239 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1240 policy.validate()?;
1241 if encrypted_key.is_some() {
1242 return Err(XmlEncError::KeyNotFound);
1243 }
1244 budget.consume(1)?;
1245 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
1246 self.resolve_key(provider, algorithm, None)?,
1247 )])
1248 }
1249
1250 fn resolve_key(
1251 &self,
1252 _provider: &dyn crate::provider::CryptoProvider,
1253 algorithm: DataEncryptionAlgorithm,
1254 _encrypted_key: Option<&EncryptedKey>,
1255 ) -> Result<Vec<u8>, XmlEncError> {
1256 if self.kind != algorithm.key_kind() {
1257 return Err(XmlEncError::KeyNotFound);
1258 }
1259 validate_key_len(algorithm, &self.key)?;
1260 Ok(self.key.clone())
1261 }
1262}
1263
1264#[derive(Clone)]
1266pub struct PrivateKeyDecryptor {
1267 key: Arc<dyn crate::provider::KeyRecoveryKey>,
1268}
1269
1270#[derive(Clone)]
1273pub struct KekDecryptor<'a> {
1274 kek: std::borrow::Cow<'a, [u8]>,
1275 kind: crate::key_manager::SymmetricKeyKind,
1276}
1277
1278impl zeroize::Zeroize for KekDecryptor<'_> {
1279 fn zeroize(&mut self) {
1280 if let std::borrow::Cow::Owned(kek) = &mut self.kek {
1282 kek.zeroize();
1283 }
1284 }
1285}
1286
1287impl zeroize::ZeroizeOnDrop for KekDecryptor<'_> {}
1288
1289impl Drop for KekDecryptor<'_> {
1290 fn drop(&mut self) {
1291 zeroize::Zeroize::zeroize(self);
1292 }
1293}
1294
1295impl fmt::Debug for KekDecryptor<'_> {
1296 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1297 formatter
1298 .debug_struct("KekDecryptor")
1299 .field("kek", &"[REDACTED]")
1300 .field("kind", &self.kind)
1301 .finish()
1302 }
1303}
1304
1305impl KekDecryptor<'static> {
1306 pub fn new(kek: impl Into<Vec<u8>>) -> Self {
1308 Self::with_kind(kek, crate::key_manager::SymmetricKeyKind::Aes)
1309 }
1310
1311 pub fn with_kind(kek: impl Into<Vec<u8>>, kind: crate::key_manager::SymmetricKeyKind) -> Self {
1313 Self {
1314 kek: std::borrow::Cow::Owned(kek.into()),
1315 kind,
1316 }
1317 }
1318}
1319
1320impl<'a> KekDecryptor<'a> {
1321 pub fn borrowed(kek: &'a [u8]) -> Self {
1323 Self::borrowed_with_kind(kek, crate::key_manager::SymmetricKeyKind::Aes)
1324 }
1325
1326 pub fn borrowed_with_kind(kek: &'a [u8], kind: crate::key_manager::SymmetricKeyKind) -> Self {
1328 Self {
1329 kek: std::borrow::Cow::Borrowed(kek),
1330 kind,
1331 }
1332 }
1333}
1334
1335impl DecryptionKeyResolver for KekDecryptor<'_> {
1336 fn resolve_key_encryption_keys_with_policy(
1337 &self,
1338 provider: &dyn crate::provider::CryptoProvider,
1339 algorithm: KeyWrapAlgorithm,
1340 source: KeyEncryptionKeySource<'_>,
1341 policy: &crate::policy::DecryptionPolicy,
1342 budget: &mut KeyCandidateBudget,
1343 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1344 match source {
1345 KeyEncryptionKeySource::Direct => {
1346 if self.kind != algorithm.key_kind() || self.kek.len() != algorithm.key_len() {
1347 return Err(XmlEncError::KeyNotFound);
1348 }
1349 budget.consume(1)?;
1350 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
1351 self.kek.to_vec(),
1352 )])
1353 }
1354 KeyEncryptionKeySource::Encrypted(key) => {
1355 validate_encrypted_key_policy(key, policy)?;
1356 budget.consume(2)?;
1357 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
1358 self.unwrap(provider, algorithm.key_len(), key)?,
1359 )])
1360 }
1361 _ => Err(XmlEncError::KeyNotFound),
1362 }
1363 }
1364 fn resolve_content_keys_with_policy(
1365 &self,
1366 provider: &dyn crate::provider::CryptoProvider,
1367 algorithm: DataEncryptionAlgorithm,
1368 encrypted_key: Option<&EncryptedKey>,
1369 policy: &crate::policy::DecryptionPolicy,
1370 budget: &mut KeyCandidateBudget,
1371 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1372 policy.validate()?;
1373 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
1374 validate_encrypted_key_policy(encrypted_key, policy)?;
1375 budget.consume(1)?;
1376 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
1377 self.resolve_key(provider, algorithm, Some(encrypted_key))?,
1378 )])
1379 }
1380
1381 fn resolve_key_candidates_with_policy(
1382 &self,
1383 provider: &dyn crate::provider::CryptoProvider,
1384 algorithm: DataEncryptionAlgorithm,
1385 encrypted_key: Option<&EncryptedKey>,
1386 policy: &crate::policy::DecryptionPolicy,
1387 budget: &mut KeyCandidateBudget,
1388 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1389 self.resolve_content_keys_with_policy(provider, algorithm, encrypted_key, policy, budget)?
1390 .into_iter()
1391 .map(|key| key.into_key().map_err(XmlEncError::from))
1392 .collect()
1393 }
1394
1395 fn resolve_key(
1396 &self,
1397 provider: &dyn crate::provider::CryptoProvider,
1398 algorithm: DataEncryptionAlgorithm,
1399 encrypted_key: Option<&EncryptedKey>,
1400 ) -> Result<Vec<u8>, XmlEncError> {
1401 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
1402 let key = self.unwrap(provider, algorithm.key_len(), encrypted_key)?;
1403 validate_key_len(algorithm, &key)?;
1404 Ok(key)
1405 }
1406}
1407
1408impl KekDecryptor<'_> {
1409 fn unwrap(
1410 &self,
1411 provider: &dyn crate::provider::CryptoProvider,
1412 output_len: usize,
1413 encrypted_key: &EncryptedKey,
1414 ) -> Result<Vec<u8>, XmlEncError> {
1415 encrypted_key.encryption_method.validate_structure()?;
1416 let wrap_algorithm =
1417 KeyWrapAlgorithm::from_uri(&encrypted_key.encryption_method.algorithm)?;
1418 if self.kind != wrap_algorithm.key_kind() {
1422 return Err(XmlEncError::KeyNotFound);
1423 }
1424 let wrapped = encrypted_key.cipher_data.octets()?;
1425 let expected_kek_len = wrap_algorithm.key_len();
1426 if self.kek.len() != expected_kek_len {
1427 return Err(XmlEncError::InvalidKekSize {
1428 algorithm: wrap_algorithm,
1429 expected: expected_kek_len,
1430 actual: self.kek.len(),
1431 });
1432 }
1433 let expected_wrapped_len = wrap_algorithm
1434 .wrapped_len(output_len)
1435 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(wrap_algorithm.uri().into()))?;
1436 if wrapped.len() != expected_wrapped_len {
1437 return Err(XmlEncError::InvalidWrappedKeyLength {
1438 expected: expected_wrapped_len,
1439 actual: wrapped.len(),
1440 });
1441 }
1442 provider.require_capability(crate::provider::ProviderCapability::KeyUnwrap(
1443 wrap_algorithm,
1444 ))?;
1445 let key = provider
1446 .unwrap_key(wrap_algorithm, &self.kek, &wrapped)
1447 .map_err(|error| match error {
1448 crate::provider::ProviderError::InvalidKeySize { expected, actual } => {
1449 XmlEncError::InvalidKekSize {
1450 algorithm: wrap_algorithm,
1451 expected,
1452 actual,
1453 }
1454 }
1455 crate::provider::ProviderError::AuthenticationFailed
1456 | crate::provider::ProviderError::InvalidInput(
1457 crate::provider::ProviderInputError::AesKeyWrapFraming,
1458 ) => XmlEncError::KeyWrapIntegrity,
1459 error => XmlEncError::Provider(error),
1460 })?;
1461 if key.len() != output_len {
1462 return Err(XmlEncError::InvalidWrappedKeyLength {
1463 expected: output_len,
1464 actual: key.len(),
1465 });
1466 }
1467 Ok(key)
1468 }
1469}
1470
1471impl PrivateKeyDecryptor {
1472 pub fn new(key: RsaPrivateKey) -> Self {
1474 Self::provider_key(Arc::new(crate::provider::RustCryptoRsaPrivateKey::new(key)))
1475 }
1476
1477 pub fn provider_key(key: Arc<dyn crate::provider::KeyRecoveryKey>) -> Self {
1479 Self { key }
1480 }
1481}
1482
1483impl DecryptionKeyResolver for PrivateKeyDecryptor {
1484 fn resolve_key_encryption_keys_with_policy(
1485 &self,
1486 provider: &dyn crate::provider::CryptoProvider,
1487 algorithm: KeyWrapAlgorithm,
1488 source: KeyEncryptionKeySource<'_>,
1489 policy: &crate::policy::DecryptionPolicy,
1490 budget: &mut KeyCandidateBudget,
1491 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1492 let KeyEncryptionKeySource::Encrypted(key) = source else {
1493 return Err(XmlEncError::KeyNotFound);
1494 };
1495 validate_encrypted_key_policy(key, policy)?;
1496 let width = policy.rsa_keys.validate_public_metadata(
1497 "decryption",
1498 self.key.rsa_modulus_bits(),
1499 self.key.rsa_public_exponent(),
1500 )?;
1501 let ciphertext = key.cipher_data.octets()?;
1502 if width != self.key.ciphertext_len() || ciphertext.len() != width {
1503 return Err(XmlEncError::InvalidWrappedKeyLength {
1504 expected: width,
1505 actual: ciphertext.len(),
1506 });
1507 }
1508 let transport = KeyTransportAlgorithm::from_uri(&key.encryption_method.algorithm)?;
1509 budget.consume(2)?;
1510 #[cfg(feature = "legacy-algorithms")]
1511 if transport == KeyTransportAlgorithm::RsaPkcs1v15 {
1512 provider.require_capability(crate::provider::ProviderCapability::Pkcs1v15Recovery)?;
1513 return Ok(vec![provider.recover_pkcs1v15(
1514 self.key.as_ref(),
1515 &ciphertext,
1516 algorithm.key_len(),
1517 )?]);
1518 }
1519 let method = &key.encryption_method;
1520 let parameters = RsaOaepParameters {
1521 algorithm: transport,
1522 digest: parse_oaep_digest(method.oaep_digest.as_deref())?,
1523 mgf_digest: parse_oaep_mgf_digest(method.mgf_algorithm.as_deref())?,
1524 label: method.oaep_params.clone().unwrap_or_default(),
1525 };
1526 provider.require_capability(crate::provider::ProviderCapability::KeyRecovery(
1527 ¶meters,
1528 ))?;
1529 let key = zeroize::Zeroizing::new(provider.recover_key(
1530 self.key.as_ref(),
1531 ¶meters,
1532 &ciphertext,
1533 )?);
1534 if key.len() != algorithm.key_len() {
1535 return Err(XmlEncError::InvalidKekSize {
1536 algorithm,
1537 expected: algorithm.key_len(),
1538 actual: key.len(),
1539 });
1540 }
1541 let mut key = key;
1542 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
1543 core::mem::take(&mut *key),
1544 )])
1545 }
1546 fn resolve_content_keys_with_policy(
1547 &self,
1548 provider: &dyn crate::provider::CryptoProvider,
1549 algorithm: DataEncryptionAlgorithm,
1550 encrypted_key: Option<&EncryptedKey>,
1551 policy: &crate::policy::DecryptionPolicy,
1552 budget: &mut KeyCandidateBudget,
1553 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1554 policy.validate()?;
1555 budget.consume(1)?;
1556 self.resolve_key_with_policy(provider, algorithm, encrypted_key, policy)
1557 .map(|key| vec![key])
1558 }
1559 fn resolve_key_candidates_with_policy(
1560 &self,
1561 provider: &dyn crate::provider::CryptoProvider,
1562 algorithm: DataEncryptionAlgorithm,
1563 encrypted_key: Option<&EncryptedKey>,
1564 policy: &crate::policy::DecryptionPolicy,
1565 budget: &mut KeyCandidateBudget,
1566 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1567 policy.validate()?;
1568 budget.consume(1)?;
1569 self.resolve_key_with_policy(provider, algorithm, encrypted_key, policy)
1570 .and_then(|key| key.into_key().map_err(XmlEncError::Provider))
1571 .map(|key| vec![key])
1572 }
1573
1574 fn resolve_key(
1575 &self,
1576 provider: &dyn crate::provider::CryptoProvider,
1577 algorithm: DataEncryptionAlgorithm,
1578 encrypted_key: Option<&EncryptedKey>,
1579 ) -> Result<Vec<u8>, XmlEncError> {
1580 self.resolve_key_with_policy(
1581 provider,
1582 algorithm,
1583 encrypted_key,
1584 &crate::policy::DecryptionPolicy::default(),
1585 )
1586 .and_then(|key| key.into_key().map_err(XmlEncError::Provider))
1587 }
1588}
1589
1590impl PrivateKeyDecryptor {
1591 pub fn resolve_key_with_policy(
1594 &self,
1595 provider: &dyn crate::provider::CryptoProvider,
1596 algorithm: DataEncryptionAlgorithm,
1597 encrypted_key: Option<&EncryptedKey>,
1598 policy: &crate::policy::DecryptionPolicy,
1599 ) -> Result<crate::provider::RecoveredContentKey, XmlEncError> {
1600 policy.validate()?;
1601 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
1602 validate_encrypted_key_policy(encrypted_key, policy)?;
1603 let width = policy.rsa_keys.validate_public_metadata(
1604 "decryption",
1605 self.key.rsa_modulus_bits(),
1606 self.key.rsa_public_exponent(),
1607 )?;
1608 if width != self.key.ciphertext_len() {
1609 return Err(crate::policy::PolicyViolation::InvalidKeyMaterial {
1610 operation: "decryption",
1611 key_type: "RSA",
1612 reason: "ciphertext width disagrees with modulus",
1613 }
1614 .into());
1615 }
1616 encrypted_key.encryption_method.validate_structure()?;
1617 let wrapped = encrypted_key.cipher_data.octets()?;
1618 let label = encrypted_key
1619 .encryption_method
1620 .oaep_params
1621 .clone()
1622 .unwrap_or_default();
1623 let transport =
1624 KeyTransportAlgorithm::from_uri(&encrypted_key.encryption_method.algorithm)?;
1625 let key = match transport {
1626 #[cfg(feature = "legacy-algorithms")]
1627 KeyTransportAlgorithm::RsaPkcs1v15 => {
1628 if wrapped.len() != self.key.ciphertext_len() {
1629 return Err(XmlEncError::InvalidWrappedKeyLength {
1630 expected: self.key.ciphertext_len(),
1631 actual: wrapped.len(),
1632 });
1633 }
1634 provider
1635 .require_capability(crate::provider::ProviderCapability::Pkcs1v15Recovery)?;
1636 provider
1641 .recover_pkcs1v15(self.key.as_ref(), &wrapped, algorithm.key_len())
1642 .map_err(XmlEncError::Provider)
1643 }
1644 KeyTransportAlgorithm::RsaOaepMgf1p => self.decrypt_oaep_mgf1p(
1645 provider,
1646 encrypted_key.encryption_method.oaep_digest.as_deref(),
1647 encrypted_key.encryption_method.mgf_algorithm.as_deref(),
1648 label,
1649 &wrapped,
1650 algorithm,
1651 ),
1652 KeyTransportAlgorithm::RsaOaep11 => self.decrypt_oaep11(
1653 provider,
1654 encrypted_key.encryption_method.oaep_digest.as_deref(),
1655 encrypted_key.encryption_method.mgf_algorithm.as_deref(),
1656 label,
1657 &wrapped,
1658 algorithm,
1659 ),
1660 }?;
1661 validate_content_key_len(algorithm, key.key_len())?;
1662 Ok(key)
1663 }
1664}
1665
1666impl PrivateKeyDecryptor {
1667 fn decrypt_oaep_mgf1p(
1668 &self,
1669 provider: &dyn crate::provider::CryptoProvider,
1670 digest: Option<&str>,
1671 mgf: Option<&str>,
1672 label: Vec<u8>,
1673 wrapped: &[u8],
1674 algorithm: DataEncryptionAlgorithm,
1675 ) -> Result<crate::provider::RecoveredContentKey, XmlEncError> {
1676 let parameters = RsaOaepParameters {
1677 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p,
1678 digest: parse_oaep_digest(digest)?,
1679 mgf_digest: parse_oaep_mgf_digest(mgf)?,
1680 label,
1681 };
1682 recover_rsa_oaep(provider, self.key.as_ref(), ¶meters, wrapped, algorithm)
1683 }
1684
1685 fn decrypt_oaep11(
1686 &self,
1687 provider: &dyn crate::provider::CryptoProvider,
1688 digest: Option<&str>,
1689 mgf: Option<&str>,
1690 label: Vec<u8>,
1691 wrapped: &[u8],
1692 algorithm: DataEncryptionAlgorithm,
1693 ) -> Result<crate::provider::RecoveredContentKey, XmlEncError> {
1694 let parameters = RsaOaepParameters {
1695 algorithm: KeyTransportAlgorithm::RsaOaep11,
1696 digest: parse_oaep_digest(digest)?,
1697 mgf_digest: parse_oaep_mgf_digest(mgf)?,
1698 label,
1699 };
1700 recover_rsa_oaep(provider, self.key.as_ref(), ¶meters, wrapped, algorithm)
1701 }
1702}
1703
1704fn parse_oaep_digest(uri: Option<&str>) -> Result<OaepDigestAlgorithm, XmlEncError> {
1705 let uri = uri.unwrap_or("http://www.w3.org/2000/09/xmldsig#sha1");
1706 OaepDigestAlgorithm::from_uri(uri)
1707 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(uri.to_owned()))
1708}
1709
1710fn parse_oaep_mgf_digest(uri: Option<&str>) -> Result<OaepDigestAlgorithm, XmlEncError> {
1711 let uri = uri.unwrap_or("http://www.w3.org/2009/xmlenc11#mgf1sha1");
1712 OaepDigestAlgorithm::from_mgf_uri(uri)
1713 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(uri.to_owned()))
1714}
1715
1716fn recover_rsa_oaep(
1717 provider: &dyn crate::provider::CryptoProvider,
1718 key: &dyn crate::provider::KeyRecoveryKey,
1719 parameters: &RsaOaepParameters,
1720 wrapped: &[u8],
1721 algorithm: DataEncryptionAlgorithm,
1722) -> Result<crate::provider::RecoveredContentKey, XmlEncError> {
1723 let expected = key.ciphertext_len();
1724 if wrapped.len() != expected {
1725 return Err(XmlEncError::InvalidWrappedKeyLength {
1726 expected,
1727 actual: wrapped.len(),
1728 });
1729 }
1730 provider.require_capability(crate::provider::ProviderCapability::KeyRecovery(parameters))?;
1731 provider
1732 .recover_content_key(key, parameters, algorithm, wrapped)
1733 .map_err(|error| match error {
1734 crate::provider::ProviderError::Random(message) => XmlEncError::Rng(message),
1735 error @ (crate::provider::ProviderError::AuthenticationFailed
1736 | crate::provider::ProviderError::InvalidInput(_)) => {
1737 XmlEncError::Rsa(error.to_string())
1738 }
1739 error => XmlEncError::Provider(error),
1740 })
1741}
1742
1743pub fn decrypt(
1745 xml: &str,
1746 resolver: &dyn DecryptionKeyResolver,
1747) -> Result<DecryptedContent, XmlEncError> {
1748 DecryptContext::new(resolver).decrypt(xml)
1749}
1750
1751pub fn decrypt_document(
1758 xml: &str,
1759 encrypted_data_id: Option<&str>,
1760 resolver: &dyn DecryptionKeyResolver,
1761) -> Result<String, XmlEncError> {
1762 decrypt_document_with_options(
1763 xml,
1764 DocumentDecryptionOptions { encrypted_data_id },
1765 resolver,
1766 )
1767}
1768
1769pub fn decrypt_document_with_options(
1774 xml: &str,
1775 options: DocumentDecryptionOptions<'_>,
1776 resolver: &dyn DecryptionKeyResolver,
1777) -> Result<String, XmlEncError> {
1778 DecryptContext::new(resolver).decrypt_document(xml, options.encrypted_data_id)
1779}
1780
1781#[derive(Clone, Copy)]
1782enum DocumentEncryptedDataSelector<'a> {
1783 EncryptedDataId(Option<&'a str>),
1784 UniqueBelowStartNode(Option<&'a str>),
1785 FirstBelowStartNode(Option<&'a str>),
1786}
1787
1788fn decrypt_document_with_context(
1789 xml: &str,
1790 selector: DocumentEncryptedDataSelector<'_>,
1791 context: &DecryptContext<'_>,
1792) -> Result<String, XmlEncError> {
1793 context.policy.resources.validate()?;
1794 validate_encryption_document_len(xml.len(), &context.policy)?;
1795 let budgets = DecryptionOperationBudgets::from_policy(&context.policy);
1796 let settings = context.document_parse_settings();
1797 let mut document =
1798 XmlDocument::parse_with_settings_and_budget(xml.to_owned(), settings, &budgets.xml_parse)
1799 .map_err(|error| map_document_error(error, settings))?;
1800 decrypt_owned_document_with_context(&mut document, selector, context, budgets)?;
1801 Ok(document.into_xml())
1802}
1803
1804fn decrypt_owned_document_with_context(
1805 document: &mut XmlDocument,
1806 selector: DocumentEncryptedDataSelector<'_>,
1807 context: &DecryptContext<'_>,
1808 budgets: DecryptionOperationBudgets,
1809) -> Result<(), XmlEncError> {
1810 context.policy.resources.validate()?;
1811 document.validate_operation_policy(&context.policy.xml, &context.policy.resources)?;
1812 let (target, target_len, target_start, replacement_type) = document.with_view(|view| {
1813 let start = match selector {
1814 DocumentEncryptedDataSelector::UniqueBelowStartNode(Some(id))
1815 | DocumentEncryptedDataSelector::FirstBelowStartNode(Some(id)) => view
1816 .node_for_id(id, context.id_attributes)
1817 .ok_or_else(|| XmlEncError::SelectedNodeUnavailable { id: id.to_owned() })
1818 .and_then(|identity| view.resolve_node(identity).map_err(XmlEncError::from))?,
1819 DocumentEncryptedDataSelector::UniqueBelowStartNode(None)
1820 | DocumentEncryptedDataSelector::FirstBelowStartNode(None)
1821 | DocumentEncryptedDataSelector::EncryptedDataId(_) => view.document().root(),
1822 };
1823 let encrypted_data_id = match selector {
1824 DocumentEncryptedDataSelector::EncryptedDataId(id) => id,
1825 DocumentEncryptedDataSelector::UniqueBelowStartNode(_)
1826 | DocumentEncryptedDataSelector::FirstBelowStartNode(_) => None,
1827 };
1828 let mut matches = start.descendants().filter(|node| {
1829 node.has_tag_name((XMLENC_NS, "EncryptedData"))
1830 && encrypted_data_id.is_none_or(|id| node.attribute("Id") == Some(id))
1831 });
1832 let selected = matches.next().ok_or(XmlEncError::EncryptedDataNotFound)?;
1833 if matches!(
1834 selector,
1835 DocumentEncryptedDataSelector::EncryptedDataId(_)
1836 | DocumentEncryptedDataSelector::UniqueBelowStartNode(_)
1837 ) && matches.next().is_some()
1838 {
1839 return Err(XmlEncError::AmbiguousEncryptedData);
1840 }
1841 Ok::<_, XmlEncError>((
1842 view.node_identity(selected),
1843 selected.range().len(),
1844 selected.range().start,
1845 match selected.attribute("Type") {
1846 Some("http://www.w3.org/2001/04/xmlenc#Element") => {
1847 Some(EncryptedDataType::Element)
1848 }
1849 Some("http://www.w3.org/2001/04/xmlenc#Content") => {
1850 Some(EncryptedDataType::Content)
1851 }
1852 _ => None,
1853 },
1854 ))
1855 })?;
1856 let document_binding = Some((document.identity(), document.generation()));
1857 let mut processed = context.process_decryption_input(
1858 DecryptionInput::Xml {
1859 source: document.as_xml(),
1860 node_start: Some(target_start),
1861 },
1862 document_binding,
1863 true,
1864 budgets,
1865 |candidate, operation_parse_budget| {
1866 let DecryptedContent::Xml(plaintext) = candidate else {
1867 return Err(XmlEncError::ReplacementRequiresXml);
1868 };
1869 validate_encryption_document_len(
1870 document
1871 .as_xml()
1872 .len()
1873 .saturating_sub(target_len)
1874 .saturating_add(plaintext.len()),
1875 &context.policy,
1876 )?;
1877 let settings = context.document_parse_settings();
1878 match replacement_type.as_ref() {
1879 Some(EncryptedDataType::Element) => document
1880 .prepare_element_replacement_with_budget(
1881 target,
1882 &plaintext,
1883 settings,
1884 operation_parse_budget,
1885 )
1886 .map_err(|error| map_document_error(error, settings)),
1887 Some(EncryptedDataType::Content) => document
1888 .prepare_node_fragment_replacement_with_budget(
1889 target,
1890 &plaintext,
1891 settings,
1892 operation_parse_budget,
1893 )
1894 .map_err(|error| map_document_error(error, settings)),
1895 Some(EncryptedDataType::Other(_)) | None => {
1896 Err(XmlEncError::ReplacementRequiresXml)
1897 }
1898 }
1899 },
1900 )?;
1901 let mutation = processed.mutation.ok_or_else(|| {
1902 XmlEncError::OperationPlan("decryption mutation node is unavailable".into())
1903 })?;
1904 processed
1905 .operation
1906 .run_document_transition(mutation, document, |document, _| {
1907 document
1908 .commit_prepared(processed.output)
1909 .map_err(|error| map_document_error(error, context.document_parse_settings()))
1910 })
1911}
1912
1913fn validate_encryption_document_len(
1914 actual: usize,
1915 policy: &crate::policy::DecryptionPolicy,
1916) -> Result<(), XmlEncError> {
1917 policy.resources.validate_xml_document_len(actual)?;
1918 Ok(())
1919}
1920
1921fn validate_recipient_count(actual: usize, maximum: usize) -> Result<(), XmlEncError> {
1922 if actual > maximum {
1923 return Err(crate::policy::PolicyViolation::ResourceLimit {
1924 resource: crate::policy::resource_name::ENCRYPTION_RECIPIENTS,
1925 maximum,
1926 actual,
1927 }
1928 .into());
1929 }
1930 Ok(())
1931}
1932
1933pub fn decrypt_data(
1935 encrypted: &EncryptedData,
1936 resolver: &dyn DecryptionKeyResolver,
1937) -> Result<DecryptedContent, XmlEncError> {
1938 DecryptContext::new(resolver).decrypt_data(encrypted)
1939}
1940
1941fn resolve_content_key_candidates(
1942 provider: &dyn crate::provider::CryptoProvider,
1943 algorithm: DataEncryptionAlgorithm,
1944 encrypted: &EncryptedData,
1945 resolver: &dyn DecryptionKeyResolver,
1946 policy: &crate::policy::DecryptionPolicy,
1947 budget: &mut KeyCandidateBudget,
1948 mut document: Option<KeySourceDocument<'_, '_>>,
1949) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1950 let mut last_error = None;
1951 let mut candidates = if encrypted.derived_keys.is_empty()
1952 && encrypted.agreement_methods.is_empty()
1953 && encrypted.encapsulation_methods.is_empty()
1954 {
1955 match resolve_candidates_with_budget(resolver, provider, algorithm, None, policy, budget) {
1956 Ok(keys) => keys,
1957 Err(error) => {
1958 record_candidate_source_error_or_fail_operation(error, &mut last_error)?;
1959 Vec::new()
1960 }
1961 }
1962 } else {
1963 Vec::new()
1964 };
1965 for descriptor in &encrypted.encapsulation_methods {
1966 policy
1967 .key_establishment
1968 .check_encapsulation(descriptor.algorithm)?;
1969 let remaining_before = budget.remaining();
1970 budget.require_available(1)?;
1971 match resolver.resolve_encapsulation_content_keys_with_policy(
1972 provider, algorithm, descriptor, policy, budget,
1973 ) {
1974 Ok(keys) => {
1975 budget.account_returned_candidates(remaining_before, keys.len())?;
1976 candidates.extend(keys);
1977 }
1978 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
1979 }
1980 }
1981 for descriptor in &encrypted.agreement_methods {
1982 let remaining_before = budget.remaining();
1983 match resolver.resolve_agreement_content_keys_with_policy(
1984 provider, algorithm, descriptor, policy, budget,
1985 ) {
1986 Ok(keys) => {
1987 budget.account_returned_candidates(remaining_before, keys.len())?;
1988 candidates.extend(keys);
1989 }
1990 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
1991 }
1992 }
1993 for descriptor in &encrypted.derived_keys {
1994 if !reference_list_applies_to_target(
1995 descriptor.reference_list.as_ref(),
1996 ReferenceTarget::new(
1997 document,
1998 encrypted.id.as_deref(),
1999 policy.transforms.same_document_id_semantics,
2000 ),
2001 ReferenceKind::Data,
2002 ) {
2003 continue;
2004 }
2005 let remaining_before = budget.remaining();
2006 match resolver.resolve_derived_content_keys_with_policy(
2007 provider, algorithm, descriptor, policy, budget,
2008 ) {
2009 Ok(keys) => {
2010 budget.account_returned_candidates(remaining_before, keys.len())?;
2011 candidates.extend(keys);
2012 }
2013 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
2014 }
2015 }
2016 for encrypted_key in &encrypted.encrypted_keys {
2017 let source = KeySourceView {
2018 key: encrypted_key,
2019 document: take_key_document(&mut document, encrypted_key)?,
2020 };
2021 if !encrypted_key_applies_to_data(
2022 encrypted_key,
2023 encrypted,
2024 ReferenceTarget::new(
2025 document,
2026 encrypted.id.as_deref(),
2027 policy.transforms.same_document_id_semantics,
2028 ),
2029 ) {
2030 continue;
2031 }
2032 if let Err(error) = validate_encrypted_key_policy(encrypted_key, policy) {
2033 last_error = Some(error);
2034 continue;
2035 }
2036 let resolution = if encrypted_key.sources.is_empty() {
2037 resolve_candidates_with_budget(
2038 resolver,
2039 provider,
2040 algorithm,
2041 Some(encrypted_key),
2042 policy,
2043 budget,
2044 )
2045 } else {
2046 resolve_nested_key(
2047 provider,
2048 source,
2049 algorithm.key_len(),
2050 resolver,
2051 policy,
2052 budget,
2053 1,
2054 )
2055 };
2056 match resolution {
2057 Ok(keys) => candidates.extend(keys),
2058 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
2059 }
2060 }
2061 if candidates.is_empty() {
2062 Err(last_error.unwrap_or(XmlEncError::KeyNotFound))
2063 } else {
2064 Ok(candidates)
2065 }
2066}
2067
2068pub(super) fn resolve_nested_cipher_references<'a>(
2069 keys: &mut [EncryptedKey],
2070 document: &crate::XmlDomDocument<'_>,
2071 origins: &mut core::slice::Iter<'_, Option<crate::NodeId>>,
2072 context: &super::cipher_reference::BoundCipherReferenceContext<'a, 'a>,
2073 parse: &XmlParseWorkBudget,
2074) -> Result<(), XmlEncError> {
2075 for key in keys {
2078 let origin = origins
2079 .next()
2080 .ok_or_else(|| XmlEncError::OperationPlan("missing encrypted key origin".into()))?;
2081 if let super::CipherData::Reference { uri, transforms } = &key.cipher_data {
2082 let node = origin.and_then(|id| document.get_node(id)).ok_or_else(|| {
2083 XmlEncError::OperationPlan("unresolved foreign encrypted key origin".into())
2084 })?;
2085 key.cipher_data = super::CipherData::Bytes(context.resolve_parsed(
2086 cipher_reference_node(node)?,
2087 uri,
2088 transforms,
2089 parse,
2090 )?);
2091 }
2092 resolve_nested_cipher_references(
2093 &mut key.sources.encrypted_keys,
2094 document,
2095 origins,
2096 context,
2097 parse,
2098 )?;
2099 }
2100 Ok(())
2101}
2102
2103fn resolve_nested_key(
2104 provider: &dyn crate::provider::CryptoProvider,
2105 source: KeySourceView<'_, '_>,
2106 output_len: usize,
2107 resolver: &dyn DecryptionKeyResolver,
2108 policy: &crate::policy::DecryptionPolicy,
2109 budget: &mut KeyCandidateBudget,
2110 depth: usize,
2111) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
2112 let key = source.key;
2113 let mut document = source.document;
2114 let target = ReferenceTarget::new(
2115 document,
2116 key.id.as_deref(),
2117 policy.transforms.same_document_id_semantics,
2118 );
2119 policy.resources.validate_key_info_reference_depth(depth)?;
2120 validate_encrypted_key_policy(key, policy)?;
2121 let wrap = KeyWrapAlgorithm::from_uri(&key.encryption_method.algorithm)?;
2122 provider.require_capability(crate::provider::ProviderCapability::KeyUnwrap(wrap))?;
2123 let ciphertext = key.cipher_data.octets()?;
2124 let expected = wrap
2125 .wrapped_len(output_len)
2126 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(wrap.uri().into()))?;
2127 if ciphertext.len() != expected {
2128 return Err(XmlEncError::InvalidWrappedKeyLength {
2129 expected,
2130 actual: ciphertext.len(),
2131 });
2132 }
2133 let mut keys = Vec::new();
2134 let mut last_error = None;
2135 let mut resolve_source = |source| -> Result<(), XmlEncError> {
2136 budget.require_available(1)?;
2137 let remaining = budget.remaining();
2138 match resolver
2139 .resolve_key_encryption_keys_with_policy(provider, wrap, source, policy, budget)
2140 {
2141 Ok(resolved) => {
2142 budget.account_returned_candidates(remaining, resolved.len())?;
2143 keys.extend(resolved);
2144 Ok(())
2145 }
2146 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error),
2147 }
2148 };
2149 for mechanism in &key.sources.encapsulation_methods {
2150 resolve_source(KeyEncryptionKeySource::Encapsulation(mechanism))?;
2151 }
2152 for agreement in &key.sources.agreement_methods {
2153 resolve_source(KeyEncryptionKeySource::Agreement(agreement))?;
2154 }
2155 for derived in &key.sources.derived_keys {
2156 if !reference_list_applies_to_target(
2157 derived.reference_list.as_ref(),
2158 target,
2159 ReferenceKind::Key,
2160 ) {
2161 continue;
2162 }
2163 resolve_source(KeyEncryptionKeySource::Derived(derived))?;
2164 }
2165 for nested in &key.sources.encrypted_keys {
2166 let source = KeySourceView {
2167 key: nested,
2168 document: take_key_document(&mut document, nested)?,
2169 };
2170 if !reference_list_applies_to_target(
2171 nested.reference_list.as_ref(),
2172 target,
2173 ReferenceKind::Key,
2174 ) {
2175 continue;
2176 }
2177 let resolved = if nested.sources.is_empty() {
2178 budget.require_available(3)?;
2179 let remaining = budget.remaining();
2180 resolver
2181 .resolve_key_encryption_keys_with_policy(
2182 provider,
2183 wrap,
2184 KeyEncryptionKeySource::Encrypted(nested),
2185 policy,
2186 budget,
2187 )
2188 .and_then(|resolved| {
2189 budget.account_returned_candidates(remaining, resolved.len())?;
2190 Ok(resolved)
2191 })
2192 } else {
2193 resolve_nested_key(
2194 provider,
2195 source,
2196 wrap.key_len(),
2197 resolver,
2198 policy,
2199 budget,
2200 depth + 1,
2201 )
2202 };
2203 match resolved {
2204 Ok(resolved) => keys.extend(resolved),
2205 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
2206 }
2207 }
2208 let mut outputs = Vec::new();
2209 for kek in keys {
2213 if kek.key_len() != wrap.key_len() {
2214 last_error = Some(XmlEncError::InvalidKekSize {
2215 algorithm: wrap,
2216 expected: wrap.key_len(),
2217 actual: kek.key_len(),
2218 });
2219 continue;
2220 }
2221 budget.consume(1)?;
2222 match kek.unwrap_nested(provider, wrap, &ciphertext, output_len) {
2223 Ok(result) if result.key_len() == output_len => outputs.push(result),
2224 Ok(result) => {
2225 last_error = Some(XmlEncError::InvalidWrappedKeyLength {
2226 expected: output_len,
2227 actual: result.key_len(),
2228 })
2229 }
2230 Err(error) => last_error = Some(XmlEncError::Provider(error)),
2231 }
2232 }
2233 if outputs.is_empty() {
2234 Err(last_error.unwrap_or(XmlEncError::KeyNotFound))
2235 } else {
2236 Ok(outputs)
2237 }
2238}
2239
2240#[derive(Clone, Copy)]
2241struct KeySourceDocument<'a, 'doc> {
2242 references: &'a super::cipher_reference::BoundCipherReferenceContext<'doc, 'doc>,
2243 target: crate::NodeId,
2244 origins: &'a [Option<crate::NodeId>],
2245}
2246
2247struct KeySourceView<'a, 'doc> {
2248 key: &'a EncryptedKey,
2249 document: Option<KeySourceDocument<'a, 'doc>>,
2250}
2251
2252fn key_origin_count(key: &EncryptedKey) -> usize {
2253 1 + key
2256 .sources
2257 .encrypted_keys
2258 .iter()
2259 .map(key_origin_count)
2260 .sum::<usize>()
2261}
2262
2263fn take_key_document<'a, 'doc>(
2264 document: &mut Option<KeySourceDocument<'a, 'doc>>,
2265 key: &EncryptedKey,
2266) -> Result<Option<KeySourceDocument<'a, 'doc>>, XmlEncError> {
2267 let Some(parent) = document else {
2268 return Ok(None);
2269 };
2270 let count = key_origin_count(key);
2271 if count > parent.origins.len() {
2272 return Err(XmlEncError::OperationPlan(
2273 "missing key association origin".into(),
2274 ));
2275 }
2276 let (subtree, remaining) = parent.origins.split_at(count);
2277 parent.origins = remaining;
2278 Ok(subtree[0].map(|target| KeySourceDocument {
2279 references: parent.references,
2280 target,
2281 origins: &subtree[1..],
2282 }))
2283}
2284
2285#[derive(Clone, Copy)]
2286enum ReferenceTarget<'a, 'doc> {
2287 Document {
2288 references: &'a super::cipher_reference::BoundCipherReferenceContext<'doc, 'doc>,
2289 node: crate::NodeId,
2290 },
2291 TypedId {
2292 id: Option<&'a str>,
2293 semantics: crate::policy::SameDocumentIdSemantics,
2294 },
2295}
2296
2297impl<'a, 'doc> ReferenceTarget<'a, 'doc> {
2298 fn new(
2299 document: Option<KeySourceDocument<'a, 'doc>>,
2300 id: Option<&'a str>,
2301 semantics: crate::policy::SameDocumentIdSemantics,
2302 ) -> Self {
2303 match document {
2304 Some(source) => Self::Document {
2305 references: source.references,
2306 node: source.target,
2307 },
2308 None => Self::TypedId { id, semantics },
2309 }
2310 }
2311
2312 fn matches(self, uri: &str) -> bool {
2313 match self {
2319 Self::Document { references, node } => references
2320 .resolver()
2321 .same_document_reference_targets(uri, node),
2322 Self::TypedId { id, semantics } => {
2323 id.is_some_and(|id| reference_targets_id(uri, id, semantics))
2324 }
2325 }
2326 }
2327}
2328
2329enum ReferenceKind {
2330 Data,
2331 Key,
2332}
2333
2334fn record_candidate_source_error_or_fail_operation(
2335 error: XmlEncError,
2336 last_error: &mut Option<XmlEncError>,
2337) -> Result<(), XmlEncError> {
2338 if matches!(
2342 &error,
2343 XmlEncError::Policy(_)
2344 | XmlEncError::KeyEstablishment(
2345 crate::key_establishment::KeyEstablishmentError::Policy(_)
2346 )
2347 ) {
2348 return Err(error);
2349 }
2350 *last_error = Some(error);
2351 Ok(())
2352}
2353
2354fn resolve_candidates_with_budget(
2355 resolver: &dyn DecryptionKeyResolver,
2356 provider: &dyn crate::provider::CryptoProvider,
2357 algorithm: DataEncryptionAlgorithm,
2358 encrypted_key: Option<&EncryptedKey>,
2359 policy: &crate::policy::DecryptionPolicy,
2360 budget: &mut KeyCandidateBudget,
2361) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
2362 let remaining_before = budget.remaining();
2363 let keys = resolver.resolve_content_keys_with_policy(
2364 provider,
2365 algorithm,
2366 encrypted_key,
2367 policy,
2368 budget,
2369 )?;
2370 budget.account_returned_candidates(remaining_before, keys.len())?;
2371 Ok(keys)
2372}
2373
2374fn encrypted_key_applies_to_data(
2375 encrypted_key: &EncryptedKey,
2376 encrypted_data: &EncryptedData,
2377 target: ReferenceTarget<'_, '_>,
2378) -> bool {
2379 if !reference_list_applies_to_target(
2383 encrypted_key.reference_list.as_ref(),
2384 target,
2385 ReferenceKind::Data,
2386 ) {
2387 return false;
2388 }
2389 if let (Some(carried), Some(expected)) = (
2390 encrypted_key.carried_key_name.as_deref(),
2391 encrypted_data.key_name.as_deref(),
2392 ) && carried != expected
2393 {
2394 return false;
2395 }
2396 true
2397}
2398
2399pub(super) struct EncapsulationRecipient {
2400 key_path: [usize; crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING],
2401 depth: usize,
2402 method: usize,
2403}
2404
2405impl EncapsulationRecipient {
2406 pub(super) fn mechanism<'a>(
2407 &self,
2408 data: &'a EncryptedData,
2409 ) -> &'a crate::key_establishment::EncapsulationMechanism {
2410 let mut methods = &data.encapsulation_methods;
2411 let mut keys = &data.encrypted_keys;
2412 for index in &self.key_path[..self.depth] {
2413 let sources = &keys[*index].sources;
2414 methods = &sources.encapsulation_methods;
2415 keys = &sources.encrypted_keys;
2416 }
2417 &methods[self.method]
2418 }
2419}
2420
2421pub(super) fn select_encapsulation_recipient<'doc>(
2422 data: &EncryptedData,
2423 references: &super::cipher_reference::BoundCipherReferenceContext<'doc, 'doc>,
2424 target: crate::NodeId,
2425 origins: &[Option<crate::NodeId>],
2426 policy: &crate::policy::DecryptionPolicy,
2427) -> Result<Option<EncapsulationRecipient>, XmlEncError> {
2428 struct Selection<'a> {
2429 data: &'a EncryptedData,
2430 semantics: crate::policy::SameDocumentIdSemantics,
2431 path: [usize; crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING],
2432 depth: usize,
2433 selected: Option<EncapsulationRecipient>,
2434 }
2435 fn visit(
2436 methods: &[crate::key_establishment::EncapsulationMechanism],
2437 keys: &[EncryptedKey],
2438 mut document: Option<KeySourceDocument<'_, '_>>,
2439 parent: Option<&EncryptedKey>,
2440 selection: &mut Selection<'_>,
2441 ) -> Result<(), XmlEncError> {
2442 for method in 0..methods.len() {
2443 if selection.selected.is_some() {
2444 return Err(XmlEncError::InvalidStructure(
2445 "multiple KEM recipients require explicit application key resolution".into(),
2446 ));
2447 }
2448 selection.selected = Some(EncapsulationRecipient {
2449 key_path: selection.path,
2450 depth: selection.depth,
2451 method,
2452 });
2453 }
2454 for (index, key) in keys.iter().enumerate() {
2455 let source = take_key_document(&mut document, key)?;
2456 let target = ReferenceTarget::new(
2457 document,
2458 match parent {
2459 Some(parent) => parent.id.as_deref(),
2460 None => selection.data.id.as_deref(),
2461 },
2462 selection.semantics,
2463 );
2464 let applies = match parent {
2468 None => encrypted_key_applies_to_data(key, selection.data, target),
2469 Some(_) => reference_list_applies_to_target(
2470 key.reference_list.as_ref(),
2471 target,
2472 ReferenceKind::Key,
2473 ),
2474 };
2475 if !applies {
2476 continue;
2477 }
2478 debug_assert!(selection.depth < selection.path.len());
2481 selection.path[selection.depth] = index;
2482 selection.depth += 1;
2483 visit(
2484 &key.sources.encapsulation_methods,
2485 &key.sources.encrypted_keys,
2486 source,
2487 Some(key),
2488 selection,
2489 )?;
2490 selection.depth -= 1;
2491 }
2492 Ok(())
2493 }
2494 let mut selection = Selection {
2495 data,
2496 semantics: policy.transforms.same_document_id_semantics,
2497 path: [0; crate::hard_limits::KEY_INFO_REFERENCE_DEPTH_CEILING],
2498 depth: 0,
2499 selected: None,
2500 };
2501 visit(
2502 &data.encapsulation_methods,
2503 &data.encrypted_keys,
2504 Some(KeySourceDocument {
2505 references,
2506 target,
2507 origins,
2508 }),
2509 None,
2510 &mut selection,
2511 )?;
2512 Ok(selection.selected)
2513}
2514
2515fn reference_list_applies_to_target(
2516 references: Option<&super::ReferenceList>,
2517 target: ReferenceTarget<'_, '_>,
2518 kind: ReferenceKind,
2519) -> bool {
2520 let Some(references) = references else {
2521 return true;
2522 };
2523 let uris = match kind {
2524 ReferenceKind::Data => &references.data_references,
2525 ReferenceKind::Key => &references.key_references,
2526 };
2527 if uris.is_empty() {
2528 return true;
2529 }
2530 for uri in uris {
2531 if target.matches(uri) {
2532 return true;
2533 }
2534 }
2535 false
2536}
2537
2538fn reference_targets_id(
2539 uri: &str,
2540 id: &str,
2541 semantics: crate::policy::SameDocumentIdSemantics,
2542) -> bool {
2543 let Some(fragment) = uri.strip_prefix('#') else {
2544 return false;
2545 };
2546 let Ok(fragment) =
2550 crate::xmldsig::uri::UriReferenceResolver::normalize_fragment(fragment, None, semantics)
2551 else {
2552 return false;
2553 };
2554 crate::xmldsig::uri::UriReferenceResolver::id_fragment(&fragment, semantics)
2555 .is_some_and(|(target, _)| target == id)
2556}
2557
2558fn compatible_decryption_key_candidates(
2559 algorithm: DataEncryptionAlgorithm,
2560 mut keys: Vec<crate::provider::RecoveredContentKey>,
2561) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
2562 let mut accepted = 0;
2563 let mut last_error = None;
2564 for index in 0..keys.len() {
2565 let key = &keys[index];
2566 match validate_content_key_len(algorithm, key.key_len()) {
2567 Ok(())
2568 if !keys[..accepted]
2569 .iter()
2570 .any(|existing| existing.same_candidate(key)) =>
2571 {
2572 keys.swap(accepted, index);
2576 accepted += 1;
2577 }
2578 Ok(()) => {}
2579 Err(error) => last_error = Some(error),
2580 }
2581 }
2582 keys.truncate(accepted);
2583 if keys.is_empty() {
2584 return Err(last_error.unwrap_or(XmlEncError::KeyNotFound));
2585 }
2586 Ok(keys)
2587}
2588
2589fn validate_decryption_key_candidates(
2590 algorithm: DataEncryptionAlgorithm,
2591 actual: usize,
2592) -> Result<(), XmlEncError> {
2593 let maximum = crate::hard_limits::KEY_CANDIDATE_CEILING;
2594 if actual > maximum {
2595 return Err(crate::policy::PolicyViolation::ResourceLimit {
2596 resource: crate::policy::resource_name::KEY_CANDIDATES,
2597 maximum,
2598 actual,
2599 }
2600 .into());
2601 }
2602 if actual > 1 && !algorithm.is_authenticated() {
2603 return Err(XmlEncError::AmbiguousKeyCandidates { algorithm, actual });
2604 }
2605 Ok(())
2606}
2607
2608fn validate_encrypted_key_policy(
2609 encrypted_key: &EncryptedKey,
2610 policy: &crate::policy::DecryptionPolicy,
2611) -> Result<(), XmlEncError> {
2612 validate_key_encryption_method_policy(&encrypted_key.encryption_method, policy)
2613}
2614
2615pub(super) fn validate_key_encryption_method_policy(
2616 method: &super::EncryptionMethod,
2617 policy: &crate::policy::DecryptionPolicy,
2618) -> Result<(), XmlEncError> {
2619 method.validate_structure()?;
2620 let uri = &method.algorithm;
2621 if let Ok(transport) = KeyTransportAlgorithm::from_uri(uri) {
2622 if policy
2623 .key_transport_algorithms
2624 .as_ref()
2625 .map_or(transport.requires_explicit_permission(), |allowed| {
2626 !allowed.contains(&transport)
2627 })
2628 {
2629 return Err(crate::policy::PolicyViolation::Algorithm {
2630 operation: "decryption",
2631 algorithm: uri.clone(),
2632 }
2633 .into());
2634 }
2635 #[cfg(feature = "legacy-algorithms")]
2636 if transport == KeyTransportAlgorithm::RsaPkcs1v15 {
2637 return Ok(());
2638 }
2639 let digest = parse_oaep_digest(method.oaep_digest.as_deref())?;
2640 let mgf_digest = parse_oaep_mgf_digest(method.mgf_algorithm.as_deref())?;
2641 let selected_algorithms = [
2642 (
2643 digest,
2644 method.oaep_digest.as_deref().unwrap_or(digest.uri()),
2645 ),
2646 (
2647 mgf_digest,
2648 method
2649 .mgf_algorithm
2650 .as_deref()
2651 .unwrap_or(mgf_digest.mgf_uri().ok_or_else(|| {
2652 XmlEncError::InvalidStructure(
2653 "OAEP mask digest has no XML Encryption identifier".into(),
2654 )
2655 })?),
2656 ),
2657 ];
2658 for (selected, wire_uri) in selected_algorithms {
2659 if selected.requires_explicit_permission()
2660 && !policy
2661 .oaep_digests
2662 .as_ref()
2663 .is_some_and(|allowed| allowed.contains(&selected))
2664 {
2665 return Err(crate::policy::PolicyViolation::Algorithm {
2666 operation: "decryption",
2667 algorithm: wire_uri.to_owned(),
2668 }
2669 .into());
2670 }
2671 if policy
2672 .oaep_digests
2673 .as_ref()
2674 .is_some_and(|allowed| !allowed.contains(&selected))
2675 {
2676 return Err(crate::policy::PolicyViolation::Algorithm {
2677 operation: "decryption",
2678 algorithm: wire_uri.to_owned(),
2679 }
2680 .into());
2681 }
2682 }
2683 } else {
2684 let wrap = KeyWrapAlgorithm::from_uri(uri)?;
2685 if policy
2686 .key_wrap_algorithms
2687 .as_ref()
2688 .map_or(wrap.requires_explicit_permission(), |allowed| {
2689 !allowed.contains(&wrap)
2690 })
2691 {
2692 return Err(crate::policy::PolicyViolation::Algorithm {
2693 operation: "decryption",
2694 algorithm: uri.clone(),
2695 }
2696 .into());
2697 }
2698 }
2699 Ok(())
2700}
2701
2702fn validate_content_framing_before_resolution(
2703 algorithm: DataEncryptionAlgorithm,
2704 ciphertext_len: usize,
2705 encrypted_keys: &[EncryptedKey],
2706 policy: &crate::policy::DecryptionPolicy,
2707) -> Result<(), XmlEncError> {
2708 let Err(framing_error) = validate_ciphertext_framing(algorithm, ciphertext_len) else {
2709 return Ok(());
2710 };
2711
2712 if !encrypted_keys.is_empty() {
2717 let mut last_key_error = None;
2718 for encrypted_key in encrypted_keys {
2719 match validate_encrypted_key_policy(encrypted_key, policy) {
2720 Ok(()) => return Err(framing_error),
2721 Err(error) => last_key_error = Some(error),
2722 }
2723 }
2724 if let Some(error) = last_key_error {
2725 return Err(error);
2726 }
2727 }
2728 Err(framing_error)
2729}
2730
2731fn validate_typed_cipher_values(
2732 encrypted: &EncryptedData,
2733 algorithm: DataEncryptionAlgorithm,
2734 maximum_plaintext: usize,
2735 maximum_cipher_values: usize,
2736) -> Result<(), XmlEncError> {
2737 let maximum_ciphertext = algorithm
2738 .ciphertext_len_for_plaintext(maximum_plaintext)
2739 .ok_or(XmlEncError::InvalidEncryptionConfig(
2740 "ciphertext size overflow".into(),
2741 ))?;
2742 let projected = validate_cipher_data_len(&encrypted.cipher_data, maximum_ciphertext)?;
2743 if projected > maximum_ciphertext {
2744 return Err(crate::policy::PolicyViolation::ResourceLimit {
2745 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
2746 maximum: maximum_plaintext,
2747 actual: projected.saturating_sub(algorithm.minimum_ciphertext_len()),
2748 }
2749 .into());
2750 }
2751
2752 let mut aggregate_encoded = cipher_data_storage_len(&encrypted.cipher_data);
2753 if aggregate_encoded > maximum_cipher_values {
2754 return Err(crate::policy::PolicyViolation::ResourceLimit {
2755 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
2756 maximum: maximum_cipher_values,
2757 actual: aggregate_encoded,
2758 }
2759 .into());
2760 }
2761
2762 let maximum_wrapped_key = projected_decoded_len_for_encoded_len(MAX_CIPHER_VALUE_BASE64_LEN);
2763 validate_encapsulation_cipher_values(
2764 &encrypted.encapsulation_methods,
2765 maximum_cipher_values,
2766 &mut aggregate_encoded,
2767 )?;
2768 validate_wrapped_cipher_values(
2769 &encrypted.encrypted_keys,
2770 maximum_wrapped_key,
2771 maximum_cipher_values,
2772 &mut aggregate_encoded,
2773 )
2774}
2775
2776fn validate_wrapped_cipher_values(
2777 keys: &[EncryptedKey],
2778 maximum_wrapped_key: usize,
2779 maximum_cipher_values: usize,
2780 aggregate_encoded: &mut usize,
2781) -> Result<(), XmlEncError> {
2782 for encrypted_key in keys {
2783 validate_cipher_data_len(&encrypted_key.cipher_data, maximum_wrapped_key)?;
2784 *aggregate_encoded =
2785 aggregate_encoded.saturating_add(cipher_data_storage_len(&encrypted_key.cipher_data));
2786 if *aggregate_encoded > maximum_cipher_values {
2787 return Err(crate::policy::PolicyViolation::ResourceLimit {
2788 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
2789 maximum: maximum_cipher_values,
2790 actual: *aggregate_encoded,
2791 }
2792 .into());
2793 }
2794 validate_encapsulation_cipher_values(
2795 &encrypted_key.sources.encapsulation_methods,
2796 maximum_cipher_values,
2797 aggregate_encoded,
2798 )?;
2799 validate_wrapped_cipher_values(
2800 &encrypted_key.sources.encrypted_keys,
2801 maximum_wrapped_key,
2802 maximum_cipher_values,
2803 aggregate_encoded,
2804 )?;
2805 }
2806 Ok(())
2807}
2808
2809fn validate_encapsulation_cipher_values(
2810 methods: &[crate::key_establishment::EncapsulationMechanism],
2811 maximum: usize,
2812 aggregate: &mut usize,
2813) -> Result<(), XmlEncError> {
2814 for method in methods {
2815 let encoded = method.ciphertext.len().div_ceil(3).checked_mul(4);
2818 let total = encoded.and_then(|encoded| aggregate.checked_add(encoded));
2819 let total = total.ok_or(crate::policy::PolicyViolation::ResourceLimitExceeded {
2820 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
2821 maximum,
2822 })?;
2823 if total > maximum {
2824 return Err(crate::policy::PolicyViolation::ResourceLimit {
2825 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
2826 maximum,
2827 actual: total,
2828 }
2829 .into());
2830 }
2831 *aggregate = total;
2832 }
2833 Ok(())
2834}
2835
2836fn cipher_data_storage_len(cipher: &super::CipherData) -> usize {
2837 match cipher {
2838 super::CipherData::Value { value } => value.len(),
2839 super::CipherData::Bytes(bytes) => bytes.len(),
2840 super::CipherData::Reference { .. } => 0,
2841 }
2842}
2843
2844fn validate_cipher_data_len(
2845 cipher: &super::CipherData,
2846 maximum: usize,
2847) -> Result<usize, XmlEncError> {
2848 match cipher {
2849 super::CipherData::Value { value } => validate_cipher_value_len(value, maximum),
2850 super::CipherData::Bytes(bytes) if bytes.len() <= maximum => Ok(bytes.len()),
2851 super::CipherData::Bytes(bytes) => Err(crate::policy::PolicyViolation::ResourceLimit {
2852 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
2853 maximum,
2854 actual: bytes.len(),
2855 }
2856 .into()),
2857 super::CipherData::Reference { .. } => Ok(0),
2858 }
2859}
2860
2861fn validate_cipher_value_len(value: &str, maximum_decoded: usize) -> Result<usize, XmlEncError> {
2862 if value.len() > MAX_CIPHER_VALUE_BASE64_LEN {
2863 return Err(XmlEncError::InvalidStructure(format!(
2864 "CipherValue exceeds {MAX_CIPHER_VALUE_BASE64_LEN}-byte limit"
2865 )));
2866 }
2867 Ok(projected_decoded_len(value).min(maximum_decoded.saturating_add(1)))
2868}
2869
2870fn projected_decoded_len(value: &str) -> usize {
2871 let padding = value
2872 .as_bytes()
2873 .iter()
2874 .rev()
2875 .take(2)
2876 .take_while(|byte| **byte == b'=')
2877 .count();
2878 projected_decoded_len_for_encoded_len(value.len()).saturating_sub(padding)
2879}
2880
2881fn projected_decoded_len_for_encoded_len(encoded_len: usize) -> usize {
2882 encoded_len
2883 .checked_add(3)
2884 .map(|length| length / 4)
2885 .and_then(|quanta| quanta.checked_mul(3))
2886 .unwrap_or(usize::MAX)
2887}
2888
2889pub(crate) fn validate_key_len(
2890 algorithm: DataEncryptionAlgorithm,
2891 key: &[u8],
2892) -> Result<(), XmlEncError> {
2893 validate_content_key_len(algorithm, key.len())
2894}
2895
2896fn validate_content_key_len(
2897 algorithm: DataEncryptionAlgorithm,
2898 actual: usize,
2899) -> Result<(), XmlEncError> {
2900 if actual == algorithm.key_len() {
2901 Ok(())
2902 } else {
2903 Err(XmlEncError::InvalidKeySize {
2904 algorithm,
2905 expected: algorithm.key_len(),
2906 actual,
2907 })
2908 }
2909}
2910
2911fn validate_possible_plaintext_len(
2912 algorithm: DataEncryptionAlgorithm,
2913 ciphertext_len: usize,
2914 maximum: usize,
2915) -> Result<(), XmlEncError> {
2916 let framing = algorithm.minimum_ciphertext_len();
2919 validate_plaintext_len(ciphertext_len.saturating_sub(framing), maximum)
2920}
2921
2922fn validate_provider_plaintext_len(
2923 algorithm: DataEncryptionAlgorithm,
2924 ciphertext_len: usize,
2925 plaintext_len: usize,
2926) -> Result<(), XmlEncError> {
2927 use crate::provider::{ProviderError, ProviderOperation};
2928
2929 match algorithm.cbc_block_len() {
2930 None => {
2931 let expected = ciphertext_len - algorithm.minimum_ciphertext_len();
2932 if plaintext_len != expected {
2933 return Err(ProviderError::InvalidOutputSize {
2934 operation: ProviderOperation::Decrypt,
2935 expected,
2936 actual: plaintext_len,
2937 }
2938 .into());
2939 }
2940 }
2941 Some(block) => {
2942 let padded_len = ciphertext_len - block;
2943 let minimum = padded_len - block;
2944 let maximum = padded_len - 1;
2945 if !(minimum..=maximum).contains(&plaintext_len) {
2946 return Err(ProviderError::InvalidOutputSizeRange {
2947 operation: ProviderOperation::Decrypt,
2948 minimum,
2949 maximum,
2950 actual: plaintext_len,
2951 }
2952 .into());
2953 }
2954 }
2955 }
2956 Ok(())
2957}
2958
2959fn validate_plaintext_len(actual: usize, maximum: usize) -> Result<(), XmlEncError> {
2960 if actual <= maximum {
2961 Ok(())
2962 } else {
2963 Err(crate::policy::PolicyViolation::ResourceLimit {
2964 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
2965 maximum,
2966 actual,
2967 }
2968 .into())
2969 }
2970}
2971
2972fn map_data_decryption_error(
2973 algorithm: DataEncryptionAlgorithm,
2974 ciphertext_len: usize,
2975 error: crate::provider::ProviderError,
2976) -> XmlEncError {
2977 use crate::provider::ProviderError;
2978
2979 match error {
2980 ProviderError::AuthenticationFailed if algorithm.is_authenticated() => {
2981 XmlEncError::AeadAuthenticationFailed
2982 }
2983 ProviderError::InvalidInput(crate::provider::ProviderInputError::AesGcmFraming)
2984 if algorithm.cbc_block_len().is_none() =>
2985 {
2986 XmlEncError::DataTooShort {
2987 algorithm: "AES-GCM",
2988 minimum: 28,
2989 actual: ciphertext_len,
2990 }
2991 }
2992 error @ ProviderError::InvalidInput(crate::provider::ProviderInputError::AesCbcFraming)
2993 if algorithm.cbc_block_len().is_some() =>
2994 {
2995 match super::types::validate_ciphertext_framing(algorithm, ciphertext_len) {
2996 Err(error) => error,
2997 Ok(()) => XmlEncError::Provider(error),
2998 }
2999 }
3000 ProviderError::InvalidInput(crate::provider::ProviderInputError::AesCbcCiphertext)
3001 if algorithm.cbc_block_len().is_some() =>
3002 {
3003 XmlEncError::InvalidPadding
3004 }
3005 error => XmlEncError::Provider(error),
3006 }
3007}
3008
3009#[cfg(test)]
3010mod tests {
3011 #[test]
3012 fn owned_secret_wipe_reuses_storage_and_preserves_independent_clones() {
3013 use zeroize::Zeroize;
3015 let bytes = vec![0x63; 32];
3016 let pointer = bytes.as_ptr();
3017 let capacity = bytes.capacity();
3018 let mut direct = super::SymmetricKeyDecryptor::new(bytes);
3019 assert_eq!(direct.key.as_ptr(), pointer);
3020 assert_eq!(direct.key.capacity(), capacity);
3021 let clone = direct.clone();
3022 direct.zeroize();
3023 assert!(direct.key.is_empty());
3024 assert_eq!(clone.key, [0x63; 32]);
3025
3026 let bytes = vec![0x74; 32];
3027 let pointer = bytes.as_ptr();
3028 let mut owned = super::KekDecryptor::new(bytes);
3029 assert_eq!(owned.kek.as_ptr(), pointer);
3030 let clone = owned.clone();
3031 owned.zeroize();
3032 assert!(matches!(&owned.kek, std::borrow::Cow::Owned(bytes) if bytes.is_empty()));
3033 assert_eq!(clone.kek.as_ref(), &[0x74; 32]);
3034
3035 let bytes = [0x85; 32];
3036 let mut borrowed = super::KekDecryptor::borrowed(&bytes);
3037 borrowed.zeroize();
3038 assert!(matches!(&borrowed.kek, std::borrow::Cow::Borrowed(_)));
3039 assert_eq!(borrowed.kek.as_ptr(), bytes.as_ptr());
3040 assert_eq!(borrowed.kek.as_ref(), &bytes);
3041 }
3042
3043 #[test]
3044 fn raw_chacha_cannot_select_keys_or_confirm_kem_decapsulation() {
3045 let algorithm = super::DataEncryptionAlgorithm::ChaCha20;
3047 assert!(matches!(
3048 super::validate_decryption_key_candidates(algorithm, 2),
3049 Err(super::XmlEncError::AmbiguousKeyCandidates { actual: 2, .. })
3050 ));
3051 assert!(
3052 crate::policy::KeyEstablishmentPolicy::default()
3053 .check_kem_content(algorithm)
3054 .is_err()
3055 );
3056 let externally_authenticated = crate::policy::KeyEstablishmentPolicy {
3057 kem_content_authentication:
3058 crate::policy::KemContentAuthentication::ExternalAuthenticated,
3059 ..Default::default()
3060 };
3061 assert!(
3062 externally_authenticated
3063 .check_kem_content(algorithm)
3064 .is_ok()
3065 );
3066 for authenticated in [
3067 super::DataEncryptionAlgorithm::Aes128Gcm,
3068 super::DataEncryptionAlgorithm::ChaCha20Poly1305,
3069 ] {
3070 assert!(super::validate_decryption_key_candidates(authenticated, 2).is_ok());
3071 assert!(
3072 crate::policy::KeyEstablishmentPolicy::default()
3073 .check_kem_content(authenticated)
3074 .is_ok()
3075 );
3076 }
3077 }
3078
3079 #[test]
3080 fn raw_chacha_public_decryption_rejects_distinct_candidate_keys() {
3081 let algorithm = DataEncryptionAlgorithm::ChaCha20;
3084 let encrypted = super::super::EncryptedDataBuilder::new(algorithm)
3085 .policy(crate::policy::EncryptionPolicy {
3086 data_algorithms: Some([algorithm].into()),
3087 ..Default::default()
3088 })
3089 .chacha_parameters(super::super::ChaChaParameters {
3090 nonce: Some([1; 12]),
3091 counter: Some(1_u32.to_le_bytes()),
3092 aad: None,
3093 })
3094 .direct_key([7; 32])
3095 .encrypt_binary(b"payload")
3096 .expect("explicitly permitted raw ChaCha encryption");
3097 let resolver = CandidateResolver {
3098 keys: vec![vec![1; 32], vec![7; 32]],
3099 };
3100 assert!(matches!(
3101 DecryptContext::new(&resolver)
3102 .policy(crate::policy::DecryptionPolicy {
3103 data_algorithms: Some([algorithm].into()),
3104 ..Default::default()
3105 })
3106 .decrypt(&encrypted.encrypted_data_xml),
3107 Err(XmlEncError::AmbiguousKeyCandidates { actual: 2, .. })
3108 ));
3109 }
3110
3111 #[test]
3112 fn typed_reference_association_obeys_id_semantics() {
3113 use crate::policy::SameDocumentIdSemantics as Semantics;
3116 for semantics in [
3117 Semantics::Specification,
3118 Semantics::XmlSecBarename,
3119 Semantics::XmlSecVisa3d,
3120 ] {
3121 assert!(!super::reference_targets_id("#", "", semantics));
3122 }
3123 assert!(super::reference_targets_id(
3124 "#tar%67et",
3125 "target",
3126 Semantics::Specification
3127 ));
3128 assert!(super::reference_targets_id(
3129 "#xpointer(id('target'))",
3130 "target",
3131 Semantics::Specification
3132 ));
3133 assert!(!super::reference_targets_id(
3134 "#123",
3135 "123",
3136 Semantics::Specification
3137 ));
3138 assert!(super::reference_targets_id(
3139 "#123",
3140 "123",
3141 Semantics::XmlSecVisa3d
3142 ));
3143 assert!(!super::reference_targets_id(
3144 "#xpointer(id('a' or 'b'))",
3145 "a' or 'b",
3146 Semantics::Specification
3147 ));
3148 }
3149 #[cfg(feature = "legacy-algorithms")]
3150 #[test]
3151 fn legacy_cbc_rejects_ambiguous_candidates_before_decryption() {
3152 for algorithm in [
3155 super::DataEncryptionAlgorithm::TripleDesCbc,
3156 super::DataEncryptionAlgorithm::Aes192Cbc,
3157 ] {
3158 assert!(matches!(
3159 super::validate_decryption_key_candidates(algorithm, 2),
3160 Err(super::XmlEncError::AmbiguousKeyCandidates { actual: 2, .. })
3161 ));
3162 }
3163 }
3164 use std::cell::{Cell, RefCell};
3165 use std::sync::atomic::{AtomicUsize, Ordering};
3166
3167 use crate::rsa_encoding::RsaPrivateKeyEncoding as _;
3168 use aes_gcm::{
3169 Aes128Gcm,
3170 aead::{AeadInOut, KeyInit},
3171 };
3172 use aes_kw::KwAes128;
3173 use base64::engine::general_purpose::STANDARD;
3174 use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
3175 use rsa::{Oaep, RsaPublicKey};
3176 use sha1::Sha1;
3177 use sha2::{Sha256, Sha384};
3178
3179 use super::*;
3180 use crate::xmlenc::{CipherData, EncryptionMethod};
3181
3182 struct RecipientKeyResolver {
3183 recipient: &'static str,
3184 key: Vec<u8>,
3185 }
3186
3187 struct CountingResolver {
3188 candidate_calls: Cell<usize>,
3189 key: Vec<u8>,
3190 }
3191
3192 struct AllCallsResolver {
3193 calls: Cell<usize>,
3194 key: Vec<u8>,
3195 }
3196
3197 struct CandidateResolver {
3198 keys: Vec<Vec<u8>>,
3199 }
3200
3201 #[test]
3202 fn policy_unaware_recipient_resolver_is_not_dispatched() {
3203 struct LegacyRecipient {
3207 recipient_calls: Cell<usize>,
3208 key: Vec<u8>,
3209 }
3210 impl DecryptionKeyResolver for LegacyRecipient {
3211 fn resolve_key(
3212 &self,
3213 _provider: &dyn crate::provider::CryptoProvider,
3214 _algorithm: DataEncryptionAlgorithm,
3215 encrypted_key: Option<&EncryptedKey>,
3216 ) -> Result<Vec<u8>, XmlEncError> {
3217 if encrypted_key.is_none() {
3218 return Err(XmlEncError::KeyNotFound);
3219 }
3220 self.recipient_calls.set(self.recipient_calls.get() + 1);
3221 Ok(self.key.clone())
3222 }
3223 }
3224 let resolver = LegacyRecipient {
3225 recipient_calls: Cell::new(0),
3226 key: vec![0x63; 16],
3227 };
3228 let encrypted = encrypted_data_with_recipients(
3229 &resolver.key,
3230 vec![associated_encrypted_key("recipient", None, None)],
3231 None,
3232 );
3233 assert!(matches!(
3234 DecryptContext::new(&resolver).decrypt_data(&encrypted),
3235 Err(XmlEncError::KeyNotFound)
3236 ));
3237 assert_eq!(resolver.recipient_calls.get(), 0);
3238
3239 let direct = SymmetricKeyDecryptor::new(resolver.key.clone());
3240 let mut budget = KeyCandidateBudget::with_limit(1);
3241 assert!(matches!(
3242 resolver.resolve_key_candidates_with_policy(
3243 crate::provider::default_provider(),
3244 DataEncryptionAlgorithm::Aes128Gcm,
3245 encrypted.encrypted_keys.first(),
3246 &crate::policy::DecryptionPolicy::default(),
3247 &mut budget,
3248 ),
3249 Err(XmlEncError::KeyNotFound)
3250 ));
3251 assert_eq!(budget.remaining(), 1, "no recipient work was performed");
3252 assert_eq!(
3253 DecryptContext::new(&direct)
3254 .decrypt_data(&encrypted)
3255 .expect("direct AES does not require RSA metadata"),
3256 DecryptedContent::Bytes(b"payload".to_vec())
3257 );
3258 }
3259
3260 #[test]
3261 fn document_decryption_initial_parse_uses_the_policy_work_budget() {
3262 let xml = "<root/>";
3265 let policy = crate::policy::DecryptionPolicy {
3266 resources: crate::policy::ResourcePolicy {
3267 max_xml_parse_work_bytes: 0,
3268 ..crate::policy::ResourcePolicy::default()
3269 },
3270 ..crate::policy::DecryptionPolicy::default()
3271 };
3272 let resolver = SymmetricKeyDecryptor::new([0_u8; 16]);
3273
3274 let error = DecryptContext::new(&resolver)
3275 .policy(policy)
3276 .decrypt_document(xml, None)
3277 .expect_err("a zero parse-work budget must reject the input parse");
3278
3279 assert!(matches!(
3280 error,
3281 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3282 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
3283 maximum: 0,
3284 actual,
3285 }) if actual == xml.len()
3286 ));
3287 }
3288
3289 struct AggregateRecipientResolver {
3290 attempts: Cell<usize>,
3291 key: Vec<u8>,
3292 }
3293
3294 struct AssociationRecordingResolver {
3295 visited: RefCell<Vec<String>>,
3296 key: Vec<u8>,
3297 }
3298
3299 struct OrderedRecipientResolver {
3300 wrong: Vec<u8>,
3301 correct: Vec<u8>,
3302 }
3303
3304 struct DirectAndRecipientResolver {
3305 direct: Vec<u8>,
3306 recipient: Vec<u8>,
3307 }
3308
3309 struct FailingDirectResolver {
3310 recipient: Vec<u8>,
3311 }
3312
3313 struct PolicyRejectingDirectResolver {
3314 recipient: Vec<u8>,
3315 }
3316
3317 struct MislabelledExhaustionResolver {
3318 direct: Vec<u8>,
3319 }
3320
3321 impl DecryptionKeyResolver for DirectAndRecipientResolver {
3322 fn resolve_key_candidates_with_policy(
3323 &self,
3324 provider: &dyn crate::provider::CryptoProvider,
3325 algorithm: DataEncryptionAlgorithm,
3326 encrypted_key: Option<&EncryptedKey>,
3327 policy: &crate::policy::DecryptionPolicy,
3328 budget: &mut KeyCandidateBudget,
3329 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3330 policy.validate()?;
3332 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
3333 }
3334
3335 fn resolve_key(
3336 &self,
3337 _provider: &dyn crate::provider::CryptoProvider,
3338 _algorithm: DataEncryptionAlgorithm,
3339 encrypted_key: Option<&EncryptedKey>,
3340 ) -> Result<Vec<u8>, XmlEncError> {
3341 Ok(if encrypted_key.is_some() {
3342 self.recipient.clone()
3343 } else {
3344 self.direct.clone()
3345 })
3346 }
3347 }
3348
3349 impl DecryptionKeyResolver for FailingDirectResolver {
3350 fn resolve_key_candidates_with_policy(
3351 &self,
3352 provider: &dyn crate::provider::CryptoProvider,
3353 algorithm: DataEncryptionAlgorithm,
3354 encrypted_key: Option<&EncryptedKey>,
3355 policy: &crate::policy::DecryptionPolicy,
3356 budget: &mut KeyCandidateBudget,
3357 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3358 policy.validate()?;
3360 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
3361 }
3362
3363 fn resolve_key(
3364 &self,
3365 _provider: &dyn crate::provider::CryptoProvider,
3366 algorithm: DataEncryptionAlgorithm,
3367 encrypted_key: Option<&EncryptedKey>,
3368 ) -> Result<Vec<u8>, XmlEncError> {
3369 if encrypted_key.is_some() {
3370 Ok(self.recipient.clone())
3371 } else {
3372 Err(XmlEncError::InvalidKeySize {
3373 algorithm,
3374 expected: 16,
3375 actual: 8,
3376 })
3377 }
3378 }
3379 }
3380
3381 impl DecryptionKeyResolver for PolicyRejectingDirectResolver {
3382 fn resolve_key(
3383 &self,
3384 _provider: &dyn crate::provider::CryptoProvider,
3385 _algorithm: DataEncryptionAlgorithm,
3386 encrypted_key: Option<&EncryptedKey>,
3387 ) -> Result<Vec<u8>, XmlEncError> {
3388 if encrypted_key.is_some() {
3389 Ok(self.recipient.clone())
3390 } else {
3391 Err(crate::policy::PolicyViolation::KeyTrust {
3392 reason: "test resolver rejected the operation",
3393 }
3394 .into())
3395 }
3396 }
3397 }
3398
3399 impl DecryptionKeyResolver for MislabelledExhaustionResolver {
3400 fn resolve_key(
3401 &self,
3402 _provider: &dyn crate::provider::CryptoProvider,
3403 _algorithm: DataEncryptionAlgorithm,
3404 _encrypted_key: Option<&EncryptedKey>,
3405 ) -> Result<Vec<u8>, XmlEncError> {
3406 Err(XmlEncError::KeyNotFound)
3407 }
3408
3409 fn resolve_key_candidates_with_policy(
3410 &self,
3411 _provider: &dyn crate::provider::CryptoProvider,
3412 _algorithm: DataEncryptionAlgorithm,
3413 encrypted_key: Option<&EncryptedKey>,
3414 policy: &crate::policy::DecryptionPolicy,
3415 budget: &mut KeyCandidateBudget,
3416 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3417 policy.validate()?;
3419 if encrypted_key.is_none() {
3420 budget.consume(1)?;
3421 return Ok(vec![self.direct.clone()]);
3422 }
3423 budget.consume(budget.remaining().saturating_add(1))?;
3424 unreachable!("candidate budget exhaustion must return first")
3425 }
3426 }
3427
3428 impl DecryptionKeyResolver for OrderedRecipientResolver {
3429 fn resolve_key(
3430 &self,
3431 _provider: &dyn crate::provider::CryptoProvider,
3432 _algorithm: DataEncryptionAlgorithm,
3433 _encrypted_key: Option<&EncryptedKey>,
3434 ) -> Result<Vec<u8>, XmlEncError> {
3435 Err(XmlEncError::KeyNotFound)
3436 }
3437
3438 fn resolve_key_candidates_with_policy(
3439 &self,
3440 _provider: &dyn crate::provider::CryptoProvider,
3441 _algorithm: DataEncryptionAlgorithm,
3442 encrypted_key: Option<&EncryptedKey>,
3443 policy: &crate::policy::DecryptionPolicy,
3444 budget: &mut KeyCandidateBudget,
3445 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3446 policy.validate()?;
3448 budget.consume(1)?;
3449 match encrypted_key.and_then(|key| key.id.as_deref()) {
3450 Some("first") => Ok(vec![self.wrong.clone()]),
3451 Some("second") => Ok(vec![self.correct.clone()]),
3452 _ => Err(XmlEncError::KeyNotFound),
3453 }
3454 }
3455 }
3456
3457 impl DecryptionKeyResolver for CandidateResolver {
3458 fn resolve_key(
3459 &self,
3460 _provider: &dyn crate::provider::CryptoProvider,
3461 _algorithm: DataEncryptionAlgorithm,
3462 _encrypted_key: Option<&EncryptedKey>,
3463 ) -> Result<Vec<u8>, XmlEncError> {
3464 Err(XmlEncError::KeyNotFound)
3465 }
3466
3467 fn resolve_key_candidates(
3468 &self,
3469 _provider: &dyn crate::provider::CryptoProvider,
3470 _algorithm: DataEncryptionAlgorithm,
3471 encrypted_key: Option<&EncryptedKey>,
3472 budget: &mut KeyCandidateBudget,
3473 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3474 if encrypted_key.is_none() {
3475 budget.consume(self.keys.len())?;
3476 Ok(self.keys.clone())
3477 } else {
3478 Err(XmlEncError::KeyNotFound)
3479 }
3480 }
3481 }
3482
3483 impl DecryptionKeyResolver for AggregateRecipientResolver {
3484 fn resolve_key(
3485 &self,
3486 _provider: &dyn crate::provider::CryptoProvider,
3487 _algorithm: DataEncryptionAlgorithm,
3488 _encrypted_key: Option<&EncryptedKey>,
3489 ) -> Result<Vec<u8>, XmlEncError> {
3490 Err(XmlEncError::KeyNotFound)
3491 }
3492
3493 fn resolve_key_candidates_with_policy(
3494 &self,
3495 _provider: &dyn crate::provider::CryptoProvider,
3496 _algorithm: DataEncryptionAlgorithm,
3497 encrypted_key: Option<&EncryptedKey>,
3498 policy: &crate::policy::DecryptionPolicy,
3499 budget: &mut KeyCandidateBudget,
3500 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3501 policy.validate()?;
3503 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
3504 let attempts = budget.remaining();
3505 if attempts == 0 {
3506 budget.consume(1)?;
3507 }
3508 budget.consume(attempts)?;
3509 self.attempts.set(self.attempts.get() + attempts);
3510 if encrypted_key.id.as_deref() == Some("first") {
3511 Err(XmlEncError::KeyNotFound)
3512 } else {
3513 Ok(vec![self.key.clone()])
3514 }
3515 }
3516 }
3517
3518 impl DecryptionKeyResolver for AssociationRecordingResolver {
3519 fn resolve_key(
3520 &self,
3521 _provider: &dyn crate::provider::CryptoProvider,
3522 _algorithm: DataEncryptionAlgorithm,
3523 _encrypted_key: Option<&EncryptedKey>,
3524 ) -> Result<Vec<u8>, XmlEncError> {
3525 Err(XmlEncError::KeyNotFound)
3526 }
3527
3528 fn resolve_key_candidates_with_policy(
3529 &self,
3530 _provider: &dyn crate::provider::CryptoProvider,
3531 _algorithm: DataEncryptionAlgorithm,
3532 encrypted_key: Option<&EncryptedKey>,
3533 policy: &crate::policy::DecryptionPolicy,
3534 budget: &mut KeyCandidateBudget,
3535 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3536 policy.validate()?;
3538 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
3539 budget.consume(1)?;
3540 self.visited
3541 .borrow_mut()
3542 .push(encrypted_key.id.clone().unwrap_or_default());
3543 Ok(vec![self.key.clone()])
3544 }
3545 }
3546
3547 fn associated_encrypted_key(
3548 id: &str,
3549 data_reference: Option<&str>,
3550 carried_key_name: Option<&str>,
3551 ) -> EncryptedKey {
3552 EncryptedKey {
3553 id: Some(id.into()),
3554 sources: Default::default(),
3555 recipient: None,
3556 key_name: None,
3557 encryption_method: EncryptionMethod {
3558 chacha: None,
3559 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
3560 key_size_bits: None,
3561 oaep_digest: None,
3562 mgf_algorithm: None,
3563 oaep_params: None,
3564 },
3565 cipher_data: CipherData::Value {
3566 value: STANDARD.encode([0_u8; 256]),
3567 },
3568 reference_list: data_reference.map(|uri| crate::xmlenc::ReferenceList {
3569 data_references: vec![uri.into()],
3570 key_references: Vec::new(),
3571 }),
3572 carried_key_name: carried_key_name.map(str::to_owned),
3573 }
3574 }
3575
3576 fn encrypted_data_with_recipients(
3577 key: &[u8],
3578 encrypted_keys: Vec<EncryptedKey>,
3579 key_name: Option<&str>,
3580 ) -> EncryptedData {
3581 EncryptedData {
3582 encapsulation_methods: Vec::new(),
3583 id: Some("target".into()),
3584 derived_keys: Vec::new(),
3585 agreement_methods: Vec::new(),
3586 encrypted_type: None,
3587 encryption_method: EncryptionMethod {
3588 chacha: None,
3589 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3590 key_size_bits: None,
3591 oaep_digest: None,
3592 mgf_algorithm: None,
3593 oaep_params: None,
3594 },
3595 key_name: key_name.map(str::to_owned),
3596 encrypted_keys,
3597 cipher_data: CipherData::Value {
3598 value: STANDARD.encode(
3599 crate::provider::default_provider()
3600 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, key, b"payload")
3601 .expect("test encryption must succeed"),
3602 ),
3603 },
3604 }
3605 }
3606
3607 #[derive(Debug, Default)]
3608 struct PermissiveUnwrapProvider {
3609 decrypt_calls: AtomicUsize,
3610 unwrap_calls: AtomicUsize,
3611 recover_calls: AtomicUsize,
3612 plaintext: Vec<u8>,
3613 candidate_plaintexts: Vec<Vec<u8>>,
3614 }
3615
3616 struct OpaqueRecoveryKey;
3617
3618 impl crate::provider::KeyRecoveryKey for OpaqueRecoveryKey {
3619 fn rsa_modulus_bits(&self) -> usize {
3620 2048
3621 }
3622 fn rsa_public_exponent(&self) -> Option<u64> {
3623 Some(65537)
3624 }
3625 fn ciphertext_len(&self) -> usize {
3626 256
3627 }
3628
3629 fn recover_with_provider(
3630 &self,
3631 _provider: &dyn crate::provider::CryptoProvider,
3632 _parameters: &RsaOaepParameters,
3633 _ciphertext: &[u8],
3634 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3635 panic!("custom provider must own recovery for its opaque key")
3636 }
3637 }
3638
3639 impl crate::provider::CryptoProvider for PermissiveUnwrapProvider {
3640 #[cfg(feature = "legacy-algorithms")]
3641 fn recover_pkcs1v15(
3642 &self,
3643 key: &dyn crate::provider::KeyRecoveryKey,
3644 ciphertext: &[u8],
3645 key_len: usize,
3646 ) -> Result<crate::provider::RecoveredContentKey, crate::provider::ProviderError> {
3647 key.recover_pkcs1v15(self, ciphertext, key_len)
3648 }
3649 fn name(&self) -> &'static str {
3650 "permissive-unwrap-test"
3651 }
3652
3653 fn supports(&self, capability: crate::provider::ProviderCapability<'_>) -> bool {
3654 crate::provider::CryptoProvider::supports(
3655 &crate::provider::RustCryptoProvider,
3656 capability,
3657 )
3658 }
3659
3660 fn fill_random(&self, output: &mut [u8]) -> Result<(), crate::provider::ProviderError> {
3661 crate::provider::CryptoProvider::fill_random(
3662 &crate::provider::RustCryptoProvider,
3663 output,
3664 )
3665 }
3666
3667 fn derive_key(
3668 &self,
3669 parameters: &crate::provider::KdfParameters<'_>,
3670 secret: &[u8],
3671 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3672 crate::provider::RustCryptoProvider.derive_key(parameters, secret)
3673 }
3674
3675 #[cfg(feature = "xmldsig")]
3676 fn begin_digest(
3677 &self,
3678 algorithm: crate::xmldsig::DigestAlgorithm,
3679 ) -> Result<Box<dyn crate::provider::DigestState + '_>, crate::provider::ProviderError>
3680 {
3681 crate::provider::default_provider().begin_digest(algorithm)
3682 }
3683
3684 #[cfg(feature = "xmldsig")]
3685 fn sign(
3686 &self,
3687 key: &dyn crate::xmldsig::SigningKey,
3688 algorithm: crate::xmldsig::SignatureAlgorithm,
3689 data: &[u8],
3690 ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
3691 crate::provider::CryptoProvider::sign(
3692 &crate::provider::RustCryptoProvider,
3693 key,
3694 algorithm,
3695 data,
3696 )
3697 }
3698
3699 #[cfg(feature = "xmldsig")]
3700 fn verify(
3701 &self,
3702 key: &dyn crate::xmldsig::VerifyingKey,
3703 algorithm: crate::xmldsig::SignatureAlgorithm,
3704 data: &[u8],
3705 signature: &[u8],
3706 ) -> Result<bool, crate::xmldsig::DsigError> {
3707 crate::provider::CryptoProvider::verify(
3708 &crate::provider::RustCryptoProvider,
3709 key,
3710 algorithm,
3711 data,
3712 signature,
3713 )
3714 }
3715
3716 fn encrypt_data(
3717 &self,
3718 algorithm: DataEncryptionAlgorithm,
3719 key: &[u8],
3720 plaintext: &[u8],
3721 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3722 crate::provider::CryptoProvider::encrypt_data(
3723 &crate::provider::RustCryptoProvider,
3724 algorithm,
3725 key,
3726 plaintext,
3727 )
3728 }
3729
3730 fn decrypt_data(
3731 &self,
3732 _algorithm: DataEncryptionAlgorithm,
3733 _key: &[u8],
3734 _ciphertext: &[u8],
3735 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3736 let index = self.decrypt_calls.fetch_add(1, Ordering::Relaxed);
3737 Ok(self
3738 .candidate_plaintexts
3739 .get(index)
3740 .unwrap_or(&self.plaintext)
3741 .clone())
3742 }
3743
3744 fn wrap_key(
3745 &self,
3746 algorithm: KeyWrapAlgorithm,
3747 kek: &[u8],
3748 key: &[u8],
3749 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3750 crate::provider::CryptoProvider::wrap_key(
3751 &crate::provider::RustCryptoProvider,
3752 algorithm,
3753 kek,
3754 key,
3755 )
3756 }
3757
3758 fn unwrap_key(
3759 &self,
3760 _algorithm: KeyWrapAlgorithm,
3761 _kek: &[u8],
3762 _wrapped: &[u8],
3763 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3764 self.unwrap_calls.fetch_add(1, Ordering::Relaxed);
3765 Ok(vec![0_u8; 16])
3766 }
3767
3768 fn transport_key(
3769 &self,
3770 key: &dyn crate::provider::KeyTransportKey,
3771 parameters: &RsaOaepParameters,
3772 plaintext: &[u8],
3773 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3774 crate::provider::CryptoProvider::transport_key(
3775 &crate::provider::RustCryptoProvider,
3776 key,
3777 parameters,
3778 plaintext,
3779 )
3780 }
3781
3782 fn recover_key(
3783 &self,
3784 _key: &dyn crate::provider::KeyRecoveryKey,
3785 _parameters: &RsaOaepParameters,
3786 _ciphertext: &[u8],
3787 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3788 self.recover_calls.fetch_add(1, Ordering::Relaxed);
3789 Ok(vec![0_u8; 16])
3790 }
3791 }
3792
3793 impl DecryptionKeyResolver for CountingResolver {
3794 fn resolve_encapsulation_content_keys_with_policy(
3795 &self,
3796 _: &dyn crate::provider::CryptoProvider,
3797 _: DataEncryptionAlgorithm,
3798 _: &crate::key_establishment::EncapsulationMechanism,
3799 _: &crate::policy::DecryptionPolicy,
3800 _: &mut KeyCandidateBudget,
3801 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
3802 self.candidate_calls.set(self.candidate_calls.get() + 1);
3803 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
3804 self.key.clone(),
3805 )])
3806 }
3807
3808 fn resolve_key_candidates_with_policy(
3809 &self,
3810 provider: &dyn crate::provider::CryptoProvider,
3811 algorithm: DataEncryptionAlgorithm,
3812 encrypted_key: Option<&EncryptedKey>,
3813 policy: &crate::policy::DecryptionPolicy,
3814 budget: &mut KeyCandidateBudget,
3815 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3816 policy.validate()?;
3818 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
3819 }
3820
3821 fn resolve_key(
3822 &self,
3823 _provider: &dyn crate::provider::CryptoProvider,
3824 _algorithm: DataEncryptionAlgorithm,
3825 encrypted_key: Option<&EncryptedKey>,
3826 ) -> Result<Vec<u8>, XmlEncError> {
3827 if encrypted_key.is_some() {
3828 self.candidate_calls.set(self.candidate_calls.get() + 1);
3829 Ok(self.key.clone())
3830 } else {
3831 Err(XmlEncError::KeyNotFound)
3832 }
3833 }
3834 }
3835
3836 impl DecryptionKeyResolver for AllCallsResolver {
3837 fn resolve_key(
3838 &self,
3839 _provider: &dyn crate::provider::CryptoProvider,
3840 _algorithm: DataEncryptionAlgorithm,
3841 _encrypted_key: Option<&EncryptedKey>,
3842 ) -> Result<Vec<u8>, XmlEncError> {
3843 self.calls.set(self.calls.get() + 1);
3844 Ok(self.key.clone())
3845 }
3846 }
3847
3848 impl DecryptionKeyResolver for RecipientKeyResolver {
3849 fn resolve_key_candidates_with_policy(
3850 &self,
3851 provider: &dyn crate::provider::CryptoProvider,
3852 algorithm: DataEncryptionAlgorithm,
3853 encrypted_key: Option<&EncryptedKey>,
3854 policy: &crate::policy::DecryptionPolicy,
3855 budget: &mut KeyCandidateBudget,
3856 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3857 policy.validate()?;
3859 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
3860 }
3861
3862 fn resolve_key(
3863 &self,
3864 _provider: &dyn crate::provider::CryptoProvider,
3865 _algorithm: DataEncryptionAlgorithm,
3866 encrypted_key: Option<&EncryptedKey>,
3867 ) -> Result<Vec<u8>, XmlEncError> {
3868 if encrypted_key.and_then(|key| key.recipient.as_deref()) == Some(self.recipient) {
3869 Ok(self.key.clone())
3870 } else {
3871 Err(XmlEncError::KeyNotFound)
3872 }
3873 }
3874 }
3875
3876 #[test]
3877 fn decrypts_gcm_and_rejects_tampering() {
3878 let key = [7_u8; 16];
3880 let nonce = [9_u8; 12];
3881 let mut ciphertext = b"<Assertion>trusted</Assertion>".to_vec();
3882 Aes128Gcm::new_from_slice(&key)
3883 .expect("fixed key length")
3884 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
3885 .expect("test encryption must succeed");
3886 let mut wire = nonce.to_vec();
3887 wire.extend_from_slice(&ciphertext);
3888 let xml = format!(
3889 "<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>",
3890 STANDARD.encode(&wire)
3891 );
3892 let decrypted = decrypt(&xml, &SymmetricKeyDecryptor::new(key))
3893 .expect("valid AES-GCM XML must decrypt");
3894 assert_eq!(
3895 decrypted,
3896 DecryptedContent::Xml("<Assertion>trusted</Assertion>".into())
3897 );
3898 let last = wire.len() - 1;
3899 wire[last] ^= 1;
3900 let tampered = xml.replace(&STANDARD.encode(ciphertext), &STANDARD.encode(&wire[12..]));
3901 assert!(matches!(
3902 decrypt(&tampered, &SymmetricKeyDecryptor::new(key)),
3903 Err(XmlEncError::AeadAuthenticationFailed)
3904 ));
3905 }
3906
3907 #[test]
3908 fn candidate_keys_retry_only_authenticated_decryption() {
3909 let key = [7_u8; 16];
3912 let nonce = [9_u8; 12];
3913 let mut ciphertext = b"candidate plaintext".to_vec();
3914 Aes128Gcm::new_from_slice(&key)
3915 .expect("fixed key length")
3916 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
3917 .expect("test encryption must succeed");
3918 let mut wire = nonce.to_vec();
3919 wire.extend_from_slice(&ciphertext);
3920 let encrypted = EncryptedData {
3921 encapsulation_methods: Vec::new(),
3922 id: None,
3923 derived_keys: Vec::new(),
3924 agreement_methods: Vec::new(),
3925 encrypted_type: None,
3926 encryption_method: EncryptionMethod {
3927 chacha: None,
3928 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3929 key_size_bits: None,
3930 oaep_digest: None,
3931 mgf_algorithm: None,
3932 oaep_params: None,
3933 },
3934 key_name: None,
3935 encrypted_keys: Vec::new(),
3936 cipher_data: CipherData::Value {
3937 value: STANDARD.encode(wire),
3938 },
3939 };
3940 let resolver = CandidateResolver {
3941 keys: vec![vec![1_u8; 16], key.to_vec()],
3942 };
3943
3944 let decrypted = DecryptContext::new(&resolver)
3945 .decrypt_data(&encrypted)
3946 .expect("a later authenticated candidate must decrypt");
3947
3948 assert_eq!(
3949 decrypted,
3950 DecryptedContent::Bytes(b"candidate plaintext".to_vec())
3951 );
3952 }
3953
3954 #[test]
3955 fn standalone_decryption_stops_after_first_successful_candidate() {
3956 let provider = PermissiveUnwrapProvider {
3959 plaintext: b"accepted".to_vec(),
3960 ..PermissiveUnwrapProvider::default()
3961 };
3962 let resolver = CandidateResolver {
3963 keys: vec![vec![1_u8; 16], vec![2_u8; 16], vec![3_u8; 16]],
3964 };
3965 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::Aes128Gcm.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([0_u8; 36]),
3983 },
3984 };
3985
3986 let result = DecryptContext::new(&resolver)
3987 .provider(&provider)
3988 .decrypt_data(&encrypted)
3989 .expect("the first successful candidate must be returned");
3990
3991 assert_eq!(result, DecryptedContent::Bytes(b"accepted".to_vec()));
3992 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 1);
3993 }
3994
3995 #[test]
3996 fn document_decryption_discards_rejected_plaintext_before_next_candidate() {
3997 let provider = PermissiveUnwrapProvider {
4000 candidate_plaintexts: vec![b"<bad".to_vec(), b"<x/>".to_vec(), b"<u/>".to_vec()],
4001 ..PermissiveUnwrapProvider::default()
4002 };
4003 let resolver = CandidateResolver {
4004 keys: vec![vec![1_u8; 16], vec![2_u8; 16], vec![3_u8; 16]],
4005 };
4006 let encrypted = format!(
4007 "<xenc:EncryptedData xmlns:xenc=\"{XMLENC_NS}\" Type=\"{XMLENC_NS}Element\"><xenc:EncryptionMethod Algorithm=\"{}\"/><xenc:CipherData><xenc:CipherValue>{}</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData>",
4008 DataEncryptionAlgorithm::Aes128Gcm.uri(),
4009 STANDARD.encode([0_u8; 32]),
4010 );
4011
4012 let result = DecryptContext::new(&resolver)
4013 .provider(&provider)
4014 .decrypt_document(&encrypted, None)
4015 .expect("a later candidate with valid replacement XML must succeed");
4016
4017 assert_eq!(result, "<x/>");
4018 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 2);
4019 }
4020
4021 #[test]
4022 fn cbc_rejects_multiple_unordered_key_candidates() {
4023 let encrypted = EncryptedData {
4026 encapsulation_methods: Vec::new(),
4027 id: None,
4028 derived_keys: Vec::new(),
4029 agreement_methods: Vec::new(),
4030 encrypted_type: None,
4031 encryption_method: EncryptionMethod {
4032 chacha: None,
4033 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
4034 key_size_bits: None,
4035 oaep_digest: None,
4036 mgf_algorithm: None,
4037 oaep_params: None,
4038 },
4039 key_name: None,
4040 encrypted_keys: Vec::new(),
4041 cipher_data: CipherData::Value {
4042 value: STANDARD.encode(
4043 crate::provider::default_provider()
4044 .encrypt_data(
4045 DataEncryptionAlgorithm::Aes128Cbc,
4046 &[7_u8; 16],
4047 b"opaque plaintext",
4048 )
4049 .expect("test encryption must succeed"),
4050 ),
4051 },
4052 };
4053 let resolver = CandidateResolver {
4054 keys: vec![vec![1_u8; 16], vec![7_u8; 16]],
4055 };
4056
4057 let error = DecryptContext::new(&resolver)
4058 .decrypt_data(&encrypted)
4059 .expect_err("unauthenticated CBC must not guess among candidate keys");
4060
4061 assert!(matches!(
4062 error,
4063 XmlEncError::AmbiguousKeyCandidates {
4064 algorithm: DataEncryptionAlgorithm::Aes128Cbc,
4065 actual: 2,
4066 }
4067 ));
4068 }
4069
4070 #[test]
4071 fn cbc_accepts_duplicate_copies_of_one_key_identity() {
4072 let key = vec![0x27_u8; 16];
4075 let encrypted = EncryptedData {
4076 encapsulation_methods: Vec::new(),
4077 id: None,
4078 derived_keys: Vec::new(),
4079 agreement_methods: Vec::new(),
4080 encrypted_type: None,
4081 encryption_method: EncryptionMethod {
4082 chacha: None,
4083 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
4084 key_size_bits: None,
4085 oaep_digest: None,
4086 mgf_algorithm: None,
4087 oaep_params: None,
4088 },
4089 key_name: None,
4090 encrypted_keys: Vec::new(),
4091 cipher_data: CipherData::Value {
4092 value: STANDARD.encode(
4093 crate::provider::default_provider()
4094 .encrypt_data(
4095 DataEncryptionAlgorithm::Aes128Cbc,
4096 &key,
4097 b"duplicate identity",
4098 )
4099 .expect("test encryption must succeed"),
4100 ),
4101 },
4102 };
4103 let resolver = CandidateResolver {
4104 keys: vec![key.clone(), key],
4105 };
4106
4107 assert_eq!(
4108 DecryptContext::new(&resolver)
4109 .decrypt_data(&encrypted)
4110 .expect("one distinct CBC key identity must decrypt"),
4111 DecryptedContent::Bytes(b"duplicate identity".to_vec())
4112 );
4113 }
4114
4115 #[test]
4116 fn candidate_keys_are_bounded_before_cryptographic_processing() {
4117 let encrypted = EncryptedData {
4120 encapsulation_methods: Vec::new(),
4121 id: None,
4122 derived_keys: Vec::new(),
4123 agreement_methods: Vec::new(),
4124 encrypted_type: None,
4125 encryption_method: EncryptionMethod {
4126 chacha: None,
4127 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
4128 key_size_bits: None,
4129 oaep_digest: None,
4130 mgf_algorithm: None,
4131 oaep_params: None,
4132 },
4133 key_name: None,
4134 encrypted_keys: Vec::new(),
4135 cipher_data: CipherData::Value {
4136 value: STANDARD.encode(vec![0_u8; 28]),
4137 },
4138 };
4139 let actual = crate::hard_limits::KEY_CANDIDATE_CEILING + 1;
4140 let resolver = CandidateResolver {
4141 keys: vec![vec![0_u8; 16]; actual],
4142 };
4143
4144 let error = DecryptContext::new(&resolver)
4145 .decrypt_data(&encrypted)
4146 .expect_err("oversized candidate sets must fail before decryption");
4147
4148 assert!(matches!(
4149 error,
4150 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4151 resource: crate::policy::resource_name::KEY_CANDIDATES,
4152 maximum: crate::hard_limits::KEY_CANDIDATE_CEILING,
4153 actual: observed,
4154 }) if observed == actual
4155 ));
4156 }
4157
4158 #[test]
4159 fn operation_policy_controls_candidate_budget() {
4160 let encrypted = EncryptedData {
4163 encapsulation_methods: Vec::new(),
4164 id: None,
4165 derived_keys: Vec::new(),
4166 agreement_methods: Vec::new(),
4167 encrypted_type: None,
4168 encryption_method: EncryptionMethod {
4169 chacha: None,
4170 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
4171 key_size_bits: None,
4172 oaep_digest: None,
4173 mgf_algorithm: None,
4174 oaep_params: None,
4175 },
4176 key_name: None,
4177 encrypted_keys: Vec::new(),
4178 cipher_data: CipherData::Value {
4179 value: STANDARD.encode(vec![0_u8; 28]),
4180 },
4181 };
4182 let resolver = CandidateResolver {
4183 keys: vec![vec![0_u8; 16]; 3],
4184 };
4185 let mut policy = crate::policy::DecryptionPolicy::default();
4186 policy.resources.max_key_candidates = 2;
4187
4188 let error = DecryptContext::new(&resolver)
4189 .policy(policy)
4190 .decrypt_data(&encrypted)
4191 .expect_err("candidate accounting must use the operation policy ceiling");
4192
4193 assert!(matches!(
4194 error,
4195 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4196 resource: crate::policy::resource_name::KEY_CANDIDATES,
4197 maximum: 2,
4198 actual: 3,
4199 })
4200 ));
4201 }
4202
4203 #[test]
4204 fn candidate_work_ceiling_is_shared_across_recipients() {
4205 let key = vec![0x39_u8; 16];
4208 let resolver = AggregateRecipientResolver {
4209 attempts: Cell::new(0),
4210 key: key.clone(),
4211 };
4212 let encrypted = encrypted_data_with_recipients(
4213 &key,
4214 vec![
4215 associated_encrypted_key("first", None, None),
4216 associated_encrypted_key("second", None, None),
4217 ],
4218 None,
4219 );
4220
4221 DecryptContext::new(&resolver)
4222 .decrypt_data(&encrypted)
4223 .expect_err("a second recipient must not receive a fresh candidate allowance");
4224 assert_eq!(
4225 resolver.attempts.get(),
4226 crate::hard_limits::KEY_CANDIDATE_CEILING
4227 );
4228 }
4229
4230 #[test]
4231 fn candidate_budget_exhaustion_is_fatal_after_a_key_was_found() {
4232 let key = vec![0x49_u8; 16];
4235 let resolver = MislabelledExhaustionResolver {
4236 direct: key.clone(),
4237 };
4238 let encrypted = encrypted_data_with_recipients(
4239 &key,
4240 vec![associated_encrypted_key("recipient", None, None)],
4241 None,
4242 );
4243
4244 let error = DecryptContext::new(&resolver)
4245 .decrypt_data(&encrypted)
4246 .expect_err("candidate exhaustion must override an earlier usable key");
4247
4248 assert!(matches!(
4249 error,
4250 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4251 resource: "key candidates",
4252 maximum: crate::hard_limits::KEY_CANDIDATE_CEILING,
4253 actual,
4254 }) if actual == crate::hard_limits::KEY_CANDIDATE_CEILING + 1
4255 ));
4256 }
4257
4258 #[test]
4259 fn authenticated_decryption_continues_after_wrong_unwrapped_recipient_key() {
4260 let correct = vec![0x53_u8; 16];
4263 let encrypted = encrypted_data_with_recipients(
4264 &correct,
4265 vec![
4266 associated_encrypted_key("first", None, None),
4267 associated_encrypted_key("second", None, None),
4268 ],
4269 None,
4270 );
4271 let resolver = OrderedRecipientResolver {
4272 wrong: vec![0x11_u8; 16],
4273 correct,
4274 };
4275
4276 let plaintext = DecryptContext::new(&resolver)
4277 .decrypt_data(&encrypted)
4278 .expect("the second recipient key must authenticate");
4279 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
4280 }
4281
4282 #[test]
4283 fn authenticated_decryption_continues_from_direct_key_to_recipient() {
4284 let correct = vec![0x63_u8; 16];
4287 let encrypted = encrypted_data_with_recipients(
4288 &correct,
4289 vec![associated_encrypted_key("recipient", None, None)],
4290 None,
4291 );
4292 let resolver = DirectAndRecipientResolver {
4293 direct: vec![0x19_u8; 16],
4294 recipient: correct,
4295 };
4296
4297 let plaintext = DecryptContext::new(&resolver)
4298 .decrypt_data(&encrypted)
4299 .expect("the embedded recipient must remain available after a direct candidate");
4300
4301 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
4302 }
4303
4304 #[test]
4305 fn authenticated_decryption_continues_after_direct_lookup_error() {
4306 let correct = vec![0x64_u8; 16];
4309 let encrypted = encrypted_data_with_recipients(
4310 &correct,
4311 vec![associated_encrypted_key("recipient", None, None)],
4312 None,
4313 );
4314 let resolver = FailingDirectResolver { recipient: correct };
4315
4316 let plaintext = DecryptContext::new(&resolver)
4317 .decrypt_data(&encrypted)
4318 .expect("recipient lookup must follow a candidate-local direct error");
4319
4320 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
4321 }
4322
4323 #[test]
4324 fn resolver_policy_rejection_stops_before_later_recipient() {
4325 let correct = vec![0x65_u8; 16];
4328 let encrypted = encrypted_data_with_recipients(
4329 &correct,
4330 vec![associated_encrypted_key("recipient", None, None)],
4331 None,
4332 );
4333 let resolver = PolicyRejectingDirectResolver { recipient: correct };
4334
4335 let error = DecryptContext::new(&resolver)
4336 .decrypt_data(&encrypted)
4337 .expect_err("operation policy rejection must be fatal");
4338
4339 assert!(matches!(
4340 error,
4341 XmlEncError::Policy(crate::policy::PolicyViolation::KeyTrust {
4342 reason: "test resolver rejected the operation",
4343 })
4344 ));
4345 }
4346
4347 #[test]
4348 fn cbc_rejects_distinct_direct_and_recipient_candidates() {
4349 let recipient = vec![0x73_u8; 16];
4352 let mut encrypted = encrypted_data_with_recipients(
4353 &recipient,
4354 vec![associated_encrypted_key("recipient", None, None)],
4355 None,
4356 );
4357 encrypted.encryption_method.algorithm = DataEncryptionAlgorithm::Aes128Cbc.uri().into();
4358 *encrypted
4359 .cipher_data
4360 .inline_value_mut()
4361 .expect("inline ciphertext") = STANDARD.encode(
4362 crate::provider::default_provider()
4363 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &recipient, b"payload")
4364 .expect("test encryption must succeed"),
4365 );
4366 let resolver = DirectAndRecipientResolver {
4367 direct: vec![0x29_u8; 16],
4368 recipient,
4369 };
4370
4371 let error = DecryptContext::new(&resolver)
4372 .decrypt_data(&encrypted)
4373 .expect_err("CBC must not guess between direct and recipient keys");
4374
4375 assert!(matches!(
4376 error,
4377 XmlEncError::AmbiguousKeyCandidates {
4378 algorithm: DataEncryptionAlgorithm::Aes128Cbc,
4379 actual: 2,
4380 }
4381 ));
4382 }
4383
4384 #[test]
4385 fn cbc_ambiguity_ignores_algorithm_incompatible_key_widths() {
4386 let key = vec![0x47_u8; 16];
4389 let ciphertext = crate::provider::default_provider()
4390 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &key, b"payload")
4391 .expect("test encryption must succeed");
4392 let encrypted = EncryptedData {
4393 encapsulation_methods: Vec::new(),
4394 id: None,
4395 derived_keys: Vec::new(),
4396 agreement_methods: Vec::new(),
4397 encrypted_type: None,
4398 encryption_method: EncryptionMethod {
4399 chacha: None,
4400 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
4401 key_size_bits: None,
4402 oaep_digest: None,
4403 mgf_algorithm: None,
4404 oaep_params: None,
4405 },
4406 key_name: None,
4407 encrypted_keys: Vec::new(),
4408 cipher_data: CipherData::Value {
4409 value: STANDARD.encode(ciphertext),
4410 },
4411 };
4412 let resolver = CandidateResolver {
4413 keys: vec![vec![0_u8; 32], key],
4414 };
4415
4416 assert_eq!(
4417 DecryptContext::new(&resolver)
4418 .decrypt_data(&encrypted)
4419 .expect("the sole width-compatible CBC key must be selected"),
4420 DecryptedContent::Bytes(b"payload".to_vec())
4421 );
4422 }
4423
4424 #[test]
4425 fn data_reference_selects_the_associated_encrypted_key() {
4426 let key = vec![0x51_u8; 16];
4429 let resolver = AssociationRecordingResolver {
4430 visited: RefCell::new(Vec::new()),
4431 key: key.clone(),
4432 };
4433 let encrypted = encrypted_data_with_recipients(
4434 &key,
4435 vec![
4436 associated_encrypted_key("unrelated", Some("#other"), None),
4437 associated_encrypted_key("matching", Some("#target"), None),
4438 ],
4439 None,
4440 );
4441
4442 DecryptContext::new(&resolver)
4443 .decrypt_data(&encrypted)
4444 .expect("the associated recipient must decrypt");
4445 assert_eq!(resolver.visited.into_inner(), ["matching"]);
4446 }
4447
4448 #[test]
4449 fn carried_key_name_selects_the_named_content_key() {
4450 let key = vec![0x52_u8; 16];
4453 let resolver = AssociationRecordingResolver {
4454 visited: RefCell::new(Vec::new()),
4455 key: key.clone(),
4456 };
4457 let encrypted = encrypted_data_with_recipients(
4458 &key,
4459 vec![
4460 associated_encrypted_key("unrelated", None, Some("other")),
4461 associated_encrypted_key("matching", None, Some("wanted")),
4462 ],
4463 Some("wanted"),
4464 );
4465
4466 DecryptContext::new(&resolver)
4467 .decrypt_data(&encrypted)
4468 .expect("the matching carried key name must decrypt");
4469 assert_eq!(resolver.visited.into_inner(), ["matching"]);
4470 }
4471
4472 #[test]
4473 fn contradictory_encrypted_key_associations_fail_closed() {
4474 let key = vec![0x53_u8; 16];
4477 let resolver = AssociationRecordingResolver {
4478 visited: RefCell::new(Vec::new()),
4479 key: key.clone(),
4480 };
4481 let encrypted = encrypted_data_with_recipients(
4482 &key,
4483 vec![associated_encrypted_key("unrelated", Some("#other"), None)],
4484 None,
4485 );
4486
4487 assert!(matches!(
4488 DecryptContext::new(&resolver).decrypt_data(&encrypted),
4489 Err(XmlEncError::KeyNotFound)
4490 ));
4491 assert!(resolver.visited.into_inner().is_empty());
4492 }
4493
4494 #[test]
4495 fn direct_symmetric_key_ignores_embedded_key_hints() {
4496 let key = [0x28_u8; 16];
4499 let unrelated = EncryptedKey {
4500 sources: Default::default(),
4501 id: None,
4502 recipient: Some("other-recipient".into()),
4503 key_name: None,
4504 encryption_method: super::super::EncryptionMethod {
4505 chacha: None,
4506 algorithm: "urn:unrelated:key-transport".into(),
4507 key_size_bits: None,
4508 oaep_digest: None,
4509 mgf_algorithm: None,
4510 oaep_params: None,
4511 },
4512 cipher_data: super::super::CipherData::Value {
4513 value: STANDARD.encode([0_u8; 24]),
4514 },
4515 reference_list: None,
4516 carried_key_name: None,
4517 };
4518
4519 assert_eq!(
4520 SymmetricKeyDecryptor::new(key)
4521 .resolve_key(
4522 crate::provider::default_provider(),
4523 DataEncryptionAlgorithm::Aes128Gcm,
4524 Some(&unrelated)
4525 )
4526 .expect("direct key must ignore unrelated embedded hints"),
4527 key
4528 );
4529 }
4530
4531 #[test]
4532 fn decrypts_with_the_matching_recipient_key() {
4533 let key = [0x29_u8; 16];
4536 let plaintext = "recipient-specific plaintext";
4537 let encrypted = encrypted_gcm_element("", plaintext, None, true, &key);
4538 let recipient_key = |recipient: &str, method: &str| {
4539 format!(
4540 "<xenc:EncryptedKey Recipient=\"{recipient}\"><xenc:EncryptionMethod Algorithm=\"{method}\">{}</xenc:EncryptionMethod><xenc:CipherData><xenc:CipherValue>YQ==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey>",
4541 if recipient == "alice" {
4542 "<ds:DigestMethod Algorithm=\"urn:unsupported:digest\"/>"
4543 } else {
4544 ""
4545 }
4546 )
4547 };
4548 let key_info = format!(
4549 "<ds:KeyInfo xmlns:ds=\"{}\">{}{}</ds:KeyInfo>",
4550 crate::xmlenc::types::XMLDSIG_NS,
4551 recipient_key("alice", KeyTransportAlgorithm::RsaOaep11.uri()),
4552 recipient_key("bob", KeyWrapAlgorithm::AesKw128.uri())
4553 );
4554 let xml = encrypted.replacen(
4555 "<xenc:CipherData>",
4556 &format!("{key_info}<xenc:CipherData>"),
4557 1,
4558 );
4559 let resolver = RecipientKeyResolver {
4560 recipient: "bob",
4561 key: key.to_vec(),
4562 };
4563
4564 assert_eq!(
4565 decrypt(&xml, &resolver).expect("second recipient key must be tried"),
4566 DecryptedContent::Bytes(plaintext.as_bytes().to_vec())
4567 );
4568 }
4569
4570 #[test]
4571 fn decryption_policy_bounds_recipients_before_key_resolution() {
4572 let key = [0x29_u8; 16];
4575 let encrypted = encrypted_gcm_element("", "bounded recipients", None, true, &key);
4576 let recipient_key = |recipient: &str| {
4577 format!(
4578 "<xenc:EncryptedKey Recipient=\"{recipient}\"><xenc:EncryptionMethod Algorithm=\"urn:test:key\"/><xenc:CipherData><xenc:CipherValue>YQ==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey>"
4579 )
4580 };
4581 let key_info = format!(
4582 "<ds:KeyInfo xmlns:ds=\"{}\">{}{}</ds:KeyInfo>",
4583 crate::xmlenc::types::XMLDSIG_NS,
4584 recipient_key("alice"),
4585 recipient_key("bob")
4586 );
4587 let xml = encrypted.replacen(
4588 "<xenc:CipherData>",
4589 &format!("{key_info}<xenc:CipherData>"),
4590 1,
4591 );
4592 let parsed = parse_encrypted_data(&xml).expect("default parser accepts two recipients");
4593 let policy = crate::policy::DecryptionPolicy {
4594 resources: crate::policy::ResourcePolicy {
4595 max_encryption_recipients: 1,
4596 ..crate::policy::ResourcePolicy::default()
4597 },
4598 ..crate::policy::DecryptionPolicy::default()
4599 };
4600 let resolver = SymmetricKeyDecryptor::new(key);
4601 let context = DecryptContext::new(&resolver).policy(policy);
4602
4603 for error in [
4604 context
4605 .decrypt(&xml)
4606 .expect_err("XML recipient collection must be bounded"),
4607 context
4608 .decrypt_data(&parsed)
4609 .expect_err("typed recipient collection must be bounded"),
4610 ] {
4611 assert!(matches!(
4612 error,
4613 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4614 resource: crate::policy::resource_name::ENCRYPTION_RECIPIENTS,
4615 maximum: 1,
4616 actual: 2,
4617 })
4618 ));
4619 }
4620 }
4621
4622 #[test]
4623 fn decrypts_session_key_wrapped_with_aes_kw() {
4624 let kek = [3_u8; 16];
4626 let session_key = [4_u8; 16];
4627 let mut wrapped = [0_u8; 24];
4628 KwAes128::new_from_slice(&kek)
4629 .expect("fixed KEK length")
4630 .wrap_key(&session_key, &mut wrapped)
4631 .expect("RFC 3394 test wrapping must succeed");
4632 let encrypted_key = EncryptedKey {
4633 id: None,
4634 sources: Default::default(),
4635 recipient: None,
4636 key_name: None,
4637 encryption_method: super::super::EncryptionMethod {
4638 chacha: None,
4639 algorithm: "http://www.w3.org/2001/04/xmlenc#kw-aes128".into(),
4640 key_size_bits: None,
4641 oaep_digest: None,
4642 mgf_algorithm: None,
4643 oaep_params: None,
4644 },
4645 cipher_data: super::super::CipherData::Value {
4646 value: STANDARD.encode(wrapped),
4647 },
4648 reference_list: None,
4649 carried_key_name: None,
4650 };
4651 let resolved = KekDecryptor::new(kek)
4652 .resolve_key(
4653 crate::provider::default_provider(),
4654 DataEncryptionAlgorithm::Aes128Gcm,
4655 Some(&encrypted_key),
4656 )
4657 .expect("wrapped session key must resolve");
4658 assert_eq!(resolved, session_key);
4659 }
4660
4661 #[test]
4662 fn rejects_invalid_kek_before_custom_provider_dispatch() {
4663 let encrypted_key = EncryptedKey {
4666 id: None,
4667 sources: Default::default(),
4668 recipient: None,
4669 key_name: None,
4670 encryption_method: super::super::EncryptionMethod {
4671 chacha: None,
4672 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
4673 key_size_bits: None,
4674 oaep_digest: None,
4675 mgf_algorithm: None,
4676 oaep_params: None,
4677 },
4678 cipher_data: super::super::CipherData::Value {
4679 value: STANDARD.encode([0_u8; 24]),
4680 },
4681 reference_list: None,
4682 carried_key_name: None,
4683 };
4684 let provider = PermissiveUnwrapProvider::default();
4685
4686 assert!(matches!(
4687 KekDecryptor::new([0_u8; 32]).resolve_key(
4688 &provider,
4689 DataEncryptionAlgorithm::Aes128Gcm,
4690 Some(&encrypted_key),
4691 ),
4692 Err(XmlEncError::InvalidKekSize {
4693 algorithm: KeyWrapAlgorithm::AesKw128,
4694 expected: 16,
4695 actual: 32,
4696 })
4697 ));
4698 assert_eq!(provider.unwrap_calls.load(Ordering::Relaxed), 0);
4699 }
4700
4701 #[test]
4702 fn rejects_content_ciphertext_framing_before_resolution_or_provider_dispatch() {
4703 for (algorithm, ciphertext_len) in [
4706 (DataEncryptionAlgorithm::Aes128Gcm, 27),
4707 (DataEncryptionAlgorithm::Aes128Cbc, 33),
4708 ] {
4709 let resolver = AllCallsResolver {
4710 calls: Cell::new(0),
4711 key: vec![0_u8; algorithm.key_len()],
4712 };
4713 let provider = PermissiveUnwrapProvider::default();
4714 let encrypted = EncryptedData {
4715 encapsulation_methods: Vec::new(),
4716 id: None,
4717 derived_keys: Vec::new(),
4718 agreement_methods: Vec::new(),
4719 encrypted_type: None,
4720 key_name: None,
4721 encryption_method: super::super::EncryptionMethod {
4722 chacha: None,
4723 algorithm: algorithm.uri().into(),
4724 key_size_bits: None,
4725 oaep_digest: None,
4726 mgf_algorithm: None,
4727 oaep_params: None,
4728 },
4729 encrypted_keys: Vec::new(),
4730 cipher_data: super::super::CipherData::Value {
4731 value: STANDARD.encode(vec![0_u8; ciphertext_len]),
4732 },
4733 };
4734
4735 assert!(
4736 DecryptContext::new(&resolver)
4737 .provider(&provider)
4738 .decrypt_data(&encrypted)
4739 .is_err()
4740 );
4741 assert_eq!(resolver.calls.get(), 0);
4742 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 0);
4743 }
4744 }
4745
4746 #[test]
4747 fn rejects_custom_provider_plaintext_outside_algorithm_bounds() {
4748 for (algorithm, ciphertext_len, plaintext_len) in [
4751 (DataEncryptionAlgorithm::Aes128Gcm, 32, 5),
4752 (DataEncryptionAlgorithm::Aes128Cbc, 32, 16),
4753 ] {
4754 let resolver = AllCallsResolver {
4755 calls: Cell::new(0),
4756 key: vec![0_u8; algorithm.key_len()],
4757 };
4758 let provider = PermissiveUnwrapProvider {
4759 plaintext: vec![0_u8; plaintext_len],
4760 ..PermissiveUnwrapProvider::default()
4761 };
4762 let encrypted = EncryptedData {
4763 encapsulation_methods: Vec::new(),
4764 id: None,
4765 derived_keys: Vec::new(),
4766 agreement_methods: Vec::new(),
4767 encrypted_type: None,
4768 key_name: None,
4769 encryption_method: super::super::EncryptionMethod {
4770 chacha: None,
4771 algorithm: algorithm.uri().into(),
4772 key_size_bits: None,
4773 oaep_digest: None,
4774 mgf_algorithm: None,
4775 oaep_params: None,
4776 },
4777 encrypted_keys: Vec::new(),
4778 cipher_data: super::super::CipherData::Value {
4779 value: STANDARD.encode(vec![0_u8; ciphertext_len]),
4780 },
4781 };
4782
4783 let error = DecryptContext::new(&resolver)
4784 .provider(&provider)
4785 .decrypt_data(&encrypted)
4786 .expect_err("impossible provider output length must fail");
4787 match algorithm {
4788 DataEncryptionAlgorithm::Aes128Gcm => assert!(matches!(
4789 error,
4790 XmlEncError::Provider(crate::provider::ProviderError::InvalidOutputSize {
4791 operation: crate::provider::ProviderOperation::Decrypt,
4792 expected: 4,
4793 actual: 5,
4794 })
4795 )),
4796 DataEncryptionAlgorithm::Aes128Cbc => assert!(matches!(
4797 error,
4798 XmlEncError::Provider(crate::provider::ProviderError::InvalidOutputSizeRange {
4799 operation: crate::provider::ProviderOperation::Decrypt,
4800 minimum: 0,
4801 maximum: 15,
4802 actual: 16,
4803 })
4804 )),
4805 _ => unreachable!("the regression table covers one GCM and one CBC algorithm"),
4806 }
4807 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 1);
4808 }
4809 }
4810
4811 #[test]
4812 fn rejects_malformed_aes_kw_before_custom_provider_dispatch() {
4813 let provider = PermissiveUnwrapProvider::default();
4816 for actual in [0, 23, 25] {
4817 let encrypted_key = EncryptedKey {
4818 sources: Default::default(),
4819 id: None,
4820 recipient: None,
4821 key_name: None,
4822 encryption_method: super::super::EncryptionMethod {
4823 chacha: None,
4824 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
4825 key_size_bits: None,
4826 oaep_digest: None,
4827 mgf_algorithm: None,
4828 oaep_params: None,
4829 },
4830 cipher_data: super::super::CipherData::Value {
4831 value: STANDARD.encode(vec![0_u8; actual]),
4832 },
4833 reference_list: None,
4834 carried_key_name: None,
4835 };
4836 assert!(matches!(
4837 KekDecryptor::new([0_u8; 16]).resolve_key(
4838 &provider,
4839 DataEncryptionAlgorithm::Aes128Gcm,
4840 Some(&encrypted_key),
4841 ),
4842 Err(XmlEncError::InvalidWrappedKeyLength {
4843 expected: 24,
4844 actual: output_len,
4845 }) if output_len == actual
4846 ));
4847 }
4848 assert_eq!(provider.unwrap_calls.load(Ordering::Relaxed), 0);
4849 }
4850
4851 #[test]
4852 fn rejects_malformed_rsa_oaep_before_custom_provider_dispatch() {
4853 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
4856 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
4857 ))
4858 .expect("RSA donor private key must parse");
4859 let provider = PermissiveUnwrapProvider::default();
4860 for actual in [0, 255, 257] {
4861 assert!(matches!(
4862 recover_rsa_oaep(
4863 &provider,
4864 &private_key,
4865 &RsaOaepParameters::default(),
4866 &vec![0_u8; actual],
4867 DataEncryptionAlgorithm::Aes128Gcm,
4868 ),
4869 Err(XmlEncError::InvalidWrappedKeyLength {
4870 expected: 256,
4871 actual: output_len,
4872 }) if output_len == actual
4873 ));
4874 }
4875 assert_eq!(provider.recover_calls.load(Ordering::Relaxed), 0);
4876 }
4877
4878 #[cfg(feature = "legacy-algorithms")]
4879 #[test]
4880 fn invalid_rsa_recovery_cannot_release_successful_cbc_plaintext() {
4881 let key = RsaPrivateKey::from_pkcs8_pem(include_str!(
4884 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
4885 ))
4886 .expect("valid RSA fixture");
4887 let resolver = PrivateKeyDecryptor::new(key);
4888 for algorithm in [
4889 DataEncryptionAlgorithm::Aes128Cbc,
4890 DataEncryptionAlgorithm::Aes192Cbc,
4891 DataEncryptionAlgorithm::Aes256Cbc,
4892 DataEncryptionAlgorithm::TripleDesCbc,
4893 DataEncryptionAlgorithm::Aes128Gcm,
4894 ] {
4895 let provider = PermissiveUnwrapProvider {
4896 plaintext: b"accepted CBC plaintext".to_vec(),
4897 ..PermissiveUnwrapProvider::default()
4898 };
4899 let mut encrypted = encrypted_data_with_recipients(
4900 &[0; 16],
4901 vec![associated_encrypted_key("recipient", None, None)],
4902 None,
4903 );
4904 encrypted.encryption_method.algorithm = algorithm.uri().into();
4905 *encrypted
4906 .cipher_data
4907 .inline_value_mut()
4908 .expect("inline ciphertext") =
4909 STANDARD.encode(vec![
4910 0;
4911 algorithm
4912 .ciphertext_len_for_plaintext(provider.plaintext.len())
4913 .expect("bounded test frame")
4914 ]);
4915 encrypted.encrypted_keys[0].encryption_method.algorithm =
4916 KeyTransportAlgorithm::RsaPkcs1v15.uri().into();
4917 *encrypted.encrypted_keys[0]
4918 .cipher_data
4919 .inline_value_mut()
4920 .expect("inline wrapped key") = STANDARD.encode([0; 256]);
4921 let result = DecryptContext::new(&resolver)
4922 .provider(&provider)
4923 .policy(crate::policy::DecryptionPolicy {
4924 data_algorithms: Some([algorithm].into()),
4925 key_transport_algorithms: Some([KeyTransportAlgorithm::RsaPkcs1v15].into()),
4926 ..crate::policy::DecryptionPolicy::default()
4927 })
4928 .decrypt_data(&encrypted);
4929 assert!(
4930 result.is_err(),
4931 "invalid RSA padding must never release fallback plaintext"
4932 );
4933 assert_eq!(
4934 provider.decrypt_calls.load(Ordering::Relaxed),
4935 1,
4936 "content decryption must run before rejecting the recovery"
4937 );
4938 }
4939 }
4940
4941 #[test]
4942 fn candidate_filter_reuses_storage_and_preserves_precedence() {
4943 let keys = [
4946 vec![0; 8],
4947 vec![2; 16],
4948 vec![3; 16],
4949 vec![2; 16],
4950 vec![4; 32],
4951 vec![5; 16],
4952 ]
4953 .into_iter()
4954 .map(crate::provider::RecoveredContentKey::confirmed)
4955 .collect::<Vec<_>>();
4956 let allocation = keys.as_ptr();
4957 let keys = compatible_decryption_key_candidates(DataEncryptionAlgorithm::Aes128Gcm, keys)
4958 .expect("compatible candidates survive");
4959 assert_eq!(keys.as_ptr(), allocation);
4960 assert_eq!(keys.len(), 3);
4961 for (key, expected) in keys.iter().zip([2, 3, 5]) {
4962 assert_eq!(key.bytes(), &[expected; 16]);
4963 }
4964 }
4965
4966 #[test]
4967 fn custom_provider_recovers_with_an_opaque_private_key() {
4968 let encrypted_key = EncryptedKey {
4971 id: None,
4972 sources: Default::default(),
4973 recipient: None,
4974 key_name: None,
4975 encryption_method: super::super::EncryptionMethod {
4976 chacha: None,
4977 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
4978 key_size_bits: None,
4979 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
4980 mgf_algorithm: Some(
4981 OaepDigestAlgorithm::Sha256
4982 .mgf_uri()
4983 .expect("MGF1-SHA256 URI")
4984 .into(),
4985 ),
4986 oaep_params: None,
4987 },
4988 cipher_data: super::super::CipherData::Value {
4989 value: STANDARD.encode(vec![0x5a; 256]),
4990 },
4991 reference_list: None,
4992 carried_key_name: None,
4993 };
4994 let provider = PermissiveUnwrapProvider::default();
4995 let decryptor = PrivateKeyDecryptor::provider_key(Arc::new(OpaqueRecoveryKey));
4996
4997 let key = decryptor
4998 .resolve_key(
4999 &provider,
5000 DataEncryptionAlgorithm::Aes128Gcm,
5001 Some(&encrypted_key),
5002 )
5003 .expect("custom provider must recover through its opaque private key");
5004
5005 assert_eq!(key, vec![0_u8; 16]);
5006 assert_eq!(provider.recover_calls.load(Ordering::Relaxed), 1);
5007 }
5008
5009 #[test]
5010 fn rejects_truncated_gcm_and_invalid_wrapped_key() {
5011 assert!(matches!(
5013 crate::provider::default_provider().decrypt_data(
5014 DataEncryptionAlgorithm::Aes128Gcm,
5015 &[0_u8; 16],
5016 &[0_u8; 27],
5017 ),
5018 Err(crate::provider::ProviderError::InvalidInput(
5019 crate::provider::ProviderInputError::AesGcmFraming
5020 ))
5021 ));
5022 let truncated = EncryptedData {
5023 encapsulation_methods: Vec::new(),
5024 id: None,
5025 derived_keys: Vec::new(),
5026 agreement_methods: Vec::new(),
5027 encrypted_type: None,
5028 key_name: None,
5029 encryption_method: super::super::EncryptionMethod {
5030 chacha: None,
5031 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5032 key_size_bits: None,
5033 oaep_digest: None,
5034 mgf_algorithm: None,
5035 oaep_params: None,
5036 },
5037 encrypted_keys: Vec::new(),
5038 cipher_data: super::super::CipherData::Value {
5039 value: STANDARD.encode([0_u8; 27]),
5040 },
5041 };
5042 assert!(matches!(
5043 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16])).decrypt_data(&truncated),
5044 Err(XmlEncError::DataTooShort {
5045 algorithm: "AES-GCM",
5046 actual: 27,
5047 ..
5048 })
5049 ));
5050 let encrypted_key = EncryptedKey {
5051 id: None,
5052 sources: Default::default(),
5053 recipient: None,
5054 key_name: None,
5055 encryption_method: super::super::EncryptionMethod {
5056 chacha: None,
5057 algorithm: "http://www.w3.org/2001/04/xmlenc#kw-aes128".into(),
5058 key_size_bits: None,
5059 oaep_digest: None,
5060 mgf_algorithm: None,
5061 oaep_params: None,
5062 },
5063 cipher_data: super::super::CipherData::Value {
5064 value: STANDARD.encode([0_u8; 24]),
5065 },
5066 reference_list: None,
5067 carried_key_name: None,
5068 };
5069 assert!(matches!(
5070 KekDecryptor::new([0_u8; 16]).resolve_key(
5071 crate::provider::default_provider(),
5072 DataEncryptionAlgorithm::Aes128Gcm,
5073 Some(&encrypted_key)
5074 ),
5075 Err(XmlEncError::KeyWrapIntegrity)
5076 ));
5077 assert!(matches!(
5078 KekDecryptor::new([0_u8; 32]).resolve_key(
5079 crate::provider::default_provider(),
5080 DataEncryptionAlgorithm::Aes128Gcm,
5081 Some(&encrypted_key)
5082 ),
5083 Err(XmlEncError::InvalidKekSize {
5084 algorithm: KeyWrapAlgorithm::AesKw128,
5085 expected: 16,
5086 actual: 32
5087 })
5088 ));
5089 }
5090
5091 #[test]
5092 fn decrypts_oaep11_with_independent_digest_and_mgf() {
5093 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5095 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5096 ))
5097 .expect("RSA donor private key must parse");
5098 let public_key = RsaPublicKey::from(&private_key);
5099 let session_key = [6_u8; 16];
5100 let label = b"xmlenc-label".to_vec();
5101 let wrapped = public_key
5102 .encrypt(
5103 &mut ChaCha20Rng::from_seed([17_u8; 32]),
5104 Oaep::<Sha256, Sha384>::new_with_mgf_hash_and_label(label.clone()),
5105 &session_key,
5106 )
5107 .expect("OAEP test wrapping must succeed");
5108 let encrypted_key = EncryptedKey {
5109 id: Some("wrapped-key".into()),
5110 sources: Default::default(),
5111 recipient: Some("recipient-a".into()),
5112 key_name: None,
5113 encryption_method: super::super::EncryptionMethod {
5114 chacha: None,
5115 algorithm: "http://www.w3.org/2009/xmlenc11#rsa-oaep".into(),
5116 key_size_bits: None,
5117 oaep_digest: Some("http://www.w3.org/2001/04/xmlenc#sha256".into()),
5118 mgf_algorithm: Some("http://www.w3.org/2009/xmlenc11#mgf1sha384".into()),
5119 oaep_params: Some(label),
5120 },
5121 cipher_data: super::super::CipherData::Value {
5122 value: STANDARD.encode(wrapped),
5123 },
5124 reference_list: None,
5125 carried_key_name: None,
5126 };
5127 let resolved = PrivateKeyDecryptor::new(private_key)
5128 .resolve_key(
5129 crate::provider::default_provider(),
5130 DataEncryptionAlgorithm::Aes128Gcm,
5131 Some(&encrypted_key),
5132 )
5133 .expect("OAEP 1.1 wrapped key must resolve");
5134 assert_eq!(resolved, session_key);
5135 }
5136
5137 #[test]
5138 fn decrypts_legacy_oaep_uri_with_sha256_digest() {
5139 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5141 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5142 ))
5143 .expect("RSA donor private key must parse");
5144 let public_key = RsaPublicKey::from(&private_key);
5145 let session_key = [8_u8; 16];
5146 let wrapped = public_key
5147 .encrypt(
5148 &mut ChaCha20Rng::from_seed([19_u8; 32]),
5149 Oaep::<Sha256, Sha1>::new_with_mgf_hash(),
5150 &session_key,
5151 )
5152 .expect("legacy OAEP URI test wrapping must succeed");
5153 let encrypted_key = EncryptedKey {
5154 id: None,
5155 sources: Default::default(),
5156 recipient: None,
5157 key_name: None,
5158 encryption_method: super::super::EncryptionMethod {
5159 chacha: None,
5160 algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p".into(),
5161 key_size_bits: None,
5162 oaep_digest: Some("http://www.w3.org/2001/04/xmlenc#sha256".into()),
5163 mgf_algorithm: None,
5164 oaep_params: None,
5165 },
5166 cipher_data: super::super::CipherData::Value {
5167 value: STANDARD.encode(wrapped),
5168 },
5169 reference_list: None,
5170 carried_key_name: None,
5171 };
5172 let resolved = PrivateKeyDecryptor::new(private_key)
5173 .resolve_key(
5174 crate::provider::default_provider(),
5175 DataEncryptionAlgorithm::Aes128Gcm,
5176 Some(&encrypted_key),
5177 )
5178 .expect("legacy OAEP URI with SHA-256 must resolve");
5179 assert_eq!(resolved, session_key);
5180 }
5181
5182 #[test]
5183 fn decrypts_sha384_oaep_with_the_xmlenc_digest_uri() {
5184 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5187 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5188 ))
5189 .expect("RSA donor private key must parse");
5190 let public_key = RsaPublicKey::from(&private_key);
5191 let session_key = [9_u8; 16];
5192 let digest = "http://www.w3.org/2001/04/xmlenc#sha384";
5193
5194 for (algorithm, mgf_algorithm) in [
5195 ("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p", None),
5196 (
5197 "http://www.w3.org/2009/xmlenc11#rsa-oaep",
5198 Some("http://www.w3.org/2009/xmlenc11#mgf1sha1"),
5199 ),
5200 ] {
5201 let wrapped = public_key
5202 .encrypt(
5203 &mut ChaCha20Rng::from_seed([23_u8; 32]),
5204 Oaep::<Sha384, Sha1>::new_with_mgf_hash(),
5205 &session_key,
5206 )
5207 .expect("SHA-384 OAEP test wrapping must succeed");
5208 let encrypted_key = EncryptedKey {
5209 id: None,
5210 sources: Default::default(),
5211 recipient: None,
5212 key_name: None,
5213 encryption_method: super::super::EncryptionMethod {
5214 chacha: None,
5215 algorithm: algorithm.into(),
5216 key_size_bits: None,
5217 oaep_digest: Some(digest.into()),
5218 mgf_algorithm: mgf_algorithm.map(str::to_owned),
5219 oaep_params: None,
5220 },
5221 cipher_data: super::super::CipherData::Value {
5222 value: STANDARD.encode(wrapped),
5223 },
5224 reference_list: None,
5225 carried_key_name: None,
5226 };
5227 let resolved = PrivateKeyDecryptor::new(private_key.clone())
5228 .resolve_key(
5229 crate::provider::default_provider(),
5230 DataEncryptionAlgorithm::Aes128Gcm,
5231 Some(&encrypted_key),
5232 )
5233 .expect("official XMLENC SHA-384 URI must resolve");
5234 assert_eq!(resolved, session_key);
5235 }
5236 }
5237
5238 #[test]
5239 fn rejects_unknown_oaep_digest_and_mgf_as_unsupported() {
5240 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5242 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5243 ))
5244 .expect("RSA donor private key must parse");
5245 let decryptor = PrivateKeyDecryptor::new(private_key);
5246 let mut encrypted_key = EncryptedKey {
5247 sources: Default::default(),
5248 id: None,
5249 recipient: None,
5250 key_name: None,
5251 encryption_method: super::super::EncryptionMethod {
5252 chacha: None,
5253 algorithm: "http://www.w3.org/2009/xmlenc11#rsa-oaep".into(),
5254 key_size_bits: None,
5255 oaep_digest: Some("urn:unsupported:digest".into()),
5256 mgf_algorithm: Some("http://www.w3.org/2009/xmlenc11#mgf1sha1".into()),
5257 oaep_params: None,
5258 },
5259 cipher_data: super::super::CipherData::Value {
5260 value: STANDARD.encode([0_u8; 256]),
5261 },
5262 reference_list: None,
5263 carried_key_name: None,
5264 };
5265 assert!(matches!(
5266 decryptor.resolve_key(crate::provider::default_provider(), DataEncryptionAlgorithm::Aes128Gcm, Some(&encrypted_key)),
5267 Err(XmlEncError::UnsupportedAlgorithm(uri)) if uri == "urn:unsupported:digest"
5268 ));
5269
5270 encrypted_key.encryption_method.oaep_digest = None;
5271 encrypted_key.encryption_method.mgf_algorithm = Some("urn:unsupported:mgf".into());
5272 assert!(matches!(
5273 decryptor.resolve_key(crate::provider::default_provider(), DataEncryptionAlgorithm::Aes128Gcm, Some(&encrypted_key)),
5274 Err(XmlEncError::UnsupportedAlgorithm(uri)) if uri == "urn:unsupported:mgf"
5275 ));
5276 }
5277
5278 #[test]
5279 fn decryption_policy_enforces_oaep_digest_and_plaintext_limits() {
5280 let encrypted_key = EncryptedKey {
5283 id: None,
5284 recipient: Some("selected".into()),
5285 sources: Default::default(),
5286 key_name: None,
5287 encryption_method: super::super::EncryptionMethod {
5288 chacha: None,
5289 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p.uri().into(),
5290 key_size_bits: None,
5291 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
5292 mgf_algorithm: Some(
5293 OaepDigestAlgorithm::Sha384
5294 .mgf_uri()
5295 .expect("MGF1-SHA384 URI")
5296 .into(),
5297 ),
5298 oaep_params: None,
5299 },
5300 cipher_data: super::super::CipherData::Value {
5301 value: STANDARD.encode([0_u8; 256]),
5302 },
5303 reference_list: None,
5304 carried_key_name: None,
5305 };
5306 let encrypted = EncryptedData {
5307 encapsulation_methods: Vec::new(),
5308 id: None,
5309 derived_keys: Vec::new(),
5310 agreement_methods: Vec::new(),
5311 encrypted_type: None,
5312 key_name: None,
5313 encryption_method: super::super::EncryptionMethod {
5314 chacha: None,
5315 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5316 key_size_bits: None,
5317 oaep_digest: None,
5318 mgf_algorithm: None,
5319 oaep_params: None,
5320 },
5321 encrypted_keys: vec![encrypted_key],
5322 cipher_data: super::super::CipherData::Value {
5323 value: STANDARD.encode([0_u8; 28]),
5324 },
5325 };
5326 let policy = crate::policy::DecryptionPolicy {
5327 oaep_digests: Some(std::collections::HashSet::from([
5328 OaepDigestAlgorithm::Sha256,
5329 ])),
5330 ..crate::policy::DecryptionPolicy::default()
5331 };
5332 assert!(matches!(
5333 DecryptContext::new(&RecipientKeyResolver {
5334 recipient: "selected",
5335 key: vec![0_u8; 16],
5336 })
5337 .policy(policy)
5338 .decrypt_data(&encrypted),
5339 Err(XmlEncError::Policy(
5340 crate::policy::PolicyViolation::Algorithm { algorithm, .. }
5341 )) if algorithm == OaepDigestAlgorithm::Sha384.mgf_uri().expect("MGF1-SHA384 URI")
5342 ));
5343
5344 let ciphertext = crate::provider::default_provider()
5345 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &[0_u8; 16], b"four")
5346 .expect("test encryption must succeed");
5347 let bounded = EncryptedData {
5348 encrypted_keys: Vec::new(),
5349 cipher_data: super::super::CipherData::Value {
5350 value: STANDARD.encode(ciphertext),
5351 },
5352 ..encrypted
5353 };
5354 let policy = crate::policy::DecryptionPolicy {
5355 resources: crate::policy::ResourcePolicy {
5356 max_encryption_plaintext_bytes: 3,
5357 ..crate::policy::ResourcePolicy::default()
5358 },
5359 ..crate::policy::DecryptionPolicy::default()
5360 };
5361 assert!(matches!(
5362 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16]))
5363 .policy(policy)
5364 .decrypt_data(&bounded),
5365 Err(XmlEncError::Policy(
5366 crate::policy::PolicyViolation::ResourceLimit {
5367 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
5368 maximum: 3,
5369 actual: 4
5370 }
5371 ))
5372 ));
5373
5374 let cbc_ciphertext = crate::provider::default_provider()
5375 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &[0_u8; 16], b"four")
5376 .expect("test CBC encryption must succeed");
5377 let bounded_cbc = EncryptedData {
5378 encryption_method: super::super::EncryptionMethod {
5379 chacha: None,
5380 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
5381 key_size_bits: None,
5382 oaep_digest: None,
5383 mgf_algorithm: None,
5384 oaep_params: None,
5385 },
5386 encrypted_keys: Vec::new(),
5387 cipher_data: super::super::CipherData::Value {
5388 value: STANDARD.encode(cbc_ciphertext),
5389 },
5390 ..bounded
5391 };
5392 let policy = crate::policy::DecryptionPolicy {
5393 resources: crate::policy::ResourcePolicy {
5394 max_encryption_plaintext_bytes: 4,
5395 ..crate::policy::ResourcePolicy::default()
5396 },
5397 ..crate::policy::DecryptionPolicy::default()
5398 };
5399 assert_eq!(
5400 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16]))
5401 .policy(policy)
5402 .decrypt_data(&bounded_cbc)
5403 .expect("CBC plaintext at the configured limit must decrypt"),
5404 DecryptedContent::Bytes(b"four".to_vec())
5405 );
5406 }
5407
5408 #[test]
5409 fn typed_decryption_input_cannot_bypass_metadata_policy() {
5410 let ciphertext = crate::provider::default_provider()
5413 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &[0_u8; 16], b"data")
5414 .expect("test encryption must succeed");
5415 let encrypted = EncryptedData {
5416 encapsulation_methods: Vec::new(),
5417 id: Some("oversized".into()),
5418 derived_keys: Vec::new(),
5419 agreement_methods: Vec::new(),
5420 encrypted_type: None,
5421 key_name: None,
5422 encryption_method: super::super::EncryptionMethod {
5423 chacha: None,
5424 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5425 key_size_bits: None,
5426 oaep_digest: None,
5427 mgf_algorithm: None,
5428 oaep_params: None,
5429 },
5430 encrypted_keys: Vec::new(),
5431 cipher_data: super::super::CipherData::Value {
5432 value: STANDARD.encode(ciphertext),
5433 },
5434 };
5435 let policy = crate::policy::DecryptionPolicy {
5436 resources: crate::policy::ResourcePolicy {
5437 max_encryption_metadata_bytes: 8,
5438 ..crate::policy::ResourcePolicy::default()
5439 },
5440 ..crate::policy::DecryptionPolicy::default()
5441 };
5442
5443 let resolver = AllCallsResolver {
5444 calls: Cell::new(0),
5445 key: vec![0_u8; 16],
5446 };
5447 assert!(matches!(
5448 DecryptContext::new(&resolver)
5449 .policy(policy)
5450 .decrypt_data(&encrypted),
5451 Err(XmlEncError::Policy(
5452 crate::policy::PolicyViolation::ResourceLimit {
5453 resource: crate::policy::resource_name::ENCRYPTION_METADATA_BYTES,
5454 maximum: 8,
5455 actual: 9,
5456 }
5457 ))
5458 ));
5459 assert_eq!(
5460 resolver.calls.get(),
5461 0,
5462 "metadata preflight must gate resolver-controlled work"
5463 );
5464 }
5465
5466 #[test]
5467 fn typed_cipher_values_are_bounded_before_decode_or_resolution() {
5468 let key = [0x41_u8; 16];
5471 let ciphertext = crate::provider::default_provider()
5472 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5473 .expect("test encryption must succeed");
5474 let mut encrypted = EncryptedData {
5475 encapsulation_methods: Vec::new(),
5476 id: None,
5477 derived_keys: Vec::new(),
5478 agreement_methods: Vec::new(),
5479 encrypted_type: None,
5480 key_name: None,
5481 encryption_method: super::super::EncryptionMethod {
5482 chacha: None,
5483 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5484 key_size_bits: None,
5485 oaep_digest: None,
5486 mgf_algorithm: None,
5487 oaep_params: None,
5488 },
5489 encrypted_keys: Vec::new(),
5490 cipher_data: super::super::CipherData::Value {
5491 value: STANDARD.encode(ciphertext),
5492 },
5493 };
5494 let policy = crate::policy::DecryptionPolicy {
5495 resources: crate::policy::ResourcePolicy {
5496 max_encryption_plaintext_bytes: 4,
5497 ..crate::policy::ResourcePolicy::default()
5498 },
5499 ..crate::policy::DecryptionPolicy::default()
5500 };
5501 *encrypted
5502 .cipher_data
5503 .inline_value_mut()
5504 .expect("inline ciphertext") = "A".repeat(48);
5505 assert!(matches!(
5506 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
5507 .policy(policy)
5508 .decrypt_data(&encrypted),
5509 Err(XmlEncError::Policy(
5510 crate::policy::PolicyViolation::ResourceLimit {
5511 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
5512 ..
5513 }
5514 ))
5515 ));
5516
5517 *encrypted
5518 .cipher_data
5519 .inline_value_mut()
5520 .expect("inline ciphertext") = STANDARD.encode([0_u8; 28]);
5521 encrypted.encrypted_keys.push(EncryptedKey {
5522 sources: Default::default(),
5523 id: None,
5524 recipient: None,
5525 key_name: None,
5526 encryption_method: super::super::EncryptionMethod {
5527 chacha: None,
5528 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
5529 key_size_bits: None,
5530 oaep_digest: None,
5531 mgf_algorithm: None,
5532 oaep_params: None,
5533 },
5534 cipher_data: super::super::CipherData::Value {
5535 value: "A".repeat(MAX_CIPHER_VALUE_BASE64_LEN + 4),
5536 },
5537 reference_list: None,
5538 carried_key_name: None,
5539 });
5540 let resolver = CountingResolver {
5541 candidate_calls: Cell::new(0),
5542 key: key.to_vec(),
5543 };
5544 assert!(matches!(
5545 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5546 Err(XmlEncError::InvalidStructure(_))
5547 ));
5548 assert_eq!(resolver.candidate_calls.get(), 0);
5549
5550 *encrypted.encrypted_keys[0]
5551 .cipher_data
5552 .inline_value_mut()
5553 .expect("inline wrapped key") = "AAAA".into();
5554 let aggregate_encoded_len = encrypted
5555 .cipher_data
5556 .inline_value()
5557 .expect("inline ciphertext")
5558 .len()
5559 + encrypted.encrypted_keys[0]
5560 .cipher_data
5561 .inline_value()
5562 .expect("inline wrapped key")
5563 .len();
5564 let policy = crate::policy::DecryptionPolicy {
5565 resources: crate::policy::ResourcePolicy {
5566 max_encryption_plaintext_bytes: 4,
5567 max_xml_document_bytes: aggregate_encoded_len - 1,
5568 ..crate::policy::ResourcePolicy::default()
5569 },
5570 ..crate::policy::DecryptionPolicy::default()
5571 };
5572 let resolver = CountingResolver {
5573 candidate_calls: Cell::new(0),
5574 key: key.to_vec(),
5575 };
5576 assert!(matches!(
5577 DecryptContext::new(&resolver)
5578 .policy(policy)
5579 .decrypt_data(&encrypted),
5580 Err(XmlEncError::Policy(
5581 crate::policy::PolicyViolation::ResourceLimit {
5582 resource:
5583 crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
5584 maximum,
5585 actual,
5586 }
5587 )) if maximum == aggregate_encoded_len - 1 && actual == aggregate_encoded_len
5588 ));
5589 assert_eq!(resolver.candidate_calls.get(), 0);
5590 }
5591
5592 #[cfg(feature = "experimental-pq")]
5593 #[test]
5594 fn typed_kem_width_is_checked_before_any_resolver_dispatch() {
5595 let document = crate::XmlDomDocument::parse(include_str!(
5598 "../../tests/fixtures/xmlenc/aleksey-xmlenc-01/enc-aes128gcm-em-ml-kem-512.xml"
5599 ))
5600 .expect("valid donor XML");
5601 let node = document
5602 .descendants()
5603 .find(|node| node.has_tag_name((crate::xmlenc::types::XMLENC_NS, "EncryptedData")))
5604 .expect("donor EncryptedData");
5605 let mut policy = crate::policy::DecryptionPolicy::default();
5606 policy
5607 .key_establishment
5608 .encapsulation_algorithms
5609 .insert(crate::provider::KeyEncapsulationAlgorithm::MlKem512);
5610 let data = super::super::parse_encrypted_data_node_with_policy(node, &policy)
5611 .expect("valid donor encryption metadata");
5612 for width in [0, 767, 769] {
5613 for nested in [false, true] {
5614 let mut encrypted = data.clone();
5615 encrypted.encapsulation_methods[0]
5616 .ciphertext
5617 .resize(width, 0);
5618 if nested {
5619 encrypted.encrypted_keys.push(EncryptedKey {
5620 sources: super::super::EncryptionKeySources {
5621 encapsulation_methods: std::mem::take(
5622 &mut encrypted.encapsulation_methods,
5623 ),
5624 ..Default::default()
5625 },
5626 id: None,
5627 recipient: None,
5628 key_name: None,
5629 encryption_method: super::super::EncryptionMethod {
5630 chacha: None,
5631 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
5632 key_size_bits: None,
5633 oaep_digest: None,
5634 mgf_algorithm: None,
5635 oaep_params: None,
5636 },
5637 cipher_data: super::super::CipherData::Bytes(vec![0; 24]),
5638 reference_list: None,
5639 carried_key_name: None,
5640 });
5641 }
5642 let resolver = CountingResolver {
5643 candidate_calls: Cell::new(0),
5644 key: vec![0; 16],
5645 };
5646 let error = DecryptContext::new(&resolver)
5647 .policy(policy.clone())
5648 .decrypt_data(&encrypted)
5649 .expect_err("malformed typed KEM width rejected");
5650 assert!(
5651 error
5652 .to_string()
5653 .contains("ciphertext size does not match Algorithm"),
5654 "{error}"
5655 );
5656 assert_eq!(resolver.candidate_calls.get(), 0);
5657 }
5658 }
5659 let mut encrypted = data;
5661 encrypted.encryption_method.algorithm = DataEncryptionAlgorithm::Aes256Cbc.uri().into();
5662 encrypted.cipher_data = super::super::CipherData::Bytes(vec![0; 32]);
5663 let resolver = CountingResolver {
5664 candidate_calls: Cell::new(0),
5665 key: vec![0; 32],
5666 };
5667 assert!(matches!(
5668 DecryptContext::new(&resolver)
5669 .policy(policy)
5670 .decrypt_data(&encrypted),
5671 Err(XmlEncError::Policy(
5672 crate::policy::PolicyViolation::Algorithm {
5673 operation: "direct KEM content without external authentication",
5674 ..
5675 }
5676 ))
5677 ));
5678 assert_eq!(resolver.candidate_calls.get(), 0);
5679 }
5680
5681 #[cfg(feature = "experimental-pq")]
5682 #[test]
5683 fn typed_kem_ciphertext_shares_aggregate_cipher_value_budget() {
5684 let xml = include_str!(
5687 "../../tests/fixtures/xmlenc/aleksey-xmlenc-01/enc-aes128gcm-em-ml-kem-512.xml"
5688 );
5689 let document = crate::XmlDomDocument::parse(xml).expect("donor XML parses");
5690 let node = document
5691 .descendants()
5692 .find(|node| node.has_tag_name((crate::xmlenc::types::XMLENC_NS, "EncryptedData")))
5693 .expect("donor EncryptedData");
5694 let mut policy = crate::policy::DecryptionPolicy::default();
5695 policy
5696 .key_establishment
5697 .encapsulation_algorithms
5698 .insert(crate::provider::KeyEncapsulationAlgorithm::MlKem512);
5699 let mut encrypted = super::super::parse_encrypted_data_node_with_policy(node, &policy)
5700 .expect("permitted donor recipient");
5701 let content = cipher_data_storage_len(&encrypted.cipher_data);
5702 let kem_encoded = 4 * encrypted.encapsulation_methods[0]
5703 .ciphertext
5704 .len()
5705 .div_ceil(3);
5706 assert!(
5707 validate_typed_cipher_values(
5708 &encrypted,
5709 DataEncryptionAlgorithm::Aes128Gcm,
5710 usize::MAX / 2,
5711 content + kem_encoded
5712 )
5713 .is_ok()
5714 );
5715 for nested in [false, true] {
5716 if nested {
5717 encrypted.encrypted_keys.push(EncryptedKey {
5718 sources: super::super::EncryptionKeySources {
5719 encapsulation_methods: std::mem::take(&mut encrypted.encapsulation_methods),
5720 ..Default::default()
5721 },
5722 id: None,
5723 recipient: None,
5724 key_name: None,
5725 encryption_method: super::super::EncryptionMethod {
5726 chacha: None,
5727 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
5728 key_size_bits: None,
5729 oaep_digest: None,
5730 mgf_algorithm: None,
5731 oaep_params: None,
5732 },
5733 cipher_data: super::super::CipherData::Bytes(vec![0; 24]),
5734 reference_list: None,
5735 carried_key_name: None,
5736 });
5737 }
5738 let total = content + kem_encoded + if nested { 24 } else { 0 };
5739 policy.resources.max_xml_document_bytes = total - 1;
5740 let resolver = CountingResolver {
5741 candidate_calls: Cell::new(0),
5742 key: vec![0; 16],
5743 };
5744 assert!(
5745 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)
5746 );
5747 assert_eq!(resolver.candidate_calls.get(), 0);
5748 }
5749 }
5750
5751 #[test]
5752 fn typed_legacy_oaep_accepts_explicit_mgf() {
5753 let key = [0x43_u8; 16];
5756 let ciphertext = crate::provider::default_provider()
5757 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5758 .expect("test encryption must succeed");
5759 let encrypted = EncryptedData {
5760 encapsulation_methods: Vec::new(),
5761 id: None,
5762 derived_keys: Vec::new(),
5763 agreement_methods: Vec::new(),
5764 encrypted_type: None,
5765 key_name: None,
5766 encryption_method: super::super::EncryptionMethod {
5767 chacha: None,
5768 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5769 key_size_bits: None,
5770 oaep_digest: None,
5771 mgf_algorithm: None,
5772 oaep_params: None,
5773 },
5774 encrypted_keys: vec![EncryptedKey {
5775 sources: Default::default(),
5776 id: None,
5777 recipient: None,
5778 key_name: None,
5779 encryption_method: super::super::EncryptionMethod {
5780 chacha: None,
5781 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p.uri().into(),
5782 key_size_bits: None,
5783 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
5784 mgf_algorithm: Some(
5785 OaepDigestAlgorithm::Sha384
5786 .mgf_uri()
5787 .expect("MGF1-SHA384 URI")
5788 .into(),
5789 ),
5790 oaep_params: None,
5791 },
5792 cipher_data: super::super::CipherData::Value {
5793 value: STANDARD.encode([0_u8; 256]),
5794 },
5795 reference_list: None,
5796 carried_key_name: None,
5797 }],
5798 cipher_data: super::super::CipherData::Value {
5799 value: STANDARD.encode(ciphertext),
5800 },
5801 };
5802 let resolver = CountingResolver {
5803 candidate_calls: Cell::new(0),
5804 key: key.to_vec(),
5805 };
5806
5807 assert_eq!(
5808 DecryptContext::new(&resolver)
5809 .decrypt_data(&encrypted)
5810 .expect("explicit legacy OAEP MGF must reach key resolution"),
5811 DecryptedContent::Bytes(b"data".to_vec())
5812 );
5813 assert_eq!(resolver.candidate_calls.get(), 1);
5814 }
5815
5816 #[test]
5817 fn typed_zero_key_size_is_rejected_before_key_resolution() {
5818 let key = [0x45_u8; 16];
5821 let ciphertext = crate::provider::default_provider()
5822 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5823 .expect("test encryption must succeed");
5824 let encrypted = EncryptedData {
5825 encapsulation_methods: Vec::new(),
5826 id: None,
5827 derived_keys: Vec::new(),
5828 agreement_methods: Vec::new(),
5829 encrypted_type: None,
5830 key_name: None,
5831 encryption_method: super::super::EncryptionMethod {
5832 chacha: None,
5833 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5834 key_size_bits: None,
5835 oaep_digest: None,
5836 mgf_algorithm: None,
5837 oaep_params: None,
5838 },
5839 encrypted_keys: vec![EncryptedKey {
5840 id: None,
5841 sources: Default::default(),
5842 recipient: None,
5843 key_name: None,
5844 encryption_method: super::super::EncryptionMethod {
5845 chacha: None,
5846 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
5847 key_size_bits: Some(0),
5848 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
5849 mgf_algorithm: Some(
5850 OaepDigestAlgorithm::Sha256
5851 .mgf_uri()
5852 .expect("MGF1-SHA256 URI")
5853 .into(),
5854 ),
5855 oaep_params: None,
5856 },
5857 cipher_data: super::super::CipherData::Value {
5858 value: STANDARD.encode([0_u8; 256]),
5859 },
5860 reference_list: None,
5861 carried_key_name: None,
5862 }],
5863 cipher_data: super::super::CipherData::Value {
5864 value: STANDARD.encode(ciphertext),
5865 },
5866 };
5867 let resolver = CountingResolver {
5868 candidate_calls: Cell::new(0),
5869 key: key.to_vec(),
5870 };
5871
5872 assert!(matches!(
5873 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5874 Err(XmlEncError::InvalidStructure(message))
5875 if message == "KeySize must be a positive integer"
5876 ));
5877 assert_eq!(resolver.candidate_calls.get(), 0);
5878 }
5879
5880 #[test]
5881 fn typed_content_method_is_validated_before_key_resolution() {
5882 let key = [0x44_u8; 16];
5885 let ciphertext = crate::provider::default_provider()
5886 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5887 .expect("test encryption must succeed");
5888 let encrypted = EncryptedData {
5889 encapsulation_methods: Vec::new(),
5890 id: None,
5891 derived_keys: Vec::new(),
5892 agreement_methods: Vec::new(),
5893 encrypted_type: None,
5894 key_name: None,
5895 encryption_method: super::super::EncryptionMethod {
5896 chacha: None,
5897 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5898 key_size_bits: Some(256),
5899 oaep_digest: None,
5900 mgf_algorithm: None,
5901 oaep_params: None,
5902 },
5903 encrypted_keys: Vec::new(),
5904 cipher_data: super::super::CipherData::Value {
5905 value: STANDARD.encode(ciphertext),
5906 },
5907 };
5908 let resolver = AllCallsResolver {
5909 calls: Cell::new(0),
5910 key: key.to_vec(),
5911 };
5912
5913 assert!(matches!(
5914 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5915 Err(XmlEncError::InvalidStructure(message))
5916 if message.contains("requires KeySize 128, got 256")
5917 ));
5918 assert_eq!(resolver.calls.get(), 0);
5919 }
5920
5921 #[test]
5922 fn unknown_encrypted_key_algorithm_never_reaches_resolver() {
5923 let key = [0x42_u8; 16];
5926 let ciphertext = crate::provider::default_provider()
5927 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5928 .expect("test encryption must succeed");
5929 let encrypted = EncryptedData {
5930 encapsulation_methods: Vec::new(),
5931 id: None,
5932 derived_keys: Vec::new(),
5933 agreement_methods: Vec::new(),
5934 encrypted_type: None,
5935 key_name: None,
5936 encryption_method: super::super::EncryptionMethod {
5937 chacha: None,
5938 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5939 key_size_bits: None,
5940 oaep_digest: None,
5941 mgf_algorithm: None,
5942 oaep_params: None,
5943 },
5944 encrypted_keys: vec![EncryptedKey {
5945 id: None,
5946 recipient: None,
5947 sources: Default::default(),
5948 key_name: None,
5949 encryption_method: super::super::EncryptionMethod {
5950 chacha: None,
5951 algorithm: "urn:example:unknown-key-algorithm".into(),
5952 key_size_bits: None,
5953 oaep_digest: None,
5954 mgf_algorithm: None,
5955 oaep_params: None,
5956 },
5957 cipher_data: super::super::CipherData::Value {
5958 value: STANDARD.encode([0_u8; 24]),
5959 },
5960 reference_list: None,
5961 carried_key_name: None,
5962 }],
5963 cipher_data: super::super::CipherData::Value {
5964 value: STANDARD.encode(ciphertext),
5965 },
5966 };
5967 let resolver = CountingResolver {
5968 candidate_calls: Cell::new(0),
5969 key: key.to_vec(),
5970 };
5971
5972 assert!(matches!(
5973 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5974 Err(XmlEncError::UnsupportedAlgorithm(_))
5975 ));
5976 assert_eq!(resolver.candidate_calls.get(), 0);
5977 }
5978
5979 #[test]
5980 fn cbc_padding_errors_do_not_expose_decrypted_octets() {
5981 let error = map_data_decryption_error(
5984 DataEncryptionAlgorithm::Aes128Cbc,
5985 32,
5986 crate::provider::ProviderError::InvalidInput(
5987 crate::provider::ProviderInputError::AesCbcCiphertext,
5988 ),
5989 );
5990
5991 assert_eq!(error.to_string(), "invalid XMLEnc padding");
5992 }
5993
5994 #[test]
5995 fn replaces_element_and_content_in_caller_owned_documents() {
5996 let key = [0x31_u8; 16];
5999 let element = encrypted_gcm_element(
6000 "http://www.w3.org/2001/04/xmlenc#Element",
6001 "<secret id=\"visible\">value</secret>",
6002 None,
6003 true,
6004 &key,
6005 );
6006 assert_eq!(
6007 decrypt_document(&element, None, &SymmetricKeyDecryptor::new(key))
6008 .expect("root Element replacement must succeed"),
6009 "<secret id=\"visible\">value</secret>"
6010 );
6011
6012 let content = encrypted_gcm_element(
6013 "http://www.w3.org/2001/04/xmlenc#Content",
6014 "before<child/>after",
6015 None,
6016 false,
6017 &key,
6018 );
6019 let document =
6020 format!("<root xmlns:xenc=\"{XMLENC_NS}\"><prefix/>{content}<suffix/></root>");
6021 let replaced = decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key))
6022 .expect("nested Content replacement must succeed");
6023 assert_eq!(
6024 replaced,
6025 format!(
6026 "<root xmlns:xenc=\"{XMLENC_NS}\"><prefix/>before<child/>after<suffix/></root>"
6027 )
6028 );
6029 }
6030
6031 #[test]
6032 fn accepts_whitespace_and_comments_around_element_plaintext() {
6033 let key = [0x34_u8; 16];
6036 let plaintext = "\n<!--before--><secret/><!--after-->\n";
6037 let encrypted = encrypted_gcm_element(
6038 "http://www.w3.org/2001/04/xmlenc#Element",
6039 plaintext,
6040 None,
6041 true,
6042 &key,
6043 );
6044
6045 assert_eq!(
6046 decrypt_document(&encrypted, None, &SymmetricKeyDecryptor::new(key))
6047 .expect("one element with boundary trivia must be accepted"),
6048 plaintext
6049 );
6050 }
6051
6052 #[test]
6053 fn decrypts_unknown_and_empty_type_hints_as_opaque_bytes() {
6054 let key = [0x35_u8; 16];
6057 let plaintext = "\0opaque\u{ff}bytes";
6058 let unknown = encrypted_gcm_element("urn:example:binary", plaintext, None, true, &key);
6059 let empty = encrypted_gcm_element("", plaintext, None, true, &key).replacen(
6060 "<xenc:EncryptedData",
6061 "<xenc:EncryptedData Type=\"\"",
6062 1,
6063 );
6064
6065 let parsed = parse_encrypted_data(&unknown).expect("unknown Type must remain parseable");
6066 assert_eq!(
6067 parsed.encrypted_type,
6068 Some(EncryptedDataType::Other("urn:example:binary".into()))
6069 );
6070 assert!(matches!(
6071 decrypt_document(&unknown, None, &SymmetricKeyDecryptor::new(key)),
6072 Err(XmlEncError::ReplacementRequiresXml)
6073 ));
6074
6075 for encrypted in [unknown, empty] {
6076 assert_eq!(
6077 decrypt(&encrypted, &SymmetricKeyDecryptor::new(key))
6078 .expect("opaque Type hints must not block decryption"),
6079 DecryptedContent::Bytes(plaintext.as_bytes().to_vec())
6080 );
6081 }
6082 }
6083
6084 #[test]
6085 fn selects_document_encrypted_data_by_id_and_rejects_ambiguity() {
6086 let key = [0x32_u8; 16];
6089 let first = encrypted_gcm_element(
6090 "http://www.w3.org/2001/04/xmlenc#Content",
6091 "first",
6092 Some("first"),
6093 false,
6094 &key,
6095 );
6096 let second = encrypted_gcm_element(
6097 "http://www.w3.org/2001/04/xmlenc#Content",
6098 "second",
6099 Some("second"),
6100 false,
6101 &key,
6102 );
6103 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{first}{second}</root>");
6104 let resolver = SymmetricKeyDecryptor::new(key);
6105 assert!(matches!(
6106 decrypt_document(&document, None, &resolver),
6107 Err(XmlEncError::AmbiguousEncryptedData)
6108 ));
6109 let replaced = decrypt_document(&document, Some("second"), &resolver)
6110 .expect("Id selection must choose exactly one encrypted region");
6111 assert!(replaced.contains("second"));
6112 assert!(replaced.contains("Id=\"first\""));
6113 assert!(matches!(
6114 decrypt_document(&document, Some("missing"), &resolver),
6115 Err(XmlEncError::EncryptedDataNotFound)
6116 ));
6117 }
6118
6119 #[test]
6120 fn selects_encrypted_data_below_a_unique_operation_start_node() {
6121 let key = [0x42_u8; 16];
6124 let first = encrypted_gcm_element(
6125 "http://www.w3.org/2001/04/xmlenc#Content",
6126 "first",
6127 None,
6128 false,
6129 &key,
6130 );
6131 let second = encrypted_gcm_element(
6132 "http://www.w3.org/2001/04/xmlenc#Content",
6133 "second",
6134 None,
6135 false,
6136 &key,
6137 );
6138 let document = format!(
6139 "<root xmlns:xenc=\"{XMLENC_NS}\"><scope Id=\"first\">{first}</scope><scope Id=\"second\">{second}</scope></root>"
6140 );
6141 let resolver = SymmetricKeyDecryptor::new(key);
6142 let context = DecryptContext::new(&resolver);
6143 let replaced = context
6144 .decrypt_document_from_start_node(&document, Some("second"))
6145 .expect("ancestor ID must select its encrypted descendant");
6146 assert!(replaced.contains("<scope Id=\"second\">second</scope>"));
6147 assert!(replaced.contains("<scope Id=\"first\"><xenc:EncryptedData"));
6148
6149 assert!(matches!(
6150 context.decrypt_document_from_start_node(&document, Some("missing")),
6151 Err(XmlEncError::SelectedNodeUnavailable { id }) if id == "missing"
6152 ));
6153 let duplicate = document.replace("Id=\"second\"", "Id=\"first\"");
6154 assert!(matches!(
6155 context.decrypt_document_from_start_node(&duplicate, Some("first")),
6156 Err(XmlEncError::SelectedNodeUnavailable { id }) if id == "first"
6157 ));
6158 let ambiguous = format!(
6159 "<root xmlns:xenc=\"{XMLENC_NS}\"><scope Id=\"selected\">{first}{second}</scope></root>"
6160 );
6161 assert!(matches!(
6162 context.decrypt_document_from_start_node(&ambiguous, Some("selected")),
6163 Err(XmlEncError::AmbiguousEncryptedData)
6164 ));
6165
6166 let first_replaced = context
6167 .decrypt_first_document_from_start_node(&ambiguous, Some("selected"))
6168 .expect("first-match selection must leave later encrypted descendants untouched");
6169 assert!(first_replaced.contains("<scope Id=\"selected\">first<xenc:EncryptedData"));
6170 let replaced_document =
6171 Document::parse(&first_replaced).expect("first-match output must remain valid XML");
6172 assert_eq!(
6173 replaced_document
6174 .descendants()
6175 .filter(|node| node.has_tag_name((XMLENC_NS, "EncryptedData")))
6176 .count(),
6177 1
6178 );
6179 }
6180
6181 #[test]
6182 fn rejects_non_xml_or_malformed_document_replacement_plaintext() {
6183 let key = [0x33_u8; 16];
6186 let binary = encrypted_gcm_element("", "binary", None, true, &key);
6187 assert!(matches!(
6188 decrypt_document(&binary, None, &SymmetricKeyDecryptor::new(key)),
6189 Err(XmlEncError::ReplacementRequiresXml)
6190 ));
6191
6192 let malformed = encrypted_gcm_element(
6193 "http://www.w3.org/2001/04/xmlenc#Element",
6194 "<unclosed>",
6195 None,
6196 true,
6197 &key,
6198 );
6199 assert!(matches!(
6200 decrypt_document(&malformed, None, &SymmetricKeyDecryptor::new(key)),
6201 Err(XmlEncError::XmlParse(_))
6202 ));
6203
6204 for invalid_element in ["text-only", "<first/><second/>"] {
6205 let encrypted = encrypted_gcm_element(
6206 "http://www.w3.org/2001/04/xmlenc#Element",
6207 invalid_element,
6208 None,
6209 false,
6210 &key,
6211 );
6212 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>");
6213 assert!(
6214 decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key)).is_err(),
6215 "Element plaintext must contain exactly one element: {invalid_element}"
6216 );
6217 }
6218
6219 let content = encrypted_gcm_element(
6220 "http://www.w3.org/2001/04/xmlenc#Content",
6221 "plaintext",
6222 None,
6223 false,
6224 &key,
6225 );
6226 let with_dtd = format!(
6227 "<!DOCTYPE root [<!ATTLIST root Id ID #IMPLIED>]><root xmlns:xenc=\"{XMLENC_NS}\">{content}</root>"
6228 );
6229 assert!(matches!(
6230 decrypt_document(&with_dtd, None, &SymmetricKeyDecryptor::new(key)),
6231 Err(XmlEncError::XmlParse(
6232 crate::xml::dom::ParseError::DtdDetected
6233 ))
6234 ));
6235 let mut policy = crate::policy::DecryptionPolicy::default();
6236 policy.xml.allow_internal_dtd = true;
6237 assert!(
6238 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6239 .policy(policy)
6240 .decrypt_document(&with_dtd, None)
6241 .expect("explicit internal-DTD opt-in must decrypt")
6242 .contains("plaintext")
6243 );
6244 }
6245
6246 #[test]
6247 fn rejects_plaintext_markup_that_crosses_the_encrypted_region() {
6248 let key = [0x36_u8; 16];
6252 let crossing_markup = "</parent><attacker/><parent>";
6253 for type_uri in [
6254 "http://www.w3.org/2001/04/xmlenc#Content",
6255 "http://www.w3.org/2001/04/xmlenc#Element",
6256 ] {
6257 let encrypted = encrypted_gcm_element(type_uri, crossing_markup, None, false, &key);
6258 let document =
6259 format!("<outer xmlns:xenc=\"{XMLENC_NS}\"><parent>{encrypted}</parent></outer>");
6260 assert!(
6261 decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key)).is_err(),
6262 "{type_uri} plaintext must not escape its replacement boundary"
6263 );
6264 }
6265 }
6266
6267 #[test]
6268 fn document_decryption_applies_byte_and_node_policy_before_parsing() {
6269 let key = [0x38_u8; 16];
6272 let encrypted = encrypted_gcm_element(
6273 "http://www.w3.org/2001/04/xmlenc#Content",
6274 "plaintext",
6275 None,
6276 false,
6277 &key,
6278 );
6279 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\"><a/>{encrypted}</root>");
6280 let byte_policy = crate::policy::DecryptionPolicy {
6281 resources: crate::policy::ResourcePolicy {
6282 max_xml_document_bytes: document.len() - 1,
6283 ..crate::policy::ResourcePolicy::default()
6284 },
6285 ..crate::policy::DecryptionPolicy::default()
6286 };
6287 assert!(matches!(
6288 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6289 .policy(byte_policy)
6290 .decrypt_document(&document, None),
6291 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
6292 resource: crate::policy::resource_name::XML_DOCUMENT,
6293 maximum,
6294 actual,
6295 })) if maximum == document.len() - 1 && actual == document.len()
6296 ));
6297
6298 let node_policy = crate::policy::DecryptionPolicy {
6299 resources: crate::policy::ResourcePolicy {
6300 max_xml_nodes: 3,
6301 ..crate::policy::ResourcePolicy::default()
6302 },
6303 ..crate::policy::DecryptionPolicy::default()
6304 };
6305 assert!(matches!(
6306 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6307 .policy(node_policy)
6308 .decrypt_document(&document, None),
6309 Err(XmlEncError::Policy(
6310 crate::policy::PolicyViolation::ResourceLimit {
6311 resource: crate::policy::resource_name::XML_NODES,
6312 maximum: 3,
6313 actual: 4,
6314 }
6315 ))
6316 ));
6317 }
6318
6319 #[test]
6320 fn decryption_entry_points_enforce_policy_depth() {
6321 let xml = "<root><child><leaf/></child></root>";
6324 let policy = crate::policy::DecryptionPolicy {
6325 resources: crate::policy::ResourcePolicy {
6326 max_xml_depth: 2,
6327 ..crate::policy::ResourcePolicy::default()
6328 },
6329 ..crate::policy::DecryptionPolicy::default()
6330 };
6331 let resolver = SymmetricKeyDecryptor::new([0_u8; 16]);
6332 let mut document = XmlDocument::parse(xml).expect("wide retained fixture must parse");
6333
6334 assert!(matches!(
6335 DecryptContext::new(&resolver)
6336 .policy(policy.clone())
6337 .decrypt_document(xml, None),
6338 Err(XmlEncError::Policy(
6339 crate::policy::PolicyViolation::ResourceLimit {
6340 resource: crate::policy::resource_name::XML_DEPTH,
6341 maximum: 2,
6342 actual: 3,
6343 }
6344 ))
6345 ));
6346 assert!(matches!(
6347 DecryptContext::new(&resolver)
6348 .policy(policy)
6349 .decrypt_owned_document(&mut document, None),
6350 Err(XmlEncError::Policy(
6351 crate::policy::PolicyViolation::ResourceLimit {
6352 resource: crate::policy::resource_name::XML_DEPTH,
6353 maximum: 2,
6354 actual: 3,
6355 }
6356 ))
6357 ));
6358 }
6359
6360 #[test]
6361 fn fragment_validation_does_not_charge_its_internal_wrapper_node() {
6362 let key = [0x39_u8; 16];
6365 let plaintext = "<item/>".repeat(20);
6366 let encrypted = encrypted_gcm_element(
6367 "http://www.w3.org/2001/04/xmlenc#Content",
6368 &plaintext,
6369 None,
6370 false,
6371 &key,
6372 );
6373 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>");
6374 let resolver = SymmetricKeyDecryptor::new(key);
6375 let expected = decrypt_document(&document, None, &resolver)
6376 .expect("unbounded setup decryption must succeed");
6377 let exact_output_nodes = Document::parse(&expected)
6378 .expect("decrypted output must parse")
6379 .descendants()
6380 .count();
6381 let policy = crate::policy::DecryptionPolicy {
6382 resources: crate::policy::ResourcePolicy {
6383 max_xml_nodes: exact_output_nodes,
6384 ..crate::policy::ResourcePolicy::default()
6385 },
6386 ..crate::policy::DecryptionPolicy::default()
6387 };
6388
6389 assert_eq!(
6390 DecryptContext::new(&resolver)
6391 .policy(policy)
6392 .decrypt_document(&document, None)
6393 .expect("temporary wrapper must not consume caller node budget"),
6394 expected
6395 );
6396 }
6397
6398 #[test]
6399 fn owned_decryption_rejects_projected_node_limit_atomically() {
6400 let key = [0x3a_u8; 16];
6403 let plaintext = "<item/>".repeat(64);
6404 let encrypted = encrypted_gcm_element(
6405 "http://www.w3.org/2001/04/xmlenc#Content",
6406 &plaintext,
6407 None,
6408 false,
6409 &key,
6410 );
6411 let mut document = XmlDocument::parse(format!(
6412 "<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>"
6413 ))
6414 .expect("owned encrypted fixture must parse");
6415 let input_nodes = document.with_view(|view| view.node_count());
6416 let before = document.as_xml().to_owned();
6417 let policy = crate::policy::DecryptionPolicy {
6418 resources: crate::policy::ResourcePolicy {
6419 max_xml_nodes: input_nodes,
6420 ..crate::policy::ResourcePolicy::default()
6421 },
6422 ..crate::policy::DecryptionPolicy::default()
6423 };
6424
6425 let error = DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6426 .policy(policy)
6427 .decrypt_owned_document(&mut document, None)
6428 .expect_err("expanded plaintext must exceed the operation node ceiling");
6429
6430 assert!(matches!(
6431 error,
6432 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
6433 resource: crate::policy::resource_name::XML_NODES,
6434 maximum,
6435 ..
6436 }) if maximum == input_nodes
6437 ));
6438 assert_eq!(document.as_xml(), before);
6439 assert_eq!(document.generation(), 0);
6440 }
6441
6442 #[test]
6443 fn owned_decryption_reports_decrypted_depth_as_policy() {
6444 let key = [0x3b_u8; 16];
6448 let plaintext = format!("{}value{}", "<nested>".repeat(32), "</nested>".repeat(32));
6449 let encrypted = encrypted_gcm_element(
6450 "http://www.w3.org/2001/04/xmlenc#Element",
6451 &plaintext,
6452 None,
6453 false,
6454 &key,
6455 );
6456 let mut document = XmlDocument::parse(format!(
6457 "<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>"
6458 ))
6459 .expect("encrypted fixture must parse");
6460 let input_depth = document.with_view(|view| view.max_depth());
6461 let before = document.as_xml().to_owned();
6462 let policy = crate::policy::DecryptionPolicy {
6463 resources: crate::policy::ResourcePolicy {
6464 max_xml_depth: input_depth,
6465 ..crate::policy::ResourcePolicy::default()
6466 },
6467 ..crate::policy::DecryptionPolicy::default()
6468 };
6469
6470 let error = DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6471 .policy(policy)
6472 .decrypt_owned_document(&mut document, None)
6473 .expect_err("deep plaintext must exceed the active depth policy");
6474
6475 assert!(matches!(
6476 error,
6477 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
6478 resource: crate::policy::resource_name::XML_DEPTH,
6479 maximum,
6480 actual,
6481 }) if maximum == input_depth && actual > maximum
6482 ));
6483 assert_eq!(document.as_xml(), before);
6484 assert_eq!(document.generation(), 0);
6485 }
6486
6487 #[test]
6488 fn validates_replacement_plaintext_in_its_namespace_context() {
6489 let key = [0x37_u8; 16];
6492 let encrypted = encrypted_gcm_element(
6493 "http://www.w3.org/2001/04/xmlenc#Content",
6494 "<shared:child/>",
6495 None,
6496 false,
6497 &key,
6498 );
6499 let document = format!(
6500 "<root xmlns:xenc=\"{XMLENC_NS}\" xmlns:shared=\"urn:shared\">{encrypted}</root>"
6501 );
6502 let decrypted = decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key))
6503 .expect("inherited namespace prefixes must remain valid");
6504 assert_eq!(
6505 decrypted,
6506 format!(
6507 "<root xmlns:xenc=\"{XMLENC_NS}\" xmlns:shared=\"urn:shared\"><shared:child/></root>"
6508 )
6509 );
6510 }
6511
6512 fn encrypted_gcm_element(
6513 type_uri: &str,
6514 plaintext: &str,
6515 id: Option<&str>,
6516 declare_namespace: bool,
6517 key: &[u8; 16],
6518 ) -> String {
6519 let nonce = [0x44_u8; 12];
6520 let mut ciphertext = plaintext.as_bytes().to_vec();
6521 Aes128Gcm::new_from_slice(key)
6522 .expect("fixed content key length")
6523 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
6524 .expect("test encryption must succeed");
6525 let mut wire = nonce.to_vec();
6526 wire.extend_from_slice(&ciphertext);
6527 let namespace = declare_namespace
6528 .then_some(format!(" xmlns:xenc=\"{XMLENC_NS}\""))
6529 .unwrap_or_default();
6530 let data_type = (!type_uri.is_empty())
6531 .then_some(format!(" Type=\"{type_uri}\""))
6532 .unwrap_or_default();
6533 let id = id
6534 .map(|value| format!(" Id=\"{value}\""))
6535 .unwrap_or_default();
6536 format!(
6537 "<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>",
6538 STANDARD.encode(wire)
6539 )
6540 }
6541}