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