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