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