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 digest(
3677 &self,
3678 algorithm: crate::xmldsig::DigestAlgorithm,
3679 data: &[u8],
3680 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3681 crate::provider::CryptoProvider::digest(
3682 &crate::provider::RustCryptoProvider,
3683 algorithm,
3684 data,
3685 )
3686 }
3687
3688 #[cfg(feature = "xmldsig")]
3689 fn sign(
3690 &self,
3691 key: &dyn crate::xmldsig::SigningKey,
3692 algorithm: crate::xmldsig::SignatureAlgorithm,
3693 data: &[u8],
3694 ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
3695 crate::provider::CryptoProvider::sign(
3696 &crate::provider::RustCryptoProvider,
3697 key,
3698 algorithm,
3699 data,
3700 )
3701 }
3702
3703 #[cfg(feature = "xmldsig")]
3704 fn verify(
3705 &self,
3706 key: &dyn crate::xmldsig::VerifyingKey,
3707 algorithm: crate::xmldsig::SignatureAlgorithm,
3708 data: &[u8],
3709 signature: &[u8],
3710 ) -> Result<bool, crate::xmldsig::DsigError> {
3711 crate::provider::CryptoProvider::verify(
3712 &crate::provider::RustCryptoProvider,
3713 key,
3714 algorithm,
3715 data,
3716 signature,
3717 )
3718 }
3719
3720 fn encrypt_data(
3721 &self,
3722 algorithm: DataEncryptionAlgorithm,
3723 key: &[u8],
3724 plaintext: &[u8],
3725 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3726 crate::provider::CryptoProvider::encrypt_data(
3727 &crate::provider::RustCryptoProvider,
3728 algorithm,
3729 key,
3730 plaintext,
3731 )
3732 }
3733
3734 fn decrypt_data(
3735 &self,
3736 _algorithm: DataEncryptionAlgorithm,
3737 _key: &[u8],
3738 _ciphertext: &[u8],
3739 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3740 let index = self.decrypt_calls.fetch_add(1, Ordering::Relaxed);
3741 Ok(self
3742 .candidate_plaintexts
3743 .get(index)
3744 .unwrap_or(&self.plaintext)
3745 .clone())
3746 }
3747
3748 fn wrap_key(
3749 &self,
3750 algorithm: KeyWrapAlgorithm,
3751 kek: &[u8],
3752 key: &[u8],
3753 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3754 crate::provider::CryptoProvider::wrap_key(
3755 &crate::provider::RustCryptoProvider,
3756 algorithm,
3757 kek,
3758 key,
3759 )
3760 }
3761
3762 fn unwrap_key(
3763 &self,
3764 _algorithm: KeyWrapAlgorithm,
3765 _kek: &[u8],
3766 _wrapped: &[u8],
3767 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3768 self.unwrap_calls.fetch_add(1, Ordering::Relaxed);
3769 Ok(vec![0_u8; 16])
3770 }
3771
3772 fn transport_key(
3773 &self,
3774 key: &dyn crate::provider::KeyTransportKey,
3775 parameters: &RsaOaepParameters,
3776 plaintext: &[u8],
3777 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3778 crate::provider::CryptoProvider::transport_key(
3779 &crate::provider::RustCryptoProvider,
3780 key,
3781 parameters,
3782 plaintext,
3783 )
3784 }
3785
3786 fn recover_key(
3787 &self,
3788 _key: &dyn crate::provider::KeyRecoveryKey,
3789 _parameters: &RsaOaepParameters,
3790 _ciphertext: &[u8],
3791 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
3792 self.recover_calls.fetch_add(1, Ordering::Relaxed);
3793 Ok(vec![0_u8; 16])
3794 }
3795 }
3796
3797 impl DecryptionKeyResolver for CountingResolver {
3798 fn resolve_encapsulation_content_keys_with_policy(
3799 &self,
3800 _: &dyn crate::provider::CryptoProvider,
3801 _: DataEncryptionAlgorithm,
3802 _: &crate::key_establishment::EncapsulationMechanism,
3803 _: &crate::policy::DecryptionPolicy,
3804 _: &mut KeyCandidateBudget,
3805 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
3806 self.candidate_calls.set(self.candidate_calls.get() + 1);
3807 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
3808 self.key.clone(),
3809 )])
3810 }
3811
3812 fn resolve_key_candidates_with_policy(
3813 &self,
3814 provider: &dyn crate::provider::CryptoProvider,
3815 algorithm: DataEncryptionAlgorithm,
3816 encrypted_key: Option<&EncryptedKey>,
3817 policy: &crate::policy::DecryptionPolicy,
3818 budget: &mut KeyCandidateBudget,
3819 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3820 policy.validate()?;
3822 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
3823 }
3824
3825 fn resolve_key(
3826 &self,
3827 _provider: &dyn crate::provider::CryptoProvider,
3828 _algorithm: DataEncryptionAlgorithm,
3829 encrypted_key: Option<&EncryptedKey>,
3830 ) -> Result<Vec<u8>, XmlEncError> {
3831 if encrypted_key.is_some() {
3832 self.candidate_calls.set(self.candidate_calls.get() + 1);
3833 Ok(self.key.clone())
3834 } else {
3835 Err(XmlEncError::KeyNotFound)
3836 }
3837 }
3838 }
3839
3840 impl DecryptionKeyResolver for AllCallsResolver {
3841 fn resolve_key(
3842 &self,
3843 _provider: &dyn crate::provider::CryptoProvider,
3844 _algorithm: DataEncryptionAlgorithm,
3845 _encrypted_key: Option<&EncryptedKey>,
3846 ) -> Result<Vec<u8>, XmlEncError> {
3847 self.calls.set(self.calls.get() + 1);
3848 Ok(self.key.clone())
3849 }
3850 }
3851
3852 impl DecryptionKeyResolver for RecipientKeyResolver {
3853 fn resolve_key_candidates_with_policy(
3854 &self,
3855 provider: &dyn crate::provider::CryptoProvider,
3856 algorithm: DataEncryptionAlgorithm,
3857 encrypted_key: Option<&EncryptedKey>,
3858 policy: &crate::policy::DecryptionPolicy,
3859 budget: &mut KeyCandidateBudget,
3860 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
3861 policy.validate()?;
3863 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
3864 }
3865
3866 fn resolve_key(
3867 &self,
3868 _provider: &dyn crate::provider::CryptoProvider,
3869 _algorithm: DataEncryptionAlgorithm,
3870 encrypted_key: Option<&EncryptedKey>,
3871 ) -> Result<Vec<u8>, XmlEncError> {
3872 if encrypted_key.and_then(|key| key.recipient.as_deref()) == Some(self.recipient) {
3873 Ok(self.key.clone())
3874 } else {
3875 Err(XmlEncError::KeyNotFound)
3876 }
3877 }
3878 }
3879
3880 #[test]
3881 fn decrypts_gcm_and_rejects_tampering() {
3882 let key = [7_u8; 16];
3884 let nonce = [9_u8; 12];
3885 let mut ciphertext = b"<Assertion>trusted</Assertion>".to_vec();
3886 Aes128Gcm::new_from_slice(&key)
3887 .expect("fixed key length")
3888 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
3889 .expect("test encryption must succeed");
3890 let mut wire = nonce.to_vec();
3891 wire.extend_from_slice(&ciphertext);
3892 let xml = format!(
3893 "<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>",
3894 STANDARD.encode(&wire)
3895 );
3896 let decrypted = decrypt(&xml, &SymmetricKeyDecryptor::new(key))
3897 .expect("valid AES-GCM XML must decrypt");
3898 assert_eq!(
3899 decrypted,
3900 DecryptedContent::Xml("<Assertion>trusted</Assertion>".into())
3901 );
3902 let last = wire.len() - 1;
3903 wire[last] ^= 1;
3904 let tampered = xml.replace(&STANDARD.encode(ciphertext), &STANDARD.encode(&wire[12..]));
3905 assert!(matches!(
3906 decrypt(&tampered, &SymmetricKeyDecryptor::new(key)),
3907 Err(XmlEncError::AeadAuthenticationFailed)
3908 ));
3909 }
3910
3911 #[test]
3912 fn candidate_keys_retry_only_authenticated_decryption() {
3913 let key = [7_u8; 16];
3916 let nonce = [9_u8; 12];
3917 let mut ciphertext = b"candidate plaintext".to_vec();
3918 Aes128Gcm::new_from_slice(&key)
3919 .expect("fixed key length")
3920 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
3921 .expect("test encryption must succeed");
3922 let mut wire = nonce.to_vec();
3923 wire.extend_from_slice(&ciphertext);
3924 let encrypted = EncryptedData {
3925 encapsulation_methods: Vec::new(),
3926 id: None,
3927 derived_keys: Vec::new(),
3928 agreement_methods: Vec::new(),
3929 encrypted_type: None,
3930 encryption_method: EncryptionMethod {
3931 chacha: None,
3932 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3933 key_size_bits: None,
3934 oaep_digest: None,
3935 mgf_algorithm: None,
3936 oaep_params: None,
3937 },
3938 key_name: None,
3939 encrypted_keys: Vec::new(),
3940 cipher_data: CipherData::Value {
3941 value: STANDARD.encode(wire),
3942 },
3943 };
3944 let resolver = CandidateResolver {
3945 keys: vec![vec![1_u8; 16], key.to_vec()],
3946 };
3947
3948 let decrypted = DecryptContext::new(&resolver)
3949 .decrypt_data(&encrypted)
3950 .expect("a later authenticated candidate must decrypt");
3951
3952 assert_eq!(
3953 decrypted,
3954 DecryptedContent::Bytes(b"candidate plaintext".to_vec())
3955 );
3956 }
3957
3958 #[test]
3959 fn standalone_decryption_stops_after_first_successful_candidate() {
3960 let provider = PermissiveUnwrapProvider {
3963 plaintext: b"accepted".to_vec(),
3964 ..PermissiveUnwrapProvider::default()
3965 };
3966 let resolver = CandidateResolver {
3967 keys: vec![vec![1_u8; 16], vec![2_u8; 16], vec![3_u8; 16]],
3968 };
3969 let encrypted = EncryptedData {
3970 encapsulation_methods: Vec::new(),
3971 id: None,
3972 derived_keys: Vec::new(),
3973 agreement_methods: Vec::new(),
3974 encrypted_type: None,
3975 encryption_method: EncryptionMethod {
3976 chacha: None,
3977 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3978 key_size_bits: None,
3979 oaep_digest: None,
3980 mgf_algorithm: None,
3981 oaep_params: None,
3982 },
3983 key_name: None,
3984 encrypted_keys: Vec::new(),
3985 cipher_data: CipherData::Value {
3986 value: STANDARD.encode([0_u8; 36]),
3987 },
3988 };
3989
3990 let result = DecryptContext::new(&resolver)
3991 .provider(&provider)
3992 .decrypt_data(&encrypted)
3993 .expect("the first successful candidate must be returned");
3994
3995 assert_eq!(result, DecryptedContent::Bytes(b"accepted".to_vec()));
3996 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 1);
3997 }
3998
3999 #[test]
4000 fn document_decryption_discards_rejected_plaintext_before_next_candidate() {
4001 let provider = PermissiveUnwrapProvider {
4004 candidate_plaintexts: vec![b"<bad".to_vec(), b"<x/>".to_vec(), b"<u/>".to_vec()],
4005 ..PermissiveUnwrapProvider::default()
4006 };
4007 let resolver = CandidateResolver {
4008 keys: vec![vec![1_u8; 16], vec![2_u8; 16], vec![3_u8; 16]],
4009 };
4010 let encrypted = format!(
4011 "<xenc:EncryptedData xmlns:xenc=\"{XMLENC_NS}\" Type=\"{XMLENC_NS}Element\"><xenc:EncryptionMethod Algorithm=\"{}\"/><xenc:CipherData><xenc:CipherValue>{}</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData>",
4012 DataEncryptionAlgorithm::Aes128Gcm.uri(),
4013 STANDARD.encode([0_u8; 32]),
4014 );
4015
4016 let result = DecryptContext::new(&resolver)
4017 .provider(&provider)
4018 .decrypt_document(&encrypted, None)
4019 .expect("a later candidate with valid replacement XML must succeed");
4020
4021 assert_eq!(result, "<x/>");
4022 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 2);
4023 }
4024
4025 #[test]
4026 fn cbc_rejects_multiple_unordered_key_candidates() {
4027 let encrypted = EncryptedData {
4030 encapsulation_methods: Vec::new(),
4031 id: None,
4032 derived_keys: Vec::new(),
4033 agreement_methods: Vec::new(),
4034 encrypted_type: None,
4035 encryption_method: EncryptionMethod {
4036 chacha: None,
4037 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
4038 key_size_bits: None,
4039 oaep_digest: None,
4040 mgf_algorithm: None,
4041 oaep_params: None,
4042 },
4043 key_name: None,
4044 encrypted_keys: Vec::new(),
4045 cipher_data: CipherData::Value {
4046 value: STANDARD.encode(
4047 crate::provider::default_provider()
4048 .encrypt_data(
4049 DataEncryptionAlgorithm::Aes128Cbc,
4050 &[7_u8; 16],
4051 b"opaque plaintext",
4052 )
4053 .expect("test encryption must succeed"),
4054 ),
4055 },
4056 };
4057 let resolver = CandidateResolver {
4058 keys: vec![vec![1_u8; 16], vec![7_u8; 16]],
4059 };
4060
4061 let error = DecryptContext::new(&resolver)
4062 .decrypt_data(&encrypted)
4063 .expect_err("unauthenticated CBC must not guess among candidate keys");
4064
4065 assert!(matches!(
4066 error,
4067 XmlEncError::AmbiguousKeyCandidates {
4068 algorithm: DataEncryptionAlgorithm::Aes128Cbc,
4069 actual: 2,
4070 }
4071 ));
4072 }
4073
4074 #[test]
4075 fn cbc_accepts_duplicate_copies_of_one_key_identity() {
4076 let key = vec![0x27_u8; 16];
4079 let encrypted = EncryptedData {
4080 encapsulation_methods: Vec::new(),
4081 id: None,
4082 derived_keys: Vec::new(),
4083 agreement_methods: Vec::new(),
4084 encrypted_type: None,
4085 encryption_method: EncryptionMethod {
4086 chacha: None,
4087 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
4088 key_size_bits: None,
4089 oaep_digest: None,
4090 mgf_algorithm: None,
4091 oaep_params: None,
4092 },
4093 key_name: None,
4094 encrypted_keys: Vec::new(),
4095 cipher_data: CipherData::Value {
4096 value: STANDARD.encode(
4097 crate::provider::default_provider()
4098 .encrypt_data(
4099 DataEncryptionAlgorithm::Aes128Cbc,
4100 &key,
4101 b"duplicate identity",
4102 )
4103 .expect("test encryption must succeed"),
4104 ),
4105 },
4106 };
4107 let resolver = CandidateResolver {
4108 keys: vec![key.clone(), key],
4109 };
4110
4111 assert_eq!(
4112 DecryptContext::new(&resolver)
4113 .decrypt_data(&encrypted)
4114 .expect("one distinct CBC key identity must decrypt"),
4115 DecryptedContent::Bytes(b"duplicate identity".to_vec())
4116 );
4117 }
4118
4119 #[test]
4120 fn candidate_keys_are_bounded_before_cryptographic_processing() {
4121 let encrypted = EncryptedData {
4124 encapsulation_methods: Vec::new(),
4125 id: None,
4126 derived_keys: Vec::new(),
4127 agreement_methods: Vec::new(),
4128 encrypted_type: None,
4129 encryption_method: EncryptionMethod {
4130 chacha: None,
4131 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
4132 key_size_bits: None,
4133 oaep_digest: None,
4134 mgf_algorithm: None,
4135 oaep_params: None,
4136 },
4137 key_name: None,
4138 encrypted_keys: Vec::new(),
4139 cipher_data: CipherData::Value {
4140 value: STANDARD.encode(vec![0_u8; 28]),
4141 },
4142 };
4143 let actual = crate::hard_limits::KEY_CANDIDATE_CEILING + 1;
4144 let resolver = CandidateResolver {
4145 keys: vec![vec![0_u8; 16]; actual],
4146 };
4147
4148 let error = DecryptContext::new(&resolver)
4149 .decrypt_data(&encrypted)
4150 .expect_err("oversized candidate sets must fail before decryption");
4151
4152 assert!(matches!(
4153 error,
4154 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4155 resource: crate::policy::resource_name::KEY_CANDIDATES,
4156 maximum: crate::hard_limits::KEY_CANDIDATE_CEILING,
4157 actual: observed,
4158 }) if observed == actual
4159 ));
4160 }
4161
4162 #[test]
4163 fn operation_policy_controls_candidate_budget() {
4164 let encrypted = EncryptedData {
4167 encapsulation_methods: Vec::new(),
4168 id: None,
4169 derived_keys: Vec::new(),
4170 agreement_methods: Vec::new(),
4171 encrypted_type: None,
4172 encryption_method: EncryptionMethod {
4173 chacha: None,
4174 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
4175 key_size_bits: None,
4176 oaep_digest: None,
4177 mgf_algorithm: None,
4178 oaep_params: None,
4179 },
4180 key_name: None,
4181 encrypted_keys: Vec::new(),
4182 cipher_data: CipherData::Value {
4183 value: STANDARD.encode(vec![0_u8; 28]),
4184 },
4185 };
4186 let resolver = CandidateResolver {
4187 keys: vec![vec![0_u8; 16]; 3],
4188 };
4189 let mut policy = crate::policy::DecryptionPolicy::default();
4190 policy.resources.max_key_candidates = 2;
4191
4192 let error = DecryptContext::new(&resolver)
4193 .policy(policy)
4194 .decrypt_data(&encrypted)
4195 .expect_err("candidate accounting must use the operation policy ceiling");
4196
4197 assert!(matches!(
4198 error,
4199 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4200 resource: crate::policy::resource_name::KEY_CANDIDATES,
4201 maximum: 2,
4202 actual: 3,
4203 })
4204 ));
4205 }
4206
4207 #[test]
4208 fn candidate_work_ceiling_is_shared_across_recipients() {
4209 let key = vec![0x39_u8; 16];
4212 let resolver = AggregateRecipientResolver {
4213 attempts: Cell::new(0),
4214 key: key.clone(),
4215 };
4216 let encrypted = encrypted_data_with_recipients(
4217 &key,
4218 vec![
4219 associated_encrypted_key("first", None, None),
4220 associated_encrypted_key("second", None, None),
4221 ],
4222 None,
4223 );
4224
4225 DecryptContext::new(&resolver)
4226 .decrypt_data(&encrypted)
4227 .expect_err("a second recipient must not receive a fresh candidate allowance");
4228 assert_eq!(
4229 resolver.attempts.get(),
4230 crate::hard_limits::KEY_CANDIDATE_CEILING
4231 );
4232 }
4233
4234 #[test]
4235 fn candidate_budget_exhaustion_is_fatal_after_a_key_was_found() {
4236 let key = vec![0x49_u8; 16];
4239 let resolver = MislabelledExhaustionResolver {
4240 direct: key.clone(),
4241 };
4242 let encrypted = encrypted_data_with_recipients(
4243 &key,
4244 vec![associated_encrypted_key("recipient", None, None)],
4245 None,
4246 );
4247
4248 let error = DecryptContext::new(&resolver)
4249 .decrypt_data(&encrypted)
4250 .expect_err("candidate exhaustion must override an earlier usable key");
4251
4252 assert!(matches!(
4253 error,
4254 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4255 resource: "key candidates",
4256 maximum: crate::hard_limits::KEY_CANDIDATE_CEILING,
4257 actual,
4258 }) if actual == crate::hard_limits::KEY_CANDIDATE_CEILING + 1
4259 ));
4260 }
4261
4262 #[test]
4263 fn authenticated_decryption_continues_after_wrong_unwrapped_recipient_key() {
4264 let correct = vec![0x53_u8; 16];
4267 let encrypted = encrypted_data_with_recipients(
4268 &correct,
4269 vec![
4270 associated_encrypted_key("first", None, None),
4271 associated_encrypted_key("second", None, None),
4272 ],
4273 None,
4274 );
4275 let resolver = OrderedRecipientResolver {
4276 wrong: vec![0x11_u8; 16],
4277 correct,
4278 };
4279
4280 let plaintext = DecryptContext::new(&resolver)
4281 .decrypt_data(&encrypted)
4282 .expect("the second recipient key must authenticate");
4283 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
4284 }
4285
4286 #[test]
4287 fn authenticated_decryption_continues_from_direct_key_to_recipient() {
4288 let correct = vec![0x63_u8; 16];
4291 let encrypted = encrypted_data_with_recipients(
4292 &correct,
4293 vec![associated_encrypted_key("recipient", None, None)],
4294 None,
4295 );
4296 let resolver = DirectAndRecipientResolver {
4297 direct: vec![0x19_u8; 16],
4298 recipient: correct,
4299 };
4300
4301 let plaintext = DecryptContext::new(&resolver)
4302 .decrypt_data(&encrypted)
4303 .expect("the embedded recipient must remain available after a direct candidate");
4304
4305 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
4306 }
4307
4308 #[test]
4309 fn authenticated_decryption_continues_after_direct_lookup_error() {
4310 let correct = vec![0x64_u8; 16];
4313 let encrypted = encrypted_data_with_recipients(
4314 &correct,
4315 vec![associated_encrypted_key("recipient", None, None)],
4316 None,
4317 );
4318 let resolver = FailingDirectResolver { recipient: correct };
4319
4320 let plaintext = DecryptContext::new(&resolver)
4321 .decrypt_data(&encrypted)
4322 .expect("recipient lookup must follow a candidate-local direct error");
4323
4324 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
4325 }
4326
4327 #[test]
4328 fn resolver_policy_rejection_stops_before_later_recipient() {
4329 let correct = vec![0x65_u8; 16];
4332 let encrypted = encrypted_data_with_recipients(
4333 &correct,
4334 vec![associated_encrypted_key("recipient", None, None)],
4335 None,
4336 );
4337 let resolver = PolicyRejectingDirectResolver { recipient: correct };
4338
4339 let error = DecryptContext::new(&resolver)
4340 .decrypt_data(&encrypted)
4341 .expect_err("operation policy rejection must be fatal");
4342
4343 assert!(matches!(
4344 error,
4345 XmlEncError::Policy(crate::policy::PolicyViolation::KeyTrust {
4346 reason: "test resolver rejected the operation",
4347 })
4348 ));
4349 }
4350
4351 #[test]
4352 fn cbc_rejects_distinct_direct_and_recipient_candidates() {
4353 let recipient = vec![0x73_u8; 16];
4356 let mut encrypted = encrypted_data_with_recipients(
4357 &recipient,
4358 vec![associated_encrypted_key("recipient", None, None)],
4359 None,
4360 );
4361 encrypted.encryption_method.algorithm = DataEncryptionAlgorithm::Aes128Cbc.uri().into();
4362 *encrypted
4363 .cipher_data
4364 .inline_value_mut()
4365 .expect("inline ciphertext") = STANDARD.encode(
4366 crate::provider::default_provider()
4367 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &recipient, b"payload")
4368 .expect("test encryption must succeed"),
4369 );
4370 let resolver = DirectAndRecipientResolver {
4371 direct: vec![0x29_u8; 16],
4372 recipient,
4373 };
4374
4375 let error = DecryptContext::new(&resolver)
4376 .decrypt_data(&encrypted)
4377 .expect_err("CBC must not guess between direct and recipient keys");
4378
4379 assert!(matches!(
4380 error,
4381 XmlEncError::AmbiguousKeyCandidates {
4382 algorithm: DataEncryptionAlgorithm::Aes128Cbc,
4383 actual: 2,
4384 }
4385 ));
4386 }
4387
4388 #[test]
4389 fn cbc_ambiguity_ignores_algorithm_incompatible_key_widths() {
4390 let key = vec![0x47_u8; 16];
4393 let ciphertext = crate::provider::default_provider()
4394 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &key, b"payload")
4395 .expect("test encryption must succeed");
4396 let encrypted = EncryptedData {
4397 encapsulation_methods: Vec::new(),
4398 id: None,
4399 derived_keys: Vec::new(),
4400 agreement_methods: Vec::new(),
4401 encrypted_type: None,
4402 encryption_method: EncryptionMethod {
4403 chacha: None,
4404 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
4405 key_size_bits: None,
4406 oaep_digest: None,
4407 mgf_algorithm: None,
4408 oaep_params: None,
4409 },
4410 key_name: None,
4411 encrypted_keys: Vec::new(),
4412 cipher_data: CipherData::Value {
4413 value: STANDARD.encode(ciphertext),
4414 },
4415 };
4416 let resolver = CandidateResolver {
4417 keys: vec![vec![0_u8; 32], key],
4418 };
4419
4420 assert_eq!(
4421 DecryptContext::new(&resolver)
4422 .decrypt_data(&encrypted)
4423 .expect("the sole width-compatible CBC key must be selected"),
4424 DecryptedContent::Bytes(b"payload".to_vec())
4425 );
4426 }
4427
4428 #[test]
4429 fn data_reference_selects_the_associated_encrypted_key() {
4430 let key = vec![0x51_u8; 16];
4433 let resolver = AssociationRecordingResolver {
4434 visited: RefCell::new(Vec::new()),
4435 key: key.clone(),
4436 };
4437 let encrypted = encrypted_data_with_recipients(
4438 &key,
4439 vec![
4440 associated_encrypted_key("unrelated", Some("#other"), None),
4441 associated_encrypted_key("matching", Some("#target"), None),
4442 ],
4443 None,
4444 );
4445
4446 DecryptContext::new(&resolver)
4447 .decrypt_data(&encrypted)
4448 .expect("the associated recipient must decrypt");
4449 assert_eq!(resolver.visited.into_inner(), ["matching"]);
4450 }
4451
4452 #[test]
4453 fn carried_key_name_selects_the_named_content_key() {
4454 let key = vec![0x52_u8; 16];
4457 let resolver = AssociationRecordingResolver {
4458 visited: RefCell::new(Vec::new()),
4459 key: key.clone(),
4460 };
4461 let encrypted = encrypted_data_with_recipients(
4462 &key,
4463 vec![
4464 associated_encrypted_key("unrelated", None, Some("other")),
4465 associated_encrypted_key("matching", None, Some("wanted")),
4466 ],
4467 Some("wanted"),
4468 );
4469
4470 DecryptContext::new(&resolver)
4471 .decrypt_data(&encrypted)
4472 .expect("the matching carried key name must decrypt");
4473 assert_eq!(resolver.visited.into_inner(), ["matching"]);
4474 }
4475
4476 #[test]
4477 fn contradictory_encrypted_key_associations_fail_closed() {
4478 let key = vec![0x53_u8; 16];
4481 let resolver = AssociationRecordingResolver {
4482 visited: RefCell::new(Vec::new()),
4483 key: key.clone(),
4484 };
4485 let encrypted = encrypted_data_with_recipients(
4486 &key,
4487 vec![associated_encrypted_key("unrelated", Some("#other"), None)],
4488 None,
4489 );
4490
4491 assert!(matches!(
4492 DecryptContext::new(&resolver).decrypt_data(&encrypted),
4493 Err(XmlEncError::KeyNotFound)
4494 ));
4495 assert!(resolver.visited.into_inner().is_empty());
4496 }
4497
4498 #[test]
4499 fn direct_symmetric_key_ignores_embedded_key_hints() {
4500 let key = [0x28_u8; 16];
4503 let unrelated = EncryptedKey {
4504 sources: Default::default(),
4505 id: None,
4506 recipient: Some("other-recipient".into()),
4507 key_name: None,
4508 encryption_method: super::super::EncryptionMethod {
4509 chacha: None,
4510 algorithm: "urn:unrelated:key-transport".into(),
4511 key_size_bits: None,
4512 oaep_digest: None,
4513 mgf_algorithm: None,
4514 oaep_params: None,
4515 },
4516 cipher_data: super::super::CipherData::Value {
4517 value: STANDARD.encode([0_u8; 24]),
4518 },
4519 reference_list: None,
4520 carried_key_name: None,
4521 };
4522
4523 assert_eq!(
4524 SymmetricKeyDecryptor::new(key)
4525 .resolve_key(
4526 crate::provider::default_provider(),
4527 DataEncryptionAlgorithm::Aes128Gcm,
4528 Some(&unrelated)
4529 )
4530 .expect("direct key must ignore unrelated embedded hints"),
4531 key
4532 );
4533 }
4534
4535 #[test]
4536 fn decrypts_with_the_matching_recipient_key() {
4537 let key = [0x29_u8; 16];
4540 let plaintext = "recipient-specific plaintext";
4541 let encrypted = encrypted_gcm_element("", plaintext, None, true, &key);
4542 let recipient_key = |recipient: &str, method: &str| {
4543 format!(
4544 "<xenc:EncryptedKey Recipient=\"{recipient}\"><xenc:EncryptionMethod Algorithm=\"{method}\">{}</xenc:EncryptionMethod><xenc:CipherData><xenc:CipherValue>YQ==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey>",
4545 if recipient == "alice" {
4546 "<ds:DigestMethod Algorithm=\"urn:unsupported:digest\"/>"
4547 } else {
4548 ""
4549 }
4550 )
4551 };
4552 let key_info = format!(
4553 "<ds:KeyInfo xmlns:ds=\"{}\">{}{}</ds:KeyInfo>",
4554 crate::xmlenc::types::XMLDSIG_NS,
4555 recipient_key("alice", KeyTransportAlgorithm::RsaOaep11.uri()),
4556 recipient_key("bob", KeyWrapAlgorithm::AesKw128.uri())
4557 );
4558 let xml = encrypted.replacen(
4559 "<xenc:CipherData>",
4560 &format!("{key_info}<xenc:CipherData>"),
4561 1,
4562 );
4563 let resolver = RecipientKeyResolver {
4564 recipient: "bob",
4565 key: key.to_vec(),
4566 };
4567
4568 assert_eq!(
4569 decrypt(&xml, &resolver).expect("second recipient key must be tried"),
4570 DecryptedContent::Bytes(plaintext.as_bytes().to_vec())
4571 );
4572 }
4573
4574 #[test]
4575 fn decryption_policy_bounds_recipients_before_key_resolution() {
4576 let key = [0x29_u8; 16];
4579 let encrypted = encrypted_gcm_element("", "bounded recipients", None, true, &key);
4580 let recipient_key = |recipient: &str| {
4581 format!(
4582 "<xenc:EncryptedKey Recipient=\"{recipient}\"><xenc:EncryptionMethod Algorithm=\"urn:test:key\"/><xenc:CipherData><xenc:CipherValue>YQ==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey>"
4583 )
4584 };
4585 let key_info = format!(
4586 "<ds:KeyInfo xmlns:ds=\"{}\">{}{}</ds:KeyInfo>",
4587 crate::xmlenc::types::XMLDSIG_NS,
4588 recipient_key("alice"),
4589 recipient_key("bob")
4590 );
4591 let xml = encrypted.replacen(
4592 "<xenc:CipherData>",
4593 &format!("{key_info}<xenc:CipherData>"),
4594 1,
4595 );
4596 let parsed = parse_encrypted_data(&xml).expect("default parser accepts two recipients");
4597 let policy = crate::policy::DecryptionPolicy {
4598 resources: crate::policy::ResourcePolicy {
4599 max_encryption_recipients: 1,
4600 ..crate::policy::ResourcePolicy::default()
4601 },
4602 ..crate::policy::DecryptionPolicy::default()
4603 };
4604 let resolver = SymmetricKeyDecryptor::new(key);
4605 let context = DecryptContext::new(&resolver).policy(policy);
4606
4607 for error in [
4608 context
4609 .decrypt(&xml)
4610 .expect_err("XML recipient collection must be bounded"),
4611 context
4612 .decrypt_data(&parsed)
4613 .expect_err("typed recipient collection must be bounded"),
4614 ] {
4615 assert!(matches!(
4616 error,
4617 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4618 resource: crate::policy::resource_name::ENCRYPTION_RECIPIENTS,
4619 maximum: 1,
4620 actual: 2,
4621 })
4622 ));
4623 }
4624 }
4625
4626 #[test]
4627 fn decrypts_session_key_wrapped_with_aes_kw() {
4628 let kek = [3_u8; 16];
4630 let session_key = [4_u8; 16];
4631 let mut wrapped = [0_u8; 24];
4632 KwAes128::new_from_slice(&kek)
4633 .expect("fixed KEK length")
4634 .wrap_key(&session_key, &mut wrapped)
4635 .expect("RFC 3394 test wrapping must succeed");
4636 let encrypted_key = EncryptedKey {
4637 id: None,
4638 sources: Default::default(),
4639 recipient: None,
4640 key_name: None,
4641 encryption_method: super::super::EncryptionMethod {
4642 chacha: None,
4643 algorithm: "http://www.w3.org/2001/04/xmlenc#kw-aes128".into(),
4644 key_size_bits: None,
4645 oaep_digest: None,
4646 mgf_algorithm: None,
4647 oaep_params: None,
4648 },
4649 cipher_data: super::super::CipherData::Value {
4650 value: STANDARD.encode(wrapped),
4651 },
4652 reference_list: None,
4653 carried_key_name: None,
4654 };
4655 let resolved = KekDecryptor::new(kek)
4656 .resolve_key(
4657 crate::provider::default_provider(),
4658 DataEncryptionAlgorithm::Aes128Gcm,
4659 Some(&encrypted_key),
4660 )
4661 .expect("wrapped session key must resolve");
4662 assert_eq!(resolved, session_key);
4663 }
4664
4665 #[test]
4666 fn rejects_invalid_kek_before_custom_provider_dispatch() {
4667 let encrypted_key = EncryptedKey {
4670 id: None,
4671 sources: Default::default(),
4672 recipient: None,
4673 key_name: None,
4674 encryption_method: super::super::EncryptionMethod {
4675 chacha: None,
4676 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
4677 key_size_bits: None,
4678 oaep_digest: None,
4679 mgf_algorithm: None,
4680 oaep_params: None,
4681 },
4682 cipher_data: super::super::CipherData::Value {
4683 value: STANDARD.encode([0_u8; 24]),
4684 },
4685 reference_list: None,
4686 carried_key_name: None,
4687 };
4688 let provider = PermissiveUnwrapProvider::default();
4689
4690 assert!(matches!(
4691 KekDecryptor::new([0_u8; 32]).resolve_key(
4692 &provider,
4693 DataEncryptionAlgorithm::Aes128Gcm,
4694 Some(&encrypted_key),
4695 ),
4696 Err(XmlEncError::InvalidKekSize {
4697 algorithm: KeyWrapAlgorithm::AesKw128,
4698 expected: 16,
4699 actual: 32,
4700 })
4701 ));
4702 assert_eq!(provider.unwrap_calls.load(Ordering::Relaxed), 0);
4703 }
4704
4705 #[test]
4706 fn rejects_content_ciphertext_framing_before_resolution_or_provider_dispatch() {
4707 for (algorithm, ciphertext_len) in [
4710 (DataEncryptionAlgorithm::Aes128Gcm, 27),
4711 (DataEncryptionAlgorithm::Aes128Cbc, 33),
4712 ] {
4713 let resolver = AllCallsResolver {
4714 calls: Cell::new(0),
4715 key: vec![0_u8; algorithm.key_len()],
4716 };
4717 let provider = PermissiveUnwrapProvider::default();
4718 let encrypted = EncryptedData {
4719 encapsulation_methods: Vec::new(),
4720 id: None,
4721 derived_keys: Vec::new(),
4722 agreement_methods: Vec::new(),
4723 encrypted_type: None,
4724 key_name: None,
4725 encryption_method: super::super::EncryptionMethod {
4726 chacha: None,
4727 algorithm: algorithm.uri().into(),
4728 key_size_bits: None,
4729 oaep_digest: None,
4730 mgf_algorithm: None,
4731 oaep_params: None,
4732 },
4733 encrypted_keys: Vec::new(),
4734 cipher_data: super::super::CipherData::Value {
4735 value: STANDARD.encode(vec![0_u8; ciphertext_len]),
4736 },
4737 };
4738
4739 assert!(
4740 DecryptContext::new(&resolver)
4741 .provider(&provider)
4742 .decrypt_data(&encrypted)
4743 .is_err()
4744 );
4745 assert_eq!(resolver.calls.get(), 0);
4746 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 0);
4747 }
4748 }
4749
4750 #[test]
4751 fn rejects_custom_provider_plaintext_outside_algorithm_bounds() {
4752 for (algorithm, ciphertext_len, plaintext_len) in [
4755 (DataEncryptionAlgorithm::Aes128Gcm, 32, 5),
4756 (DataEncryptionAlgorithm::Aes128Cbc, 32, 16),
4757 ] {
4758 let resolver = AllCallsResolver {
4759 calls: Cell::new(0),
4760 key: vec![0_u8; algorithm.key_len()],
4761 };
4762 let provider = PermissiveUnwrapProvider {
4763 plaintext: vec![0_u8; plaintext_len],
4764 ..PermissiveUnwrapProvider::default()
4765 };
4766 let encrypted = EncryptedData {
4767 encapsulation_methods: Vec::new(),
4768 id: None,
4769 derived_keys: Vec::new(),
4770 agreement_methods: Vec::new(),
4771 encrypted_type: None,
4772 key_name: None,
4773 encryption_method: super::super::EncryptionMethod {
4774 chacha: None,
4775 algorithm: algorithm.uri().into(),
4776 key_size_bits: None,
4777 oaep_digest: None,
4778 mgf_algorithm: None,
4779 oaep_params: None,
4780 },
4781 encrypted_keys: Vec::new(),
4782 cipher_data: super::super::CipherData::Value {
4783 value: STANDARD.encode(vec![0_u8; ciphertext_len]),
4784 },
4785 };
4786
4787 let error = DecryptContext::new(&resolver)
4788 .provider(&provider)
4789 .decrypt_data(&encrypted)
4790 .expect_err("impossible provider output length must fail");
4791 match algorithm {
4792 DataEncryptionAlgorithm::Aes128Gcm => assert!(matches!(
4793 error,
4794 XmlEncError::Provider(crate::provider::ProviderError::InvalidOutputSize {
4795 operation: crate::provider::ProviderOperation::Decrypt,
4796 expected: 4,
4797 actual: 5,
4798 })
4799 )),
4800 DataEncryptionAlgorithm::Aes128Cbc => assert!(matches!(
4801 error,
4802 XmlEncError::Provider(crate::provider::ProviderError::InvalidOutputSizeRange {
4803 operation: crate::provider::ProviderOperation::Decrypt,
4804 minimum: 0,
4805 maximum: 15,
4806 actual: 16,
4807 })
4808 )),
4809 _ => unreachable!("the regression table covers one GCM and one CBC algorithm"),
4810 }
4811 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 1);
4812 }
4813 }
4814
4815 #[test]
4816 fn rejects_malformed_aes_kw_before_custom_provider_dispatch() {
4817 let provider = PermissiveUnwrapProvider::default();
4820 for actual in [0, 23, 25] {
4821 let encrypted_key = EncryptedKey {
4822 sources: Default::default(),
4823 id: None,
4824 recipient: None,
4825 key_name: None,
4826 encryption_method: super::super::EncryptionMethod {
4827 chacha: None,
4828 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
4829 key_size_bits: None,
4830 oaep_digest: None,
4831 mgf_algorithm: None,
4832 oaep_params: None,
4833 },
4834 cipher_data: super::super::CipherData::Value {
4835 value: STANDARD.encode(vec![0_u8; actual]),
4836 },
4837 reference_list: None,
4838 carried_key_name: None,
4839 };
4840 assert!(matches!(
4841 KekDecryptor::new([0_u8; 16]).resolve_key(
4842 &provider,
4843 DataEncryptionAlgorithm::Aes128Gcm,
4844 Some(&encrypted_key),
4845 ),
4846 Err(XmlEncError::InvalidWrappedKeyLength {
4847 expected: 24,
4848 actual: output_len,
4849 }) if output_len == actual
4850 ));
4851 }
4852 assert_eq!(provider.unwrap_calls.load(Ordering::Relaxed), 0);
4853 }
4854
4855 #[test]
4856 fn rejects_malformed_rsa_oaep_before_custom_provider_dispatch() {
4857 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
4860 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
4861 ))
4862 .expect("RSA donor private key must parse");
4863 let provider = PermissiveUnwrapProvider::default();
4864 for actual in [0, 255, 257] {
4865 assert!(matches!(
4866 recover_rsa_oaep(
4867 &provider,
4868 &private_key,
4869 &RsaOaepParameters::default(),
4870 &vec![0_u8; actual],
4871 DataEncryptionAlgorithm::Aes128Gcm,
4872 ),
4873 Err(XmlEncError::InvalidWrappedKeyLength {
4874 expected: 256,
4875 actual: output_len,
4876 }) if output_len == actual
4877 ));
4878 }
4879 assert_eq!(provider.recover_calls.load(Ordering::Relaxed), 0);
4880 }
4881
4882 #[cfg(feature = "legacy-algorithms")]
4883 #[test]
4884 fn invalid_rsa_recovery_cannot_release_successful_cbc_plaintext() {
4885 let key = RsaPrivateKey::from_pkcs8_pem(include_str!(
4888 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
4889 ))
4890 .expect("valid RSA fixture");
4891 let resolver = PrivateKeyDecryptor::new(key);
4892 for algorithm in [
4893 DataEncryptionAlgorithm::Aes128Cbc,
4894 DataEncryptionAlgorithm::Aes192Cbc,
4895 DataEncryptionAlgorithm::Aes256Cbc,
4896 DataEncryptionAlgorithm::TripleDesCbc,
4897 DataEncryptionAlgorithm::Aes128Gcm,
4898 ] {
4899 let provider = PermissiveUnwrapProvider {
4900 plaintext: b"accepted CBC plaintext".to_vec(),
4901 ..PermissiveUnwrapProvider::default()
4902 };
4903 let mut encrypted = encrypted_data_with_recipients(
4904 &[0; 16],
4905 vec![associated_encrypted_key("recipient", None, None)],
4906 None,
4907 );
4908 encrypted.encryption_method.algorithm = algorithm.uri().into();
4909 *encrypted
4910 .cipher_data
4911 .inline_value_mut()
4912 .expect("inline ciphertext") =
4913 STANDARD.encode(vec![
4914 0;
4915 algorithm
4916 .ciphertext_len_for_plaintext(provider.plaintext.len())
4917 .expect("bounded test frame")
4918 ]);
4919 encrypted.encrypted_keys[0].encryption_method.algorithm =
4920 KeyTransportAlgorithm::RsaPkcs1v15.uri().into();
4921 *encrypted.encrypted_keys[0]
4922 .cipher_data
4923 .inline_value_mut()
4924 .expect("inline wrapped key") = STANDARD.encode([0; 256]);
4925 let result = DecryptContext::new(&resolver)
4926 .provider(&provider)
4927 .policy(crate::policy::DecryptionPolicy {
4928 data_algorithms: Some([algorithm].into()),
4929 key_transport_algorithms: Some([KeyTransportAlgorithm::RsaPkcs1v15].into()),
4930 ..crate::policy::DecryptionPolicy::default()
4931 })
4932 .decrypt_data(&encrypted);
4933 assert!(
4934 result.is_err(),
4935 "invalid RSA padding must never release fallback plaintext"
4936 );
4937 assert_eq!(
4938 provider.decrypt_calls.load(Ordering::Relaxed),
4939 1,
4940 "content decryption must run before rejecting the recovery"
4941 );
4942 }
4943 }
4944
4945 #[test]
4946 fn candidate_filter_reuses_storage_and_preserves_precedence() {
4947 let keys = [
4950 vec![0; 8],
4951 vec![2; 16],
4952 vec![3; 16],
4953 vec![2; 16],
4954 vec![4; 32],
4955 vec![5; 16],
4956 ]
4957 .into_iter()
4958 .map(crate::provider::RecoveredContentKey::confirmed)
4959 .collect::<Vec<_>>();
4960 let allocation = keys.as_ptr();
4961 let keys = compatible_decryption_key_candidates(DataEncryptionAlgorithm::Aes128Gcm, keys)
4962 .expect("compatible candidates survive");
4963 assert_eq!(keys.as_ptr(), allocation);
4964 assert_eq!(keys.len(), 3);
4965 for (key, expected) in keys.iter().zip([2, 3, 5]) {
4966 assert_eq!(key.bytes(), &[expected; 16]);
4967 }
4968 }
4969
4970 #[test]
4971 fn custom_provider_recovers_with_an_opaque_private_key() {
4972 let encrypted_key = EncryptedKey {
4975 id: None,
4976 sources: Default::default(),
4977 recipient: None,
4978 key_name: None,
4979 encryption_method: super::super::EncryptionMethod {
4980 chacha: None,
4981 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
4982 key_size_bits: None,
4983 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
4984 mgf_algorithm: Some(
4985 OaepDigestAlgorithm::Sha256
4986 .mgf_uri()
4987 .expect("MGF1-SHA256 URI")
4988 .into(),
4989 ),
4990 oaep_params: None,
4991 },
4992 cipher_data: super::super::CipherData::Value {
4993 value: STANDARD.encode(vec![0x5a; 256]),
4994 },
4995 reference_list: None,
4996 carried_key_name: None,
4997 };
4998 let provider = PermissiveUnwrapProvider::default();
4999 let decryptor = PrivateKeyDecryptor::provider_key(Arc::new(OpaqueRecoveryKey));
5000
5001 let key = decryptor
5002 .resolve_key(
5003 &provider,
5004 DataEncryptionAlgorithm::Aes128Gcm,
5005 Some(&encrypted_key),
5006 )
5007 .expect("custom provider must recover through its opaque private key");
5008
5009 assert_eq!(key, vec![0_u8; 16]);
5010 assert_eq!(provider.recover_calls.load(Ordering::Relaxed), 1);
5011 }
5012
5013 #[test]
5014 fn rejects_truncated_gcm_and_invalid_wrapped_key() {
5015 assert!(matches!(
5017 crate::provider::default_provider().decrypt_data(
5018 DataEncryptionAlgorithm::Aes128Gcm,
5019 &[0_u8; 16],
5020 &[0_u8; 27],
5021 ),
5022 Err(crate::provider::ProviderError::InvalidInput(
5023 crate::provider::ProviderInputError::AesGcmFraming
5024 ))
5025 ));
5026 let truncated = EncryptedData {
5027 encapsulation_methods: Vec::new(),
5028 id: None,
5029 derived_keys: Vec::new(),
5030 agreement_methods: Vec::new(),
5031 encrypted_type: None,
5032 key_name: None,
5033 encryption_method: super::super::EncryptionMethod {
5034 chacha: None,
5035 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5036 key_size_bits: None,
5037 oaep_digest: None,
5038 mgf_algorithm: None,
5039 oaep_params: None,
5040 },
5041 encrypted_keys: Vec::new(),
5042 cipher_data: super::super::CipherData::Value {
5043 value: STANDARD.encode([0_u8; 27]),
5044 },
5045 };
5046 assert!(matches!(
5047 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16])).decrypt_data(&truncated),
5048 Err(XmlEncError::DataTooShort {
5049 algorithm: "AES-GCM",
5050 actual: 27,
5051 ..
5052 })
5053 ));
5054 let encrypted_key = EncryptedKey {
5055 id: None,
5056 sources: Default::default(),
5057 recipient: None,
5058 key_name: None,
5059 encryption_method: super::super::EncryptionMethod {
5060 chacha: None,
5061 algorithm: "http://www.w3.org/2001/04/xmlenc#kw-aes128".into(),
5062 key_size_bits: None,
5063 oaep_digest: None,
5064 mgf_algorithm: None,
5065 oaep_params: None,
5066 },
5067 cipher_data: super::super::CipherData::Value {
5068 value: STANDARD.encode([0_u8; 24]),
5069 },
5070 reference_list: None,
5071 carried_key_name: None,
5072 };
5073 assert!(matches!(
5074 KekDecryptor::new([0_u8; 16]).resolve_key(
5075 crate::provider::default_provider(),
5076 DataEncryptionAlgorithm::Aes128Gcm,
5077 Some(&encrypted_key)
5078 ),
5079 Err(XmlEncError::KeyWrapIntegrity)
5080 ));
5081 assert!(matches!(
5082 KekDecryptor::new([0_u8; 32]).resolve_key(
5083 crate::provider::default_provider(),
5084 DataEncryptionAlgorithm::Aes128Gcm,
5085 Some(&encrypted_key)
5086 ),
5087 Err(XmlEncError::InvalidKekSize {
5088 algorithm: KeyWrapAlgorithm::AesKw128,
5089 expected: 16,
5090 actual: 32
5091 })
5092 ));
5093 }
5094
5095 #[test]
5096 fn decrypts_oaep11_with_independent_digest_and_mgf() {
5097 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5099 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5100 ))
5101 .expect("RSA donor private key must parse");
5102 let public_key = RsaPublicKey::from(&private_key);
5103 let session_key = [6_u8; 16];
5104 let label = b"xmlenc-label".to_vec();
5105 let wrapped = public_key
5106 .encrypt(
5107 &mut ChaCha20Rng::from_seed([17_u8; 32]),
5108 Oaep::<Sha256, Sha384>::new_with_mgf_hash_and_label(label.clone()),
5109 &session_key,
5110 )
5111 .expect("OAEP test wrapping must succeed");
5112 let encrypted_key = EncryptedKey {
5113 id: Some("wrapped-key".into()),
5114 sources: Default::default(),
5115 recipient: Some("recipient-a".into()),
5116 key_name: None,
5117 encryption_method: super::super::EncryptionMethod {
5118 chacha: None,
5119 algorithm: "http://www.w3.org/2009/xmlenc11#rsa-oaep".into(),
5120 key_size_bits: None,
5121 oaep_digest: Some("http://www.w3.org/2001/04/xmlenc#sha256".into()),
5122 mgf_algorithm: Some("http://www.w3.org/2009/xmlenc11#mgf1sha384".into()),
5123 oaep_params: Some(label),
5124 },
5125 cipher_data: super::super::CipherData::Value {
5126 value: STANDARD.encode(wrapped),
5127 },
5128 reference_list: None,
5129 carried_key_name: None,
5130 };
5131 let resolved = PrivateKeyDecryptor::new(private_key)
5132 .resolve_key(
5133 crate::provider::default_provider(),
5134 DataEncryptionAlgorithm::Aes128Gcm,
5135 Some(&encrypted_key),
5136 )
5137 .expect("OAEP 1.1 wrapped key must resolve");
5138 assert_eq!(resolved, session_key);
5139 }
5140
5141 #[test]
5142 fn decrypts_legacy_oaep_uri_with_sha256_digest() {
5143 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5145 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5146 ))
5147 .expect("RSA donor private key must parse");
5148 let public_key = RsaPublicKey::from(&private_key);
5149 let session_key = [8_u8; 16];
5150 let wrapped = public_key
5151 .encrypt(
5152 &mut ChaCha20Rng::from_seed([19_u8; 32]),
5153 Oaep::<Sha256, Sha1>::new_with_mgf_hash(),
5154 &session_key,
5155 )
5156 .expect("legacy OAEP URI test wrapping must succeed");
5157 let encrypted_key = EncryptedKey {
5158 id: None,
5159 sources: Default::default(),
5160 recipient: None,
5161 key_name: None,
5162 encryption_method: super::super::EncryptionMethod {
5163 chacha: None,
5164 algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p".into(),
5165 key_size_bits: None,
5166 oaep_digest: Some("http://www.w3.org/2001/04/xmlenc#sha256".into()),
5167 mgf_algorithm: None,
5168 oaep_params: None,
5169 },
5170 cipher_data: super::super::CipherData::Value {
5171 value: STANDARD.encode(wrapped),
5172 },
5173 reference_list: None,
5174 carried_key_name: None,
5175 };
5176 let resolved = PrivateKeyDecryptor::new(private_key)
5177 .resolve_key(
5178 crate::provider::default_provider(),
5179 DataEncryptionAlgorithm::Aes128Gcm,
5180 Some(&encrypted_key),
5181 )
5182 .expect("legacy OAEP URI with SHA-256 must resolve");
5183 assert_eq!(resolved, session_key);
5184 }
5185
5186 #[test]
5187 fn decrypts_sha384_oaep_with_the_xmlenc_digest_uri() {
5188 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5191 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5192 ))
5193 .expect("RSA donor private key must parse");
5194 let public_key = RsaPublicKey::from(&private_key);
5195 let session_key = [9_u8; 16];
5196 let digest = "http://www.w3.org/2001/04/xmlenc#sha384";
5197
5198 for (algorithm, mgf_algorithm) in [
5199 ("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p", None),
5200 (
5201 "http://www.w3.org/2009/xmlenc11#rsa-oaep",
5202 Some("http://www.w3.org/2009/xmlenc11#mgf1sha1"),
5203 ),
5204 ] {
5205 let wrapped = public_key
5206 .encrypt(
5207 &mut ChaCha20Rng::from_seed([23_u8; 32]),
5208 Oaep::<Sha384, Sha1>::new_with_mgf_hash(),
5209 &session_key,
5210 )
5211 .expect("SHA-384 OAEP test wrapping must succeed");
5212 let encrypted_key = EncryptedKey {
5213 id: None,
5214 sources: Default::default(),
5215 recipient: None,
5216 key_name: None,
5217 encryption_method: super::super::EncryptionMethod {
5218 chacha: None,
5219 algorithm: algorithm.into(),
5220 key_size_bits: None,
5221 oaep_digest: Some(digest.into()),
5222 mgf_algorithm: mgf_algorithm.map(str::to_owned),
5223 oaep_params: None,
5224 },
5225 cipher_data: super::super::CipherData::Value {
5226 value: STANDARD.encode(wrapped),
5227 },
5228 reference_list: None,
5229 carried_key_name: None,
5230 };
5231 let resolved = PrivateKeyDecryptor::new(private_key.clone())
5232 .resolve_key(
5233 crate::provider::default_provider(),
5234 DataEncryptionAlgorithm::Aes128Gcm,
5235 Some(&encrypted_key),
5236 )
5237 .expect("official XMLENC SHA-384 URI must resolve");
5238 assert_eq!(resolved, session_key);
5239 }
5240 }
5241
5242 #[test]
5243 fn rejects_unknown_oaep_digest_and_mgf_as_unsupported() {
5244 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
5246 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
5247 ))
5248 .expect("RSA donor private key must parse");
5249 let decryptor = PrivateKeyDecryptor::new(private_key);
5250 let mut encrypted_key = EncryptedKey {
5251 sources: Default::default(),
5252 id: None,
5253 recipient: None,
5254 key_name: None,
5255 encryption_method: super::super::EncryptionMethod {
5256 chacha: None,
5257 algorithm: "http://www.w3.org/2009/xmlenc11#rsa-oaep".into(),
5258 key_size_bits: None,
5259 oaep_digest: Some("urn:unsupported:digest".into()),
5260 mgf_algorithm: Some("http://www.w3.org/2009/xmlenc11#mgf1sha1".into()),
5261 oaep_params: None,
5262 },
5263 cipher_data: super::super::CipherData::Value {
5264 value: STANDARD.encode([0_u8; 256]),
5265 },
5266 reference_list: None,
5267 carried_key_name: None,
5268 };
5269 assert!(matches!(
5270 decryptor.resolve_key(crate::provider::default_provider(), DataEncryptionAlgorithm::Aes128Gcm, Some(&encrypted_key)),
5271 Err(XmlEncError::UnsupportedAlgorithm(uri)) if uri == "urn:unsupported:digest"
5272 ));
5273
5274 encrypted_key.encryption_method.oaep_digest = None;
5275 encrypted_key.encryption_method.mgf_algorithm = Some("urn:unsupported:mgf".into());
5276 assert!(matches!(
5277 decryptor.resolve_key(crate::provider::default_provider(), DataEncryptionAlgorithm::Aes128Gcm, Some(&encrypted_key)),
5278 Err(XmlEncError::UnsupportedAlgorithm(uri)) if uri == "urn:unsupported:mgf"
5279 ));
5280 }
5281
5282 #[test]
5283 fn decryption_policy_enforces_oaep_digest_and_plaintext_limits() {
5284 let encrypted_key = EncryptedKey {
5287 id: None,
5288 recipient: Some("selected".into()),
5289 sources: Default::default(),
5290 key_name: None,
5291 encryption_method: super::super::EncryptionMethod {
5292 chacha: None,
5293 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p.uri().into(),
5294 key_size_bits: None,
5295 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
5296 mgf_algorithm: Some(
5297 OaepDigestAlgorithm::Sha384
5298 .mgf_uri()
5299 .expect("MGF1-SHA384 URI")
5300 .into(),
5301 ),
5302 oaep_params: None,
5303 },
5304 cipher_data: super::super::CipherData::Value {
5305 value: STANDARD.encode([0_u8; 256]),
5306 },
5307 reference_list: None,
5308 carried_key_name: None,
5309 };
5310 let encrypted = EncryptedData {
5311 encapsulation_methods: Vec::new(),
5312 id: None,
5313 derived_keys: Vec::new(),
5314 agreement_methods: Vec::new(),
5315 encrypted_type: None,
5316 key_name: None,
5317 encryption_method: super::super::EncryptionMethod {
5318 chacha: None,
5319 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5320 key_size_bits: None,
5321 oaep_digest: None,
5322 mgf_algorithm: None,
5323 oaep_params: None,
5324 },
5325 encrypted_keys: vec![encrypted_key],
5326 cipher_data: super::super::CipherData::Value {
5327 value: STANDARD.encode([0_u8; 28]),
5328 },
5329 };
5330 let policy = crate::policy::DecryptionPolicy {
5331 oaep_digests: Some(std::collections::HashSet::from([
5332 OaepDigestAlgorithm::Sha256,
5333 ])),
5334 ..crate::policy::DecryptionPolicy::default()
5335 };
5336 assert!(matches!(
5337 DecryptContext::new(&RecipientKeyResolver {
5338 recipient: "selected",
5339 key: vec![0_u8; 16],
5340 })
5341 .policy(policy)
5342 .decrypt_data(&encrypted),
5343 Err(XmlEncError::Policy(
5344 crate::policy::PolicyViolation::Algorithm { algorithm, .. }
5345 )) if algorithm == OaepDigestAlgorithm::Sha384.mgf_uri().expect("MGF1-SHA384 URI")
5346 ));
5347
5348 let ciphertext = crate::provider::default_provider()
5349 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &[0_u8; 16], b"four")
5350 .expect("test encryption must succeed");
5351 let bounded = EncryptedData {
5352 encrypted_keys: Vec::new(),
5353 cipher_data: super::super::CipherData::Value {
5354 value: STANDARD.encode(ciphertext),
5355 },
5356 ..encrypted
5357 };
5358 let policy = crate::policy::DecryptionPolicy {
5359 resources: crate::policy::ResourcePolicy {
5360 max_encryption_plaintext_bytes: 3,
5361 ..crate::policy::ResourcePolicy::default()
5362 },
5363 ..crate::policy::DecryptionPolicy::default()
5364 };
5365 assert!(matches!(
5366 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16]))
5367 .policy(policy)
5368 .decrypt_data(&bounded),
5369 Err(XmlEncError::Policy(
5370 crate::policy::PolicyViolation::ResourceLimit {
5371 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
5372 maximum: 3,
5373 actual: 4
5374 }
5375 ))
5376 ));
5377
5378 let cbc_ciphertext = crate::provider::default_provider()
5379 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &[0_u8; 16], b"four")
5380 .expect("test CBC encryption must succeed");
5381 let bounded_cbc = EncryptedData {
5382 encryption_method: super::super::EncryptionMethod {
5383 chacha: None,
5384 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
5385 key_size_bits: None,
5386 oaep_digest: None,
5387 mgf_algorithm: None,
5388 oaep_params: None,
5389 },
5390 encrypted_keys: Vec::new(),
5391 cipher_data: super::super::CipherData::Value {
5392 value: STANDARD.encode(cbc_ciphertext),
5393 },
5394 ..bounded
5395 };
5396 let policy = crate::policy::DecryptionPolicy {
5397 resources: crate::policy::ResourcePolicy {
5398 max_encryption_plaintext_bytes: 4,
5399 ..crate::policy::ResourcePolicy::default()
5400 },
5401 ..crate::policy::DecryptionPolicy::default()
5402 };
5403 assert_eq!(
5404 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16]))
5405 .policy(policy)
5406 .decrypt_data(&bounded_cbc)
5407 .expect("CBC plaintext at the configured limit must decrypt"),
5408 DecryptedContent::Bytes(b"four".to_vec())
5409 );
5410 }
5411
5412 #[test]
5413 fn typed_decryption_input_cannot_bypass_metadata_policy() {
5414 let ciphertext = crate::provider::default_provider()
5417 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &[0_u8; 16], b"data")
5418 .expect("test encryption must succeed");
5419 let encrypted = EncryptedData {
5420 encapsulation_methods: Vec::new(),
5421 id: Some("oversized".into()),
5422 derived_keys: Vec::new(),
5423 agreement_methods: Vec::new(),
5424 encrypted_type: None,
5425 key_name: None,
5426 encryption_method: super::super::EncryptionMethod {
5427 chacha: None,
5428 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5429 key_size_bits: None,
5430 oaep_digest: None,
5431 mgf_algorithm: None,
5432 oaep_params: None,
5433 },
5434 encrypted_keys: Vec::new(),
5435 cipher_data: super::super::CipherData::Value {
5436 value: STANDARD.encode(ciphertext),
5437 },
5438 };
5439 let policy = crate::policy::DecryptionPolicy {
5440 resources: crate::policy::ResourcePolicy {
5441 max_encryption_metadata_bytes: 8,
5442 ..crate::policy::ResourcePolicy::default()
5443 },
5444 ..crate::policy::DecryptionPolicy::default()
5445 };
5446
5447 let resolver = AllCallsResolver {
5448 calls: Cell::new(0),
5449 key: vec![0_u8; 16],
5450 };
5451 assert!(matches!(
5452 DecryptContext::new(&resolver)
5453 .policy(policy)
5454 .decrypt_data(&encrypted),
5455 Err(XmlEncError::Policy(
5456 crate::policy::PolicyViolation::ResourceLimit {
5457 resource: crate::policy::resource_name::ENCRYPTION_METADATA_BYTES,
5458 maximum: 8,
5459 actual: 9,
5460 }
5461 ))
5462 ));
5463 assert_eq!(
5464 resolver.calls.get(),
5465 0,
5466 "metadata preflight must gate resolver-controlled work"
5467 );
5468 }
5469
5470 #[test]
5471 fn typed_cipher_values_are_bounded_before_decode_or_resolution() {
5472 let key = [0x41_u8; 16];
5475 let ciphertext = crate::provider::default_provider()
5476 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5477 .expect("test encryption must succeed");
5478 let mut encrypted = EncryptedData {
5479 encapsulation_methods: Vec::new(),
5480 id: None,
5481 derived_keys: Vec::new(),
5482 agreement_methods: Vec::new(),
5483 encrypted_type: None,
5484 key_name: None,
5485 encryption_method: super::super::EncryptionMethod {
5486 chacha: None,
5487 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5488 key_size_bits: None,
5489 oaep_digest: None,
5490 mgf_algorithm: None,
5491 oaep_params: None,
5492 },
5493 encrypted_keys: Vec::new(),
5494 cipher_data: super::super::CipherData::Value {
5495 value: STANDARD.encode(ciphertext),
5496 },
5497 };
5498 let policy = crate::policy::DecryptionPolicy {
5499 resources: crate::policy::ResourcePolicy {
5500 max_encryption_plaintext_bytes: 4,
5501 ..crate::policy::ResourcePolicy::default()
5502 },
5503 ..crate::policy::DecryptionPolicy::default()
5504 };
5505 *encrypted
5506 .cipher_data
5507 .inline_value_mut()
5508 .expect("inline ciphertext") = "A".repeat(48);
5509 assert!(matches!(
5510 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
5511 .policy(policy)
5512 .decrypt_data(&encrypted),
5513 Err(XmlEncError::Policy(
5514 crate::policy::PolicyViolation::ResourceLimit {
5515 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
5516 ..
5517 }
5518 ))
5519 ));
5520
5521 *encrypted
5522 .cipher_data
5523 .inline_value_mut()
5524 .expect("inline ciphertext") = STANDARD.encode([0_u8; 28]);
5525 encrypted.encrypted_keys.push(EncryptedKey {
5526 sources: Default::default(),
5527 id: None,
5528 recipient: None,
5529 key_name: None,
5530 encryption_method: super::super::EncryptionMethod {
5531 chacha: None,
5532 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
5533 key_size_bits: None,
5534 oaep_digest: None,
5535 mgf_algorithm: None,
5536 oaep_params: None,
5537 },
5538 cipher_data: super::super::CipherData::Value {
5539 value: "A".repeat(MAX_CIPHER_VALUE_BASE64_LEN + 4),
5540 },
5541 reference_list: None,
5542 carried_key_name: None,
5543 });
5544 let resolver = CountingResolver {
5545 candidate_calls: Cell::new(0),
5546 key: key.to_vec(),
5547 };
5548 assert!(matches!(
5549 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5550 Err(XmlEncError::InvalidStructure(_))
5551 ));
5552 assert_eq!(resolver.candidate_calls.get(), 0);
5553
5554 *encrypted.encrypted_keys[0]
5555 .cipher_data
5556 .inline_value_mut()
5557 .expect("inline wrapped key") = "AAAA".into();
5558 let aggregate_encoded_len = encrypted
5559 .cipher_data
5560 .inline_value()
5561 .expect("inline ciphertext")
5562 .len()
5563 + encrypted.encrypted_keys[0]
5564 .cipher_data
5565 .inline_value()
5566 .expect("inline wrapped key")
5567 .len();
5568 let policy = crate::policy::DecryptionPolicy {
5569 resources: crate::policy::ResourcePolicy {
5570 max_encryption_plaintext_bytes: 4,
5571 max_xml_document_bytes: aggregate_encoded_len - 1,
5572 ..crate::policy::ResourcePolicy::default()
5573 },
5574 ..crate::policy::DecryptionPolicy::default()
5575 };
5576 let resolver = CountingResolver {
5577 candidate_calls: Cell::new(0),
5578 key: key.to_vec(),
5579 };
5580 assert!(matches!(
5581 DecryptContext::new(&resolver)
5582 .policy(policy)
5583 .decrypt_data(&encrypted),
5584 Err(XmlEncError::Policy(
5585 crate::policy::PolicyViolation::ResourceLimit {
5586 resource:
5587 crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
5588 maximum,
5589 actual,
5590 }
5591 )) if maximum == aggregate_encoded_len - 1 && actual == aggregate_encoded_len
5592 ));
5593 assert_eq!(resolver.candidate_calls.get(), 0);
5594 }
5595
5596 #[cfg(feature = "experimental-pq")]
5597 #[test]
5598 fn typed_kem_width_is_checked_before_any_resolver_dispatch() {
5599 let document = crate::XmlDomDocument::parse(include_str!(
5602 "../../tests/fixtures/xmlenc/aleksey-xmlenc-01/enc-aes128gcm-em-ml-kem-512.xml"
5603 ))
5604 .expect("valid donor XML");
5605 let node = document
5606 .descendants()
5607 .find(|node| node.has_tag_name((crate::xmlenc::types::XMLENC_NS, "EncryptedData")))
5608 .expect("donor EncryptedData");
5609 let mut policy = crate::policy::DecryptionPolicy::default();
5610 policy
5611 .key_establishment
5612 .encapsulation_algorithms
5613 .insert(crate::provider::KeyEncapsulationAlgorithm::MlKem512);
5614 let data = super::super::parse_encrypted_data_node_with_policy(node, &policy)
5615 .expect("valid donor encryption metadata");
5616 for width in [0, 767, 769] {
5617 for nested in [false, true] {
5618 let mut encrypted = data.clone();
5619 encrypted.encapsulation_methods[0]
5620 .ciphertext
5621 .resize(width, 0);
5622 if nested {
5623 encrypted.encrypted_keys.push(EncryptedKey {
5624 sources: super::super::EncryptionKeySources {
5625 encapsulation_methods: std::mem::take(
5626 &mut encrypted.encapsulation_methods,
5627 ),
5628 ..Default::default()
5629 },
5630 id: None,
5631 recipient: None,
5632 key_name: None,
5633 encryption_method: super::super::EncryptionMethod {
5634 chacha: None,
5635 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
5636 key_size_bits: None,
5637 oaep_digest: None,
5638 mgf_algorithm: None,
5639 oaep_params: None,
5640 },
5641 cipher_data: super::super::CipherData::Bytes(vec![0; 24]),
5642 reference_list: None,
5643 carried_key_name: None,
5644 });
5645 }
5646 let resolver = CountingResolver {
5647 candidate_calls: Cell::new(0),
5648 key: vec![0; 16],
5649 };
5650 let error = DecryptContext::new(&resolver)
5651 .policy(policy.clone())
5652 .decrypt_data(&encrypted)
5653 .expect_err("malformed typed KEM width rejected");
5654 assert!(
5655 error
5656 .to_string()
5657 .contains("ciphertext size does not match Algorithm"),
5658 "{error}"
5659 );
5660 assert_eq!(resolver.candidate_calls.get(), 0);
5661 }
5662 }
5663 let mut encrypted = data;
5665 encrypted.encryption_method.algorithm = DataEncryptionAlgorithm::Aes256Cbc.uri().into();
5666 encrypted.cipher_data = super::super::CipherData::Bytes(vec![0; 32]);
5667 let resolver = CountingResolver {
5668 candidate_calls: Cell::new(0),
5669 key: vec![0; 32],
5670 };
5671 assert!(matches!(
5672 DecryptContext::new(&resolver)
5673 .policy(policy)
5674 .decrypt_data(&encrypted),
5675 Err(XmlEncError::Policy(
5676 crate::policy::PolicyViolation::Algorithm {
5677 operation: "direct KEM content without external authentication",
5678 ..
5679 }
5680 ))
5681 ));
5682 assert_eq!(resolver.candidate_calls.get(), 0);
5683 }
5684
5685 #[cfg(feature = "experimental-pq")]
5686 #[test]
5687 fn typed_kem_ciphertext_shares_aggregate_cipher_value_budget() {
5688 let xml = include_str!(
5691 "../../tests/fixtures/xmlenc/aleksey-xmlenc-01/enc-aes128gcm-em-ml-kem-512.xml"
5692 );
5693 let document = crate::XmlDomDocument::parse(xml).expect("donor XML parses");
5694 let node = document
5695 .descendants()
5696 .find(|node| node.has_tag_name((crate::xmlenc::types::XMLENC_NS, "EncryptedData")))
5697 .expect("donor EncryptedData");
5698 let mut policy = crate::policy::DecryptionPolicy::default();
5699 policy
5700 .key_establishment
5701 .encapsulation_algorithms
5702 .insert(crate::provider::KeyEncapsulationAlgorithm::MlKem512);
5703 let mut encrypted = super::super::parse_encrypted_data_node_with_policy(node, &policy)
5704 .expect("permitted donor recipient");
5705 let content = cipher_data_storage_len(&encrypted.cipher_data);
5706 let kem_encoded = 4 * encrypted.encapsulation_methods[0]
5707 .ciphertext
5708 .len()
5709 .div_ceil(3);
5710 assert!(
5711 validate_typed_cipher_values(
5712 &encrypted,
5713 DataEncryptionAlgorithm::Aes128Gcm,
5714 usize::MAX / 2,
5715 content + kem_encoded
5716 )
5717 .is_ok()
5718 );
5719 for nested in [false, true] {
5720 if nested {
5721 encrypted.encrypted_keys.push(EncryptedKey {
5722 sources: super::super::EncryptionKeySources {
5723 encapsulation_methods: std::mem::take(&mut encrypted.encapsulation_methods),
5724 ..Default::default()
5725 },
5726 id: None,
5727 recipient: None,
5728 key_name: None,
5729 encryption_method: super::super::EncryptionMethod {
5730 chacha: None,
5731 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
5732 key_size_bits: None,
5733 oaep_digest: None,
5734 mgf_algorithm: None,
5735 oaep_params: None,
5736 },
5737 cipher_data: super::super::CipherData::Bytes(vec![0; 24]),
5738 reference_list: None,
5739 carried_key_name: None,
5740 });
5741 }
5742 let total = content + kem_encoded + if nested { 24 } else { 0 };
5743 policy.resources.max_xml_document_bytes = total - 1;
5744 let resolver = CountingResolver {
5745 candidate_calls: Cell::new(0),
5746 key: vec![0; 16],
5747 };
5748 assert!(
5749 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)
5750 );
5751 assert_eq!(resolver.candidate_calls.get(), 0);
5752 }
5753 }
5754
5755 #[test]
5756 fn typed_legacy_oaep_accepts_explicit_mgf() {
5757 let key = [0x43_u8; 16];
5760 let ciphertext = crate::provider::default_provider()
5761 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5762 .expect("test encryption must succeed");
5763 let encrypted = EncryptedData {
5764 encapsulation_methods: Vec::new(),
5765 id: None,
5766 derived_keys: Vec::new(),
5767 agreement_methods: Vec::new(),
5768 encrypted_type: None,
5769 key_name: None,
5770 encryption_method: super::super::EncryptionMethod {
5771 chacha: None,
5772 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5773 key_size_bits: None,
5774 oaep_digest: None,
5775 mgf_algorithm: None,
5776 oaep_params: None,
5777 },
5778 encrypted_keys: vec![EncryptedKey {
5779 sources: Default::default(),
5780 id: None,
5781 recipient: None,
5782 key_name: None,
5783 encryption_method: super::super::EncryptionMethod {
5784 chacha: None,
5785 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p.uri().into(),
5786 key_size_bits: None,
5787 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
5788 mgf_algorithm: Some(
5789 OaepDigestAlgorithm::Sha384
5790 .mgf_uri()
5791 .expect("MGF1-SHA384 URI")
5792 .into(),
5793 ),
5794 oaep_params: None,
5795 },
5796 cipher_data: super::super::CipherData::Value {
5797 value: STANDARD.encode([0_u8; 256]),
5798 },
5799 reference_list: None,
5800 carried_key_name: None,
5801 }],
5802 cipher_data: super::super::CipherData::Value {
5803 value: STANDARD.encode(ciphertext),
5804 },
5805 };
5806 let resolver = CountingResolver {
5807 candidate_calls: Cell::new(0),
5808 key: key.to_vec(),
5809 };
5810
5811 assert_eq!(
5812 DecryptContext::new(&resolver)
5813 .decrypt_data(&encrypted)
5814 .expect("explicit legacy OAEP MGF must reach key resolution"),
5815 DecryptedContent::Bytes(b"data".to_vec())
5816 );
5817 assert_eq!(resolver.candidate_calls.get(), 1);
5818 }
5819
5820 #[test]
5821 fn typed_zero_key_size_is_rejected_before_key_resolution() {
5822 let key = [0x45_u8; 16];
5825 let ciphertext = crate::provider::default_provider()
5826 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5827 .expect("test encryption must succeed");
5828 let encrypted = EncryptedData {
5829 encapsulation_methods: Vec::new(),
5830 id: None,
5831 derived_keys: Vec::new(),
5832 agreement_methods: Vec::new(),
5833 encrypted_type: None,
5834 key_name: None,
5835 encryption_method: super::super::EncryptionMethod {
5836 chacha: None,
5837 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5838 key_size_bits: None,
5839 oaep_digest: None,
5840 mgf_algorithm: None,
5841 oaep_params: None,
5842 },
5843 encrypted_keys: vec![EncryptedKey {
5844 id: None,
5845 sources: Default::default(),
5846 recipient: None,
5847 key_name: None,
5848 encryption_method: super::super::EncryptionMethod {
5849 chacha: None,
5850 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
5851 key_size_bits: Some(0),
5852 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
5853 mgf_algorithm: Some(
5854 OaepDigestAlgorithm::Sha256
5855 .mgf_uri()
5856 .expect("MGF1-SHA256 URI")
5857 .into(),
5858 ),
5859 oaep_params: None,
5860 },
5861 cipher_data: super::super::CipherData::Value {
5862 value: STANDARD.encode([0_u8; 256]),
5863 },
5864 reference_list: None,
5865 carried_key_name: None,
5866 }],
5867 cipher_data: super::super::CipherData::Value {
5868 value: STANDARD.encode(ciphertext),
5869 },
5870 };
5871 let resolver = CountingResolver {
5872 candidate_calls: Cell::new(0),
5873 key: key.to_vec(),
5874 };
5875
5876 assert!(matches!(
5877 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5878 Err(XmlEncError::InvalidStructure(message))
5879 if message == "KeySize must be a positive integer"
5880 ));
5881 assert_eq!(resolver.candidate_calls.get(), 0);
5882 }
5883
5884 #[test]
5885 fn typed_content_method_is_validated_before_key_resolution() {
5886 let key = [0x44_u8; 16];
5889 let ciphertext = crate::provider::default_provider()
5890 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5891 .expect("test encryption must succeed");
5892 let encrypted = EncryptedData {
5893 encapsulation_methods: Vec::new(),
5894 id: None,
5895 derived_keys: Vec::new(),
5896 agreement_methods: Vec::new(),
5897 encrypted_type: None,
5898 key_name: None,
5899 encryption_method: super::super::EncryptionMethod {
5900 chacha: None,
5901 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5902 key_size_bits: Some(256),
5903 oaep_digest: None,
5904 mgf_algorithm: None,
5905 oaep_params: None,
5906 },
5907 encrypted_keys: Vec::new(),
5908 cipher_data: super::super::CipherData::Value {
5909 value: STANDARD.encode(ciphertext),
5910 },
5911 };
5912 let resolver = AllCallsResolver {
5913 calls: Cell::new(0),
5914 key: key.to_vec(),
5915 };
5916
5917 assert!(matches!(
5918 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5919 Err(XmlEncError::InvalidStructure(message))
5920 if message.contains("requires KeySize 128, got 256")
5921 ));
5922 assert_eq!(resolver.calls.get(), 0);
5923 }
5924
5925 #[test]
5926 fn unknown_encrypted_key_algorithm_never_reaches_resolver() {
5927 let key = [0x42_u8; 16];
5930 let ciphertext = crate::provider::default_provider()
5931 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
5932 .expect("test encryption must succeed");
5933 let encrypted = EncryptedData {
5934 encapsulation_methods: Vec::new(),
5935 id: None,
5936 derived_keys: Vec::new(),
5937 agreement_methods: Vec::new(),
5938 encrypted_type: None,
5939 key_name: None,
5940 encryption_method: super::super::EncryptionMethod {
5941 chacha: None,
5942 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
5943 key_size_bits: None,
5944 oaep_digest: None,
5945 mgf_algorithm: None,
5946 oaep_params: None,
5947 },
5948 encrypted_keys: vec![EncryptedKey {
5949 id: None,
5950 recipient: None,
5951 sources: Default::default(),
5952 key_name: None,
5953 encryption_method: super::super::EncryptionMethod {
5954 chacha: None,
5955 algorithm: "urn:example:unknown-key-algorithm".into(),
5956 key_size_bits: None,
5957 oaep_digest: None,
5958 mgf_algorithm: None,
5959 oaep_params: None,
5960 },
5961 cipher_data: super::super::CipherData::Value {
5962 value: STANDARD.encode([0_u8; 24]),
5963 },
5964 reference_list: None,
5965 carried_key_name: None,
5966 }],
5967 cipher_data: super::super::CipherData::Value {
5968 value: STANDARD.encode(ciphertext),
5969 },
5970 };
5971 let resolver = CountingResolver {
5972 candidate_calls: Cell::new(0),
5973 key: key.to_vec(),
5974 };
5975
5976 assert!(matches!(
5977 DecryptContext::new(&resolver).decrypt_data(&encrypted),
5978 Err(XmlEncError::UnsupportedAlgorithm(_))
5979 ));
5980 assert_eq!(resolver.candidate_calls.get(), 0);
5981 }
5982
5983 #[test]
5984 fn cbc_padding_errors_do_not_expose_decrypted_octets() {
5985 let error = map_data_decryption_error(
5988 DataEncryptionAlgorithm::Aes128Cbc,
5989 32,
5990 crate::provider::ProviderError::InvalidInput(
5991 crate::provider::ProviderInputError::AesCbcCiphertext,
5992 ),
5993 );
5994
5995 assert_eq!(error.to_string(), "invalid XMLEnc padding");
5996 }
5997
5998 #[test]
5999 fn replaces_element_and_content_in_caller_owned_documents() {
6000 let key = [0x31_u8; 16];
6003 let element = encrypted_gcm_element(
6004 "http://www.w3.org/2001/04/xmlenc#Element",
6005 "<secret id=\"visible\">value</secret>",
6006 None,
6007 true,
6008 &key,
6009 );
6010 assert_eq!(
6011 decrypt_document(&element, None, &SymmetricKeyDecryptor::new(key))
6012 .expect("root Element replacement must succeed"),
6013 "<secret id=\"visible\">value</secret>"
6014 );
6015
6016 let content = encrypted_gcm_element(
6017 "http://www.w3.org/2001/04/xmlenc#Content",
6018 "before<child/>after",
6019 None,
6020 false,
6021 &key,
6022 );
6023 let document =
6024 format!("<root xmlns:xenc=\"{XMLENC_NS}\"><prefix/>{content}<suffix/></root>");
6025 let replaced = decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key))
6026 .expect("nested Content replacement must succeed");
6027 assert_eq!(
6028 replaced,
6029 format!(
6030 "<root xmlns:xenc=\"{XMLENC_NS}\"><prefix/>before<child/>after<suffix/></root>"
6031 )
6032 );
6033 }
6034
6035 #[test]
6036 fn accepts_whitespace_and_comments_around_element_plaintext() {
6037 let key = [0x34_u8; 16];
6040 let plaintext = "\n<!--before--><secret/><!--after-->\n";
6041 let encrypted = encrypted_gcm_element(
6042 "http://www.w3.org/2001/04/xmlenc#Element",
6043 plaintext,
6044 None,
6045 true,
6046 &key,
6047 );
6048
6049 assert_eq!(
6050 decrypt_document(&encrypted, None, &SymmetricKeyDecryptor::new(key))
6051 .expect("one element with boundary trivia must be accepted"),
6052 plaintext
6053 );
6054 }
6055
6056 #[test]
6057 fn decrypts_unknown_and_empty_type_hints_as_opaque_bytes() {
6058 let key = [0x35_u8; 16];
6061 let plaintext = "\0opaque\u{ff}bytes";
6062 let unknown = encrypted_gcm_element("urn:example:binary", plaintext, None, true, &key);
6063 let empty = encrypted_gcm_element("", plaintext, None, true, &key).replacen(
6064 "<xenc:EncryptedData",
6065 "<xenc:EncryptedData Type=\"\"",
6066 1,
6067 );
6068
6069 let parsed = parse_encrypted_data(&unknown).expect("unknown Type must remain parseable");
6070 assert_eq!(
6071 parsed.encrypted_type,
6072 Some(EncryptedDataType::Other("urn:example:binary".into()))
6073 );
6074 assert!(matches!(
6075 decrypt_document(&unknown, None, &SymmetricKeyDecryptor::new(key)),
6076 Err(XmlEncError::ReplacementRequiresXml)
6077 ));
6078
6079 for encrypted in [unknown, empty] {
6080 assert_eq!(
6081 decrypt(&encrypted, &SymmetricKeyDecryptor::new(key))
6082 .expect("opaque Type hints must not block decryption"),
6083 DecryptedContent::Bytes(plaintext.as_bytes().to_vec())
6084 );
6085 }
6086 }
6087
6088 #[test]
6089 fn selects_document_encrypted_data_by_id_and_rejects_ambiguity() {
6090 let key = [0x32_u8; 16];
6093 let first = encrypted_gcm_element(
6094 "http://www.w3.org/2001/04/xmlenc#Content",
6095 "first",
6096 Some("first"),
6097 false,
6098 &key,
6099 );
6100 let second = encrypted_gcm_element(
6101 "http://www.w3.org/2001/04/xmlenc#Content",
6102 "second",
6103 Some("second"),
6104 false,
6105 &key,
6106 );
6107 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{first}{second}</root>");
6108 let resolver = SymmetricKeyDecryptor::new(key);
6109 assert!(matches!(
6110 decrypt_document(&document, None, &resolver),
6111 Err(XmlEncError::AmbiguousEncryptedData)
6112 ));
6113 let replaced = decrypt_document(&document, Some("second"), &resolver)
6114 .expect("Id selection must choose exactly one encrypted region");
6115 assert!(replaced.contains("second"));
6116 assert!(replaced.contains("Id=\"first\""));
6117 assert!(matches!(
6118 decrypt_document(&document, Some("missing"), &resolver),
6119 Err(XmlEncError::EncryptedDataNotFound)
6120 ));
6121 }
6122
6123 #[test]
6124 fn selects_encrypted_data_below_a_unique_operation_start_node() {
6125 let key = [0x42_u8; 16];
6128 let first = encrypted_gcm_element(
6129 "http://www.w3.org/2001/04/xmlenc#Content",
6130 "first",
6131 None,
6132 false,
6133 &key,
6134 );
6135 let second = encrypted_gcm_element(
6136 "http://www.w3.org/2001/04/xmlenc#Content",
6137 "second",
6138 None,
6139 false,
6140 &key,
6141 );
6142 let document = format!(
6143 "<root xmlns:xenc=\"{XMLENC_NS}\"><scope Id=\"first\">{first}</scope><scope Id=\"second\">{second}</scope></root>"
6144 );
6145 let resolver = SymmetricKeyDecryptor::new(key);
6146 let context = DecryptContext::new(&resolver);
6147 let replaced = context
6148 .decrypt_document_from_start_node(&document, Some("second"))
6149 .expect("ancestor ID must select its encrypted descendant");
6150 assert!(replaced.contains("<scope Id=\"second\">second</scope>"));
6151 assert!(replaced.contains("<scope Id=\"first\"><xenc:EncryptedData"));
6152
6153 assert!(matches!(
6154 context.decrypt_document_from_start_node(&document, Some("missing")),
6155 Err(XmlEncError::SelectedNodeUnavailable { id }) if id == "missing"
6156 ));
6157 let duplicate = document.replace("Id=\"second\"", "Id=\"first\"");
6158 assert!(matches!(
6159 context.decrypt_document_from_start_node(&duplicate, Some("first")),
6160 Err(XmlEncError::SelectedNodeUnavailable { id }) if id == "first"
6161 ));
6162 let ambiguous = format!(
6163 "<root xmlns:xenc=\"{XMLENC_NS}\"><scope Id=\"selected\">{first}{second}</scope></root>"
6164 );
6165 assert!(matches!(
6166 context.decrypt_document_from_start_node(&ambiguous, Some("selected")),
6167 Err(XmlEncError::AmbiguousEncryptedData)
6168 ));
6169
6170 let first_replaced = context
6171 .decrypt_first_document_from_start_node(&ambiguous, Some("selected"))
6172 .expect("first-match selection must leave later encrypted descendants untouched");
6173 assert!(first_replaced.contains("<scope Id=\"selected\">first<xenc:EncryptedData"));
6174 let replaced_document =
6175 Document::parse(&first_replaced).expect("first-match output must remain valid XML");
6176 assert_eq!(
6177 replaced_document
6178 .descendants()
6179 .filter(|node| node.has_tag_name((XMLENC_NS, "EncryptedData")))
6180 .count(),
6181 1
6182 );
6183 }
6184
6185 #[test]
6186 fn rejects_non_xml_or_malformed_document_replacement_plaintext() {
6187 let key = [0x33_u8; 16];
6190 let binary = encrypted_gcm_element("", "binary", None, true, &key);
6191 assert!(matches!(
6192 decrypt_document(&binary, None, &SymmetricKeyDecryptor::new(key)),
6193 Err(XmlEncError::ReplacementRequiresXml)
6194 ));
6195
6196 let malformed = encrypted_gcm_element(
6197 "http://www.w3.org/2001/04/xmlenc#Element",
6198 "<unclosed>",
6199 None,
6200 true,
6201 &key,
6202 );
6203 assert!(matches!(
6204 decrypt_document(&malformed, None, &SymmetricKeyDecryptor::new(key)),
6205 Err(XmlEncError::XmlParse(_))
6206 ));
6207
6208 for invalid_element in ["text-only", "<first/><second/>"] {
6209 let encrypted = encrypted_gcm_element(
6210 "http://www.w3.org/2001/04/xmlenc#Element",
6211 invalid_element,
6212 None,
6213 false,
6214 &key,
6215 );
6216 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>");
6217 assert!(
6218 decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key)).is_err(),
6219 "Element plaintext must contain exactly one element: {invalid_element}"
6220 );
6221 }
6222
6223 let content = encrypted_gcm_element(
6224 "http://www.w3.org/2001/04/xmlenc#Content",
6225 "plaintext",
6226 None,
6227 false,
6228 &key,
6229 );
6230 let with_dtd = format!(
6231 "<!DOCTYPE root [<!ATTLIST root Id ID #IMPLIED>]><root xmlns:xenc=\"{XMLENC_NS}\">{content}</root>"
6232 );
6233 assert!(matches!(
6234 decrypt_document(&with_dtd, None, &SymmetricKeyDecryptor::new(key)),
6235 Err(XmlEncError::XmlParse(
6236 crate::xml::dom::ParseError::DtdDetected
6237 ))
6238 ));
6239 let mut policy = crate::policy::DecryptionPolicy::default();
6240 policy.xml.allow_internal_dtd = true;
6241 assert!(
6242 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6243 .policy(policy)
6244 .decrypt_document(&with_dtd, None)
6245 .expect("explicit internal-DTD opt-in must decrypt")
6246 .contains("plaintext")
6247 );
6248 }
6249
6250 #[test]
6251 fn rejects_plaintext_markup_that_crosses_the_encrypted_region() {
6252 let key = [0x36_u8; 16];
6256 let crossing_markup = "</parent><attacker/><parent>";
6257 for type_uri in [
6258 "http://www.w3.org/2001/04/xmlenc#Content",
6259 "http://www.w3.org/2001/04/xmlenc#Element",
6260 ] {
6261 let encrypted = encrypted_gcm_element(type_uri, crossing_markup, None, false, &key);
6262 let document =
6263 format!("<outer xmlns:xenc=\"{XMLENC_NS}\"><parent>{encrypted}</parent></outer>");
6264 assert!(
6265 decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key)).is_err(),
6266 "{type_uri} plaintext must not escape its replacement boundary"
6267 );
6268 }
6269 }
6270
6271 #[test]
6272 fn document_decryption_applies_byte_and_node_policy_before_parsing() {
6273 let key = [0x38_u8; 16];
6276 let encrypted = encrypted_gcm_element(
6277 "http://www.w3.org/2001/04/xmlenc#Content",
6278 "plaintext",
6279 None,
6280 false,
6281 &key,
6282 );
6283 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\"><a/>{encrypted}</root>");
6284 let byte_policy = crate::policy::DecryptionPolicy {
6285 resources: crate::policy::ResourcePolicy {
6286 max_xml_document_bytes: document.len() - 1,
6287 ..crate::policy::ResourcePolicy::default()
6288 },
6289 ..crate::policy::DecryptionPolicy::default()
6290 };
6291 assert!(matches!(
6292 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6293 .policy(byte_policy)
6294 .decrypt_document(&document, None),
6295 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
6296 resource: crate::policy::resource_name::XML_DOCUMENT,
6297 maximum,
6298 actual,
6299 })) if maximum == document.len() - 1 && actual == document.len()
6300 ));
6301
6302 let node_policy = crate::policy::DecryptionPolicy {
6303 resources: crate::policy::ResourcePolicy {
6304 max_xml_nodes: 3,
6305 ..crate::policy::ResourcePolicy::default()
6306 },
6307 ..crate::policy::DecryptionPolicy::default()
6308 };
6309 assert!(matches!(
6310 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6311 .policy(node_policy)
6312 .decrypt_document(&document, None),
6313 Err(XmlEncError::Policy(
6314 crate::policy::PolicyViolation::ResourceLimit {
6315 resource: crate::policy::resource_name::XML_NODES,
6316 maximum: 3,
6317 actual: 4,
6318 }
6319 ))
6320 ));
6321 }
6322
6323 #[test]
6324 fn decryption_entry_points_enforce_policy_depth() {
6325 let xml = "<root><child><leaf/></child></root>";
6328 let policy = crate::policy::DecryptionPolicy {
6329 resources: crate::policy::ResourcePolicy {
6330 max_xml_depth: 2,
6331 ..crate::policy::ResourcePolicy::default()
6332 },
6333 ..crate::policy::DecryptionPolicy::default()
6334 };
6335 let resolver = SymmetricKeyDecryptor::new([0_u8; 16]);
6336 let mut document = XmlDocument::parse(xml).expect("wide retained fixture must parse");
6337
6338 assert!(matches!(
6339 DecryptContext::new(&resolver)
6340 .policy(policy.clone())
6341 .decrypt_document(xml, None),
6342 Err(XmlEncError::Policy(
6343 crate::policy::PolicyViolation::ResourceLimit {
6344 resource: crate::policy::resource_name::XML_DEPTH,
6345 maximum: 2,
6346 actual: 3,
6347 }
6348 ))
6349 ));
6350 assert!(matches!(
6351 DecryptContext::new(&resolver)
6352 .policy(policy)
6353 .decrypt_owned_document(&mut document, None),
6354 Err(XmlEncError::Policy(
6355 crate::policy::PolicyViolation::ResourceLimit {
6356 resource: crate::policy::resource_name::XML_DEPTH,
6357 maximum: 2,
6358 actual: 3,
6359 }
6360 ))
6361 ));
6362 }
6363
6364 #[test]
6365 fn fragment_validation_does_not_charge_its_internal_wrapper_node() {
6366 let key = [0x39_u8; 16];
6369 let plaintext = "<item/>".repeat(20);
6370 let encrypted = encrypted_gcm_element(
6371 "http://www.w3.org/2001/04/xmlenc#Content",
6372 &plaintext,
6373 None,
6374 false,
6375 &key,
6376 );
6377 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>");
6378 let resolver = SymmetricKeyDecryptor::new(key);
6379 let expected = decrypt_document(&document, None, &resolver)
6380 .expect("unbounded setup decryption must succeed");
6381 let exact_output_nodes = Document::parse(&expected)
6382 .expect("decrypted output must parse")
6383 .descendants()
6384 .count();
6385 let policy = crate::policy::DecryptionPolicy {
6386 resources: crate::policy::ResourcePolicy {
6387 max_xml_nodes: exact_output_nodes,
6388 ..crate::policy::ResourcePolicy::default()
6389 },
6390 ..crate::policy::DecryptionPolicy::default()
6391 };
6392
6393 assert_eq!(
6394 DecryptContext::new(&resolver)
6395 .policy(policy)
6396 .decrypt_document(&document, None)
6397 .expect("temporary wrapper must not consume caller node budget"),
6398 expected
6399 );
6400 }
6401
6402 #[test]
6403 fn owned_decryption_rejects_projected_node_limit_atomically() {
6404 let key = [0x3a_u8; 16];
6407 let plaintext = "<item/>".repeat(64);
6408 let encrypted = encrypted_gcm_element(
6409 "http://www.w3.org/2001/04/xmlenc#Content",
6410 &plaintext,
6411 None,
6412 false,
6413 &key,
6414 );
6415 let mut document = XmlDocument::parse(format!(
6416 "<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>"
6417 ))
6418 .expect("owned encrypted fixture must parse");
6419 let input_nodes = document.with_view(|view| view.node_count());
6420 let before = document.as_xml().to_owned();
6421 let policy = crate::policy::DecryptionPolicy {
6422 resources: crate::policy::ResourcePolicy {
6423 max_xml_nodes: input_nodes,
6424 ..crate::policy::ResourcePolicy::default()
6425 },
6426 ..crate::policy::DecryptionPolicy::default()
6427 };
6428
6429 let error = DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6430 .policy(policy)
6431 .decrypt_owned_document(&mut document, None)
6432 .expect_err("expanded plaintext must exceed the operation node ceiling");
6433
6434 assert!(matches!(
6435 error,
6436 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
6437 resource: crate::policy::resource_name::XML_NODES,
6438 maximum,
6439 ..
6440 }) if maximum == input_nodes
6441 ));
6442 assert_eq!(document.as_xml(), before);
6443 assert_eq!(document.generation(), 0);
6444 }
6445
6446 #[test]
6447 fn owned_decryption_reports_decrypted_depth_as_policy() {
6448 let key = [0x3b_u8; 16];
6452 let plaintext = format!("{}value{}", "<nested>".repeat(32), "</nested>".repeat(32));
6453 let encrypted = encrypted_gcm_element(
6454 "http://www.w3.org/2001/04/xmlenc#Element",
6455 &plaintext,
6456 None,
6457 false,
6458 &key,
6459 );
6460 let mut document = XmlDocument::parse(format!(
6461 "<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>"
6462 ))
6463 .expect("encrypted fixture must parse");
6464 let input_depth = document.with_view(|view| view.max_depth());
6465 let before = document.as_xml().to_owned();
6466 let policy = crate::policy::DecryptionPolicy {
6467 resources: crate::policy::ResourcePolicy {
6468 max_xml_depth: input_depth,
6469 ..crate::policy::ResourcePolicy::default()
6470 },
6471 ..crate::policy::DecryptionPolicy::default()
6472 };
6473
6474 let error = DecryptContext::new(&SymmetricKeyDecryptor::new(key))
6475 .policy(policy)
6476 .decrypt_owned_document(&mut document, None)
6477 .expect_err("deep plaintext must exceed the active depth policy");
6478
6479 assert!(matches!(
6480 error,
6481 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
6482 resource: crate::policy::resource_name::XML_DEPTH,
6483 maximum,
6484 actual,
6485 }) if maximum == input_depth && actual > maximum
6486 ));
6487 assert_eq!(document.as_xml(), before);
6488 assert_eq!(document.generation(), 0);
6489 }
6490
6491 #[test]
6492 fn validates_replacement_plaintext_in_its_namespace_context() {
6493 let key = [0x37_u8; 16];
6496 let encrypted = encrypted_gcm_element(
6497 "http://www.w3.org/2001/04/xmlenc#Content",
6498 "<shared:child/>",
6499 None,
6500 false,
6501 &key,
6502 );
6503 let document = format!(
6504 "<root xmlns:xenc=\"{XMLENC_NS}\" xmlns:shared=\"urn:shared\">{encrypted}</root>"
6505 );
6506 let decrypted = decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key))
6507 .expect("inherited namespace prefixes must remain valid");
6508 assert_eq!(
6509 decrypted,
6510 format!(
6511 "<root xmlns:xenc=\"{XMLENC_NS}\" xmlns:shared=\"urn:shared\"><shared:child/></root>"
6512 )
6513 );
6514 }
6515
6516 fn encrypted_gcm_element(
6517 type_uri: &str,
6518 plaintext: &str,
6519 id: Option<&str>,
6520 declare_namespace: bool,
6521 key: &[u8; 16],
6522 ) -> String {
6523 let nonce = [0x44_u8; 12];
6524 let mut ciphertext = plaintext.as_bytes().to_vec();
6525 Aes128Gcm::new_from_slice(key)
6526 .expect("fixed content key length")
6527 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
6528 .expect("test encryption must succeed");
6529 let mut wire = nonce.to_vec();
6530 wire.extend_from_slice(&ciphertext);
6531 let namespace = declare_namespace
6532 .then_some(format!(" xmlns:xenc=\"{XMLENC_NS}\""))
6533 .unwrap_or_default();
6534 let data_type = (!type_uri.is_empty())
6535 .then_some(format!(" Type=\"{type_uri}\""))
6536 .unwrap_or_default();
6537 let id = id
6538 .map(|value| format!(" Id=\"{value}\""))
6539 .unwrap_or_default();
6540 format!(
6541 "<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>",
6542 STANDARD.encode(wire)
6543 )
6544 }
6545}