1use std::{fmt, sync::Arc};
4
5#[cfg(test)]
6use crate::xml::dom::Document;
7use base64::{Engine as _, engine::general_purpose::STANDARD};
8
9use crate::document::{DocumentParseSettings, XmlDocument, XmlParseWorkBudget};
10use crate::operation::{
11 OperationExecutionContext, OperationNodeId, OperationNodeKind, OperationPlanError,
12 OperationStage,
13};
14use rsa::RsaPrivateKey;
15
16use super::parse::{
17 parse_encrypted_data_node_with_policy_and_budget,
18 parse_encrypted_data_with_policy_backend_and_budget, validate_encrypted_data_metadata,
19};
20use super::types::{MAX_CIPHER_VALUE_BASE64_LEN, XMLENC_NS, validate_ciphertext_framing};
21use super::{
22 DataEncryptionAlgorithm, DecryptedContent, EncryptedData, EncryptedDataType, EncryptedKey,
23 KeyTransportAlgorithm, KeyWrapAlgorithm, OaepDigestAlgorithm, RsaOaepParameters, XmlEncError,
24 map_document_error,
25};
26
27#[cfg(test)]
28use super::parse_encrypted_data;
29
30#[derive(Debug)]
35pub struct KeyCandidateBudget {
36 maximum: usize,
37 remaining: usize,
38}
39
40impl KeyCandidateBudget {
41 pub fn for_operation() -> Self {
43 Self::with_limit(crate::hard_limits::KEY_CANDIDATE_CEILING)
44 }
45
46 pub fn with_limit(maximum: usize) -> Self {
48 Self {
49 maximum,
50 remaining: maximum,
51 }
52 }
53
54 pub const fn remaining(&self) -> usize {
56 self.remaining
57 }
58
59 pub fn consume(&mut self, count: usize) -> Result<(), XmlEncError> {
61 if count > self.remaining {
62 return Err(crate::policy::PolicyViolation::ResourceLimit {
63 resource: crate::policy::resource_name::KEY_CANDIDATES,
64 maximum: self.maximum,
65 actual: self
66 .maximum
67 .saturating_sub(self.remaining)
68 .saturating_add(count),
69 }
70 .into());
71 }
72 self.remaining -= count;
73 Ok(())
74 }
75
76 fn account_returned_candidates(
77 &mut self,
78 remaining_before: usize,
79 returned: usize,
80 ) -> Result<(), XmlEncError> {
81 let resolver_charged = remaining_before.saturating_sub(self.remaining);
82 self.consume(returned.saturating_sub(resolver_charged))
83 }
84}
85
86pub trait DecryptionKeyResolver {
88 fn resolve_content_keys_with_policy(
92 &self,
93 provider: &dyn crate::provider::CryptoProvider,
94 algorithm: DataEncryptionAlgorithm,
95 encrypted_key: Option<&EncryptedKey>,
96 policy: &crate::policy::DecryptionPolicy,
97 budget: &mut KeyCandidateBudget,
98 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
99 self.resolve_key_candidates_with_policy(provider, algorithm, encrypted_key, policy, budget)
100 .map(|keys| {
101 keys.into_iter()
102 .map(crate::provider::RecoveredContentKey::confirmed)
103 .collect()
104 })
105 }
106 fn resolve_key_candidates_with_policy(
114 &self,
115 provider: &dyn crate::provider::CryptoProvider,
116 algorithm: DataEncryptionAlgorithm,
117 encrypted_key: Option<&EncryptedKey>,
118 policy: &crate::policy::DecryptionPolicy,
119 budget: &mut KeyCandidateBudget,
120 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
121 policy.validate()?;
122 if encrypted_key.is_some() {
123 return Err(XmlEncError::KeyNotFound);
124 }
125 self.resolve_key_candidates(provider, algorithm, None, budget)
126 }
127
128 fn resolve_key(
130 &self,
131 provider: &dyn crate::provider::CryptoProvider,
132 algorithm: DataEncryptionAlgorithm,
133 encrypted_key: Option<&EncryptedKey>,
134 ) -> Result<Vec<u8>, XmlEncError>;
135
136 fn resolve_key_candidates(
148 &self,
149 provider: &dyn crate::provider::CryptoProvider,
150 algorithm: DataEncryptionAlgorithm,
151 encrypted_key: Option<&EncryptedKey>,
152 budget: &mut KeyCandidateBudget,
153 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
154 budget.consume(1)?;
155 self.resolve_key(provider, algorithm, encrypted_key)
156 .map(|key| vec![key])
157 }
158}
159
160#[derive(Debug, Clone, Copy, Default)]
162pub struct DocumentDecryptionOptions<'a> {
163 pub encrypted_data_id: Option<&'a str>,
165}
166
167pub struct DecryptContext<'a> {
169 resolver: &'a dyn DecryptionKeyResolver,
170 policy: crate::policy::DecryptionPolicy,
171 provider: &'a dyn crate::provider::CryptoProvider,
172 xml_backend: crate::XmlBackend,
173 id_attributes: &'a [crate::IdAttributeRegistration],
174}
175
176struct DecryptionPlanNodes {
177 document: OperationNodeId,
178 key: OperationNodeId,
179 crypto: OperationNodeId,
180 evidence: OperationNodeId,
181 mutation: Option<OperationNodeId>,
182}
183
184struct DecryptionOperationBudgets {
185 key_candidates: std::cell::RefCell<KeyCandidateBudget>,
186 xml_parse: XmlParseWorkBudget,
187}
188
189impl DecryptionOperationBudgets {
190 fn from_policy(policy: &crate::policy::DecryptionPolicy) -> Self {
191 Self {
192 key_candidates: std::cell::RefCell::new(KeyCandidateBudget::with_limit(
193 policy.resources.max_key_candidates,
194 )),
195 xml_parse: XmlParseWorkBudget::from_resources(&policy.resources),
196 }
197 }
198}
199
200fn compile_decryption_plan(
201 operation: &mut OperationExecutionContext<
202 crate::policy::DecryptionPolicy,
203 DecryptionOperationBudgets,
204 >,
205 mutation: bool,
206) -> Result<DecryptionPlanNodes, XmlEncError> {
207 let document = operation.add_node(OperationNodeKind::Document, OperationStage::Parse, None);
208 let key = operation.add_node(
209 OperationNodeKind::Key { index: 0 },
210 OperationStage::Resolve,
211 None,
212 );
213 operation
214 .add_dependency(key, document)
215 .map_err(map_decryption_plan_error)?;
216 let crypto = operation.add_node(OperationNodeKind::Crypto, OperationStage::Crypto, None);
217 operation
218 .add_dependency(crypto, key)
219 .map_err(map_decryption_plan_error)?;
220 let evidence = operation.add_node(OperationNodeKind::Evidence, OperationStage::Evidence, None);
221 operation
222 .add_dependency(evidence, crypto)
223 .map_err(map_decryption_plan_error)?;
224 let mutation = mutation.then(|| {
225 let node = operation.add_node(OperationNodeKind::Mutation, OperationStage::Mutation, None);
226 operation
227 .add_dependency(node, evidence)
228 .expect("evidence-to-mutation stage order is fixed");
229 node
230 });
231 operation.compile().map_err(map_decryption_plan_error)?;
232 Ok(DecryptionPlanNodes {
233 document,
234 key,
235 crypto,
236 evidence,
237 mutation,
238 })
239}
240
241struct ProcessedDecryption<T> {
242 output: T,
243 operation:
244 OperationExecutionContext<crate::policy::DecryptionPolicy, DecryptionOperationBudgets>,
245 mutation: Option<OperationNodeId>,
246}
247
248fn map_decryption_plan_error(error: OperationPlanError) -> XmlEncError {
249 XmlEncError::from(error)
250}
251
252impl<'a> DecryptContext<'a> {
253 pub fn new(resolver: &'a dyn DecryptionKeyResolver) -> Self {
255 Self {
256 resolver,
257 policy: crate::policy::DecryptionPolicy::default(),
258 provider: crate::provider::default_provider(),
259 xml_backend: crate::XmlBackend::default(),
260 id_attributes: &[],
261 }
262 }
263
264 pub fn policy(mut self, policy: crate::policy::DecryptionPolicy) -> Self {
266 self.policy = policy;
267 self
268 }
269
270 pub fn provider(mut self, provider: &'a dyn crate::provider::CryptoProvider) -> Self {
272 self.provider = provider;
273 self
274 }
275
276 pub fn xml_backend(mut self, backend: crate::XmlBackend) -> Self {
278 self.xml_backend = backend;
279 self
280 }
281
282 fn document_parse_settings(&self) -> DocumentParseSettings {
283 DocumentParseSettings::from_policy(&self.policy.xml, &self.policy.resources)
284 .with_backend(self.xml_backend)
285 }
286
287 pub fn id_attributes(mut self, registrations: &'a [crate::IdAttributeRegistration]) -> Self {
289 self.id_attributes = registrations;
290 self
291 }
292
293 pub fn decrypt(&self, xml: &str) -> Result<DecryptedContent, XmlEncError> {
295 let budgets = DecryptionOperationBudgets::from_policy(&self.policy);
296 let encrypted = parse_encrypted_data_with_policy_backend_and_budget(
297 xml,
298 &self.policy,
299 self.xml_backend,
300 &budgets.xml_parse,
301 )?;
302 self.process_decryption_candidates(&encrypted, None, false, budgets, |content, _| {
303 Ok(content)
304 })
305 .map(|processed| processed.output)
306 }
307
308 pub fn decrypt_data(&self, encrypted: &EncryptedData) -> Result<DecryptedContent, XmlEncError> {
310 self.process_decryption_candidates(
311 encrypted,
312 None,
313 false,
314 DecryptionOperationBudgets::from_policy(&self.policy),
315 |content, _| Ok(content),
316 )
317 .map(|processed| processed.output)
318 }
319
320 fn process_decryption_candidates<T>(
321 &self,
322 encrypted: &EncryptedData,
323 document_binding: Option<(crate::DocumentIdentity, u64)>,
324 mutates_document: bool,
325 budgets: DecryptionOperationBudgets,
326 mut accept: impl FnMut(DecryptedContent, &XmlParseWorkBudget) -> Result<T, XmlEncError>,
327 ) -> Result<ProcessedDecryption<T>, XmlEncError> {
328 let mut operation =
329 OperationExecutionContext::new(self.policy.clone(), budgets, document_binding);
330 operation.policy().resources.validate()?;
331 validate_encrypted_data_metadata(encrypted, operation.policy())?;
332 let plan_nodes = compile_decryption_plan(&mut operation, mutates_document)?;
333 let (algorithm, ciphertext) = operation.run(plan_nodes.document, || {
334 encrypted.encryption_method.validate_structure()?;
335 validate_recipient_count(
336 encrypted.encrypted_keys.len(),
337 operation.policy().resources.max_encryption_recipients,
338 )?;
339 let algorithm =
340 DataEncryptionAlgorithm::from_uri(&encrypted.encryption_method.algorithm)?;
341 crate::policy::check_content_algorithm(
342 operation.policy().data_algorithms.as_ref(),
343 algorithm,
344 "decryption",
345 )?;
346 self.provider
347 .require_capability(crate::provider::ProviderCapability::Decrypt(algorithm))?;
348 validate_typed_cipher_values(
349 encrypted,
350 algorithm,
351 operation.policy().resources.max_encryption_plaintext_bytes,
352 operation.policy().resources.max_xml_document_bytes,
353 )?;
354 let ciphertext = STANDARD
355 .decode(&encrypted.cipher_data.value)
356 .map_err(|error| XmlEncError::Base64(error.to_string()))?;
357 validate_content_framing_before_resolution(
358 algorithm,
359 ciphertext.len(),
360 &encrypted.encrypted_keys,
361 operation.policy(),
362 )?;
363 validate_possible_plaintext_len(
364 algorithm,
365 ciphertext.len(),
366 operation.policy().resources.max_encryption_plaintext_bytes,
367 )?;
368 Ok::<_, XmlEncError>((algorithm, ciphertext))
369 })?;
370 let keys = operation.run(plan_nodes.key, || {
371 resolve_content_key_candidates(
372 self.provider,
373 algorithm,
374 encrypted,
375 self.resolver,
376 operation.policy(),
377 &mut operation.budgets().key_candidates.borrow_mut(),
378 )
379 })?;
380 let keys = compatible_decryption_key_candidates(algorithm, keys)?;
381 validate_decryption_key_candidates(algorithm, keys.len())?;
382 let output = operation.run(plan_nodes.crypto, || {
383 let mut last_error = None;
384 for key in keys {
385 let attempt = (|| {
386 validate_key_len(algorithm, key.bytes())?;
387 let result = self
388 .provider
389 .decrypt_data(algorithm, key.bytes(), &ciphertext);
390 if !key.valid() {
395 if let Ok(mut plaintext) = result {
396 zeroize::Zeroize::zeroize(&mut plaintext);
397 }
398 return Err(if algorithm.cbc_block_len().is_some() {
399 XmlEncError::InvalidPadding
400 } else {
401 XmlEncError::AeadAuthenticationFailed
402 });
403 }
404 let plaintext = result.map_err(|error| {
405 map_data_decryption_error(algorithm, ciphertext.len(), error)
406 })?;
407 validate_provider_plaintext_len(algorithm, ciphertext.len(), plaintext.len())?;
408 validate_plaintext_len(
409 plaintext.len(),
410 operation.policy().resources.max_encryption_plaintext_bytes,
411 )?;
412 let content = match encrypted.encrypted_type.as_ref() {
413 Some(EncryptedDataType::Element | EncryptedDataType::Content) => {
414 DecryptedContent::Xml(String::from_utf8(plaintext)?)
415 }
416 Some(EncryptedDataType::Other(_)) | None => {
417 DecryptedContent::Bytes(plaintext)
418 }
419 };
420 accept(content, &operation.budgets().xml_parse)
421 })();
422 match attempt {
423 Ok(output) => return Ok(output),
424 Err(error) => last_error = Some(error),
425 }
426 }
427 Err(last_error.unwrap_or(XmlEncError::KeyNotFound))
428 })?;
429 let output = operation.run(plan_nodes.evidence, || Ok::<_, XmlEncError>(output))?;
430 Ok(ProcessedDecryption {
431 output,
432 operation,
433 mutation: plan_nodes.mutation,
434 })
435 }
436
437 pub fn decrypt_document(
439 &self,
440 xml: &str,
441 encrypted_data_id: Option<&str>,
442 ) -> Result<String, XmlEncError> {
443 decrypt_document_with_context(
444 xml,
445 DocumentEncryptedDataSelector::EncryptedDataId(encrypted_data_id),
446 self,
447 )
448 }
449
450 pub fn decrypt_owned_document(
452 &self,
453 document: &mut XmlDocument,
454 encrypted_data_id: Option<&str>,
455 ) -> Result<(), XmlEncError> {
456 let budgets = DecryptionOperationBudgets::from_policy(&self.policy);
457 decrypt_owned_document_with_context(
458 document,
459 DocumentEncryptedDataSelector::EncryptedDataId(encrypted_data_id),
460 self,
461 budgets,
462 )
463 }
464
465 pub fn decrypt_document_from_start_node(
468 &self,
469 xml: &str,
470 start_node_id: Option<&str>,
471 ) -> Result<String, XmlEncError> {
472 decrypt_document_with_context(
473 xml,
474 DocumentEncryptedDataSelector::UniqueBelowStartNode(start_node_id),
475 self,
476 )
477 }
478
479 pub fn decrypt_first_document_from_start_node(
482 &self,
483 xml: &str,
484 start_node_id: Option<&str>,
485 ) -> Result<String, XmlEncError> {
486 decrypt_document_with_context(
487 xml,
488 DocumentEncryptedDataSelector::FirstBelowStartNode(start_node_id),
489 self,
490 )
491 }
492}
493
494#[derive(Clone)]
496pub struct SymmetricKeyDecryptor {
497 key: Vec<u8>,
498 kind: crate::key_manager::SymmetricKeyKind,
499}
500
501impl fmt::Debug for SymmetricKeyDecryptor {
502 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
503 formatter
504 .debug_struct("SymmetricKeyDecryptor")
505 .field("key", &"[REDACTED]")
506 .finish()
507 }
508}
509
510impl SymmetricKeyDecryptor {
511 pub fn new(key: impl Into<Vec<u8>>) -> Self {
513 Self::with_kind(key, crate::key_manager::SymmetricKeyKind::Aes)
514 }
515
516 pub fn with_kind(key: impl Into<Vec<u8>>, kind: crate::key_manager::SymmetricKeyKind) -> Self {
520 Self {
521 key: key.into(),
522 kind,
523 }
524 }
525}
526
527impl DecryptionKeyResolver for SymmetricKeyDecryptor {
528 fn resolve_content_keys_with_policy(
529 &self,
530 provider: &dyn crate::provider::CryptoProvider,
531 algorithm: DataEncryptionAlgorithm,
532 encrypted_key: Option<&EncryptedKey>,
533 policy: &crate::policy::DecryptionPolicy,
534 budget: &mut KeyCandidateBudget,
535 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
536 policy.validate()?;
537 if encrypted_key.is_some() {
538 return Err(XmlEncError::KeyNotFound);
539 }
540 budget.consume(1)?;
541 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
542 self.resolve_key(provider, algorithm, None)?,
543 )])
544 }
545
546 fn resolve_key(
547 &self,
548 _provider: &dyn crate::provider::CryptoProvider,
549 algorithm: DataEncryptionAlgorithm,
550 _encrypted_key: Option<&EncryptedKey>,
551 ) -> Result<Vec<u8>, XmlEncError> {
552 if self.kind != algorithm.key_kind() {
553 return Err(XmlEncError::KeyNotFound);
554 }
555 validate_key_len(algorithm, &self.key)?;
556 Ok(self.key.clone())
557 }
558}
559
560#[derive(Clone)]
562pub struct PrivateKeyDecryptor {
563 key: Arc<dyn crate::provider::KeyRecoveryKey>,
564}
565
566#[derive(Clone)]
568pub struct KekDecryptor<'a> {
569 kek: std::borrow::Cow<'a, [u8]>,
570 kind: crate::key_manager::SymmetricKeyKind,
571}
572
573impl fmt::Debug for KekDecryptor<'_> {
574 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
575 formatter
576 .debug_struct("KekDecryptor")
577 .field("kek", &"[REDACTED]")
578 .field("kind", &self.kind)
579 .finish()
580 }
581}
582
583impl KekDecryptor<'static> {
584 pub fn new(kek: impl Into<Vec<u8>>) -> Self {
586 Self::with_kind(kek, crate::key_manager::SymmetricKeyKind::Aes)
587 }
588
589 pub fn with_kind(kek: impl Into<Vec<u8>>, kind: crate::key_manager::SymmetricKeyKind) -> Self {
591 Self {
592 kek: std::borrow::Cow::Owned(kek.into()),
593 kind,
594 }
595 }
596}
597
598impl<'a> KekDecryptor<'a> {
599 pub fn borrowed(kek: &'a [u8]) -> Self {
601 Self::borrowed_with_kind(kek, crate::key_manager::SymmetricKeyKind::Aes)
602 }
603
604 pub fn borrowed_with_kind(kek: &'a [u8], kind: crate::key_manager::SymmetricKeyKind) -> Self {
606 Self {
607 kek: std::borrow::Cow::Borrowed(kek),
608 kind,
609 }
610 }
611}
612
613impl DecryptionKeyResolver for KekDecryptor<'_> {
614 fn resolve_content_keys_with_policy(
615 &self,
616 provider: &dyn crate::provider::CryptoProvider,
617 algorithm: DataEncryptionAlgorithm,
618 encrypted_key: Option<&EncryptedKey>,
619 policy: &crate::policy::DecryptionPolicy,
620 budget: &mut KeyCandidateBudget,
621 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
622 policy.validate()?;
623 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
624 validate_encrypted_key_policy(encrypted_key, policy)?;
625 budget.consume(1)?;
626 Ok(vec![crate::provider::RecoveredContentKey::confirmed(
627 self.resolve_key(provider, algorithm, Some(encrypted_key))?,
628 )])
629 }
630
631 fn resolve_key_candidates_with_policy(
632 &self,
633 provider: &dyn crate::provider::CryptoProvider,
634 algorithm: DataEncryptionAlgorithm,
635 encrypted_key: Option<&EncryptedKey>,
636 policy: &crate::policy::DecryptionPolicy,
637 budget: &mut KeyCandidateBudget,
638 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
639 self.resolve_content_keys_with_policy(provider, algorithm, encrypted_key, policy, budget)?
640 .into_iter()
641 .map(|key| key.into_key().map_err(XmlEncError::from))
642 .collect()
643 }
644
645 fn resolve_key(
646 &self,
647 provider: &dyn crate::provider::CryptoProvider,
648 algorithm: DataEncryptionAlgorithm,
649 encrypted_key: Option<&EncryptedKey>,
650 ) -> Result<Vec<u8>, XmlEncError> {
651 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
652 encrypted_key.encryption_method.validate_structure()?;
653 let wrap_algorithm =
654 KeyWrapAlgorithm::from_uri(&encrypted_key.encryption_method.algorithm)?;
655 if self.kind != wrap_algorithm.key_kind() {
659 return Err(XmlEncError::KeyNotFound);
660 }
661 let wrapped = STANDARD
662 .decode(&encrypted_key.cipher_data.value)
663 .map_err(|error| XmlEncError::Base64(error.to_string()))?;
664 let expected_kek_len = wrap_algorithm.key_len();
665 if self.kek.len() != expected_kek_len {
666 return Err(XmlEncError::InvalidKekSize {
667 algorithm: wrap_algorithm,
668 expected: expected_kek_len,
669 actual: self.kek.len(),
670 });
671 }
672 let expected_wrapped_len = algorithm.key_len() + wrap_algorithm.overhead();
673 if wrapped.len() != expected_wrapped_len {
674 return Err(XmlEncError::InvalidWrappedKeyLength {
675 expected: expected_wrapped_len,
676 actual: wrapped.len(),
677 });
678 }
679 provider.require_capability(crate::provider::ProviderCapability::KeyUnwrap(
680 wrap_algorithm,
681 ))?;
682 let key = provider
683 .unwrap_key(wrap_algorithm, &self.kek, &wrapped)
684 .map_err(|error| match error {
685 crate::provider::ProviderError::InvalidKeySize { expected, actual } => {
686 XmlEncError::InvalidKekSize {
687 algorithm: wrap_algorithm,
688 expected,
689 actual,
690 }
691 }
692 crate::provider::ProviderError::AuthenticationFailed
693 | crate::provider::ProviderError::InvalidInput(
694 crate::provider::ProviderInputError::AesKeyWrapFraming,
695 ) => XmlEncError::KeyWrapIntegrity,
696 error => XmlEncError::Provider(error),
697 })?;
698 validate_key_len(algorithm, &key)?;
699 Ok(key)
700 }
701}
702
703impl PrivateKeyDecryptor {
704 pub fn new(key: RsaPrivateKey) -> Self {
706 Self::provider_key(Arc::new(crate::provider::RustCryptoRsaPrivateKey::new(key)))
707 }
708
709 pub fn provider_key(key: Arc<dyn crate::provider::KeyRecoveryKey>) -> Self {
711 Self { key }
712 }
713}
714
715impl DecryptionKeyResolver for PrivateKeyDecryptor {
716 fn resolve_content_keys_with_policy(
717 &self,
718 provider: &dyn crate::provider::CryptoProvider,
719 algorithm: DataEncryptionAlgorithm,
720 encrypted_key: Option<&EncryptedKey>,
721 policy: &crate::policy::DecryptionPolicy,
722 budget: &mut KeyCandidateBudget,
723 ) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
724 policy.validate()?;
725 budget.consume(1)?;
726 self.resolve_key_with_policy(provider, algorithm, encrypted_key, policy)
727 .map(|key| vec![key])
728 }
729 fn resolve_key_candidates_with_policy(
730 &self,
731 provider: &dyn crate::provider::CryptoProvider,
732 algorithm: DataEncryptionAlgorithm,
733 encrypted_key: Option<&EncryptedKey>,
734 policy: &crate::policy::DecryptionPolicy,
735 budget: &mut KeyCandidateBudget,
736 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
737 policy.validate()?;
738 budget.consume(1)?;
739 self.resolve_key_with_policy(provider, algorithm, encrypted_key, policy)
740 .and_then(|key| key.into_key().map_err(XmlEncError::Provider))
741 .map(|key| vec![key])
742 }
743
744 fn resolve_key(
745 &self,
746 provider: &dyn crate::provider::CryptoProvider,
747 algorithm: DataEncryptionAlgorithm,
748 encrypted_key: Option<&EncryptedKey>,
749 ) -> Result<Vec<u8>, XmlEncError> {
750 self.resolve_key_with_policy(
751 provider,
752 algorithm,
753 encrypted_key,
754 &crate::policy::DecryptionPolicy::default(),
755 )
756 .and_then(|key| key.into_key().map_err(XmlEncError::Provider))
757 }
758}
759
760impl PrivateKeyDecryptor {
761 pub fn resolve_key_with_policy(
764 &self,
765 provider: &dyn crate::provider::CryptoProvider,
766 algorithm: DataEncryptionAlgorithm,
767 encrypted_key: Option<&EncryptedKey>,
768 policy: &crate::policy::DecryptionPolicy,
769 ) -> Result<crate::provider::RecoveredContentKey, XmlEncError> {
770 policy.validate()?;
771 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
772 validate_encrypted_key_policy(encrypted_key, policy)?;
773 let width = policy.rsa_keys.validate_public_metadata(
774 "decryption",
775 self.key.rsa_modulus_bits(),
776 self.key.rsa_public_exponent(),
777 )?;
778 if width != self.key.ciphertext_len() {
779 return Err(crate::policy::PolicyViolation::InvalidKeyMaterial {
780 operation: "decryption",
781 key_type: "RSA",
782 reason: "ciphertext width disagrees with modulus",
783 }
784 .into());
785 }
786 encrypted_key.encryption_method.validate_structure()?;
787 let wrapped = STANDARD
788 .decode(&encrypted_key.cipher_data.value)
789 .map_err(|error| XmlEncError::Base64(error.to_string()))?;
790 let label = encrypted_key
791 .encryption_method
792 .oaep_params
793 .clone()
794 .unwrap_or_default();
795 let transport =
796 KeyTransportAlgorithm::from_uri(&encrypted_key.encryption_method.algorithm)?;
797 let key = match transport {
798 #[cfg(feature = "legacy-algorithms")]
799 KeyTransportAlgorithm::RsaPkcs1v15 => {
800 if wrapped.len() != self.key.ciphertext_len() {
801 return Err(XmlEncError::InvalidWrappedKeyLength {
802 expected: self.key.ciphertext_len(),
803 actual: wrapped.len(),
804 });
805 }
806 provider
807 .require_capability(crate::provider::ProviderCapability::Pkcs1v15Recovery)?;
808 provider
813 .recover_pkcs1v15(self.key.as_ref(), &wrapped, algorithm.key_len())
814 .map_err(XmlEncError::Provider)
815 }
816 KeyTransportAlgorithm::RsaOaepMgf1p => self
817 .decrypt_oaep_mgf1p(
818 provider,
819 encrypted_key.encryption_method.oaep_digest.as_deref(),
820 encrypted_key.encryption_method.mgf_algorithm.as_deref(),
821 label,
822 &wrapped,
823 )
824 .map(crate::provider::RecoveredContentKey::confirmed),
825 KeyTransportAlgorithm::RsaOaep11 => self
826 .decrypt_oaep11(
827 provider,
828 encrypted_key.encryption_method.oaep_digest.as_deref(),
829 encrypted_key.encryption_method.mgf_algorithm.as_deref(),
830 label,
831 &wrapped,
832 )
833 .map(crate::provider::RecoveredContentKey::confirmed),
834 }?;
835 validate_key_len(algorithm, key.bytes())?;
836 Ok(key)
837 }
838}
839
840impl PrivateKeyDecryptor {
841 fn decrypt_oaep_mgf1p(
842 &self,
843 provider: &dyn crate::provider::CryptoProvider,
844 digest: Option<&str>,
845 mgf: Option<&str>,
846 label: Vec<u8>,
847 wrapped: &[u8],
848 ) -> Result<Vec<u8>, XmlEncError> {
849 let parameters = RsaOaepParameters {
850 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p,
851 digest: parse_oaep_digest(digest)?,
852 mgf_digest: parse_oaep_mgf_digest(mgf)?,
853 label,
854 };
855 recover_rsa_oaep(provider, self.key.as_ref(), ¶meters, wrapped)
856 }
857
858 fn decrypt_oaep11(
859 &self,
860 provider: &dyn crate::provider::CryptoProvider,
861 digest: Option<&str>,
862 mgf: Option<&str>,
863 label: Vec<u8>,
864 wrapped: &[u8],
865 ) -> Result<Vec<u8>, XmlEncError> {
866 let parameters = RsaOaepParameters {
867 algorithm: KeyTransportAlgorithm::RsaOaep11,
868 digest: parse_oaep_digest(digest)?,
869 mgf_digest: parse_oaep_mgf_digest(mgf)?,
870 label,
871 };
872 recover_rsa_oaep(provider, self.key.as_ref(), ¶meters, wrapped)
873 }
874}
875
876fn parse_oaep_digest(uri: Option<&str>) -> Result<OaepDigestAlgorithm, XmlEncError> {
877 let uri = uri.unwrap_or("http://www.w3.org/2000/09/xmldsig#sha1");
878 OaepDigestAlgorithm::from_uri(uri)
879 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(uri.to_owned()))
880}
881
882fn parse_oaep_mgf_digest(uri: Option<&str>) -> Result<OaepDigestAlgorithm, XmlEncError> {
883 let uri = uri.unwrap_or("http://www.w3.org/2009/xmlenc11#mgf1sha1");
884 OaepDigestAlgorithm::from_mgf_uri(uri)
885 .ok_or_else(|| XmlEncError::UnsupportedAlgorithm(uri.to_owned()))
886}
887
888fn recover_rsa_oaep(
889 provider: &dyn crate::provider::CryptoProvider,
890 key: &dyn crate::provider::KeyRecoveryKey,
891 parameters: &RsaOaepParameters,
892 wrapped: &[u8],
893) -> Result<Vec<u8>, XmlEncError> {
894 let expected = key.ciphertext_len();
895 if wrapped.len() != expected {
896 return Err(XmlEncError::InvalidWrappedKeyLength {
897 expected,
898 actual: wrapped.len(),
899 });
900 }
901 provider.require_capability(crate::provider::ProviderCapability::KeyRecovery(parameters))?;
902 provider
903 .recover_key(key, parameters, wrapped)
904 .map_err(|error| match error {
905 crate::provider::ProviderError::Random(message) => XmlEncError::Rng(message),
906 error @ (crate::provider::ProviderError::AuthenticationFailed
907 | crate::provider::ProviderError::InvalidInput(_)) => {
908 XmlEncError::Rsa(error.to_string())
909 }
910 error => XmlEncError::Provider(error),
911 })
912}
913
914pub fn decrypt(
916 xml: &str,
917 resolver: &dyn DecryptionKeyResolver,
918) -> Result<DecryptedContent, XmlEncError> {
919 DecryptContext::new(resolver).decrypt(xml)
920}
921
922pub fn decrypt_document(
929 xml: &str,
930 encrypted_data_id: Option<&str>,
931 resolver: &dyn DecryptionKeyResolver,
932) -> Result<String, XmlEncError> {
933 decrypt_document_with_options(
934 xml,
935 DocumentDecryptionOptions { encrypted_data_id },
936 resolver,
937 )
938}
939
940pub fn decrypt_document_with_options(
945 xml: &str,
946 options: DocumentDecryptionOptions<'_>,
947 resolver: &dyn DecryptionKeyResolver,
948) -> Result<String, XmlEncError> {
949 DecryptContext::new(resolver).decrypt_document(xml, options.encrypted_data_id)
950}
951
952#[derive(Clone, Copy)]
953enum DocumentEncryptedDataSelector<'a> {
954 EncryptedDataId(Option<&'a str>),
955 UniqueBelowStartNode(Option<&'a str>),
956 FirstBelowStartNode(Option<&'a str>),
957}
958
959fn decrypt_document_with_context(
960 xml: &str,
961 selector: DocumentEncryptedDataSelector<'_>,
962 context: &DecryptContext<'_>,
963) -> Result<String, XmlEncError> {
964 context.policy.resources.validate()?;
965 validate_encryption_document_len(xml.len(), &context.policy)?;
966 let budgets = DecryptionOperationBudgets::from_policy(&context.policy);
967 let settings = context.document_parse_settings();
968 let mut document =
969 XmlDocument::parse_with_settings_and_budget(xml.to_owned(), settings, &budgets.xml_parse)
970 .map_err(|error| map_document_error(error, settings))?;
971 decrypt_owned_document_with_context(&mut document, selector, context, budgets)?;
972 Ok(document.into_xml())
973}
974
975fn decrypt_owned_document_with_context(
976 document: &mut XmlDocument,
977 selector: DocumentEncryptedDataSelector<'_>,
978 context: &DecryptContext<'_>,
979 budgets: DecryptionOperationBudgets,
980) -> Result<(), XmlEncError> {
981 context.policy.resources.validate()?;
982 document.validate_operation_policy(&context.policy.xml, &context.policy.resources)?;
983 let (target, target_len, encrypted) = document.with_view(|view| {
984 let start = match selector {
985 DocumentEncryptedDataSelector::UniqueBelowStartNode(Some(id))
986 | DocumentEncryptedDataSelector::FirstBelowStartNode(Some(id)) => view
987 .node_for_id(id, context.id_attributes)
988 .ok_or_else(|| XmlEncError::SelectedNodeUnavailable { id: id.to_owned() })
989 .and_then(|identity| view.resolve_node(identity).map_err(XmlEncError::from))?,
990 DocumentEncryptedDataSelector::UniqueBelowStartNode(None)
991 | DocumentEncryptedDataSelector::FirstBelowStartNode(None)
992 | DocumentEncryptedDataSelector::EncryptedDataId(_) => view.document().root(),
993 };
994 let encrypted_data_id = match selector {
995 DocumentEncryptedDataSelector::EncryptedDataId(id) => id,
996 DocumentEncryptedDataSelector::UniqueBelowStartNode(_)
997 | DocumentEncryptedDataSelector::FirstBelowStartNode(_) => None,
998 };
999 let mut matches = start.descendants().filter(|node| {
1000 node.has_tag_name((XMLENC_NS, "EncryptedData"))
1001 && encrypted_data_id.is_none_or(|id| node.attribute("Id") == Some(id))
1002 });
1003 let selected = matches.next().ok_or(XmlEncError::EncryptedDataNotFound)?;
1004 if matches!(
1005 selector,
1006 DocumentEncryptedDataSelector::EncryptedDataId(_)
1007 | DocumentEncryptedDataSelector::UniqueBelowStartNode(_)
1008 ) && matches.next().is_some()
1009 {
1010 return Err(XmlEncError::AmbiguousEncryptedData);
1011 }
1012 Ok::<_, XmlEncError>((
1013 view.node_identity(selected),
1014 selected.range().len(),
1015 parse_encrypted_data_node_with_policy_and_budget(
1016 selected,
1017 &context.policy,
1018 &budgets.xml_parse,
1019 context.xml_backend,
1020 )?,
1021 ))
1022 })?;
1023 let document_binding = Some((document.identity(), document.generation()));
1024 let mut processed = context.process_decryption_candidates(
1025 &encrypted,
1026 document_binding,
1027 true,
1028 budgets,
1029 |candidate, operation_parse_budget| {
1030 let DecryptedContent::Xml(plaintext) = candidate else {
1031 return Err(XmlEncError::ReplacementRequiresXml);
1032 };
1033 validate_encryption_document_len(
1034 document
1035 .as_xml()
1036 .len()
1037 .saturating_sub(target_len)
1038 .saturating_add(plaintext.len()),
1039 &context.policy,
1040 )?;
1041 let settings = context.document_parse_settings();
1042 match encrypted.encrypted_type.as_ref() {
1043 Some(EncryptedDataType::Element) => document
1044 .prepare_element_replacement_with_budget(
1045 target,
1046 &plaintext,
1047 settings,
1048 operation_parse_budget,
1049 )
1050 .map_err(|error| map_document_error(error, settings)),
1051 Some(EncryptedDataType::Content) => document
1052 .prepare_node_fragment_replacement_with_budget(
1053 target,
1054 &plaintext,
1055 settings,
1056 operation_parse_budget,
1057 )
1058 .map_err(|error| map_document_error(error, settings)),
1059 Some(EncryptedDataType::Other(_)) | None => {
1060 Err(XmlEncError::ReplacementRequiresXml)
1061 }
1062 }
1063 },
1064 )?;
1065 let mutation = processed.mutation.ok_or_else(|| {
1066 XmlEncError::OperationPlan("decryption mutation node is unavailable".into())
1067 })?;
1068 processed
1069 .operation
1070 .run_document_transition(mutation, document, |document, _| {
1071 document
1072 .commit_prepared(processed.output)
1073 .map_err(|error| map_document_error(error, context.document_parse_settings()))
1074 })
1075}
1076
1077fn validate_encryption_document_len(
1078 actual: usize,
1079 policy: &crate::policy::DecryptionPolicy,
1080) -> Result<(), XmlEncError> {
1081 policy.resources.validate_xml_document_len(actual)?;
1082 Ok(())
1083}
1084
1085fn validate_recipient_count(actual: usize, maximum: usize) -> Result<(), XmlEncError> {
1086 if actual > maximum {
1087 return Err(crate::policy::PolicyViolation::ResourceLimit {
1088 resource: crate::policy::resource_name::ENCRYPTION_RECIPIENTS,
1089 maximum,
1090 actual,
1091 }
1092 .into());
1093 }
1094 Ok(())
1095}
1096
1097pub fn decrypt_data(
1099 encrypted: &EncryptedData,
1100 resolver: &dyn DecryptionKeyResolver,
1101) -> Result<DecryptedContent, XmlEncError> {
1102 DecryptContext::new(resolver).decrypt_data(encrypted)
1103}
1104
1105fn resolve_content_key_candidates(
1106 provider: &dyn crate::provider::CryptoProvider,
1107 algorithm: DataEncryptionAlgorithm,
1108 encrypted: &EncryptedData,
1109 resolver: &dyn DecryptionKeyResolver,
1110 policy: &crate::policy::DecryptionPolicy,
1111 budget: &mut KeyCandidateBudget,
1112) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1113 let mut last_error = None;
1114 let mut candidates =
1115 match resolve_candidates_with_budget(resolver, provider, algorithm, None, policy, budget) {
1116 Ok(keys) => keys,
1117 Err(error) => {
1118 record_candidate_source_error_or_fail_operation(error, &mut last_error)?;
1119 Vec::new()
1120 }
1121 };
1122 for encrypted_key in &encrypted.encrypted_keys {
1123 if !encrypted_key_applies_to_data(encrypted_key, encrypted) {
1124 continue;
1125 }
1126 if let Err(error) = validate_encrypted_key_policy(encrypted_key, policy) {
1127 last_error = Some(error);
1128 continue;
1129 }
1130 match resolve_candidates_with_budget(
1131 resolver,
1132 provider,
1133 algorithm,
1134 Some(encrypted_key),
1135 policy,
1136 budget,
1137 ) {
1138 Ok(keys) => candidates.extend(keys),
1139 Err(error) => record_candidate_source_error_or_fail_operation(error, &mut last_error)?,
1140 }
1141 }
1142 if candidates.is_empty() {
1143 Err(last_error.unwrap_or(XmlEncError::KeyNotFound))
1144 } else {
1145 Ok(candidates)
1146 }
1147}
1148
1149fn record_candidate_source_error_or_fail_operation(
1150 error: XmlEncError,
1151 last_error: &mut Option<XmlEncError>,
1152) -> Result<(), XmlEncError> {
1153 if matches!(&error, XmlEncError::Policy(_)) {
1157 return Err(error);
1158 }
1159 *last_error = Some(error);
1160 Ok(())
1161}
1162
1163fn resolve_candidates_with_budget(
1164 resolver: &dyn DecryptionKeyResolver,
1165 provider: &dyn crate::provider::CryptoProvider,
1166 algorithm: DataEncryptionAlgorithm,
1167 encrypted_key: Option<&EncryptedKey>,
1168 policy: &crate::policy::DecryptionPolicy,
1169 budget: &mut KeyCandidateBudget,
1170) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1171 let remaining_before = budget.remaining();
1172 let keys = resolver.resolve_content_keys_with_policy(
1173 provider,
1174 algorithm,
1175 encrypted_key,
1176 policy,
1177 budget,
1178 )?;
1179 budget.account_returned_candidates(remaining_before, keys.len())?;
1180 Ok(keys)
1181}
1182
1183fn encrypted_key_applies_to_data(
1184 encrypted_key: &EncryptedKey,
1185 encrypted_data: &EncryptedData,
1186) -> bool {
1187 if let Some(references) = encrypted_key.reference_list.as_ref()
1191 && !references.data_references.is_empty()
1192 {
1193 let Some(id) = encrypted_data.id.as_deref() else {
1194 return false;
1195 };
1196 let target = format!("#{id}");
1197 if !references.data_references.iter().any(|uri| uri == &target) {
1198 return false;
1199 }
1200 }
1201 if let (Some(carried), Some(expected)) = (
1202 encrypted_key.carried_key_name.as_deref(),
1203 encrypted_data.key_name.as_deref(),
1204 ) && carried != expected
1205 {
1206 return false;
1207 }
1208 true
1209}
1210
1211fn compatible_decryption_key_candidates(
1212 algorithm: DataEncryptionAlgorithm,
1213 mut keys: Vec<crate::provider::RecoveredContentKey>,
1214) -> Result<Vec<crate::provider::RecoveredContentKey>, XmlEncError> {
1215 let mut accepted = 0;
1216 let mut last_error = None;
1217 for index in 0..keys.len() {
1218 let key = &keys[index];
1219 match validate_key_len(algorithm, key.bytes()) {
1220 Ok(())
1221 if !keys[..accepted].iter().any(|existing| {
1222 existing.bytes() == key.bytes() && existing.valid() == key.valid()
1223 }) =>
1224 {
1225 keys.swap(accepted, index);
1229 accepted += 1;
1230 }
1231 Ok(()) => {}
1232 Err(error) => last_error = Some(error),
1233 }
1234 }
1235 keys.truncate(accepted);
1236 if keys.is_empty() {
1237 return Err(last_error.unwrap_or(XmlEncError::KeyNotFound));
1238 }
1239 Ok(keys)
1240}
1241
1242fn validate_decryption_key_candidates(
1243 algorithm: DataEncryptionAlgorithm,
1244 actual: usize,
1245) -> Result<(), XmlEncError> {
1246 let maximum = crate::hard_limits::KEY_CANDIDATE_CEILING;
1247 if actual > maximum {
1248 return Err(crate::policy::PolicyViolation::ResourceLimit {
1249 resource: crate::policy::resource_name::KEY_CANDIDATES,
1250 maximum,
1251 actual,
1252 }
1253 .into());
1254 }
1255 if actual > 1 && algorithm.cbc_block_len().is_some() {
1256 return Err(XmlEncError::AmbiguousKeyCandidates { algorithm, actual });
1257 }
1258 Ok(())
1259}
1260
1261fn validate_encrypted_key_policy(
1262 encrypted_key: &EncryptedKey,
1263 policy: &crate::policy::DecryptionPolicy,
1264) -> Result<(), XmlEncError> {
1265 encrypted_key.encryption_method.validate_structure()?;
1266 let uri = &encrypted_key.encryption_method.algorithm;
1267 if let Ok(transport) = KeyTransportAlgorithm::from_uri(uri) {
1268 if policy
1269 .key_transport_algorithms
1270 .as_ref()
1271 .map_or(transport.requires_explicit_permission(), |allowed| {
1272 !allowed.contains(&transport)
1273 })
1274 {
1275 return Err(crate::policy::PolicyViolation::Algorithm {
1276 operation: "decryption",
1277 algorithm: uri.clone(),
1278 }
1279 .into());
1280 }
1281 #[cfg(feature = "legacy-algorithms")]
1282 if transport == KeyTransportAlgorithm::RsaPkcs1v15 {
1283 return Ok(());
1284 }
1285 let method = &encrypted_key.encryption_method;
1286 let digest = parse_oaep_digest(method.oaep_digest.as_deref())?;
1287 let mgf_digest = parse_oaep_mgf_digest(method.mgf_algorithm.as_deref())?;
1288 let selected_algorithms = [
1289 (
1290 digest,
1291 method.oaep_digest.as_deref().unwrap_or(digest.uri()),
1292 ),
1293 (
1294 mgf_digest,
1295 method
1296 .mgf_algorithm
1297 .as_deref()
1298 .unwrap_or(mgf_digest.mgf_uri()),
1299 ),
1300 ];
1301 for (selected, wire_uri) in selected_algorithms {
1302 if policy
1303 .oaep_digests
1304 .as_ref()
1305 .is_some_and(|allowed| !allowed.contains(&selected))
1306 {
1307 return Err(crate::policy::PolicyViolation::Algorithm {
1308 operation: "decryption",
1309 algorithm: wire_uri.to_owned(),
1310 }
1311 .into());
1312 }
1313 }
1314 } else {
1315 let wrap = KeyWrapAlgorithm::from_uri(uri)?;
1316 if policy
1317 .key_wrap_algorithms
1318 .as_ref()
1319 .map_or(wrap.requires_explicit_permission(), |allowed| {
1320 !allowed.contains(&wrap)
1321 })
1322 {
1323 return Err(crate::policy::PolicyViolation::Algorithm {
1324 operation: "decryption",
1325 algorithm: uri.clone(),
1326 }
1327 .into());
1328 }
1329 }
1330 Ok(())
1331}
1332
1333fn validate_content_framing_before_resolution(
1334 algorithm: DataEncryptionAlgorithm,
1335 ciphertext_len: usize,
1336 encrypted_keys: &[EncryptedKey],
1337 policy: &crate::policy::DecryptionPolicy,
1338) -> Result<(), XmlEncError> {
1339 let Err(framing_error) = validate_ciphertext_framing(algorithm, ciphertext_len) else {
1340 return Ok(());
1341 };
1342
1343 if !encrypted_keys.is_empty() {
1348 let mut last_key_error = None;
1349 for encrypted_key in encrypted_keys {
1350 match validate_encrypted_key_policy(encrypted_key, policy) {
1351 Ok(()) => return Err(framing_error),
1352 Err(error) => last_key_error = Some(error),
1353 }
1354 }
1355 if let Some(error) = last_key_error {
1356 return Err(error);
1357 }
1358 }
1359 Err(framing_error)
1360}
1361
1362fn validate_typed_cipher_values(
1363 encrypted: &EncryptedData,
1364 algorithm: DataEncryptionAlgorithm,
1365 maximum_plaintext: usize,
1366 maximum_cipher_values: usize,
1367) -> Result<(), XmlEncError> {
1368 let maximum_ciphertext = algorithm
1369 .ciphertext_len_for_plaintext(maximum_plaintext)
1370 .ok_or(XmlEncError::InvalidEncryptionConfig(
1371 "ciphertext size overflow".into(),
1372 ))?;
1373 let projected = validate_cipher_value_len(&encrypted.cipher_data.value, maximum_ciphertext)?;
1374 if projected > maximum_ciphertext {
1375 return Err(crate::policy::PolicyViolation::ResourceLimit {
1376 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
1377 maximum: maximum_plaintext,
1378 actual: projected.saturating_sub(algorithm.minimum_ciphertext_len()),
1379 }
1380 .into());
1381 }
1382
1383 let mut aggregate_encoded = encrypted.cipher_data.value.len();
1384 if aggregate_encoded > maximum_cipher_values {
1385 return Err(crate::policy::PolicyViolation::ResourceLimit {
1386 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
1387 maximum: maximum_cipher_values,
1388 actual: aggregate_encoded,
1389 }
1390 .into());
1391 }
1392
1393 let maximum_wrapped_key = projected_decoded_len_for_encoded_len(MAX_CIPHER_VALUE_BASE64_LEN);
1394 for encrypted_key in &encrypted.encrypted_keys {
1395 validate_cipher_value_len(&encrypted_key.cipher_data.value, maximum_wrapped_key)?;
1396 aggregate_encoded = aggregate_encoded.saturating_add(encrypted_key.cipher_data.value.len());
1397 if aggregate_encoded > maximum_cipher_values {
1398 return Err(crate::policy::PolicyViolation::ResourceLimit {
1399 resource: crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
1400 maximum: maximum_cipher_values,
1401 actual: aggregate_encoded,
1402 }
1403 .into());
1404 }
1405 }
1406 Ok(())
1407}
1408
1409fn validate_cipher_value_len(value: &str, maximum_decoded: usize) -> Result<usize, XmlEncError> {
1410 if value.len() > MAX_CIPHER_VALUE_BASE64_LEN {
1411 return Err(XmlEncError::InvalidStructure(format!(
1412 "CipherValue exceeds {MAX_CIPHER_VALUE_BASE64_LEN}-byte limit"
1413 )));
1414 }
1415 Ok(projected_decoded_len(value).min(maximum_decoded.saturating_add(1)))
1416}
1417
1418fn projected_decoded_len(value: &str) -> usize {
1419 let padding = value
1420 .as_bytes()
1421 .iter()
1422 .rev()
1423 .take(2)
1424 .take_while(|byte| **byte == b'=')
1425 .count();
1426 projected_decoded_len_for_encoded_len(value.len()).saturating_sub(padding)
1427}
1428
1429fn projected_decoded_len_for_encoded_len(encoded_len: usize) -> usize {
1430 encoded_len
1431 .checked_add(3)
1432 .map(|length| length / 4)
1433 .and_then(|quanta| quanta.checked_mul(3))
1434 .unwrap_or(usize::MAX)
1435}
1436
1437pub(crate) fn validate_key_len(
1438 algorithm: DataEncryptionAlgorithm,
1439 key: &[u8],
1440) -> Result<(), XmlEncError> {
1441 if key.len() == algorithm.key_len() {
1442 Ok(())
1443 } else {
1444 Err(XmlEncError::InvalidKeySize {
1445 algorithm,
1446 expected: algorithm.key_len(),
1447 actual: key.len(),
1448 })
1449 }
1450}
1451
1452fn validate_possible_plaintext_len(
1453 algorithm: DataEncryptionAlgorithm,
1454 ciphertext_len: usize,
1455 maximum: usize,
1456) -> Result<(), XmlEncError> {
1457 let framing = algorithm.minimum_ciphertext_len();
1460 validate_plaintext_len(ciphertext_len.saturating_sub(framing), maximum)
1461}
1462
1463fn validate_provider_plaintext_len(
1464 algorithm: DataEncryptionAlgorithm,
1465 ciphertext_len: usize,
1466 plaintext_len: usize,
1467) -> Result<(), XmlEncError> {
1468 use crate::provider::{ProviderError, ProviderOperation};
1469
1470 match algorithm.cbc_block_len() {
1471 None => {
1472 let expected = ciphertext_len - algorithm.minimum_ciphertext_len();
1473 if plaintext_len != expected {
1474 return Err(ProviderError::InvalidOutputSize {
1475 operation: ProviderOperation::Decrypt,
1476 expected,
1477 actual: plaintext_len,
1478 }
1479 .into());
1480 }
1481 }
1482 Some(block) => {
1483 let padded_len = ciphertext_len - block;
1484 let minimum = padded_len - block;
1485 let maximum = padded_len - 1;
1486 if !(minimum..=maximum).contains(&plaintext_len) {
1487 return Err(ProviderError::InvalidOutputSizeRange {
1488 operation: ProviderOperation::Decrypt,
1489 minimum,
1490 maximum,
1491 actual: plaintext_len,
1492 }
1493 .into());
1494 }
1495 }
1496 }
1497 Ok(())
1498}
1499
1500fn validate_plaintext_len(actual: usize, maximum: usize) -> Result<(), XmlEncError> {
1501 if actual <= maximum {
1502 Ok(())
1503 } else {
1504 Err(crate::policy::PolicyViolation::ResourceLimit {
1505 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
1506 maximum,
1507 actual,
1508 }
1509 .into())
1510 }
1511}
1512
1513fn map_data_decryption_error(
1514 algorithm: DataEncryptionAlgorithm,
1515 ciphertext_len: usize,
1516 error: crate::provider::ProviderError,
1517) -> XmlEncError {
1518 use crate::provider::ProviderError;
1519
1520 match error {
1521 ProviderError::AuthenticationFailed if algorithm.cbc_block_len().is_none() => {
1522 XmlEncError::AeadAuthenticationFailed
1523 }
1524 ProviderError::InvalidInput(crate::provider::ProviderInputError::AesGcmFraming)
1525 if algorithm.cbc_block_len().is_none() =>
1526 {
1527 XmlEncError::DataTooShort {
1528 algorithm: "AES-GCM",
1529 minimum: 28,
1530 actual: ciphertext_len,
1531 }
1532 }
1533 error @ ProviderError::InvalidInput(crate::provider::ProviderInputError::AesCbcFraming)
1534 if algorithm.cbc_block_len().is_some() =>
1535 {
1536 match super::types::validate_ciphertext_framing(algorithm, ciphertext_len) {
1537 Err(error) => error,
1538 Ok(()) => XmlEncError::Provider(error),
1539 }
1540 }
1541 ProviderError::InvalidInput(crate::provider::ProviderInputError::AesCbcCiphertext)
1542 if algorithm.cbc_block_len().is_some() =>
1543 {
1544 XmlEncError::InvalidPadding
1545 }
1546 error => XmlEncError::Provider(error),
1547 }
1548}
1549
1550#[cfg(test)]
1551mod tests {
1552 #[cfg(feature = "legacy-algorithms")]
1553 #[test]
1554 fn legacy_cbc_rejects_ambiguous_candidates_before_decryption() {
1555 for algorithm in [
1558 super::DataEncryptionAlgorithm::TripleDesCbc,
1559 super::DataEncryptionAlgorithm::Aes192Cbc,
1560 ] {
1561 assert!(matches!(
1562 super::validate_decryption_key_candidates(algorithm, 2),
1563 Err(super::XmlEncError::AmbiguousKeyCandidates { actual: 2, .. })
1564 ));
1565 }
1566 }
1567 use std::cell::{Cell, RefCell};
1568 use std::sync::atomic::{AtomicUsize, Ordering};
1569
1570 use aes_gcm::{
1571 Aes128Gcm,
1572 aead::{AeadInOut, KeyInit},
1573 };
1574 use aes_kw::KwAes128;
1575 use base64::engine::general_purpose::STANDARD;
1576 use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
1577 use rsa::{Oaep, RsaPublicKey, pkcs8::DecodePrivateKey};
1578 use sha1::Sha1;
1579 use sha2::{Sha256, Sha384};
1580
1581 use super::*;
1582 use crate::xmlenc::{CipherData, EncryptionMethod};
1583
1584 struct RecipientKeyResolver {
1585 recipient: &'static str,
1586 key: Vec<u8>,
1587 }
1588
1589 struct CountingResolver {
1590 candidate_calls: Cell<usize>,
1591 key: Vec<u8>,
1592 }
1593
1594 struct AllCallsResolver {
1595 calls: Cell<usize>,
1596 key: Vec<u8>,
1597 }
1598
1599 struct CandidateResolver {
1600 keys: Vec<Vec<u8>>,
1601 }
1602
1603 #[test]
1604 fn policy_unaware_recipient_resolver_is_not_dispatched() {
1605 struct LegacyRecipient {
1609 recipient_calls: Cell<usize>,
1610 key: Vec<u8>,
1611 }
1612 impl DecryptionKeyResolver for LegacyRecipient {
1613 fn resolve_key(
1614 &self,
1615 _provider: &dyn crate::provider::CryptoProvider,
1616 _algorithm: DataEncryptionAlgorithm,
1617 encrypted_key: Option<&EncryptedKey>,
1618 ) -> Result<Vec<u8>, XmlEncError> {
1619 if encrypted_key.is_none() {
1620 return Err(XmlEncError::KeyNotFound);
1621 }
1622 self.recipient_calls.set(self.recipient_calls.get() + 1);
1623 Ok(self.key.clone())
1624 }
1625 }
1626 let resolver = LegacyRecipient {
1627 recipient_calls: Cell::new(0),
1628 key: vec![0x63; 16],
1629 };
1630 let encrypted = encrypted_data_with_recipients(
1631 &resolver.key,
1632 vec![associated_encrypted_key("recipient", None, None)],
1633 None,
1634 );
1635 assert!(matches!(
1636 DecryptContext::new(&resolver).decrypt_data(&encrypted),
1637 Err(XmlEncError::KeyNotFound)
1638 ));
1639 assert_eq!(resolver.recipient_calls.get(), 0);
1640
1641 let direct = SymmetricKeyDecryptor::new(resolver.key.clone());
1642 let mut budget = KeyCandidateBudget::with_limit(1);
1643 assert!(matches!(
1644 resolver.resolve_key_candidates_with_policy(
1645 crate::provider::default_provider(),
1646 DataEncryptionAlgorithm::Aes128Gcm,
1647 encrypted.encrypted_keys.first(),
1648 &crate::policy::DecryptionPolicy::default(),
1649 &mut budget,
1650 ),
1651 Err(XmlEncError::KeyNotFound)
1652 ));
1653 assert_eq!(budget.remaining(), 1, "no recipient work was performed");
1654 assert_eq!(
1655 DecryptContext::new(&direct)
1656 .decrypt_data(&encrypted)
1657 .expect("direct AES does not require RSA metadata"),
1658 DecryptedContent::Bytes(b"payload".to_vec())
1659 );
1660 }
1661
1662 #[test]
1663 fn document_decryption_initial_parse_uses_the_policy_work_budget() {
1664 let xml = "<root/>";
1667 let policy = crate::policy::DecryptionPolicy {
1668 resources: crate::policy::ResourcePolicy {
1669 max_xml_parse_work_bytes: 0,
1670 ..crate::policy::ResourcePolicy::default()
1671 },
1672 ..crate::policy::DecryptionPolicy::default()
1673 };
1674 let resolver = SymmetricKeyDecryptor::new([0_u8; 16]);
1675
1676 let error = DecryptContext::new(&resolver)
1677 .policy(policy)
1678 .decrypt_document(xml, None)
1679 .expect_err("a zero parse-work budget must reject the input parse");
1680
1681 assert!(matches!(
1682 error,
1683 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
1684 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
1685 maximum: 0,
1686 actual,
1687 }) if actual == xml.len()
1688 ));
1689 }
1690
1691 struct AggregateRecipientResolver {
1692 attempts: Cell<usize>,
1693 key: Vec<u8>,
1694 }
1695
1696 struct AssociationRecordingResolver {
1697 visited: RefCell<Vec<String>>,
1698 key: Vec<u8>,
1699 }
1700
1701 struct OrderedRecipientResolver {
1702 wrong: Vec<u8>,
1703 correct: Vec<u8>,
1704 }
1705
1706 struct DirectAndRecipientResolver {
1707 direct: Vec<u8>,
1708 recipient: Vec<u8>,
1709 }
1710
1711 struct FailingDirectResolver {
1712 recipient: Vec<u8>,
1713 }
1714
1715 struct PolicyRejectingDirectResolver {
1716 recipient: Vec<u8>,
1717 }
1718
1719 struct MislabelledExhaustionResolver {
1720 direct: Vec<u8>,
1721 }
1722
1723 impl DecryptionKeyResolver for DirectAndRecipientResolver {
1724 fn resolve_key_candidates_with_policy(
1725 &self,
1726 provider: &dyn crate::provider::CryptoProvider,
1727 algorithm: DataEncryptionAlgorithm,
1728 encrypted_key: Option<&EncryptedKey>,
1729 policy: &crate::policy::DecryptionPolicy,
1730 budget: &mut KeyCandidateBudget,
1731 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1732 policy.validate()?;
1734 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
1735 }
1736
1737 fn resolve_key(
1738 &self,
1739 _provider: &dyn crate::provider::CryptoProvider,
1740 _algorithm: DataEncryptionAlgorithm,
1741 encrypted_key: Option<&EncryptedKey>,
1742 ) -> Result<Vec<u8>, XmlEncError> {
1743 Ok(if encrypted_key.is_some() {
1744 self.recipient.clone()
1745 } else {
1746 self.direct.clone()
1747 })
1748 }
1749 }
1750
1751 impl DecryptionKeyResolver for FailingDirectResolver {
1752 fn resolve_key_candidates_with_policy(
1753 &self,
1754 provider: &dyn crate::provider::CryptoProvider,
1755 algorithm: DataEncryptionAlgorithm,
1756 encrypted_key: Option<&EncryptedKey>,
1757 policy: &crate::policy::DecryptionPolicy,
1758 budget: &mut KeyCandidateBudget,
1759 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1760 policy.validate()?;
1762 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
1763 }
1764
1765 fn resolve_key(
1766 &self,
1767 _provider: &dyn crate::provider::CryptoProvider,
1768 algorithm: DataEncryptionAlgorithm,
1769 encrypted_key: Option<&EncryptedKey>,
1770 ) -> Result<Vec<u8>, XmlEncError> {
1771 if encrypted_key.is_some() {
1772 Ok(self.recipient.clone())
1773 } else {
1774 Err(XmlEncError::InvalidKeySize {
1775 algorithm,
1776 expected: 16,
1777 actual: 8,
1778 })
1779 }
1780 }
1781 }
1782
1783 impl DecryptionKeyResolver for PolicyRejectingDirectResolver {
1784 fn resolve_key(
1785 &self,
1786 _provider: &dyn crate::provider::CryptoProvider,
1787 _algorithm: DataEncryptionAlgorithm,
1788 encrypted_key: Option<&EncryptedKey>,
1789 ) -> Result<Vec<u8>, XmlEncError> {
1790 if encrypted_key.is_some() {
1791 Ok(self.recipient.clone())
1792 } else {
1793 Err(crate::policy::PolicyViolation::KeyTrust {
1794 reason: "test resolver rejected the operation",
1795 }
1796 .into())
1797 }
1798 }
1799 }
1800
1801 impl DecryptionKeyResolver for MislabelledExhaustionResolver {
1802 fn resolve_key(
1803 &self,
1804 _provider: &dyn crate::provider::CryptoProvider,
1805 _algorithm: DataEncryptionAlgorithm,
1806 _encrypted_key: Option<&EncryptedKey>,
1807 ) -> Result<Vec<u8>, XmlEncError> {
1808 Err(XmlEncError::KeyNotFound)
1809 }
1810
1811 fn resolve_key_candidates_with_policy(
1812 &self,
1813 _provider: &dyn crate::provider::CryptoProvider,
1814 _algorithm: DataEncryptionAlgorithm,
1815 encrypted_key: Option<&EncryptedKey>,
1816 policy: &crate::policy::DecryptionPolicy,
1817 budget: &mut KeyCandidateBudget,
1818 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1819 policy.validate()?;
1821 if encrypted_key.is_none() {
1822 budget.consume(1)?;
1823 return Ok(vec![self.direct.clone()]);
1824 }
1825 budget.consume(budget.remaining().saturating_add(1))?;
1826 unreachable!("candidate budget exhaustion must return first")
1827 }
1828 }
1829
1830 impl DecryptionKeyResolver for OrderedRecipientResolver {
1831 fn resolve_key(
1832 &self,
1833 _provider: &dyn crate::provider::CryptoProvider,
1834 _algorithm: DataEncryptionAlgorithm,
1835 _encrypted_key: Option<&EncryptedKey>,
1836 ) -> Result<Vec<u8>, XmlEncError> {
1837 Err(XmlEncError::KeyNotFound)
1838 }
1839
1840 fn resolve_key_candidates_with_policy(
1841 &self,
1842 _provider: &dyn crate::provider::CryptoProvider,
1843 _algorithm: DataEncryptionAlgorithm,
1844 encrypted_key: Option<&EncryptedKey>,
1845 policy: &crate::policy::DecryptionPolicy,
1846 budget: &mut KeyCandidateBudget,
1847 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1848 policy.validate()?;
1850 budget.consume(1)?;
1851 match encrypted_key.and_then(|key| key.id.as_deref()) {
1852 Some("first") => Ok(vec![self.wrong.clone()]),
1853 Some("second") => Ok(vec![self.correct.clone()]),
1854 _ => Err(XmlEncError::KeyNotFound),
1855 }
1856 }
1857 }
1858
1859 impl DecryptionKeyResolver for CandidateResolver {
1860 fn resolve_key(
1861 &self,
1862 _provider: &dyn crate::provider::CryptoProvider,
1863 _algorithm: DataEncryptionAlgorithm,
1864 _encrypted_key: Option<&EncryptedKey>,
1865 ) -> Result<Vec<u8>, XmlEncError> {
1866 Err(XmlEncError::KeyNotFound)
1867 }
1868
1869 fn resolve_key_candidates(
1870 &self,
1871 _provider: &dyn crate::provider::CryptoProvider,
1872 _algorithm: DataEncryptionAlgorithm,
1873 encrypted_key: Option<&EncryptedKey>,
1874 budget: &mut KeyCandidateBudget,
1875 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1876 if encrypted_key.is_none() {
1877 budget.consume(self.keys.len())?;
1878 Ok(self.keys.clone())
1879 } else {
1880 Err(XmlEncError::KeyNotFound)
1881 }
1882 }
1883 }
1884
1885 impl DecryptionKeyResolver for AggregateRecipientResolver {
1886 fn resolve_key(
1887 &self,
1888 _provider: &dyn crate::provider::CryptoProvider,
1889 _algorithm: DataEncryptionAlgorithm,
1890 _encrypted_key: Option<&EncryptedKey>,
1891 ) -> Result<Vec<u8>, XmlEncError> {
1892 Err(XmlEncError::KeyNotFound)
1893 }
1894
1895 fn resolve_key_candidates_with_policy(
1896 &self,
1897 _provider: &dyn crate::provider::CryptoProvider,
1898 _algorithm: DataEncryptionAlgorithm,
1899 encrypted_key: Option<&EncryptedKey>,
1900 policy: &crate::policy::DecryptionPolicy,
1901 budget: &mut KeyCandidateBudget,
1902 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1903 policy.validate()?;
1905 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
1906 let attempts = budget.remaining();
1907 if attempts == 0 {
1908 budget.consume(1)?;
1909 }
1910 budget.consume(attempts)?;
1911 self.attempts.set(self.attempts.get() + attempts);
1912 if encrypted_key.id.as_deref() == Some("first") {
1913 Err(XmlEncError::KeyNotFound)
1914 } else {
1915 Ok(vec![self.key.clone()])
1916 }
1917 }
1918 }
1919
1920 impl DecryptionKeyResolver for AssociationRecordingResolver {
1921 fn resolve_key(
1922 &self,
1923 _provider: &dyn crate::provider::CryptoProvider,
1924 _algorithm: DataEncryptionAlgorithm,
1925 _encrypted_key: Option<&EncryptedKey>,
1926 ) -> Result<Vec<u8>, XmlEncError> {
1927 Err(XmlEncError::KeyNotFound)
1928 }
1929
1930 fn resolve_key_candidates_with_policy(
1931 &self,
1932 _provider: &dyn crate::provider::CryptoProvider,
1933 _algorithm: DataEncryptionAlgorithm,
1934 encrypted_key: Option<&EncryptedKey>,
1935 policy: &crate::policy::DecryptionPolicy,
1936 budget: &mut KeyCandidateBudget,
1937 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
1938 policy.validate()?;
1940 let encrypted_key = encrypted_key.ok_or(XmlEncError::KeyNotFound)?;
1941 budget.consume(1)?;
1942 self.visited
1943 .borrow_mut()
1944 .push(encrypted_key.id.clone().unwrap_or_default());
1945 Ok(vec![self.key.clone()])
1946 }
1947 }
1948
1949 fn associated_encrypted_key(
1950 id: &str,
1951 data_reference: Option<&str>,
1952 carried_key_name: Option<&str>,
1953 ) -> EncryptedKey {
1954 EncryptedKey {
1955 id: Some(id.into()),
1956 recipient: None,
1957 key_name: None,
1958 encryption_method: EncryptionMethod {
1959 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
1960 key_size_bits: None,
1961 oaep_digest: None,
1962 mgf_algorithm: None,
1963 oaep_params: None,
1964 },
1965 cipher_data: CipherData {
1966 value: STANDARD.encode([0_u8; 256]),
1967 },
1968 reference_list: data_reference.map(|uri| crate::xmlenc::ReferenceList {
1969 data_references: vec![uri.into()],
1970 key_references: Vec::new(),
1971 }),
1972 carried_key_name: carried_key_name.map(str::to_owned),
1973 }
1974 }
1975
1976 fn encrypted_data_with_recipients(
1977 key: &[u8],
1978 encrypted_keys: Vec<EncryptedKey>,
1979 key_name: Option<&str>,
1980 ) -> EncryptedData {
1981 EncryptedData {
1982 id: Some("target".into()),
1983 encrypted_type: None,
1984 encryption_method: EncryptionMethod {
1985 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
1986 key_size_bits: None,
1987 oaep_digest: None,
1988 mgf_algorithm: None,
1989 oaep_params: None,
1990 },
1991 key_name: key_name.map(str::to_owned),
1992 encrypted_keys,
1993 cipher_data: CipherData {
1994 value: STANDARD.encode(
1995 crate::provider::default_provider()
1996 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, key, b"payload")
1997 .expect("test encryption must succeed"),
1998 ),
1999 },
2000 }
2001 }
2002
2003 #[derive(Debug, Default)]
2004 struct PermissiveUnwrapProvider {
2005 decrypt_calls: AtomicUsize,
2006 unwrap_calls: AtomicUsize,
2007 recover_calls: AtomicUsize,
2008 plaintext: Vec<u8>,
2009 candidate_plaintexts: Vec<Vec<u8>>,
2010 }
2011
2012 struct OpaqueRecoveryKey;
2013
2014 impl crate::provider::KeyRecoveryKey for OpaqueRecoveryKey {
2015 fn rsa_modulus_bits(&self) -> usize {
2016 2048
2017 }
2018 fn rsa_public_exponent(&self) -> Option<u64> {
2019 Some(65537)
2020 }
2021 fn ciphertext_len(&self) -> usize {
2022 256
2023 }
2024
2025 fn recover_with_provider(
2026 &self,
2027 _provider: &dyn crate::provider::CryptoProvider,
2028 _parameters: &RsaOaepParameters,
2029 _ciphertext: &[u8],
2030 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2031 panic!("custom provider must own recovery for its opaque key")
2032 }
2033 }
2034
2035 impl crate::provider::CryptoProvider for PermissiveUnwrapProvider {
2036 #[cfg(feature = "legacy-algorithms")]
2037 fn recover_pkcs1v15(
2038 &self,
2039 key: &dyn crate::provider::KeyRecoveryKey,
2040 ciphertext: &[u8],
2041 key_len: usize,
2042 ) -> Result<crate::provider::RecoveredContentKey, crate::provider::ProviderError> {
2043 key.recover_pkcs1v15(self, ciphertext, key_len)
2044 }
2045 fn name(&self) -> &'static str {
2046 "permissive-unwrap-test"
2047 }
2048
2049 fn supports(&self, capability: crate::provider::ProviderCapability<'_>) -> bool {
2050 crate::provider::CryptoProvider::supports(
2051 &crate::provider::RustCryptoProvider,
2052 capability,
2053 )
2054 }
2055
2056 fn fill_random(&self, output: &mut [u8]) -> Result<(), crate::provider::ProviderError> {
2057 crate::provider::CryptoProvider::fill_random(
2058 &crate::provider::RustCryptoProvider,
2059 output,
2060 )
2061 }
2062
2063 fn derive_key(
2064 &self,
2065 parameters: &crate::provider::KdfParameters<'_>,
2066 secret: &[u8],
2067 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2068 crate::provider::RustCryptoProvider.derive_key(parameters, secret)
2069 }
2070
2071 #[cfg(feature = "xmldsig")]
2072 fn digest(
2073 &self,
2074 algorithm: crate::xmldsig::DigestAlgorithm,
2075 data: &[u8],
2076 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2077 crate::provider::CryptoProvider::digest(
2078 &crate::provider::RustCryptoProvider,
2079 algorithm,
2080 data,
2081 )
2082 }
2083
2084 #[cfg(feature = "xmldsig")]
2085 fn sign(
2086 &self,
2087 key: &dyn crate::xmldsig::SigningKey,
2088 algorithm: crate::xmldsig::SignatureAlgorithm,
2089 data: &[u8],
2090 ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
2091 crate::provider::CryptoProvider::sign(
2092 &crate::provider::RustCryptoProvider,
2093 key,
2094 algorithm,
2095 data,
2096 )
2097 }
2098
2099 #[cfg(feature = "xmldsig")]
2100 fn verify(
2101 &self,
2102 key: &dyn crate::xmldsig::VerifyingKey,
2103 algorithm: crate::xmldsig::SignatureAlgorithm,
2104 data: &[u8],
2105 signature: &[u8],
2106 ) -> Result<bool, crate::xmldsig::DsigError> {
2107 crate::provider::CryptoProvider::verify(
2108 &crate::provider::RustCryptoProvider,
2109 key,
2110 algorithm,
2111 data,
2112 signature,
2113 )
2114 }
2115
2116 fn encrypt_data(
2117 &self,
2118 algorithm: DataEncryptionAlgorithm,
2119 key: &[u8],
2120 plaintext: &[u8],
2121 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2122 crate::provider::CryptoProvider::encrypt_data(
2123 &crate::provider::RustCryptoProvider,
2124 algorithm,
2125 key,
2126 plaintext,
2127 )
2128 }
2129
2130 fn decrypt_data(
2131 &self,
2132 _algorithm: DataEncryptionAlgorithm,
2133 _key: &[u8],
2134 _ciphertext: &[u8],
2135 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2136 let index = self.decrypt_calls.fetch_add(1, Ordering::Relaxed);
2137 Ok(self
2138 .candidate_plaintexts
2139 .get(index)
2140 .unwrap_or(&self.plaintext)
2141 .clone())
2142 }
2143
2144 fn wrap_key(
2145 &self,
2146 algorithm: KeyWrapAlgorithm,
2147 kek: &[u8],
2148 key: &[u8],
2149 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2150 crate::provider::CryptoProvider::wrap_key(
2151 &crate::provider::RustCryptoProvider,
2152 algorithm,
2153 kek,
2154 key,
2155 )
2156 }
2157
2158 fn unwrap_key(
2159 &self,
2160 _algorithm: KeyWrapAlgorithm,
2161 _kek: &[u8],
2162 _wrapped: &[u8],
2163 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2164 self.unwrap_calls.fetch_add(1, Ordering::Relaxed);
2165 Ok(vec![0_u8; 16])
2166 }
2167
2168 fn transport_key(
2169 &self,
2170 key: &dyn crate::provider::KeyTransportKey,
2171 parameters: &RsaOaepParameters,
2172 plaintext: &[u8],
2173 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2174 crate::provider::CryptoProvider::transport_key(
2175 &crate::provider::RustCryptoProvider,
2176 key,
2177 parameters,
2178 plaintext,
2179 )
2180 }
2181
2182 fn recover_key(
2183 &self,
2184 _key: &dyn crate::provider::KeyRecoveryKey,
2185 _parameters: &RsaOaepParameters,
2186 _ciphertext: &[u8],
2187 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2188 self.recover_calls.fetch_add(1, Ordering::Relaxed);
2189 Ok(vec![0_u8; 16])
2190 }
2191 }
2192
2193 impl DecryptionKeyResolver for CountingResolver {
2194 fn resolve_key_candidates_with_policy(
2195 &self,
2196 provider: &dyn crate::provider::CryptoProvider,
2197 algorithm: DataEncryptionAlgorithm,
2198 encrypted_key: Option<&EncryptedKey>,
2199 policy: &crate::policy::DecryptionPolicy,
2200 budget: &mut KeyCandidateBudget,
2201 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
2202 policy.validate()?;
2204 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
2205 }
2206
2207 fn resolve_key(
2208 &self,
2209 _provider: &dyn crate::provider::CryptoProvider,
2210 _algorithm: DataEncryptionAlgorithm,
2211 encrypted_key: Option<&EncryptedKey>,
2212 ) -> Result<Vec<u8>, XmlEncError> {
2213 if encrypted_key.is_some() {
2214 self.candidate_calls.set(self.candidate_calls.get() + 1);
2215 Ok(self.key.clone())
2216 } else {
2217 Err(XmlEncError::KeyNotFound)
2218 }
2219 }
2220 }
2221
2222 impl DecryptionKeyResolver for AllCallsResolver {
2223 fn resolve_key(
2224 &self,
2225 _provider: &dyn crate::provider::CryptoProvider,
2226 _algorithm: DataEncryptionAlgorithm,
2227 _encrypted_key: Option<&EncryptedKey>,
2228 ) -> Result<Vec<u8>, XmlEncError> {
2229 self.calls.set(self.calls.get() + 1);
2230 Ok(self.key.clone())
2231 }
2232 }
2233
2234 impl DecryptionKeyResolver for RecipientKeyResolver {
2235 fn resolve_key_candidates_with_policy(
2236 &self,
2237 provider: &dyn crate::provider::CryptoProvider,
2238 algorithm: DataEncryptionAlgorithm,
2239 encrypted_key: Option<&EncryptedKey>,
2240 policy: &crate::policy::DecryptionPolicy,
2241 budget: &mut KeyCandidateBudget,
2242 ) -> Result<Vec<Vec<u8>>, XmlEncError> {
2243 policy.validate()?;
2245 self.resolve_key_candidates(provider, algorithm, encrypted_key, budget)
2246 }
2247
2248 fn resolve_key(
2249 &self,
2250 _provider: &dyn crate::provider::CryptoProvider,
2251 _algorithm: DataEncryptionAlgorithm,
2252 encrypted_key: Option<&EncryptedKey>,
2253 ) -> Result<Vec<u8>, XmlEncError> {
2254 if encrypted_key.and_then(|key| key.recipient.as_deref()) == Some(self.recipient) {
2255 Ok(self.key.clone())
2256 } else {
2257 Err(XmlEncError::KeyNotFound)
2258 }
2259 }
2260 }
2261
2262 #[test]
2263 fn decrypts_gcm_and_rejects_tampering() {
2264 let key = [7_u8; 16];
2266 let nonce = [9_u8; 12];
2267 let mut ciphertext = b"<Assertion>trusted</Assertion>".to_vec();
2268 Aes128Gcm::new_from_slice(&key)
2269 .expect("fixed key length")
2270 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
2271 .expect("test encryption must succeed");
2272 let mut wire = nonce.to_vec();
2273 wire.extend_from_slice(&ciphertext);
2274 let xml = format!(
2275 "<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>",
2276 STANDARD.encode(&wire)
2277 );
2278 let decrypted = decrypt(&xml, &SymmetricKeyDecryptor::new(key))
2279 .expect("valid AES-GCM XML must decrypt");
2280 assert_eq!(
2281 decrypted,
2282 DecryptedContent::Xml("<Assertion>trusted</Assertion>".into())
2283 );
2284 let last = wire.len() - 1;
2285 wire[last] ^= 1;
2286 let tampered = xml.replace(&STANDARD.encode(ciphertext), &STANDARD.encode(&wire[12..]));
2287 assert!(matches!(
2288 decrypt(&tampered, &SymmetricKeyDecryptor::new(key)),
2289 Err(XmlEncError::AeadAuthenticationFailed)
2290 ));
2291 }
2292
2293 #[test]
2294 fn candidate_keys_retry_only_authenticated_decryption() {
2295 let key = [7_u8; 16];
2298 let nonce = [9_u8; 12];
2299 let mut ciphertext = b"candidate plaintext".to_vec();
2300 Aes128Gcm::new_from_slice(&key)
2301 .expect("fixed key length")
2302 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
2303 .expect("test encryption must succeed");
2304 let mut wire = nonce.to_vec();
2305 wire.extend_from_slice(&ciphertext);
2306 let encrypted = EncryptedData {
2307 id: None,
2308 encrypted_type: None,
2309 encryption_method: EncryptionMethod {
2310 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
2311 key_size_bits: None,
2312 oaep_digest: None,
2313 mgf_algorithm: None,
2314 oaep_params: None,
2315 },
2316 key_name: None,
2317 encrypted_keys: Vec::new(),
2318 cipher_data: CipherData {
2319 value: STANDARD.encode(wire),
2320 },
2321 };
2322 let resolver = CandidateResolver {
2323 keys: vec![vec![1_u8; 16], key.to_vec()],
2324 };
2325
2326 let decrypted = DecryptContext::new(&resolver)
2327 .decrypt_data(&encrypted)
2328 .expect("a later authenticated candidate must decrypt");
2329
2330 assert_eq!(
2331 decrypted,
2332 DecryptedContent::Bytes(b"candidate plaintext".to_vec())
2333 );
2334 }
2335
2336 #[test]
2337 fn standalone_decryption_stops_after_first_successful_candidate() {
2338 let provider = PermissiveUnwrapProvider {
2341 plaintext: b"accepted".to_vec(),
2342 ..PermissiveUnwrapProvider::default()
2343 };
2344 let resolver = CandidateResolver {
2345 keys: vec![vec![1_u8; 16], vec![2_u8; 16], vec![3_u8; 16]],
2346 };
2347 let encrypted = EncryptedData {
2348 id: None,
2349 encrypted_type: None,
2350 encryption_method: EncryptionMethod {
2351 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
2352 key_size_bits: None,
2353 oaep_digest: None,
2354 mgf_algorithm: None,
2355 oaep_params: None,
2356 },
2357 key_name: None,
2358 encrypted_keys: Vec::new(),
2359 cipher_data: CipherData {
2360 value: STANDARD.encode([0_u8; 36]),
2361 },
2362 };
2363
2364 let result = DecryptContext::new(&resolver)
2365 .provider(&provider)
2366 .decrypt_data(&encrypted)
2367 .expect("the first successful candidate must be returned");
2368
2369 assert_eq!(result, DecryptedContent::Bytes(b"accepted".to_vec()));
2370 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 1);
2371 }
2372
2373 #[test]
2374 fn document_decryption_discards_rejected_plaintext_before_next_candidate() {
2375 let provider = PermissiveUnwrapProvider {
2378 candidate_plaintexts: vec![b"<bad".to_vec(), b"<x/>".to_vec(), b"<u/>".to_vec()],
2379 ..PermissiveUnwrapProvider::default()
2380 };
2381 let resolver = CandidateResolver {
2382 keys: vec![vec![1_u8; 16], vec![2_u8; 16], vec![3_u8; 16]],
2383 };
2384 let encrypted = format!(
2385 "<xenc:EncryptedData xmlns:xenc=\"{XMLENC_NS}\" Type=\"{XMLENC_NS}Element\"><xenc:EncryptionMethod Algorithm=\"{}\"/><xenc:CipherData><xenc:CipherValue>{}</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData>",
2386 DataEncryptionAlgorithm::Aes128Gcm.uri(),
2387 STANDARD.encode([0_u8; 32]),
2388 );
2389
2390 let result = DecryptContext::new(&resolver)
2391 .provider(&provider)
2392 .decrypt_document(&encrypted, None)
2393 .expect("a later candidate with valid replacement XML must succeed");
2394
2395 assert_eq!(result, "<x/>");
2396 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 2);
2397 }
2398
2399 #[test]
2400 fn cbc_rejects_multiple_unordered_key_candidates() {
2401 let encrypted = EncryptedData {
2404 id: None,
2405 encrypted_type: None,
2406 encryption_method: EncryptionMethod {
2407 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
2408 key_size_bits: None,
2409 oaep_digest: None,
2410 mgf_algorithm: None,
2411 oaep_params: None,
2412 },
2413 key_name: None,
2414 encrypted_keys: Vec::new(),
2415 cipher_data: CipherData {
2416 value: STANDARD.encode(
2417 crate::provider::default_provider()
2418 .encrypt_data(
2419 DataEncryptionAlgorithm::Aes128Cbc,
2420 &[7_u8; 16],
2421 b"opaque plaintext",
2422 )
2423 .expect("test encryption must succeed"),
2424 ),
2425 },
2426 };
2427 let resolver = CandidateResolver {
2428 keys: vec![vec![1_u8; 16], vec![7_u8; 16]],
2429 };
2430
2431 let error = DecryptContext::new(&resolver)
2432 .decrypt_data(&encrypted)
2433 .expect_err("unauthenticated CBC must not guess among candidate keys");
2434
2435 assert!(matches!(
2436 error,
2437 XmlEncError::AmbiguousKeyCandidates {
2438 algorithm: DataEncryptionAlgorithm::Aes128Cbc,
2439 actual: 2,
2440 }
2441 ));
2442 }
2443
2444 #[test]
2445 fn cbc_accepts_duplicate_copies_of_one_key_identity() {
2446 let key = vec![0x27_u8; 16];
2449 let encrypted = EncryptedData {
2450 id: None,
2451 encrypted_type: None,
2452 encryption_method: EncryptionMethod {
2453 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
2454 key_size_bits: None,
2455 oaep_digest: None,
2456 mgf_algorithm: None,
2457 oaep_params: None,
2458 },
2459 key_name: None,
2460 encrypted_keys: Vec::new(),
2461 cipher_data: CipherData {
2462 value: STANDARD.encode(
2463 crate::provider::default_provider()
2464 .encrypt_data(
2465 DataEncryptionAlgorithm::Aes128Cbc,
2466 &key,
2467 b"duplicate identity",
2468 )
2469 .expect("test encryption must succeed"),
2470 ),
2471 },
2472 };
2473 let resolver = CandidateResolver {
2474 keys: vec![key.clone(), key],
2475 };
2476
2477 assert_eq!(
2478 DecryptContext::new(&resolver)
2479 .decrypt_data(&encrypted)
2480 .expect("one distinct CBC key identity must decrypt"),
2481 DecryptedContent::Bytes(b"duplicate identity".to_vec())
2482 );
2483 }
2484
2485 #[test]
2486 fn candidate_keys_are_bounded_before_cryptographic_processing() {
2487 let encrypted = EncryptedData {
2490 id: None,
2491 encrypted_type: None,
2492 encryption_method: EncryptionMethod {
2493 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
2494 key_size_bits: None,
2495 oaep_digest: None,
2496 mgf_algorithm: None,
2497 oaep_params: None,
2498 },
2499 key_name: None,
2500 encrypted_keys: Vec::new(),
2501 cipher_data: CipherData {
2502 value: STANDARD.encode(vec![0_u8; 28]),
2503 },
2504 };
2505 let actual = crate::hard_limits::KEY_CANDIDATE_CEILING + 1;
2506 let resolver = CandidateResolver {
2507 keys: vec![vec![0_u8; 16]; actual],
2508 };
2509
2510 let error = DecryptContext::new(&resolver)
2511 .decrypt_data(&encrypted)
2512 .expect_err("oversized candidate sets must fail before decryption");
2513
2514 assert!(matches!(
2515 error,
2516 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2517 resource: crate::policy::resource_name::KEY_CANDIDATES,
2518 maximum: crate::hard_limits::KEY_CANDIDATE_CEILING,
2519 actual: observed,
2520 }) if observed == actual
2521 ));
2522 }
2523
2524 #[test]
2525 fn operation_policy_controls_candidate_budget() {
2526 let encrypted = EncryptedData {
2529 id: None,
2530 encrypted_type: None,
2531 encryption_method: EncryptionMethod {
2532 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
2533 key_size_bits: None,
2534 oaep_digest: None,
2535 mgf_algorithm: None,
2536 oaep_params: None,
2537 },
2538 key_name: None,
2539 encrypted_keys: Vec::new(),
2540 cipher_data: CipherData {
2541 value: STANDARD.encode(vec![0_u8; 28]),
2542 },
2543 };
2544 let resolver = CandidateResolver {
2545 keys: vec![vec![0_u8; 16]; 3],
2546 };
2547 let mut policy = crate::policy::DecryptionPolicy::default();
2548 policy.resources.max_key_candidates = 2;
2549
2550 let error = DecryptContext::new(&resolver)
2551 .policy(policy)
2552 .decrypt_data(&encrypted)
2553 .expect_err("candidate accounting must use the operation policy ceiling");
2554
2555 assert!(matches!(
2556 error,
2557 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2558 resource: crate::policy::resource_name::KEY_CANDIDATES,
2559 maximum: 2,
2560 actual: 3,
2561 })
2562 ));
2563 }
2564
2565 #[test]
2566 fn candidate_work_ceiling_is_shared_across_recipients() {
2567 let key = vec![0x39_u8; 16];
2570 let resolver = AggregateRecipientResolver {
2571 attempts: Cell::new(0),
2572 key: key.clone(),
2573 };
2574 let encrypted = encrypted_data_with_recipients(
2575 &key,
2576 vec![
2577 associated_encrypted_key("first", None, None),
2578 associated_encrypted_key("second", None, None),
2579 ],
2580 None,
2581 );
2582
2583 DecryptContext::new(&resolver)
2584 .decrypt_data(&encrypted)
2585 .expect_err("a second recipient must not receive a fresh candidate allowance");
2586 assert_eq!(
2587 resolver.attempts.get(),
2588 crate::hard_limits::KEY_CANDIDATE_CEILING
2589 );
2590 }
2591
2592 #[test]
2593 fn candidate_budget_exhaustion_is_fatal_after_a_key_was_found() {
2594 let key = vec![0x49_u8; 16];
2597 let resolver = MislabelledExhaustionResolver {
2598 direct: key.clone(),
2599 };
2600 let encrypted = encrypted_data_with_recipients(
2601 &key,
2602 vec![associated_encrypted_key("recipient", None, None)],
2603 None,
2604 );
2605
2606 let error = DecryptContext::new(&resolver)
2607 .decrypt_data(&encrypted)
2608 .expect_err("candidate exhaustion must override an earlier usable key");
2609
2610 assert!(matches!(
2611 error,
2612 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2613 resource: "key candidates",
2614 maximum: crate::hard_limits::KEY_CANDIDATE_CEILING,
2615 actual,
2616 }) if actual == crate::hard_limits::KEY_CANDIDATE_CEILING + 1
2617 ));
2618 }
2619
2620 #[test]
2621 fn authenticated_decryption_continues_after_wrong_unwrapped_recipient_key() {
2622 let correct = vec![0x53_u8; 16];
2625 let encrypted = encrypted_data_with_recipients(
2626 &correct,
2627 vec![
2628 associated_encrypted_key("first", None, None),
2629 associated_encrypted_key("second", None, None),
2630 ],
2631 None,
2632 );
2633 let resolver = OrderedRecipientResolver {
2634 wrong: vec![0x11_u8; 16],
2635 correct,
2636 };
2637
2638 let plaintext = DecryptContext::new(&resolver)
2639 .decrypt_data(&encrypted)
2640 .expect("the second recipient key must authenticate");
2641 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
2642 }
2643
2644 #[test]
2645 fn authenticated_decryption_continues_from_direct_key_to_recipient() {
2646 let correct = vec![0x63_u8; 16];
2649 let encrypted = encrypted_data_with_recipients(
2650 &correct,
2651 vec![associated_encrypted_key("recipient", None, None)],
2652 None,
2653 );
2654 let resolver = DirectAndRecipientResolver {
2655 direct: vec![0x19_u8; 16],
2656 recipient: correct,
2657 };
2658
2659 let plaintext = DecryptContext::new(&resolver)
2660 .decrypt_data(&encrypted)
2661 .expect("the embedded recipient must remain available after a direct candidate");
2662
2663 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
2664 }
2665
2666 #[test]
2667 fn authenticated_decryption_continues_after_direct_lookup_error() {
2668 let correct = vec![0x64_u8; 16];
2671 let encrypted = encrypted_data_with_recipients(
2672 &correct,
2673 vec![associated_encrypted_key("recipient", None, None)],
2674 None,
2675 );
2676 let resolver = FailingDirectResolver { recipient: correct };
2677
2678 let plaintext = DecryptContext::new(&resolver)
2679 .decrypt_data(&encrypted)
2680 .expect("recipient lookup must follow a candidate-local direct error");
2681
2682 assert_eq!(plaintext, DecryptedContent::Bytes(b"payload".to_vec()));
2683 }
2684
2685 #[test]
2686 fn resolver_policy_rejection_stops_before_later_recipient() {
2687 let correct = vec![0x65_u8; 16];
2690 let encrypted = encrypted_data_with_recipients(
2691 &correct,
2692 vec![associated_encrypted_key("recipient", None, None)],
2693 None,
2694 );
2695 let resolver = PolicyRejectingDirectResolver { recipient: correct };
2696
2697 let error = DecryptContext::new(&resolver)
2698 .decrypt_data(&encrypted)
2699 .expect_err("operation policy rejection must be fatal");
2700
2701 assert!(matches!(
2702 error,
2703 XmlEncError::Policy(crate::policy::PolicyViolation::KeyTrust {
2704 reason: "test resolver rejected the operation",
2705 })
2706 ));
2707 }
2708
2709 #[test]
2710 fn cbc_rejects_distinct_direct_and_recipient_candidates() {
2711 let recipient = vec![0x73_u8; 16];
2714 let mut encrypted = encrypted_data_with_recipients(
2715 &recipient,
2716 vec![associated_encrypted_key("recipient", None, None)],
2717 None,
2718 );
2719 encrypted.encryption_method.algorithm = DataEncryptionAlgorithm::Aes128Cbc.uri().into();
2720 encrypted.cipher_data.value = STANDARD.encode(
2721 crate::provider::default_provider()
2722 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &recipient, b"payload")
2723 .expect("test encryption must succeed"),
2724 );
2725 let resolver = DirectAndRecipientResolver {
2726 direct: vec![0x29_u8; 16],
2727 recipient,
2728 };
2729
2730 let error = DecryptContext::new(&resolver)
2731 .decrypt_data(&encrypted)
2732 .expect_err("CBC must not guess between direct and recipient keys");
2733
2734 assert!(matches!(
2735 error,
2736 XmlEncError::AmbiguousKeyCandidates {
2737 algorithm: DataEncryptionAlgorithm::Aes128Cbc,
2738 actual: 2,
2739 }
2740 ));
2741 }
2742
2743 #[test]
2744 fn cbc_ambiguity_ignores_algorithm_incompatible_key_widths() {
2745 let key = vec![0x47_u8; 16];
2748 let ciphertext = crate::provider::default_provider()
2749 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &key, b"payload")
2750 .expect("test encryption must succeed");
2751 let encrypted = EncryptedData {
2752 id: None,
2753 encrypted_type: None,
2754 encryption_method: EncryptionMethod {
2755 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
2756 key_size_bits: None,
2757 oaep_digest: None,
2758 mgf_algorithm: None,
2759 oaep_params: None,
2760 },
2761 key_name: None,
2762 encrypted_keys: Vec::new(),
2763 cipher_data: CipherData {
2764 value: STANDARD.encode(ciphertext),
2765 },
2766 };
2767 let resolver = CandidateResolver {
2768 keys: vec![vec![0_u8; 32], key],
2769 };
2770
2771 assert_eq!(
2772 DecryptContext::new(&resolver)
2773 .decrypt_data(&encrypted)
2774 .expect("the sole width-compatible CBC key must be selected"),
2775 DecryptedContent::Bytes(b"payload".to_vec())
2776 );
2777 }
2778
2779 #[test]
2780 fn data_reference_selects_the_associated_encrypted_key() {
2781 let key = vec![0x51_u8; 16];
2784 let resolver = AssociationRecordingResolver {
2785 visited: RefCell::new(Vec::new()),
2786 key: key.clone(),
2787 };
2788 let encrypted = encrypted_data_with_recipients(
2789 &key,
2790 vec![
2791 associated_encrypted_key("unrelated", Some("#other"), None),
2792 associated_encrypted_key("matching", Some("#target"), None),
2793 ],
2794 None,
2795 );
2796
2797 DecryptContext::new(&resolver)
2798 .decrypt_data(&encrypted)
2799 .expect("the associated recipient must decrypt");
2800 assert_eq!(resolver.visited.into_inner(), ["matching"]);
2801 }
2802
2803 #[test]
2804 fn carried_key_name_selects_the_named_content_key() {
2805 let key = vec![0x52_u8; 16];
2808 let resolver = AssociationRecordingResolver {
2809 visited: RefCell::new(Vec::new()),
2810 key: key.clone(),
2811 };
2812 let encrypted = encrypted_data_with_recipients(
2813 &key,
2814 vec![
2815 associated_encrypted_key("unrelated", None, Some("other")),
2816 associated_encrypted_key("matching", None, Some("wanted")),
2817 ],
2818 Some("wanted"),
2819 );
2820
2821 DecryptContext::new(&resolver)
2822 .decrypt_data(&encrypted)
2823 .expect("the matching carried key name must decrypt");
2824 assert_eq!(resolver.visited.into_inner(), ["matching"]);
2825 }
2826
2827 #[test]
2828 fn contradictory_encrypted_key_associations_fail_closed() {
2829 let key = vec![0x53_u8; 16];
2832 let resolver = AssociationRecordingResolver {
2833 visited: RefCell::new(Vec::new()),
2834 key: key.clone(),
2835 };
2836 let encrypted = encrypted_data_with_recipients(
2837 &key,
2838 vec![associated_encrypted_key("unrelated", Some("#other"), None)],
2839 None,
2840 );
2841
2842 assert!(matches!(
2843 DecryptContext::new(&resolver).decrypt_data(&encrypted),
2844 Err(XmlEncError::KeyNotFound)
2845 ));
2846 assert!(resolver.visited.into_inner().is_empty());
2847 }
2848
2849 #[test]
2850 fn direct_symmetric_key_ignores_embedded_key_hints() {
2851 let key = [0x28_u8; 16];
2854 let unrelated = EncryptedKey {
2855 id: None,
2856 recipient: Some("other-recipient".into()),
2857 key_name: None,
2858 encryption_method: super::super::EncryptionMethod {
2859 algorithm: "urn:unrelated:key-transport".into(),
2860 key_size_bits: None,
2861 oaep_digest: None,
2862 mgf_algorithm: None,
2863 oaep_params: None,
2864 },
2865 cipher_data: super::super::CipherData {
2866 value: STANDARD.encode([0_u8; 24]),
2867 },
2868 reference_list: None,
2869 carried_key_name: None,
2870 };
2871
2872 assert_eq!(
2873 SymmetricKeyDecryptor::new(key)
2874 .resolve_key(
2875 crate::provider::default_provider(),
2876 DataEncryptionAlgorithm::Aes128Gcm,
2877 Some(&unrelated)
2878 )
2879 .expect("direct key must ignore unrelated embedded hints"),
2880 key
2881 );
2882 }
2883
2884 #[test]
2885 fn decrypts_with_the_matching_recipient_key() {
2886 let key = [0x29_u8; 16];
2889 let plaintext = "recipient-specific plaintext";
2890 let encrypted = encrypted_gcm_element("", plaintext, None, true, &key);
2891 let recipient_key = |recipient: &str, method: &str| {
2892 format!(
2893 "<xenc:EncryptedKey Recipient=\"{recipient}\"><xenc:EncryptionMethod Algorithm=\"{method}\">{}</xenc:EncryptionMethod><xenc:CipherData><xenc:CipherValue>YQ==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey>",
2894 if recipient == "alice" {
2895 "<ds:DigestMethod Algorithm=\"urn:unsupported:digest\"/>"
2896 } else {
2897 ""
2898 }
2899 )
2900 };
2901 let key_info = format!(
2902 "<ds:KeyInfo xmlns:ds=\"{}\">{}{}</ds:KeyInfo>",
2903 crate::xmlenc::types::XMLDSIG_NS,
2904 recipient_key("alice", KeyTransportAlgorithm::RsaOaep11.uri()),
2905 recipient_key("bob", KeyWrapAlgorithm::AesKw128.uri())
2906 );
2907 let xml = encrypted.replacen(
2908 "<xenc:CipherData>",
2909 &format!("{key_info}<xenc:CipherData>"),
2910 1,
2911 );
2912 let resolver = RecipientKeyResolver {
2913 recipient: "bob",
2914 key: key.to_vec(),
2915 };
2916
2917 assert_eq!(
2918 decrypt(&xml, &resolver).expect("second recipient key must be tried"),
2919 DecryptedContent::Bytes(plaintext.as_bytes().to_vec())
2920 );
2921 }
2922
2923 #[test]
2924 fn decryption_policy_bounds_recipients_before_key_resolution() {
2925 let key = [0x29_u8; 16];
2928 let encrypted = encrypted_gcm_element("", "bounded recipients", None, true, &key);
2929 let recipient_key = |recipient: &str| {
2930 format!(
2931 "<xenc:EncryptedKey Recipient=\"{recipient}\"><xenc:EncryptionMethod Algorithm=\"urn:test:key\"/><xenc:CipherData><xenc:CipherValue>YQ==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey>"
2932 )
2933 };
2934 let key_info = format!(
2935 "<ds:KeyInfo xmlns:ds=\"{}\">{}{}</ds:KeyInfo>",
2936 crate::xmlenc::types::XMLDSIG_NS,
2937 recipient_key("alice"),
2938 recipient_key("bob")
2939 );
2940 let xml = encrypted.replacen(
2941 "<xenc:CipherData>",
2942 &format!("{key_info}<xenc:CipherData>"),
2943 1,
2944 );
2945 let parsed = parse_encrypted_data(&xml).expect("default parser accepts two recipients");
2946 let policy = crate::policy::DecryptionPolicy {
2947 resources: crate::policy::ResourcePolicy {
2948 max_encryption_recipients: 1,
2949 ..crate::policy::ResourcePolicy::default()
2950 },
2951 ..crate::policy::DecryptionPolicy::default()
2952 };
2953 let resolver = SymmetricKeyDecryptor::new(key);
2954 let context = DecryptContext::new(&resolver).policy(policy);
2955
2956 for error in [
2957 context
2958 .decrypt(&xml)
2959 .expect_err("XML recipient collection must be bounded"),
2960 context
2961 .decrypt_data(&parsed)
2962 .expect_err("typed recipient collection must be bounded"),
2963 ] {
2964 assert!(matches!(
2965 error,
2966 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2967 resource: crate::policy::resource_name::ENCRYPTION_RECIPIENTS,
2968 maximum: 1,
2969 actual: 2,
2970 })
2971 ));
2972 }
2973 }
2974
2975 #[test]
2976 fn decrypts_session_key_wrapped_with_aes_kw() {
2977 let kek = [3_u8; 16];
2979 let session_key = [4_u8; 16];
2980 let mut wrapped = [0_u8; 24];
2981 KwAes128::new_from_slice(&kek)
2982 .expect("fixed KEK length")
2983 .wrap_key(&session_key, &mut wrapped)
2984 .expect("RFC 3394 test wrapping must succeed");
2985 let encrypted_key = EncryptedKey {
2986 id: None,
2987 recipient: None,
2988 key_name: None,
2989 encryption_method: super::super::EncryptionMethod {
2990 algorithm: "http://www.w3.org/2001/04/xmlenc#kw-aes128".into(),
2991 key_size_bits: None,
2992 oaep_digest: None,
2993 mgf_algorithm: None,
2994 oaep_params: None,
2995 },
2996 cipher_data: super::super::CipherData {
2997 value: STANDARD.encode(wrapped),
2998 },
2999 reference_list: None,
3000 carried_key_name: None,
3001 };
3002 let resolved = KekDecryptor::new(kek)
3003 .resolve_key(
3004 crate::provider::default_provider(),
3005 DataEncryptionAlgorithm::Aes128Gcm,
3006 Some(&encrypted_key),
3007 )
3008 .expect("wrapped session key must resolve");
3009 assert_eq!(resolved, session_key);
3010 }
3011
3012 #[test]
3013 fn rejects_invalid_kek_before_custom_provider_dispatch() {
3014 let encrypted_key = EncryptedKey {
3017 id: None,
3018 recipient: None,
3019 key_name: None,
3020 encryption_method: super::super::EncryptionMethod {
3021 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
3022 key_size_bits: None,
3023 oaep_digest: None,
3024 mgf_algorithm: None,
3025 oaep_params: None,
3026 },
3027 cipher_data: super::super::CipherData {
3028 value: STANDARD.encode([0_u8; 24]),
3029 },
3030 reference_list: None,
3031 carried_key_name: None,
3032 };
3033 let provider = PermissiveUnwrapProvider::default();
3034
3035 assert!(matches!(
3036 KekDecryptor::new([0_u8; 32]).resolve_key(
3037 &provider,
3038 DataEncryptionAlgorithm::Aes128Gcm,
3039 Some(&encrypted_key),
3040 ),
3041 Err(XmlEncError::InvalidKekSize {
3042 algorithm: KeyWrapAlgorithm::AesKw128,
3043 expected: 16,
3044 actual: 32,
3045 })
3046 ));
3047 assert_eq!(provider.unwrap_calls.load(Ordering::Relaxed), 0);
3048 }
3049
3050 #[test]
3051 fn rejects_content_ciphertext_framing_before_resolution_or_provider_dispatch() {
3052 for (algorithm, ciphertext_len) in [
3055 (DataEncryptionAlgorithm::Aes128Gcm, 27),
3056 (DataEncryptionAlgorithm::Aes128Cbc, 33),
3057 ] {
3058 let resolver = AllCallsResolver {
3059 calls: Cell::new(0),
3060 key: vec![0_u8; algorithm.key_len()],
3061 };
3062 let provider = PermissiveUnwrapProvider::default();
3063 let encrypted = EncryptedData {
3064 id: None,
3065 encrypted_type: None,
3066 key_name: None,
3067 encryption_method: super::super::EncryptionMethod {
3068 algorithm: algorithm.uri().into(),
3069 key_size_bits: None,
3070 oaep_digest: None,
3071 mgf_algorithm: None,
3072 oaep_params: None,
3073 },
3074 encrypted_keys: Vec::new(),
3075 cipher_data: super::super::CipherData {
3076 value: STANDARD.encode(vec![0_u8; ciphertext_len]),
3077 },
3078 };
3079
3080 assert!(
3081 DecryptContext::new(&resolver)
3082 .provider(&provider)
3083 .decrypt_data(&encrypted)
3084 .is_err()
3085 );
3086 assert_eq!(resolver.calls.get(), 0);
3087 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 0);
3088 }
3089 }
3090
3091 #[test]
3092 fn rejects_custom_provider_plaintext_outside_algorithm_bounds() {
3093 for (algorithm, ciphertext_len, plaintext_len) in [
3096 (DataEncryptionAlgorithm::Aes128Gcm, 32, 5),
3097 (DataEncryptionAlgorithm::Aes128Cbc, 32, 16),
3098 ] {
3099 let resolver = AllCallsResolver {
3100 calls: Cell::new(0),
3101 key: vec![0_u8; algorithm.key_len()],
3102 };
3103 let provider = PermissiveUnwrapProvider {
3104 plaintext: vec![0_u8; plaintext_len],
3105 ..PermissiveUnwrapProvider::default()
3106 };
3107 let encrypted = EncryptedData {
3108 id: None,
3109 encrypted_type: None,
3110 key_name: None,
3111 encryption_method: super::super::EncryptionMethod {
3112 algorithm: algorithm.uri().into(),
3113 key_size_bits: None,
3114 oaep_digest: None,
3115 mgf_algorithm: None,
3116 oaep_params: None,
3117 },
3118 encrypted_keys: Vec::new(),
3119 cipher_data: super::super::CipherData {
3120 value: STANDARD.encode(vec![0_u8; ciphertext_len]),
3121 },
3122 };
3123
3124 let error = DecryptContext::new(&resolver)
3125 .provider(&provider)
3126 .decrypt_data(&encrypted)
3127 .expect_err("impossible provider output length must fail");
3128 match algorithm {
3129 DataEncryptionAlgorithm::Aes128Gcm => assert!(matches!(
3130 error,
3131 XmlEncError::Provider(crate::provider::ProviderError::InvalidOutputSize {
3132 operation: crate::provider::ProviderOperation::Decrypt,
3133 expected: 4,
3134 actual: 5,
3135 })
3136 )),
3137 DataEncryptionAlgorithm::Aes128Cbc => assert!(matches!(
3138 error,
3139 XmlEncError::Provider(crate::provider::ProviderError::InvalidOutputSizeRange {
3140 operation: crate::provider::ProviderOperation::Decrypt,
3141 minimum: 0,
3142 maximum: 15,
3143 actual: 16,
3144 })
3145 )),
3146 _ => unreachable!("the regression table covers one GCM and one CBC algorithm"),
3147 }
3148 assert_eq!(provider.decrypt_calls.load(Ordering::Relaxed), 1);
3149 }
3150 }
3151
3152 #[test]
3153 fn rejects_malformed_aes_kw_before_custom_provider_dispatch() {
3154 let provider = PermissiveUnwrapProvider::default();
3157 for actual in [0, 23, 25] {
3158 let encrypted_key = EncryptedKey {
3159 id: None,
3160 recipient: None,
3161 key_name: None,
3162 encryption_method: super::super::EncryptionMethod {
3163 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
3164 key_size_bits: None,
3165 oaep_digest: None,
3166 mgf_algorithm: None,
3167 oaep_params: None,
3168 },
3169 cipher_data: super::super::CipherData {
3170 value: STANDARD.encode(vec![0_u8; actual]),
3171 },
3172 reference_list: None,
3173 carried_key_name: None,
3174 };
3175 assert!(matches!(
3176 KekDecryptor::new([0_u8; 16]).resolve_key(
3177 &provider,
3178 DataEncryptionAlgorithm::Aes128Gcm,
3179 Some(&encrypted_key),
3180 ),
3181 Err(XmlEncError::InvalidWrappedKeyLength {
3182 expected: 24,
3183 actual: output_len,
3184 }) if output_len == actual
3185 ));
3186 }
3187 assert_eq!(provider.unwrap_calls.load(Ordering::Relaxed), 0);
3188 }
3189
3190 #[test]
3191 fn rejects_malformed_rsa_oaep_before_custom_provider_dispatch() {
3192 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
3195 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
3196 ))
3197 .expect("RSA donor private key must parse");
3198 let provider = PermissiveUnwrapProvider::default();
3199 for actual in [0, 255, 257] {
3200 assert!(matches!(
3201 recover_rsa_oaep(
3202 &provider,
3203 &private_key,
3204 &RsaOaepParameters::default(),
3205 &vec![0_u8; actual],
3206 ),
3207 Err(XmlEncError::InvalidWrappedKeyLength {
3208 expected: 256,
3209 actual: output_len,
3210 }) if output_len == actual
3211 ));
3212 }
3213 assert_eq!(provider.recover_calls.load(Ordering::Relaxed), 0);
3214 }
3215
3216 #[cfg(feature = "legacy-algorithms")]
3217 #[test]
3218 fn invalid_rsa_recovery_cannot_release_successful_cbc_plaintext() {
3219 let key = RsaPrivateKey::from_pkcs8_pem(include_str!(
3222 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
3223 ))
3224 .expect("valid RSA fixture");
3225 let resolver = PrivateKeyDecryptor::new(key);
3226 for algorithm in [
3227 DataEncryptionAlgorithm::Aes128Cbc,
3228 DataEncryptionAlgorithm::Aes192Cbc,
3229 DataEncryptionAlgorithm::Aes256Cbc,
3230 DataEncryptionAlgorithm::TripleDesCbc,
3231 DataEncryptionAlgorithm::Aes128Gcm,
3232 ] {
3233 let provider = PermissiveUnwrapProvider {
3234 plaintext: b"accepted CBC plaintext".to_vec(),
3235 ..PermissiveUnwrapProvider::default()
3236 };
3237 let mut encrypted = encrypted_data_with_recipients(
3238 &[0; 16],
3239 vec![associated_encrypted_key("recipient", None, None)],
3240 None,
3241 );
3242 encrypted.encryption_method.algorithm = algorithm.uri().into();
3243 encrypted.cipher_data.value = STANDARD.encode(vec![
3244 0;
3245 algorithm
3246 .ciphertext_len_for_plaintext(provider.plaintext.len())
3247 .expect("bounded test frame")
3248 ]);
3249 encrypted.encrypted_keys[0].encryption_method.algorithm =
3250 KeyTransportAlgorithm::RsaPkcs1v15.uri().into();
3251 encrypted.encrypted_keys[0].cipher_data.value = STANDARD.encode([0; 256]);
3252 let result = DecryptContext::new(&resolver)
3253 .provider(&provider)
3254 .policy(crate::policy::DecryptionPolicy {
3255 data_algorithms: Some([algorithm].into()),
3256 key_transport_algorithms: Some([KeyTransportAlgorithm::RsaPkcs1v15].into()),
3257 ..crate::policy::DecryptionPolicy::default()
3258 })
3259 .decrypt_data(&encrypted);
3260 assert!(
3261 result.is_err(),
3262 "invalid RSA padding must never release fallback plaintext"
3263 );
3264 assert_eq!(
3265 provider.decrypt_calls.load(Ordering::Relaxed),
3266 1,
3267 "content decryption must run before rejecting the recovery"
3268 );
3269 }
3270 }
3271
3272 #[test]
3273 fn candidate_filter_reuses_storage_and_preserves_precedence() {
3274 let keys = [
3277 vec![0; 8],
3278 vec![2; 16],
3279 vec![3; 16],
3280 vec![2; 16],
3281 vec![4; 32],
3282 vec![5; 16],
3283 ]
3284 .into_iter()
3285 .map(crate::provider::RecoveredContentKey::confirmed)
3286 .collect::<Vec<_>>();
3287 let allocation = keys.as_ptr();
3288 let keys = compatible_decryption_key_candidates(DataEncryptionAlgorithm::Aes128Gcm, keys)
3289 .expect("compatible candidates survive");
3290 assert_eq!(keys.as_ptr(), allocation);
3291 assert_eq!(keys.len(), 3);
3292 for (key, expected) in keys.iter().zip([2, 3, 5]) {
3293 assert_eq!(key.bytes(), &[expected; 16]);
3294 }
3295 }
3296
3297 #[test]
3298 fn custom_provider_recovers_with_an_opaque_private_key() {
3299 let encrypted_key = EncryptedKey {
3302 id: None,
3303 recipient: None,
3304 key_name: None,
3305 encryption_method: super::super::EncryptionMethod {
3306 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
3307 key_size_bits: None,
3308 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
3309 mgf_algorithm: Some(OaepDigestAlgorithm::Sha256.mgf_uri().into()),
3310 oaep_params: None,
3311 },
3312 cipher_data: super::super::CipherData {
3313 value: STANDARD.encode(vec![0x5a; 256]),
3314 },
3315 reference_list: None,
3316 carried_key_name: None,
3317 };
3318 let provider = PermissiveUnwrapProvider::default();
3319 let decryptor = PrivateKeyDecryptor::provider_key(Arc::new(OpaqueRecoveryKey));
3320
3321 let key = decryptor
3322 .resolve_key(
3323 &provider,
3324 DataEncryptionAlgorithm::Aes128Gcm,
3325 Some(&encrypted_key),
3326 )
3327 .expect("custom provider must recover through its opaque private key");
3328
3329 assert_eq!(key, vec![0_u8; 16]);
3330 assert_eq!(provider.recover_calls.load(Ordering::Relaxed), 1);
3331 }
3332
3333 #[test]
3334 fn rejects_truncated_gcm_and_invalid_wrapped_key() {
3335 assert!(matches!(
3337 crate::provider::default_provider().decrypt_data(
3338 DataEncryptionAlgorithm::Aes128Gcm,
3339 &[0_u8; 16],
3340 &[0_u8; 27],
3341 ),
3342 Err(crate::provider::ProviderError::InvalidInput(
3343 crate::provider::ProviderInputError::AesGcmFraming
3344 ))
3345 ));
3346 let truncated = EncryptedData {
3347 id: None,
3348 encrypted_type: None,
3349 key_name: None,
3350 encryption_method: super::super::EncryptionMethod {
3351 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3352 key_size_bits: None,
3353 oaep_digest: None,
3354 mgf_algorithm: None,
3355 oaep_params: None,
3356 },
3357 encrypted_keys: Vec::new(),
3358 cipher_data: super::super::CipherData {
3359 value: STANDARD.encode([0_u8; 27]),
3360 },
3361 };
3362 assert!(matches!(
3363 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16])).decrypt_data(&truncated),
3364 Err(XmlEncError::DataTooShort {
3365 algorithm: "AES-GCM",
3366 actual: 27,
3367 ..
3368 })
3369 ));
3370 let encrypted_key = EncryptedKey {
3371 id: None,
3372 recipient: None,
3373 key_name: None,
3374 encryption_method: super::super::EncryptionMethod {
3375 algorithm: "http://www.w3.org/2001/04/xmlenc#kw-aes128".into(),
3376 key_size_bits: None,
3377 oaep_digest: None,
3378 mgf_algorithm: None,
3379 oaep_params: None,
3380 },
3381 cipher_data: super::super::CipherData {
3382 value: STANDARD.encode([0_u8; 24]),
3383 },
3384 reference_list: None,
3385 carried_key_name: None,
3386 };
3387 assert!(matches!(
3388 KekDecryptor::new([0_u8; 16]).resolve_key(
3389 crate::provider::default_provider(),
3390 DataEncryptionAlgorithm::Aes128Gcm,
3391 Some(&encrypted_key)
3392 ),
3393 Err(XmlEncError::KeyWrapIntegrity)
3394 ));
3395 assert!(matches!(
3396 KekDecryptor::new([0_u8; 32]).resolve_key(
3397 crate::provider::default_provider(),
3398 DataEncryptionAlgorithm::Aes128Gcm,
3399 Some(&encrypted_key)
3400 ),
3401 Err(XmlEncError::InvalidKekSize {
3402 algorithm: KeyWrapAlgorithm::AesKw128,
3403 expected: 16,
3404 actual: 32
3405 })
3406 ));
3407 }
3408
3409 #[test]
3410 fn decrypts_oaep11_with_independent_digest_and_mgf() {
3411 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
3413 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
3414 ))
3415 .expect("RSA donor private key must parse");
3416 let public_key = RsaPublicKey::from(&private_key);
3417 let session_key = [6_u8; 16];
3418 let label = b"xmlenc-label".to_vec();
3419 let wrapped = public_key
3420 .encrypt(
3421 &mut ChaCha20Rng::from_seed([17_u8; 32]),
3422 Oaep::<Sha256, Sha384>::new_with_mgf_hash_and_label(label.clone()),
3423 &session_key,
3424 )
3425 .expect("OAEP test wrapping must succeed");
3426 let encrypted_key = EncryptedKey {
3427 id: Some("wrapped-key".into()),
3428 recipient: Some("recipient-a".into()),
3429 key_name: None,
3430 encryption_method: super::super::EncryptionMethod {
3431 algorithm: "http://www.w3.org/2009/xmlenc11#rsa-oaep".into(),
3432 key_size_bits: None,
3433 oaep_digest: Some("http://www.w3.org/2001/04/xmlenc#sha256".into()),
3434 mgf_algorithm: Some("http://www.w3.org/2009/xmlenc11#mgf1sha384".into()),
3435 oaep_params: Some(label),
3436 },
3437 cipher_data: super::super::CipherData {
3438 value: STANDARD.encode(wrapped),
3439 },
3440 reference_list: None,
3441 carried_key_name: None,
3442 };
3443 let resolved = PrivateKeyDecryptor::new(private_key)
3444 .resolve_key(
3445 crate::provider::default_provider(),
3446 DataEncryptionAlgorithm::Aes128Gcm,
3447 Some(&encrypted_key),
3448 )
3449 .expect("OAEP 1.1 wrapped key must resolve");
3450 assert_eq!(resolved, session_key);
3451 }
3452
3453 #[test]
3454 fn decrypts_legacy_oaep_uri_with_sha256_digest() {
3455 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
3457 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
3458 ))
3459 .expect("RSA donor private key must parse");
3460 let public_key = RsaPublicKey::from(&private_key);
3461 let session_key = [8_u8; 16];
3462 let wrapped = public_key
3463 .encrypt(
3464 &mut ChaCha20Rng::from_seed([19_u8; 32]),
3465 Oaep::<Sha256, Sha1>::new_with_mgf_hash(),
3466 &session_key,
3467 )
3468 .expect("legacy OAEP URI test wrapping must succeed");
3469 let encrypted_key = EncryptedKey {
3470 id: None,
3471 recipient: None,
3472 key_name: None,
3473 encryption_method: super::super::EncryptionMethod {
3474 algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p".into(),
3475 key_size_bits: None,
3476 oaep_digest: Some("http://www.w3.org/2001/04/xmlenc#sha256".into()),
3477 mgf_algorithm: None,
3478 oaep_params: None,
3479 },
3480 cipher_data: super::super::CipherData {
3481 value: STANDARD.encode(wrapped),
3482 },
3483 reference_list: None,
3484 carried_key_name: None,
3485 };
3486 let resolved = PrivateKeyDecryptor::new(private_key)
3487 .resolve_key(
3488 crate::provider::default_provider(),
3489 DataEncryptionAlgorithm::Aes128Gcm,
3490 Some(&encrypted_key),
3491 )
3492 .expect("legacy OAEP URI with SHA-256 must resolve");
3493 assert_eq!(resolved, session_key);
3494 }
3495
3496 #[test]
3497 fn decrypts_sha384_oaep_with_the_xmlenc_digest_uri() {
3498 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
3501 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
3502 ))
3503 .expect("RSA donor private key must parse");
3504 let public_key = RsaPublicKey::from(&private_key);
3505 let session_key = [9_u8; 16];
3506 let digest = "http://www.w3.org/2001/04/xmlenc#sha384";
3507
3508 for (algorithm, mgf_algorithm) in [
3509 ("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p", None),
3510 (
3511 "http://www.w3.org/2009/xmlenc11#rsa-oaep",
3512 Some("http://www.w3.org/2009/xmlenc11#mgf1sha1"),
3513 ),
3514 ] {
3515 let wrapped = public_key
3516 .encrypt(
3517 &mut ChaCha20Rng::from_seed([23_u8; 32]),
3518 Oaep::<Sha384, Sha1>::new_with_mgf_hash(),
3519 &session_key,
3520 )
3521 .expect("SHA-384 OAEP test wrapping must succeed");
3522 let encrypted_key = EncryptedKey {
3523 id: None,
3524 recipient: None,
3525 key_name: None,
3526 encryption_method: super::super::EncryptionMethod {
3527 algorithm: algorithm.into(),
3528 key_size_bits: None,
3529 oaep_digest: Some(digest.into()),
3530 mgf_algorithm: mgf_algorithm.map(str::to_owned),
3531 oaep_params: None,
3532 },
3533 cipher_data: super::super::CipherData {
3534 value: STANDARD.encode(wrapped),
3535 },
3536 reference_list: None,
3537 carried_key_name: None,
3538 };
3539 let resolved = PrivateKeyDecryptor::new(private_key.clone())
3540 .resolve_key(
3541 crate::provider::default_provider(),
3542 DataEncryptionAlgorithm::Aes128Gcm,
3543 Some(&encrypted_key),
3544 )
3545 .expect("official XMLENC SHA-384 URI must resolve");
3546 assert_eq!(resolved, session_key);
3547 }
3548 }
3549
3550 #[test]
3551 fn rejects_unknown_oaep_digest_and_mgf_as_unsupported() {
3552 let private_key = RsaPrivateKey::from_pkcs8_pem(include_str!(
3554 "../../tests/fixtures/keys/rsa/rsa-2048-key.pem"
3555 ))
3556 .expect("RSA donor private key must parse");
3557 let decryptor = PrivateKeyDecryptor::new(private_key);
3558 let mut encrypted_key = EncryptedKey {
3559 id: None,
3560 recipient: None,
3561 key_name: None,
3562 encryption_method: super::super::EncryptionMethod {
3563 algorithm: "http://www.w3.org/2009/xmlenc11#rsa-oaep".into(),
3564 key_size_bits: None,
3565 oaep_digest: Some("urn:unsupported:digest".into()),
3566 mgf_algorithm: Some("http://www.w3.org/2009/xmlenc11#mgf1sha1".into()),
3567 oaep_params: None,
3568 },
3569 cipher_data: super::super::CipherData {
3570 value: STANDARD.encode([0_u8; 256]),
3571 },
3572 reference_list: None,
3573 carried_key_name: None,
3574 };
3575 assert!(matches!(
3576 decryptor.resolve_key(crate::provider::default_provider(), DataEncryptionAlgorithm::Aes128Gcm, Some(&encrypted_key)),
3577 Err(XmlEncError::UnsupportedAlgorithm(uri)) if uri == "urn:unsupported:digest"
3578 ));
3579
3580 encrypted_key.encryption_method.oaep_digest = None;
3581 encrypted_key.encryption_method.mgf_algorithm = Some("urn:unsupported:mgf".into());
3582 assert!(matches!(
3583 decryptor.resolve_key(crate::provider::default_provider(), DataEncryptionAlgorithm::Aes128Gcm, Some(&encrypted_key)),
3584 Err(XmlEncError::UnsupportedAlgorithm(uri)) if uri == "urn:unsupported:mgf"
3585 ));
3586 }
3587
3588 #[test]
3589 fn decryption_policy_enforces_oaep_digest_and_plaintext_limits() {
3590 let encrypted_key = EncryptedKey {
3593 id: None,
3594 recipient: Some("selected".into()),
3595 key_name: None,
3596 encryption_method: super::super::EncryptionMethod {
3597 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p.uri().into(),
3598 key_size_bits: None,
3599 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
3600 mgf_algorithm: Some(OaepDigestAlgorithm::Sha384.mgf_uri().into()),
3601 oaep_params: None,
3602 },
3603 cipher_data: super::super::CipherData {
3604 value: STANDARD.encode([0_u8; 256]),
3605 },
3606 reference_list: None,
3607 carried_key_name: None,
3608 };
3609 let encrypted = EncryptedData {
3610 id: None,
3611 encrypted_type: None,
3612 key_name: None,
3613 encryption_method: super::super::EncryptionMethod {
3614 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3615 key_size_bits: None,
3616 oaep_digest: None,
3617 mgf_algorithm: None,
3618 oaep_params: None,
3619 },
3620 encrypted_keys: vec![encrypted_key],
3621 cipher_data: super::super::CipherData {
3622 value: STANDARD.encode([0_u8; 28]),
3623 },
3624 };
3625 let policy = crate::policy::DecryptionPolicy {
3626 oaep_digests: Some(std::collections::HashSet::from([
3627 OaepDigestAlgorithm::Sha256,
3628 ])),
3629 ..crate::policy::DecryptionPolicy::default()
3630 };
3631 assert!(matches!(
3632 DecryptContext::new(&RecipientKeyResolver {
3633 recipient: "selected",
3634 key: vec![0_u8; 16],
3635 })
3636 .policy(policy)
3637 .decrypt_data(&encrypted),
3638 Err(XmlEncError::Policy(
3639 crate::policy::PolicyViolation::Algorithm { algorithm, .. }
3640 )) if algorithm == OaepDigestAlgorithm::Sha384.mgf_uri()
3641 ));
3642
3643 let ciphertext = crate::provider::default_provider()
3644 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &[0_u8; 16], b"four")
3645 .expect("test encryption must succeed");
3646 let bounded = EncryptedData {
3647 encrypted_keys: Vec::new(),
3648 cipher_data: super::super::CipherData {
3649 value: STANDARD.encode(ciphertext),
3650 },
3651 ..encrypted
3652 };
3653 let policy = crate::policy::DecryptionPolicy {
3654 resources: crate::policy::ResourcePolicy {
3655 max_encryption_plaintext_bytes: 3,
3656 ..crate::policy::ResourcePolicy::default()
3657 },
3658 ..crate::policy::DecryptionPolicy::default()
3659 };
3660 assert!(matches!(
3661 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16]))
3662 .policy(policy)
3663 .decrypt_data(&bounded),
3664 Err(XmlEncError::Policy(
3665 crate::policy::PolicyViolation::ResourceLimit {
3666 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
3667 maximum: 3,
3668 actual: 4
3669 }
3670 ))
3671 ));
3672
3673 let cbc_ciphertext = crate::provider::default_provider()
3674 .encrypt_data(DataEncryptionAlgorithm::Aes128Cbc, &[0_u8; 16], b"four")
3675 .expect("test CBC encryption must succeed");
3676 let bounded_cbc = EncryptedData {
3677 encryption_method: super::super::EncryptionMethod {
3678 algorithm: DataEncryptionAlgorithm::Aes128Cbc.uri().into(),
3679 key_size_bits: None,
3680 oaep_digest: None,
3681 mgf_algorithm: None,
3682 oaep_params: None,
3683 },
3684 encrypted_keys: Vec::new(),
3685 cipher_data: super::super::CipherData {
3686 value: STANDARD.encode(cbc_ciphertext),
3687 },
3688 ..bounded
3689 };
3690 let policy = crate::policy::DecryptionPolicy {
3691 resources: crate::policy::ResourcePolicy {
3692 max_encryption_plaintext_bytes: 4,
3693 ..crate::policy::ResourcePolicy::default()
3694 },
3695 ..crate::policy::DecryptionPolicy::default()
3696 };
3697 assert_eq!(
3698 DecryptContext::new(&SymmetricKeyDecryptor::new([0_u8; 16]))
3699 .policy(policy)
3700 .decrypt_data(&bounded_cbc)
3701 .expect("CBC plaintext at the configured limit must decrypt"),
3702 DecryptedContent::Bytes(b"four".to_vec())
3703 );
3704 }
3705
3706 #[test]
3707 fn typed_decryption_input_cannot_bypass_metadata_policy() {
3708 let ciphertext = crate::provider::default_provider()
3711 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &[0_u8; 16], b"data")
3712 .expect("test encryption must succeed");
3713 let encrypted = EncryptedData {
3714 id: Some("oversized".into()),
3715 encrypted_type: None,
3716 key_name: None,
3717 encryption_method: super::super::EncryptionMethod {
3718 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3719 key_size_bits: None,
3720 oaep_digest: None,
3721 mgf_algorithm: None,
3722 oaep_params: None,
3723 },
3724 encrypted_keys: Vec::new(),
3725 cipher_data: super::super::CipherData {
3726 value: STANDARD.encode(ciphertext),
3727 },
3728 };
3729 let policy = crate::policy::DecryptionPolicy {
3730 resources: crate::policy::ResourcePolicy {
3731 max_encryption_metadata_bytes: 8,
3732 ..crate::policy::ResourcePolicy::default()
3733 },
3734 ..crate::policy::DecryptionPolicy::default()
3735 };
3736
3737 let resolver = AllCallsResolver {
3738 calls: Cell::new(0),
3739 key: vec![0_u8; 16],
3740 };
3741 assert!(matches!(
3742 DecryptContext::new(&resolver)
3743 .policy(policy)
3744 .decrypt_data(&encrypted),
3745 Err(XmlEncError::Policy(
3746 crate::policy::PolicyViolation::ResourceLimit {
3747 resource: crate::policy::resource_name::ENCRYPTION_METADATA_BYTES,
3748 maximum: 8,
3749 actual: 9,
3750 }
3751 ))
3752 ));
3753 assert_eq!(
3754 resolver.calls.get(),
3755 0,
3756 "metadata preflight must gate resolver-controlled work"
3757 );
3758 }
3759
3760 #[test]
3761 fn typed_cipher_values_are_bounded_before_decode_or_resolution() {
3762 let key = [0x41_u8; 16];
3765 let ciphertext = crate::provider::default_provider()
3766 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
3767 .expect("test encryption must succeed");
3768 let mut encrypted = EncryptedData {
3769 id: None,
3770 encrypted_type: None,
3771 key_name: None,
3772 encryption_method: super::super::EncryptionMethod {
3773 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3774 key_size_bits: None,
3775 oaep_digest: None,
3776 mgf_algorithm: None,
3777 oaep_params: None,
3778 },
3779 encrypted_keys: Vec::new(),
3780 cipher_data: super::super::CipherData {
3781 value: STANDARD.encode(ciphertext),
3782 },
3783 };
3784 let policy = crate::policy::DecryptionPolicy {
3785 resources: crate::policy::ResourcePolicy {
3786 max_encryption_plaintext_bytes: 4,
3787 ..crate::policy::ResourcePolicy::default()
3788 },
3789 ..crate::policy::DecryptionPolicy::default()
3790 };
3791 encrypted.cipher_data.value = "A".repeat(48);
3792 assert!(matches!(
3793 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
3794 .policy(policy)
3795 .decrypt_data(&encrypted),
3796 Err(XmlEncError::Policy(
3797 crate::policy::PolicyViolation::ResourceLimit {
3798 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
3799 ..
3800 }
3801 ))
3802 ));
3803
3804 encrypted.cipher_data.value = STANDARD.encode([0_u8; 28]);
3805 encrypted.encrypted_keys.push(EncryptedKey {
3806 id: None,
3807 recipient: None,
3808 key_name: None,
3809 encryption_method: super::super::EncryptionMethod {
3810 algorithm: KeyWrapAlgorithm::AesKw128.uri().into(),
3811 key_size_bits: None,
3812 oaep_digest: None,
3813 mgf_algorithm: None,
3814 oaep_params: None,
3815 },
3816 cipher_data: super::super::CipherData {
3817 value: "A".repeat(MAX_CIPHER_VALUE_BASE64_LEN + 4),
3818 },
3819 reference_list: None,
3820 carried_key_name: None,
3821 });
3822 let resolver = CountingResolver {
3823 candidate_calls: Cell::new(0),
3824 key: key.to_vec(),
3825 };
3826 assert!(matches!(
3827 DecryptContext::new(&resolver).decrypt_data(&encrypted),
3828 Err(XmlEncError::InvalidStructure(_))
3829 ));
3830 assert_eq!(resolver.candidate_calls.get(), 0);
3831
3832 encrypted.encrypted_keys[0].cipher_data.value = "AAAA".into();
3833 let aggregate_encoded_len =
3834 encrypted.cipher_data.value.len() + encrypted.encrypted_keys[0].cipher_data.value.len();
3835 let policy = crate::policy::DecryptionPolicy {
3836 resources: crate::policy::ResourcePolicy {
3837 max_encryption_plaintext_bytes: 4,
3838 max_xml_document_bytes: aggregate_encoded_len - 1,
3839 ..crate::policy::ResourcePolicy::default()
3840 },
3841 ..crate::policy::DecryptionPolicy::default()
3842 };
3843 let resolver = CountingResolver {
3844 candidate_calls: Cell::new(0),
3845 key: key.to_vec(),
3846 };
3847 assert!(matches!(
3848 DecryptContext::new(&resolver)
3849 .policy(policy)
3850 .decrypt_data(&encrypted),
3851 Err(XmlEncError::Policy(
3852 crate::policy::PolicyViolation::ResourceLimit {
3853 resource:
3854 crate::policy::resource_name::AGGREGATE_ENCRYPTION_CIPHER_VALUE_BYTES,
3855 maximum,
3856 actual,
3857 }
3858 )) if maximum == aggregate_encoded_len - 1 && actual == aggregate_encoded_len
3859 ));
3860 assert_eq!(resolver.candidate_calls.get(), 0);
3861 }
3862
3863 #[test]
3864 fn typed_legacy_oaep_accepts_explicit_mgf() {
3865 let key = [0x43_u8; 16];
3868 let ciphertext = crate::provider::default_provider()
3869 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
3870 .expect("test encryption must succeed");
3871 let encrypted = EncryptedData {
3872 id: None,
3873 encrypted_type: None,
3874 key_name: None,
3875 encryption_method: super::super::EncryptionMethod {
3876 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3877 key_size_bits: None,
3878 oaep_digest: None,
3879 mgf_algorithm: None,
3880 oaep_params: None,
3881 },
3882 encrypted_keys: vec![EncryptedKey {
3883 id: None,
3884 recipient: None,
3885 key_name: None,
3886 encryption_method: super::super::EncryptionMethod {
3887 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p.uri().into(),
3888 key_size_bits: None,
3889 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
3890 mgf_algorithm: Some(OaepDigestAlgorithm::Sha384.mgf_uri().into()),
3891 oaep_params: None,
3892 },
3893 cipher_data: super::super::CipherData {
3894 value: STANDARD.encode([0_u8; 256]),
3895 },
3896 reference_list: None,
3897 carried_key_name: None,
3898 }],
3899 cipher_data: super::super::CipherData {
3900 value: STANDARD.encode(ciphertext),
3901 },
3902 };
3903 let resolver = CountingResolver {
3904 candidate_calls: Cell::new(0),
3905 key: key.to_vec(),
3906 };
3907
3908 assert_eq!(
3909 DecryptContext::new(&resolver)
3910 .decrypt_data(&encrypted)
3911 .expect("explicit legacy OAEP MGF must reach key resolution"),
3912 DecryptedContent::Bytes(b"data".to_vec())
3913 );
3914 assert_eq!(resolver.candidate_calls.get(), 1);
3915 }
3916
3917 #[test]
3918 fn typed_zero_key_size_is_rejected_before_key_resolution() {
3919 let key = [0x45_u8; 16];
3922 let ciphertext = crate::provider::default_provider()
3923 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
3924 .expect("test encryption must succeed");
3925 let encrypted = EncryptedData {
3926 id: None,
3927 encrypted_type: None,
3928 key_name: None,
3929 encryption_method: super::super::EncryptionMethod {
3930 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3931 key_size_bits: None,
3932 oaep_digest: None,
3933 mgf_algorithm: None,
3934 oaep_params: None,
3935 },
3936 encrypted_keys: vec![EncryptedKey {
3937 id: None,
3938 recipient: None,
3939 key_name: None,
3940 encryption_method: super::super::EncryptionMethod {
3941 algorithm: KeyTransportAlgorithm::RsaOaep11.uri().into(),
3942 key_size_bits: Some(0),
3943 oaep_digest: Some(OaepDigestAlgorithm::Sha256.uri().into()),
3944 mgf_algorithm: Some(OaepDigestAlgorithm::Sha256.mgf_uri().into()),
3945 oaep_params: None,
3946 },
3947 cipher_data: super::super::CipherData {
3948 value: STANDARD.encode([0_u8; 256]),
3949 },
3950 reference_list: None,
3951 carried_key_name: None,
3952 }],
3953 cipher_data: super::super::CipherData {
3954 value: STANDARD.encode(ciphertext),
3955 },
3956 };
3957 let resolver = CountingResolver {
3958 candidate_calls: Cell::new(0),
3959 key: key.to_vec(),
3960 };
3961
3962 assert!(matches!(
3963 DecryptContext::new(&resolver).decrypt_data(&encrypted),
3964 Err(XmlEncError::InvalidStructure(message))
3965 if message == "KeySize must be a positive integer"
3966 ));
3967 assert_eq!(resolver.candidate_calls.get(), 0);
3968 }
3969
3970 #[test]
3971 fn typed_content_method_is_validated_before_key_resolution() {
3972 let key = [0x44_u8; 16];
3975 let ciphertext = crate::provider::default_provider()
3976 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
3977 .expect("test encryption must succeed");
3978 let encrypted = EncryptedData {
3979 id: None,
3980 encrypted_type: None,
3981 key_name: None,
3982 encryption_method: super::super::EncryptionMethod {
3983 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
3984 key_size_bits: Some(256),
3985 oaep_digest: None,
3986 mgf_algorithm: None,
3987 oaep_params: None,
3988 },
3989 encrypted_keys: Vec::new(),
3990 cipher_data: super::super::CipherData {
3991 value: STANDARD.encode(ciphertext),
3992 },
3993 };
3994 let resolver = AllCallsResolver {
3995 calls: Cell::new(0),
3996 key: key.to_vec(),
3997 };
3998
3999 assert!(matches!(
4000 DecryptContext::new(&resolver).decrypt_data(&encrypted),
4001 Err(XmlEncError::InvalidStructure(message))
4002 if message.contains("requires KeySize 128, got 256")
4003 ));
4004 assert_eq!(resolver.calls.get(), 0);
4005 }
4006
4007 #[test]
4008 fn unknown_encrypted_key_algorithm_never_reaches_resolver() {
4009 let key = [0x42_u8; 16];
4012 let ciphertext = crate::provider::default_provider()
4013 .encrypt_data(DataEncryptionAlgorithm::Aes128Gcm, &key, b"data")
4014 .expect("test encryption must succeed");
4015 let encrypted = EncryptedData {
4016 id: None,
4017 encrypted_type: None,
4018 key_name: None,
4019 encryption_method: super::super::EncryptionMethod {
4020 algorithm: DataEncryptionAlgorithm::Aes128Gcm.uri().into(),
4021 key_size_bits: None,
4022 oaep_digest: None,
4023 mgf_algorithm: None,
4024 oaep_params: None,
4025 },
4026 encrypted_keys: vec![EncryptedKey {
4027 id: None,
4028 recipient: None,
4029 key_name: None,
4030 encryption_method: super::super::EncryptionMethod {
4031 algorithm: "urn:example:unknown-key-algorithm".into(),
4032 key_size_bits: None,
4033 oaep_digest: None,
4034 mgf_algorithm: None,
4035 oaep_params: None,
4036 },
4037 cipher_data: super::super::CipherData {
4038 value: STANDARD.encode([0_u8; 24]),
4039 },
4040 reference_list: None,
4041 carried_key_name: None,
4042 }],
4043 cipher_data: super::super::CipherData {
4044 value: STANDARD.encode(ciphertext),
4045 },
4046 };
4047 let resolver = CountingResolver {
4048 candidate_calls: Cell::new(0),
4049 key: key.to_vec(),
4050 };
4051
4052 assert!(matches!(
4053 DecryptContext::new(&resolver).decrypt_data(&encrypted),
4054 Err(XmlEncError::UnsupportedAlgorithm(_))
4055 ));
4056 assert_eq!(resolver.candidate_calls.get(), 0);
4057 }
4058
4059 #[test]
4060 fn cbc_padding_errors_do_not_expose_decrypted_octets() {
4061 let error = map_data_decryption_error(
4064 DataEncryptionAlgorithm::Aes128Cbc,
4065 32,
4066 crate::provider::ProviderError::InvalidInput(
4067 crate::provider::ProviderInputError::AesCbcCiphertext,
4068 ),
4069 );
4070
4071 assert_eq!(error.to_string(), "invalid XMLEnc padding");
4072 }
4073
4074 #[test]
4075 fn replaces_element_and_content_in_caller_owned_documents() {
4076 let key = [0x31_u8; 16];
4079 let element = encrypted_gcm_element(
4080 "http://www.w3.org/2001/04/xmlenc#Element",
4081 "<secret id=\"visible\">value</secret>",
4082 None,
4083 true,
4084 &key,
4085 );
4086 assert_eq!(
4087 decrypt_document(&element, None, &SymmetricKeyDecryptor::new(key))
4088 .expect("root Element replacement must succeed"),
4089 "<secret id=\"visible\">value</secret>"
4090 );
4091
4092 let content = encrypted_gcm_element(
4093 "http://www.w3.org/2001/04/xmlenc#Content",
4094 "before<child/>after",
4095 None,
4096 false,
4097 &key,
4098 );
4099 let document =
4100 format!("<root xmlns:xenc=\"{XMLENC_NS}\"><prefix/>{content}<suffix/></root>");
4101 let replaced = decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key))
4102 .expect("nested Content replacement must succeed");
4103 assert_eq!(
4104 replaced,
4105 format!(
4106 "<root xmlns:xenc=\"{XMLENC_NS}\"><prefix/>before<child/>after<suffix/></root>"
4107 )
4108 );
4109 }
4110
4111 #[test]
4112 fn accepts_whitespace_and_comments_around_element_plaintext() {
4113 let key = [0x34_u8; 16];
4116 let plaintext = "\n<!--before--><secret/><!--after-->\n";
4117 let encrypted = encrypted_gcm_element(
4118 "http://www.w3.org/2001/04/xmlenc#Element",
4119 plaintext,
4120 None,
4121 true,
4122 &key,
4123 );
4124
4125 assert_eq!(
4126 decrypt_document(&encrypted, None, &SymmetricKeyDecryptor::new(key))
4127 .expect("one element with boundary trivia must be accepted"),
4128 plaintext
4129 );
4130 }
4131
4132 #[test]
4133 fn decrypts_unknown_and_empty_type_hints_as_opaque_bytes() {
4134 let key = [0x35_u8; 16];
4137 let plaintext = "\0opaque\u{ff}bytes";
4138 let unknown = encrypted_gcm_element("urn:example:binary", plaintext, None, true, &key);
4139 let empty = encrypted_gcm_element("", plaintext, None, true, &key).replacen(
4140 "<xenc:EncryptedData",
4141 "<xenc:EncryptedData Type=\"\"",
4142 1,
4143 );
4144
4145 let parsed = parse_encrypted_data(&unknown).expect("unknown Type must remain parseable");
4146 assert_eq!(
4147 parsed.encrypted_type,
4148 Some(EncryptedDataType::Other("urn:example:binary".into()))
4149 );
4150 assert!(matches!(
4151 decrypt_document(&unknown, None, &SymmetricKeyDecryptor::new(key)),
4152 Err(XmlEncError::ReplacementRequiresXml)
4153 ));
4154
4155 for encrypted in [unknown, empty] {
4156 assert_eq!(
4157 decrypt(&encrypted, &SymmetricKeyDecryptor::new(key))
4158 .expect("opaque Type hints must not block decryption"),
4159 DecryptedContent::Bytes(plaintext.as_bytes().to_vec())
4160 );
4161 }
4162 }
4163
4164 #[test]
4165 fn selects_document_encrypted_data_by_id_and_rejects_ambiguity() {
4166 let key = [0x32_u8; 16];
4169 let first = encrypted_gcm_element(
4170 "http://www.w3.org/2001/04/xmlenc#Content",
4171 "first",
4172 Some("first"),
4173 false,
4174 &key,
4175 );
4176 let second = encrypted_gcm_element(
4177 "http://www.w3.org/2001/04/xmlenc#Content",
4178 "second",
4179 Some("second"),
4180 false,
4181 &key,
4182 );
4183 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{first}{second}</root>");
4184 let resolver = SymmetricKeyDecryptor::new(key);
4185 assert!(matches!(
4186 decrypt_document(&document, None, &resolver),
4187 Err(XmlEncError::AmbiguousEncryptedData)
4188 ));
4189 let replaced = decrypt_document(&document, Some("second"), &resolver)
4190 .expect("Id selection must choose exactly one encrypted region");
4191 assert!(replaced.contains("second"));
4192 assert!(replaced.contains("Id=\"first\""));
4193 assert!(matches!(
4194 decrypt_document(&document, Some("missing"), &resolver),
4195 Err(XmlEncError::EncryptedDataNotFound)
4196 ));
4197 }
4198
4199 #[test]
4200 fn selects_encrypted_data_below_a_unique_operation_start_node() {
4201 let key = [0x42_u8; 16];
4204 let first = encrypted_gcm_element(
4205 "http://www.w3.org/2001/04/xmlenc#Content",
4206 "first",
4207 None,
4208 false,
4209 &key,
4210 );
4211 let second = encrypted_gcm_element(
4212 "http://www.w3.org/2001/04/xmlenc#Content",
4213 "second",
4214 None,
4215 false,
4216 &key,
4217 );
4218 let document = format!(
4219 "<root xmlns:xenc=\"{XMLENC_NS}\"><scope Id=\"first\">{first}</scope><scope Id=\"second\">{second}</scope></root>"
4220 );
4221 let resolver = SymmetricKeyDecryptor::new(key);
4222 let context = DecryptContext::new(&resolver);
4223 let replaced = context
4224 .decrypt_document_from_start_node(&document, Some("second"))
4225 .expect("ancestor ID must select its encrypted descendant");
4226 assert!(replaced.contains("<scope Id=\"second\">second</scope>"));
4227 assert!(replaced.contains("<scope Id=\"first\"><xenc:EncryptedData"));
4228
4229 assert!(matches!(
4230 context.decrypt_document_from_start_node(&document, Some("missing")),
4231 Err(XmlEncError::SelectedNodeUnavailable { id }) if id == "missing"
4232 ));
4233 let duplicate = document.replace("Id=\"second\"", "Id=\"first\"");
4234 assert!(matches!(
4235 context.decrypt_document_from_start_node(&duplicate, Some("first")),
4236 Err(XmlEncError::SelectedNodeUnavailable { id }) if id == "first"
4237 ));
4238 let ambiguous = format!(
4239 "<root xmlns:xenc=\"{XMLENC_NS}\"><scope Id=\"selected\">{first}{second}</scope></root>"
4240 );
4241 assert!(matches!(
4242 context.decrypt_document_from_start_node(&ambiguous, Some("selected")),
4243 Err(XmlEncError::AmbiguousEncryptedData)
4244 ));
4245
4246 let first_replaced = context
4247 .decrypt_first_document_from_start_node(&ambiguous, Some("selected"))
4248 .expect("first-match selection must leave later encrypted descendants untouched");
4249 assert!(first_replaced.contains("<scope Id=\"selected\">first<xenc:EncryptedData"));
4250 let replaced_document =
4251 Document::parse(&first_replaced).expect("first-match output must remain valid XML");
4252 assert_eq!(
4253 replaced_document
4254 .descendants()
4255 .filter(|node| node.has_tag_name((XMLENC_NS, "EncryptedData")))
4256 .count(),
4257 1
4258 );
4259 }
4260
4261 #[test]
4262 fn rejects_non_xml_or_malformed_document_replacement_plaintext() {
4263 let key = [0x33_u8; 16];
4266 let binary = encrypted_gcm_element("", "binary", None, true, &key);
4267 assert!(matches!(
4268 decrypt_document(&binary, None, &SymmetricKeyDecryptor::new(key)),
4269 Err(XmlEncError::ReplacementRequiresXml)
4270 ));
4271
4272 let malformed = encrypted_gcm_element(
4273 "http://www.w3.org/2001/04/xmlenc#Element",
4274 "<unclosed>",
4275 None,
4276 true,
4277 &key,
4278 );
4279 assert!(matches!(
4280 decrypt_document(&malformed, None, &SymmetricKeyDecryptor::new(key)),
4281 Err(XmlEncError::XmlParse(_))
4282 ));
4283
4284 for invalid_element in ["text-only", "<first/><second/>"] {
4285 let encrypted = encrypted_gcm_element(
4286 "http://www.w3.org/2001/04/xmlenc#Element",
4287 invalid_element,
4288 None,
4289 false,
4290 &key,
4291 );
4292 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>");
4293 assert!(
4294 decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key)).is_err(),
4295 "Element plaintext must contain exactly one element: {invalid_element}"
4296 );
4297 }
4298
4299 let content = encrypted_gcm_element(
4300 "http://www.w3.org/2001/04/xmlenc#Content",
4301 "plaintext",
4302 None,
4303 false,
4304 &key,
4305 );
4306 let with_dtd = format!(
4307 "<!DOCTYPE root [<!ATTLIST root Id ID #IMPLIED>]><root xmlns:xenc=\"{XMLENC_NS}\">{content}</root>"
4308 );
4309 assert!(matches!(
4310 decrypt_document(&with_dtd, None, &SymmetricKeyDecryptor::new(key)),
4311 Err(XmlEncError::XmlParse(
4312 crate::xml::dom::ParseError::DtdDetected
4313 ))
4314 ));
4315 let mut policy = crate::policy::DecryptionPolicy::default();
4316 policy.xml.allow_internal_dtd = true;
4317 assert!(
4318 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
4319 .policy(policy)
4320 .decrypt_document(&with_dtd, None)
4321 .expect("explicit internal-DTD opt-in must decrypt")
4322 .contains("plaintext")
4323 );
4324 }
4325
4326 #[test]
4327 fn rejects_plaintext_markup_that_crosses_the_encrypted_region() {
4328 let key = [0x36_u8; 16];
4332 let crossing_markup = "</parent><attacker/><parent>";
4333 for type_uri in [
4334 "http://www.w3.org/2001/04/xmlenc#Content",
4335 "http://www.w3.org/2001/04/xmlenc#Element",
4336 ] {
4337 let encrypted = encrypted_gcm_element(type_uri, crossing_markup, None, false, &key);
4338 let document =
4339 format!("<outer xmlns:xenc=\"{XMLENC_NS}\"><parent>{encrypted}</parent></outer>");
4340 assert!(
4341 decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key)).is_err(),
4342 "{type_uri} plaintext must not escape its replacement boundary"
4343 );
4344 }
4345 }
4346
4347 #[test]
4348 fn document_decryption_applies_byte_and_node_policy_before_parsing() {
4349 let key = [0x38_u8; 16];
4352 let encrypted = encrypted_gcm_element(
4353 "http://www.w3.org/2001/04/xmlenc#Content",
4354 "plaintext",
4355 None,
4356 false,
4357 &key,
4358 );
4359 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\"><a/>{encrypted}</root>");
4360 let byte_policy = crate::policy::DecryptionPolicy {
4361 resources: crate::policy::ResourcePolicy {
4362 max_xml_document_bytes: document.len() - 1,
4363 ..crate::policy::ResourcePolicy::default()
4364 },
4365 ..crate::policy::DecryptionPolicy::default()
4366 };
4367 assert!(matches!(
4368 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
4369 .policy(byte_policy)
4370 .decrypt_document(&document, None),
4371 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4372 resource: crate::policy::resource_name::XML_DOCUMENT,
4373 maximum,
4374 actual,
4375 })) if maximum == document.len() - 1 && actual == document.len()
4376 ));
4377
4378 let node_policy = crate::policy::DecryptionPolicy {
4379 resources: crate::policy::ResourcePolicy {
4380 max_xml_nodes: 3,
4381 ..crate::policy::ResourcePolicy::default()
4382 },
4383 ..crate::policy::DecryptionPolicy::default()
4384 };
4385 assert!(matches!(
4386 DecryptContext::new(&SymmetricKeyDecryptor::new(key))
4387 .policy(node_policy)
4388 .decrypt_document(&document, None),
4389 Err(XmlEncError::Policy(
4390 crate::policy::PolicyViolation::ResourceLimit {
4391 resource: crate::policy::resource_name::XML_NODES,
4392 maximum: 3,
4393 actual: 4,
4394 }
4395 ))
4396 ));
4397 }
4398
4399 #[test]
4400 fn decryption_entry_points_enforce_policy_depth() {
4401 let xml = "<root><child><leaf/></child></root>";
4404 let policy = crate::policy::DecryptionPolicy {
4405 resources: crate::policy::ResourcePolicy {
4406 max_xml_depth: 2,
4407 ..crate::policy::ResourcePolicy::default()
4408 },
4409 ..crate::policy::DecryptionPolicy::default()
4410 };
4411 let resolver = SymmetricKeyDecryptor::new([0_u8; 16]);
4412 let mut document = XmlDocument::parse(xml).expect("wide retained fixture must parse");
4413
4414 assert!(matches!(
4415 DecryptContext::new(&resolver)
4416 .policy(policy.clone())
4417 .decrypt_document(xml, None),
4418 Err(XmlEncError::Policy(
4419 crate::policy::PolicyViolation::ResourceLimit {
4420 resource: crate::policy::resource_name::XML_DEPTH,
4421 maximum: 2,
4422 actual: 3,
4423 }
4424 ))
4425 ));
4426 assert!(matches!(
4427 DecryptContext::new(&resolver)
4428 .policy(policy)
4429 .decrypt_owned_document(&mut document, None),
4430 Err(XmlEncError::Policy(
4431 crate::policy::PolicyViolation::ResourceLimit {
4432 resource: crate::policy::resource_name::XML_DEPTH,
4433 maximum: 2,
4434 actual: 3,
4435 }
4436 ))
4437 ));
4438 }
4439
4440 #[test]
4441 fn fragment_validation_does_not_charge_its_internal_wrapper_node() {
4442 let key = [0x39_u8; 16];
4445 let plaintext = "<item/>".repeat(20);
4446 let encrypted = encrypted_gcm_element(
4447 "http://www.w3.org/2001/04/xmlenc#Content",
4448 &plaintext,
4449 None,
4450 false,
4451 &key,
4452 );
4453 let document = format!("<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>");
4454 let resolver = SymmetricKeyDecryptor::new(key);
4455 let expected = decrypt_document(&document, None, &resolver)
4456 .expect("unbounded setup decryption must succeed");
4457 let exact_output_nodes = Document::parse(&expected)
4458 .expect("decrypted output must parse")
4459 .descendants()
4460 .count();
4461 let policy = crate::policy::DecryptionPolicy {
4462 resources: crate::policy::ResourcePolicy {
4463 max_xml_nodes: exact_output_nodes,
4464 ..crate::policy::ResourcePolicy::default()
4465 },
4466 ..crate::policy::DecryptionPolicy::default()
4467 };
4468
4469 assert_eq!(
4470 DecryptContext::new(&resolver)
4471 .policy(policy)
4472 .decrypt_document(&document, None)
4473 .expect("temporary wrapper must not consume caller node budget"),
4474 expected
4475 );
4476 }
4477
4478 #[test]
4479 fn owned_decryption_rejects_projected_node_limit_atomically() {
4480 let key = [0x3a_u8; 16];
4483 let plaintext = "<item/>".repeat(64);
4484 let encrypted = encrypted_gcm_element(
4485 "http://www.w3.org/2001/04/xmlenc#Content",
4486 &plaintext,
4487 None,
4488 false,
4489 &key,
4490 );
4491 let mut document = XmlDocument::parse(format!(
4492 "<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>"
4493 ))
4494 .expect("owned encrypted fixture must parse");
4495 let input_nodes = document.with_view(|view| view.node_count());
4496 let before = document.as_xml().to_owned();
4497 let policy = crate::policy::DecryptionPolicy {
4498 resources: crate::policy::ResourcePolicy {
4499 max_xml_nodes: input_nodes,
4500 ..crate::policy::ResourcePolicy::default()
4501 },
4502 ..crate::policy::DecryptionPolicy::default()
4503 };
4504
4505 let error = DecryptContext::new(&SymmetricKeyDecryptor::new(key))
4506 .policy(policy)
4507 .decrypt_owned_document(&mut document, None)
4508 .expect_err("expanded plaintext must exceed the operation node ceiling");
4509
4510 assert!(matches!(
4511 error,
4512 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4513 resource: crate::policy::resource_name::XML_NODES,
4514 maximum,
4515 ..
4516 }) if maximum == input_nodes
4517 ));
4518 assert_eq!(document.as_xml(), before);
4519 assert_eq!(document.generation(), 0);
4520 }
4521
4522 #[test]
4523 fn owned_decryption_reports_decrypted_depth_as_policy() {
4524 let key = [0x3b_u8; 16];
4528 let plaintext = format!("{}value{}", "<nested>".repeat(32), "</nested>".repeat(32));
4529 let encrypted = encrypted_gcm_element(
4530 "http://www.w3.org/2001/04/xmlenc#Element",
4531 &plaintext,
4532 None,
4533 false,
4534 &key,
4535 );
4536 let mut document = XmlDocument::parse(format!(
4537 "<root xmlns:xenc=\"{XMLENC_NS}\">{encrypted}</root>"
4538 ))
4539 .expect("encrypted fixture must parse");
4540 let input_depth = document.with_view(|view| view.max_depth());
4541 let before = document.as_xml().to_owned();
4542 let policy = crate::policy::DecryptionPolicy {
4543 resources: crate::policy::ResourcePolicy {
4544 max_xml_depth: input_depth,
4545 ..crate::policy::ResourcePolicy::default()
4546 },
4547 ..crate::policy::DecryptionPolicy::default()
4548 };
4549
4550 let error = DecryptContext::new(&SymmetricKeyDecryptor::new(key))
4551 .policy(policy)
4552 .decrypt_owned_document(&mut document, None)
4553 .expect_err("deep plaintext must exceed the active depth policy");
4554
4555 assert!(matches!(
4556 error,
4557 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4558 resource: crate::policy::resource_name::XML_DEPTH,
4559 maximum,
4560 actual,
4561 }) if maximum == input_depth && actual > maximum
4562 ));
4563 assert_eq!(document.as_xml(), before);
4564 assert_eq!(document.generation(), 0);
4565 }
4566
4567 #[test]
4568 fn validates_replacement_plaintext_in_its_namespace_context() {
4569 let key = [0x37_u8; 16];
4572 let encrypted = encrypted_gcm_element(
4573 "http://www.w3.org/2001/04/xmlenc#Content",
4574 "<shared:child/>",
4575 None,
4576 false,
4577 &key,
4578 );
4579 let document = format!(
4580 "<root xmlns:xenc=\"{XMLENC_NS}\" xmlns:shared=\"urn:shared\">{encrypted}</root>"
4581 );
4582 let decrypted = decrypt_document(&document, None, &SymmetricKeyDecryptor::new(key))
4583 .expect("inherited namespace prefixes must remain valid");
4584 assert_eq!(
4585 decrypted,
4586 format!(
4587 "<root xmlns:xenc=\"{XMLENC_NS}\" xmlns:shared=\"urn:shared\"><shared:child/></root>"
4588 )
4589 );
4590 }
4591
4592 fn encrypted_gcm_element(
4593 type_uri: &str,
4594 plaintext: &str,
4595 id: Option<&str>,
4596 declare_namespace: bool,
4597 key: &[u8; 16],
4598 ) -> String {
4599 let nonce = [0x44_u8; 12];
4600 let mut ciphertext = plaintext.as_bytes().to_vec();
4601 Aes128Gcm::new_from_slice(key)
4602 .expect("fixed content key length")
4603 .encrypt_in_place(&nonce.into(), b"", &mut ciphertext)
4604 .expect("test encryption must succeed");
4605 let mut wire = nonce.to_vec();
4606 wire.extend_from_slice(&ciphertext);
4607 let namespace = declare_namespace
4608 .then_some(format!(" xmlns:xenc=\"{XMLENC_NS}\""))
4609 .unwrap_or_default();
4610 let data_type = (!type_uri.is_empty())
4611 .then_some(format!(" Type=\"{type_uri}\""))
4612 .unwrap_or_default();
4613 let id = id
4614 .map(|value| format!(" Id=\"{value}\""))
4615 .unwrap_or_default();
4616 format!(
4617 "<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>",
4618 STANDARD.encode(wire)
4619 )
4620 }
4621}