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