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