1use crate::xml_input as xml_sec_xml_input;
4use std::{fmt, io::Write, sync::Arc};
5
6use crate::xml::dom::{Document, Node};
7use base64::{Engine as _, engine::general_purpose::STANDARD};
8use rsa::RsaPublicKey;
9use xml_sec_xml_input::lexical::Writer;
10
11use crate::document::{
12 DocumentParseSettings, XmlDocument, XmlDocumentError, XmlParseWorkBudget,
13 parse_borrowed_with_settings_and_budget,
14};
15use crate::operation::{
16 OperationExecutionContext, OperationNodeId, OperationNodeKind, OperationPlanError,
17 OperationResourceIdentity, OperationStage,
18};
19use crate::xml::{is_xml_1_0_character, is_xml_ncname};
20
21use super::types::{XMLDSIG_NS, XMLENC_NS, XMLENC11_NS};
22use super::{
23 DataEncryptionAlgorithm, DocumentEncryptionOptions, EncryptedDataType, EncryptionRecipient,
24 EncryptionResult, KeyWrapAlgorithm, ReplacementMode, RsaOaepParameters, XmlEncError,
25 has_single_element_with_boundary_trivia, map_document_error,
26};
27
28const XML_WHITESPACE: &[char] = &[' ', '\t', '\n', '\r'];
29
30pub fn validate_rsa_recipient_key(
35 key: &RsaPublicKey,
36 policy: &crate::policy::EncryptionPolicy,
37) -> Result<(), XmlEncError> {
38 validate_key_transport_recipient(key, policy)
39}
40
41pub fn validate_key_transport_recipient(
47 key: &dyn crate::provider::KeyTransportKey,
48 policy: &crate::policy::EncryptionPolicy,
49) -> Result<(), XmlEncError> {
50 let modulus = key.rsa_modulus();
51 let exponent = key.rsa_exponent();
52 policy
53 .rsa_keys
54 .validate_components("encryption", &modulus, &exponent)?;
55 Ok(())
56}
57
58#[derive(Clone)]
60pub struct EncryptedDataBuilder {
61 algorithm: DataEncryptionAlgorithm,
62 encrypted_type: EncryptedDataType,
63 id: Option<String>,
64 direct_key: Option<DirectEncryptionKey>,
65 direct_key_name: Option<String>,
66 recipients: Vec<EncryptionRecipient>,
67 policy: crate::policy::EncryptionPolicy,
68 provider: Arc<dyn crate::provider::CryptoProvider>,
69 xml_backend: crate::XmlBackend,
70}
71
72#[derive(Clone)]
73enum DirectEncryptionKey {
74 Raw(zeroize::Zeroizing<Vec<u8>>),
75 Derived {
76 method: super::KeyDerivationMethod,
77 secret: zeroize::Zeroizing<Vec<u8>>,
78 },
79 Agreement {
80 method: super::KeyDerivationMethod,
81 key: Arc<dyn crate::provider::KeyAgreementKey>,
82 algorithm: crate::policy::KeyAgreementAlgorithm,
83 peer_public_key: Vec<u8>,
84 },
85}
86
87struct EphemeralSenderKey {
89 binding: Option<crate::provider::ProviderBinding>,
90 key: std::sync::Mutex<Option<Box<dyn crate::provider::KeyAgreementKey>>>,
91}
92
93impl crate::provider::KeyAgreementKey for EphemeralSenderKey {
94 fn provider_binding(&self) -> Option<&crate::provider::ProviderBinding> {
95 self.binding.as_ref()
96 }
97
98 fn agree(
99 &self,
100 parameters: &crate::provider::KeyAgreementParameters<'_>,
101 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
102 let key = self
107 .key
108 .lock()
109 .ok()
110 .and_then(|mut slot| slot.take())
111 .ok_or(crate::provider::ProviderError::InvalidInput(
112 crate::provider::ProviderInputError::ConsumedEphemeralKey,
113 ))?;
114 key.agree(parameters)
117 }
118}
119
120enum OperationContentKey<'a> {
121 Borrowed(&'a [u8]),
122 Owned(zeroize::Zeroizing<Vec<u8>>),
123}
124
125impl AsRef<[u8]> for OperationContentKey<'_> {
126 fn as_ref(&self) -> &[u8] {
127 match self {
128 Self::Borrowed(bytes) => bytes,
129 Self::Owned(bytes) => bytes,
130 }
131 }
132}
133
134struct GeneratedEncryption {
135 result: EncryptionResult,
136 xml_nodes: usize,
137 mutation: Option<OperationNodeId>,
138}
139
140struct EncryptionPlanNodes {
141 document: OperationNodeId,
142 keys: Vec<OperationNodeId>,
143 crypto: OperationNodeId,
144 evidence: OperationNodeId,
145 mutation: Option<OperationNodeId>,
146}
147
148struct EncryptionOperationBudgets {
149 xml_parse: XmlParseWorkBudget,
150 key_establishment: std::cell::RefCell<super::key_establishment_budget::KeyEstablishmentUsage>,
151}
152
153impl EncryptionOperationBudgets {
154 fn from_policy(policy: &crate::policy::EncryptionPolicy) -> Self {
155 Self {
156 xml_parse: XmlParseWorkBudget::from_resources(&policy.resources),
157 key_establishment: Default::default(),
158 }
159 }
160}
161
162fn compile_encryption_plan(
163 operation: &mut OperationExecutionContext<
164 crate::policy::EncryptionPolicy,
165 EncryptionOperationBudgets,
166 >,
167 key_count: usize,
168 mutation: bool,
169 input_resource: OperationResourceIdentity,
170) -> Result<EncryptionPlanNodes, XmlEncError> {
171 let document = operation.add_node(
172 OperationNodeKind::Document,
173 OperationStage::Parse,
174 Some(input_resource),
175 );
176 let mut keys = Vec::with_capacity(key_count.max(1));
177 for index in 0..key_count.max(1) {
178 let key = operation.add_node(
179 OperationNodeKind::Key { index },
180 OperationStage::Resolve,
181 None,
182 );
183 operation
184 .add_dependency(key, document)
185 .map_err(map_encryption_plan_error)?;
186 keys.push(key);
187 }
188 let crypto = operation.add_node(OperationNodeKind::Crypto, OperationStage::Crypto, None);
189 for key in &keys {
190 operation
191 .add_dependency(crypto, *key)
192 .map_err(map_encryption_plan_error)?;
193 }
194 let evidence = operation.add_node(OperationNodeKind::Evidence, OperationStage::Evidence, None);
195 operation
196 .add_dependency(evidence, crypto)
197 .map_err(map_encryption_plan_error)?;
198 let mutation = mutation.then(|| {
199 let node = operation.add_node(OperationNodeKind::Mutation, OperationStage::Mutation, None);
200 operation
201 .add_dependency(node, evidence)
202 .expect("evidence-to-mutation stage order is fixed");
203 node
204 });
205 operation.compile().map_err(map_encryption_plan_error)?;
206 Ok(EncryptionPlanNodes {
207 document,
208 keys,
209 crypto,
210 evidence,
211 mutation,
212 })
213}
214
215fn map_encryption_plan_error(error: OperationPlanError) -> XmlEncError {
216 XmlEncError::from(error)
217}
218
219fn require_encryption_mutation(
220 mutation: Option<OperationNodeId>,
221) -> Result<OperationNodeId, XmlEncError> {
222 mutation
223 .ok_or_else(|| XmlEncError::OperationPlan("encryption mutation node is unavailable".into()))
224}
225
226impl fmt::Debug for EncryptedDataBuilder {
227 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
228 formatter
229 .debug_struct("EncryptedDataBuilder")
230 .field("algorithm", &self.algorithm)
231 .field("encrypted_type", &self.encrypted_type)
232 .field("id", &self.id)
233 .field(
234 "direct_key",
235 &self.direct_key.as_ref().map(|_| "[REDACTED]"),
236 )
237 .field("direct_key_name", &self.direct_key_name)
238 .field("recipients", &self.recipients)
239 .field("policy", &self.policy)
240 .field("provider", &self.provider.name())
241 .finish()
242 }
243}
244
245impl EncryptedDataBuilder {
246 pub fn new(algorithm: DataEncryptionAlgorithm) -> Self {
248 Self {
249 algorithm,
250 encrypted_type: EncryptedDataType::Element,
251 id: None,
252 direct_key: None,
253 direct_key_name: None,
254 recipients: Vec::new(),
255 policy: crate::policy::EncryptionPolicy::default(),
256 provider: Arc::new(crate::provider::RustCryptoProvider),
257 xml_backend: crate::XmlBackend::default(),
258 }
259 }
260
261 pub fn policy(mut self, policy: crate::policy::EncryptionPolicy) -> Self {
263 self.policy = policy;
264 self
265 }
266
267 pub fn provider(mut self, provider: Arc<dyn crate::provider::CryptoProvider>) -> Self {
269 self.provider = provider;
270 self
271 }
272
273 pub fn xml_backend(mut self, backend: crate::XmlBackend) -> Self {
275 self.xml_backend = backend;
276 self
277 }
278
279 fn document_parse_settings(&self) -> DocumentParseSettings {
280 DocumentParseSettings::from_policy(&self.policy.xml, &self.policy.resources)
281 .with_backend(self.xml_backend)
282 }
283
284 pub fn encryption_type(mut self, encrypted_type: EncryptedDataType) -> Self {
286 self.encrypted_type = encrypted_type;
287 self
288 }
289
290 pub fn id(mut self, id: impl Into<String>) -> Self {
292 self.id = Some(id.into());
293 self
294 }
295
296 pub fn direct_key(mut self, key: impl Into<Vec<u8>>) -> Self {
298 self.direct_key = Some(DirectEncryptionKey::Raw(zeroize::Zeroizing::new(
299 key.into(),
300 )));
301 self
302 }
303
304 pub fn derived_key(
308 mut self,
309 method: super::KeyDerivationMethod,
310 secret: impl Into<Vec<u8>>,
311 ) -> Self {
312 self.direct_key = Some(DirectEncryptionKey::Derived {
313 method,
314 secret: zeroize::Zeroizing::new(secret.into()),
315 });
316 self
317 }
318
319 pub fn agreement_key(
328 mut self,
329 method: super::KeyDerivationMethod,
330 key: Box<dyn crate::provider::KeyAgreementKey>,
331 algorithm: crate::policy::KeyAgreementAlgorithm,
332 peer_public_key: impl Into<Vec<u8>>,
333 ) -> Self {
334 let key: Arc<dyn crate::provider::KeyAgreementKey> =
335 if algorithm == crate::policy::KeyAgreementAlgorithm::EcdhEs {
336 Arc::new(EphemeralSenderKey {
337 binding: key.provider_binding().cloned(),
338 key: std::sync::Mutex::new(Some(key)),
339 })
340 } else {
341 Arc::from(key)
342 };
343 self.direct_key = Some(DirectEncryptionKey::Agreement {
344 method,
345 key,
346 algorithm,
347 peer_public_key: peer_public_key.into(),
348 });
349 self
350 }
351
352 pub fn direct_key_name(mut self, key_name: impl Into<String>) -> Self {
354 self.direct_key_name = Some(key_name.into());
355 self
356 }
357
358 pub fn add_recipient(mut self, recipient: EncryptionRecipient) -> Self {
360 self.recipients.push(recipient);
361 self
362 }
363
364 pub fn recipient_rsa_oaep(self, public_key: RsaPublicKey) -> Self {
366 self.add_recipient(EncryptionRecipient::rsa_oaep(public_key))
367 }
368
369 pub fn recipient_key_transport(
371 self,
372 public_key: Arc<dyn crate::provider::KeyTransportKey>,
373 ) -> Self {
374 self.add_recipient(EncryptionRecipient::provider_key_transport(public_key))
375 }
376
377 pub fn recipient_aes_kw(self, kek: impl Into<Vec<u8>>, algorithm: KeyWrapAlgorithm) -> Self {
379 self.add_recipient(EncryptionRecipient::aes_key_wrap(kek, algorithm))
380 }
381
382 pub fn encrypt_xml(&self, xml: &str) -> Result<EncryptionResult, XmlEncError> {
384 self.policy.validate()?;
385 self.validate_plaintext_len(xml.len())?;
386 let mut operation = OperationExecutionContext::new(
387 self.policy.clone(),
388 EncryptionOperationBudgets::from_policy(&self.policy),
389 None,
390 );
391 validate_xml_plaintext(
392 xml,
393 &self.encrypted_type,
394 operation.policy(),
395 &operation.budgets().xml_parse,
396 self.xml_backend,
397 )?;
398 let generated = self.encrypt_payload_with_operation(
399 xml.as_bytes(),
400 Some(self.encrypted_type.clone()),
401 &mut operation,
402 false,
403 )?;
404 validate_standalone_encrypted_data_nodes(
405 generated.xml_nodes,
406 operation.policy().resources.effective_xml_nodes() as usize,
407 )?;
408 Ok(generated.result)
409 }
410
411 pub fn encrypt_binary(&self, data: &[u8]) -> Result<EncryptionResult, XmlEncError> {
416 self.policy.validate()?;
417 let mut operation = OperationExecutionContext::new(
418 self.policy.clone(),
419 EncryptionOperationBudgets::from_policy(&self.policy),
420 None,
421 );
422 let encrypted_type = match &self.encrypted_type {
423 EncryptedDataType::Other(uri) => Some(EncryptedDataType::Other(uri.clone())),
424 EncryptedDataType::Element | EncryptedDataType::Content => None,
425 };
426 let generated =
427 self.encrypt_payload_with_operation(data, encrypted_type, &mut operation, false)?;
428 validate_standalone_encrypted_data_nodes(
429 generated.xml_nodes,
430 operation.policy().resources.effective_xml_nodes() as usize,
431 )?;
432 Ok(generated.result)
433 }
434
435 pub fn encrypt_document(
437 &self,
438 xml: &str,
439 options: DocumentEncryptionOptions<'_>,
440 ) -> Result<String, XmlEncError> {
441 self.policy.validate()?;
442 self.validate_document_len(xml.len())?;
443 let budgets = EncryptionOperationBudgets::from_policy(&self.policy);
444 let settings = self.document_parse_settings();
445 let mut document = XmlDocument::parse_with_settings_and_budget(
446 xml.to_owned(),
447 settings,
448 &budgets.xml_parse,
449 )
450 .map_err(|error| map_document_error(error, settings))?;
451 self.encrypt_owned_document_with_budgets(&mut document, options, budgets)?;
452 Ok(document.into_xml())
453 }
454
455 pub fn encrypt_owned_document(
460 &self,
461 document: &mut XmlDocument,
462 options: DocumentEncryptionOptions<'_>,
463 ) -> Result<(), XmlEncError> {
464 self.encrypt_owned_document_with_budgets(
465 document,
466 options,
467 EncryptionOperationBudgets::from_policy(&self.policy),
468 )
469 }
470
471 fn encrypt_owned_document_with_budgets(
472 &self,
473 document: &mut XmlDocument,
474 options: DocumentEncryptionOptions<'_>,
475 budgets: EncryptionOperationBudgets,
476 ) -> Result<(), XmlEncError> {
477 self.policy.validate()?;
478 document.validate_operation_policy(&self.policy.xml, &self.policy.resources)?;
479 let document_nodes = document.with_view(|view| view.node_count());
480 let (target, source, content_boundaries, selected_nodes) = document.with_view(|view| {
481 let selected = select_encryption_target(view.document(), options.element_id)?;
482 let source = &view.xml()[selected.range()];
483 let content_boundaries = match self.encrypted_type {
484 EncryptedDataType::Element => {
485 self.validate_plaintext_len(source.len())?;
486 None
487 }
488 EncryptedDataType::Content => {
489 let boundaries = element_content_boundaries(source)?;
490 self.validate_plaintext_len(boundaries.content.len())?;
491 Some(boundaries)
492 }
493 EncryptedDataType::Other(_) => None,
494 };
495 Ok::<_, XmlEncError>((
496 view.node_identity(selected),
497 source.to_owned(),
498 content_boundaries,
499 selected.descendants().count(),
500 ))
501 })?;
502 let mut operation = OperationExecutionContext::new(
503 self.policy.clone(),
504 budgets,
505 Some((document.identity(), document.generation())),
506 );
507 document
508 .with_view(|view| operation.validate_document_view(view))
509 .map_err(map_encryption_plan_error)?;
510
511 match self.encrypted_type {
512 EncryptedDataType::Element => {
513 let generated = self.encrypt_payload_with_operation(
514 source.as_bytes(),
515 Some(EncryptedDataType::Element),
516 &mut operation,
517 true,
518 )?;
519 let result = generated.result;
520 validate_replacement_document_len(
521 document.as_xml().len(),
522 source.len(),
523 result.encrypted_data_xml.len(),
524 operation.policy().resources.max_xml_document_bytes,
525 )?;
526 validate_replacement_node_counts(
527 document_nodes,
528 selected_nodes,
529 generated.xml_nodes,
530 ReplacementMode::ReplaceElement,
531 operation.policy().resources.effective_xml_nodes() as usize,
532 )?;
533 let settings = self.document_parse_settings();
534 let mutation = require_encryption_mutation(generated.mutation)?;
535 operation.run_document_transition(mutation, document, |document, budgets| {
536 document
537 .replace_element_with_budget(
538 target,
539 &result.encrypted_data_xml,
540 settings,
541 &budgets.xml_parse,
542 )
543 .map_err(|error| map_document_error(error, settings))
544 })?;
545 Ok(())
546 }
547 EncryptedDataType::Content => {
548 let boundaries = content_boundaries.ok_or_else(|| {
549 XmlEncError::InvalidStructure(
550 "content encryption target boundaries are unavailable".into(),
551 )
552 })?;
553 let plaintext = &source[boundaries.content.clone()];
554 let generated = self.encrypt_payload_with_operation(
555 plaintext.as_bytes(),
556 Some(EncryptedDataType::Content),
557 &mut operation,
558 true,
559 )?;
560 let result = generated.result;
561 let (removed, inserted) = if boundaries.self_closing {
562 let slash = source[..boundaries.start_tag_end]
563 .rfind('/')
564 .ok_or_else(|| {
565 XmlEncError::InvalidStructure("self-closing tag has no slash".into())
566 })?;
567 (
568 source.len(),
569 slash
570 .saturating_add(result.encrypted_data_xml.len())
571 .saturating_add(boundaries.qualified_name.len())
572 .saturating_add(4),
573 )
574 } else {
575 (boundaries.content.len(), result.encrypted_data_xml.len())
576 };
577 validate_replacement_document_len(
578 document.as_xml().len(),
579 removed,
580 inserted,
581 operation.policy().resources.max_xml_document_bytes,
582 )?;
583 validate_replacement_node_counts(
584 document_nodes,
585 selected_nodes,
586 generated.xml_nodes,
587 ReplacementMode::ReplaceContent,
588 operation.policy().resources.effective_xml_nodes() as usize,
589 )?;
590 let settings = self.document_parse_settings();
591 let mutation = require_encryption_mutation(generated.mutation)?;
592 operation.run_document_transition(mutation, document, |document, budgets| {
593 document
594 .replace_content_with_budget(
595 target,
596 &result.encrypted_data_xml,
597 settings,
598 &budgets.xml_parse,
599 )
600 .map_err(|error| map_document_error(error, settings))
601 })?;
602 Ok(())
603 }
604 EncryptedDataType::Other(_) => Err(XmlEncError::InvalidEncryptionConfig(
605 "document encryption requires Element or Content Type".into(),
606 )),
607 }
608 }
609
610 fn encrypt_payload_with_operation(
611 &self,
612 plaintext: &[u8],
613 encrypted_type: Option<EncryptedDataType>,
614 operation: &mut OperationExecutionContext<
615 crate::policy::EncryptionPolicy,
616 EncryptionOperationBudgets,
617 >,
618 mutates_document: bool,
619 ) -> Result<GeneratedEncryption, XmlEncError> {
620 validate_plaintext_len(
623 plaintext.len(),
624 operation.policy().resources.max_encryption_plaintext_bytes,
625 )?;
626 self.validate_configuration_with_policy(operation.policy())?;
627 let input_resource = OperationResourceIdentity::external("encryption-input", plaintext);
628 let plan = compile_encryption_plan(
629 operation,
630 self.recipients.len() + usize::from(self.direct_key.is_some()),
631 mutates_document,
632 input_resource.clone(),
633 )?;
634 let derivation_xml = operation.run_with_resource(plan.document, &input_resource, || {
637 match &self.direct_key {
638 Some(DirectEncryptionKey::Derived { method, .. }) => {
639 method.to_xml(&operation.policy().resources).map(Some)
640 }
641 Some(DirectEncryptionKey::Agreement { method, .. }) => {
642 method.to_xml(&operation.policy().resources).map(Some)
643 }
644 _ => Ok(None),
645 }
646 })?;
647
648 let (content_key, encrypted_keys) = operation.run_batch(&plan.keys, || {
649 let content_key = match &self.direct_key {
650 Some(DirectEncryptionKey::Raw(key)) => {
651 validate_content_key(self.algorithm, key)?;
652 OperationContentKey::Borrowed(key)
653 }
654 Some(DirectEncryptionKey::Derived { method, secret }) => {
655 let parameters = method.parameters(self.algorithm.key_len())?;
656 let key = operation
657 .budgets()
658 .key_establishment
659 .borrow_mut()
660 .derive_key(
661 &operation.policy().key_establishment,
662 self.provider.as_ref(),
663 ¶meters,
664 secret,
665 )?;
666 OperationContentKey::Owned(key)
667 }
668 Some(DirectEncryptionKey::Agreement {
669 method,
670 key,
671 algorithm,
672 peer_public_key,
673 }) => {
674 let parameters = method.parameters(self.algorithm.key_len())?;
675 let agreement = crate::provider::KeyAgreementParameters {
676 algorithm: algorithm.uri(),
677 peer_public_key,
678 };
679 let content_key = operation
680 .budgets()
681 .key_establishment
682 .borrow_mut()
683 .agree_and_derive(
684 &operation.policy().key_establishment,
685 self.provider.as_ref(),
686 key.as_ref(),
687 &agreement,
688 ¶meters,
689 )?;
690 OperationContentKey::Owned(content_key)
691 }
692 None => {
693 let key = random_bytes(self.provider.as_ref(), self.algorithm.key_len())?;
694 #[cfg(feature = "legacy-algorithms")]
695 let mut key = key;
696 #[cfg(feature = "legacy-algorithms")]
697 if self.algorithm == DataEncryptionAlgorithm::TripleDesCbc {
698 for octet in &mut key {
702 *octet =
703 (*octet & 0xfe) | (((*octet & 0xfe).count_ones() as u8 & 1) ^ 1);
704 }
705 }
706 OperationContentKey::Owned(zeroize::Zeroizing::new(key))
707 }
708 };
709 let encrypted_keys = self
710 .recipients
711 .iter()
712 .map(|recipient| {
713 wrap_content_key(self.provider.as_ref(), recipient, content_key.as_ref())
714 })
715 .collect::<Result<Vec<_>, _>>()?;
716 Ok::<_, XmlEncError>((content_key, encrypted_keys))
717 })?;
718 let ciphertext = operation.run(plan.crypto, || {
719 encrypt_content(
720 self.provider.as_ref(),
721 self.algorithm,
722 content_key.as_ref(),
723 plaintext,
724 )
725 })?;
726 let (encrypted_data_xml, xml_nodes) = operation.run(plan.evidence, || {
727 let encrypted_data_xml = render_encrypted_data(
728 &EncryptionXml {
729 algorithm: self.algorithm,
730 encrypted_type: encrypted_type.as_ref(),
731 id: self.id.as_deref(),
732 direct_key_name: self.direct_key_name.as_deref(),
733 derivation_xml: derivation_xml.as_deref(),
734 agreement_algorithm: match &self.direct_key {
735 Some(DirectEncryptionKey::Agreement { algorithm, .. }) => Some(*algorithm),
736 _ => None,
737 },
738 encrypted_keys: &encrypted_keys,
739 ciphertext: &ciphertext,
740 },
741 operation.policy().resources.max_xml_document_bytes,
742 )?;
743 validate_document_len(
744 encrypted_data_xml.len(),
745 operation.policy().resources.max_xml_document_bytes,
746 )?;
747 let xml_nodes = count_generated_encrypted_data_nodes(
748 &encrypted_data_xml,
749 operation.policy(),
750 &operation.budgets().xml_parse,
751 self.xml_backend,
752 )?;
753 Ok::<_, XmlEncError>((encrypted_data_xml, xml_nodes))
754 })?;
755 let replacement = match encrypted_type {
756 Some(EncryptedDataType::Content) => ReplacementMode::ReplaceContent,
757 Some(EncryptedDataType::Element | EncryptedDataType::Other(_)) | None => {
758 ReplacementMode::ReplaceElement
759 }
760 };
761 Ok(GeneratedEncryption {
762 result: EncryptionResult {
763 encrypted_data_xml,
764 replacement,
765 },
766 xml_nodes,
767 mutation: plan.mutation,
768 })
769 }
770
771 fn validate_configuration_with_policy(
772 &self,
773 policy: &crate::policy::EncryptionPolicy,
774 ) -> Result<(), XmlEncError> {
775 policy.validate()?;
776 let metadata_limit = policy.resources.max_encryption_metadata_bytes;
777 if let EncryptedDataType::Other(uri) = &self.encrypted_type {
778 validate_metadata("EncryptedData Type", Some(uri), metadata_limit)?;
779 }
780 crate::policy::check_content_algorithm(
781 policy.data_algorithms.as_ref(),
782 self.algorithm,
783 "encryption",
784 )?;
785 if self.recipients.len() > policy.resources.max_encryption_recipients {
786 return Err(crate::policy::PolicyViolation::ResourceLimit {
787 resource: crate::policy::resource_name::ENCRYPTION_RECIPIENTS,
788 maximum: policy.resources.max_encryption_recipients,
789 actual: self.recipients.len(),
790 }
791 .into());
792 }
793 let key_candidates = self.recipients.len() + usize::from(self.direct_key.is_some());
794 policy.resources.validate_key_candidates(key_candidates)?;
795 validate_metadata("EncryptedData Id", self.id.as_deref(), metadata_limit)?;
796 if self.id.as_deref().is_some_and(|id| !is_xml_ncname(id)) {
797 return Err(XmlEncError::InvalidEncryptionConfig(
798 "EncryptedData Id must be an XML NCName".into(),
799 ));
800 }
801 validate_key_name(
802 "direct KeyName",
803 self.direct_key_name.as_deref(),
804 metadata_limit,
805 )?;
806 for recipient in &self.recipients {
807 match recipient {
808 #[cfg(feature = "legacy-algorithms")]
809 EncryptionRecipient::RsaPkcs1v15 {
810 public_key,
811 recipient,
812 key_name,
813 } => {
814 let algorithm = super::KeyTransportAlgorithm::RsaPkcs1v15;
815 if !policy
816 .key_transport_algorithms
817 .as_ref()
818 .is_some_and(|allowed| allowed.contains(&algorithm))
819 {
820 return Err(crate::policy::PolicyViolation::Algorithm {
821 operation: "encryption",
822 algorithm: algorithm.uri().into(),
823 }
824 .into());
825 }
826 validate_key_transport_recipient(public_key.as_ref(), policy)?;
827 validate_metadata(
828 "EncryptedKey Recipient",
829 recipient.as_deref(),
830 metadata_limit,
831 )?;
832 validate_key_name("EncryptedKey KeyName", key_name.as_deref(), metadata_limit)?;
833 }
834 EncryptionRecipient::RsaOaep {
835 public_key,
836 parameters,
837 recipient,
838 key_name,
839 } => {
840 if parameters.algorithm.requires_explicit_permission() {
844 return Err(XmlEncError::InvalidEncryptionConfig(
845 "RSA-OAEP recipient requires an OAEP algorithm".into(),
846 ));
847 }
848 validate_key_transport_recipient(public_key.as_ref(), policy)?;
849 if policy
850 .key_transport_algorithms
851 .as_ref()
852 .is_some_and(|allowed| !allowed.contains(¶meters.algorithm))
853 {
854 return Err(crate::policy::PolicyViolation::Algorithm {
855 operation: "encryption",
856 algorithm: parameters.algorithm.uri().to_string(),
857 }
858 .into());
859 }
860 if let Some(allowed) = &policy.oaep_digests {
861 let rejected_uri = if !allowed.contains(¶meters.digest) {
862 Some(parameters.digest.uri())
863 } else if !allowed.contains(¶meters.mgf_digest) {
864 Some(parameters.mgf_digest.mgf_uri())
865 } else {
866 None
867 };
868 if let Some(algorithm) = rejected_uri {
869 return Err(crate::policy::PolicyViolation::Algorithm {
870 operation: "encryption",
871 algorithm: algorithm.to_owned(),
872 }
873 .into());
874 }
875 }
876 validate_metadata(
877 "EncryptedKey Recipient",
878 recipient.as_deref(),
879 metadata_limit,
880 )?;
881 validate_key_name("EncryptedKey KeyName", key_name.as_deref(), metadata_limit)?;
882 validate_metadata_len(parameters.label.len(), metadata_limit)?;
883 }
884 EncryptionRecipient::AesKeyWrap {
885 kek,
886 algorithm,
887 recipient,
888 key_name,
889 } => {
890 if policy
891 .key_wrap_algorithms
892 .as_ref()
893 .map_or(algorithm.requires_explicit_permission(), |allowed| {
894 !allowed.contains(algorithm)
895 })
896 {
897 return Err(crate::policy::PolicyViolation::Algorithm {
898 operation: "encryption",
899 algorithm: algorithm.uri().to_string(),
900 }
901 .into());
902 }
903 if kek.len() != algorithm.key_len() {
904 return Err(XmlEncError::InvalidEncryptionConfig(format!(
905 "{} requires a {}-byte key-encryption key, got {} bytes",
906 algorithm.uri(),
907 algorithm.key_len(),
908 kek.len()
909 )));
910 }
911 validate_metadata(
912 "EncryptedKey Recipient",
913 recipient.as_deref(),
914 metadata_limit,
915 )?;
916 validate_key_name("EncryptedKey KeyName", key_name.as_deref(), metadata_limit)?;
917 }
918 }
919 }
920 match (self.direct_key.is_some(), self.recipients.is_empty()) {
921 (false, true) => Err(XmlEncError::InvalidEncryptionConfig(
922 "configure a direct content key or at least one wrapped recipient".into(),
923 )),
924 (true, false) => Err(XmlEncError::InvalidEncryptionConfig(
925 "a direct content key cannot be combined with wrapped recipients".into(),
926 )),
927 _ if self.direct_key_name.is_some() && self.direct_key.is_none() => {
928 Err(XmlEncError::InvalidEncryptionConfig(
929 "direct KeyName requires a direct content key".into(),
930 ))
931 }
932 _ => Ok(()),
933 }
934 }
935
936 fn validate_plaintext_len(&self, actual: usize) -> Result<(), XmlEncError> {
937 validate_plaintext_len(actual, self.policy.resources.max_encryption_plaintext_bytes)
938 }
939
940 fn validate_document_len(&self, actual: usize) -> Result<(), XmlEncError> {
941 validate_document_len(actual, self.policy.resources.max_xml_document_bytes)
942 }
943}
944
945fn validate_metadata(
946 field: &'static str,
947 value: Option<&str>,
948 maximum: usize,
949) -> Result<(), XmlEncError> {
950 if value.is_some_and(|value| !value.chars().all(is_xml_1_0_character)) {
951 return Err(XmlEncError::InvalidEncryptionConfig(format!(
952 "{field} contains a character forbidden by XML 1.0"
953 )));
954 }
955 validate_metadata_len(value.map_or(0, str::len), maximum)
956}
957
958fn validate_key_name(
959 field: &'static str,
960 value: Option<&str>,
961 maximum: usize,
962) -> Result<(), XmlEncError> {
963 if value.is_some_and(str::is_empty) {
964 return Err(XmlEncError::InvalidEncryptionConfig(format!(
965 "{field} must not be empty"
966 )));
967 }
968 validate_metadata(field, value, maximum)
969}
970
971fn validate_metadata_len(actual: usize, maximum: usize) -> Result<(), XmlEncError> {
972 if actual <= maximum {
973 Ok(())
974 } else {
975 Err(crate::policy::PolicyViolation::ResourceLimit {
976 resource: crate::policy::resource_name::ENCRYPTION_METADATA_BYTES,
977 maximum,
978 actual,
979 }
980 .into())
981 }
982}
983
984#[derive(Debug)]
985struct WrappedKey {
986 algorithm_uri: &'static str,
987 oaep: Option<RsaOaepParameters>,
988 recipient: Option<String>,
989 key_name: Option<String>,
990 ciphertext: Vec<u8>,
991}
992
993#[derive(Debug)]
994struct ContentBoundaries {
995 content: std::ops::Range<usize>,
996 self_closing: bool,
997 qualified_name: String,
998 start_tag_end: usize,
999}
1000
1001fn validate_plaintext_len(actual: usize, maximum: usize) -> Result<(), XmlEncError> {
1002 if actual <= maximum {
1003 Ok(())
1004 } else {
1005 Err(crate::policy::PolicyViolation::ResourceLimit {
1006 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
1007 maximum,
1008 actual,
1009 }
1010 .into())
1011 }
1012}
1013
1014fn validate_document_len(actual: usize, maximum: usize) -> Result<(), XmlEncError> {
1015 if actual > maximum {
1016 return Err(crate::policy::PolicyViolation::ResourceLimit {
1017 resource: crate::policy::resource_name::XML_DOCUMENT,
1018 maximum,
1019 actual,
1020 }
1021 .into());
1022 }
1023 Ok(())
1024}
1025
1026fn validate_replacement_document_len(
1027 document_len: usize,
1028 removed_len: usize,
1029 inserted_len: usize,
1030 maximum: usize,
1031) -> Result<(), XmlEncError> {
1032 let actual = document_len
1033 .saturating_sub(removed_len)
1034 .saturating_add(inserted_len);
1035 validate_document_len(actual, maximum)
1036}
1037
1038fn validate_replacement_node_counts(
1039 document_nodes: usize,
1040 selected_nodes: usize,
1041 inserted_nodes: usize,
1042 replacement: ReplacementMode,
1043 maximum: usize,
1044) -> Result<(), XmlEncError> {
1045 let removed_nodes = match replacement {
1046 ReplacementMode::ReplaceElement => selected_nodes,
1047 ReplacementMode::ReplaceContent => selected_nodes.saturating_sub(1),
1048 };
1049 let actual = document_nodes
1050 .saturating_sub(removed_nodes)
1051 .saturating_add(inserted_nodes);
1052 if actual > maximum {
1053 return Err(crate::policy::PolicyViolation::ResourceLimit {
1054 resource: crate::policy::resource_name::XML_NODES,
1055 maximum,
1056 actual,
1057 }
1058 .into());
1059 }
1060 Ok(())
1061}
1062
1063fn count_generated_encrypted_data_nodes(
1064 encrypted_data_xml: &str,
1065 policy: &crate::policy::EncryptionPolicy,
1066 parse_budget: &XmlParseWorkBudget,
1067 backend: crate::XmlBackend,
1068) -> Result<usize, XmlEncError> {
1069 let maximum_nodes = policy.resources.effective_xml_nodes();
1070 let settings = DocumentParseSettings {
1074 nodes_limit: maximum_nodes.saturating_add(1),
1075 ..DocumentParseSettings::from_policy(&policy.xml, &policy.resources).with_backend(backend)
1076 };
1077 let generated =
1078 parse_borrowed_with_settings_and_budget(encrypted_data_xml, settings, Some(parse_budget))
1079 .map_err(|error| match error {
1080 XmlDocumentError::Parse(crate::xml::dom::ParseError::NodesLimitReached) => {
1081 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
1082 resource: crate::policy::resource_name::XML_NODES,
1083 maximum: maximum_nodes as usize,
1084 actual: maximum_nodes as usize + 1,
1085 })
1086 }
1087 error => map_document_error(error, settings),
1088 })?;
1089 Ok(generated.root_element().descendants().count())
1090}
1091
1092fn validate_standalone_encrypted_data_nodes(
1093 encrypted_data_subtree_nodes: usize,
1094 maximum: usize,
1095) -> Result<(), XmlEncError> {
1096 let actual = encrypted_data_subtree_nodes.saturating_add(1);
1097 if actual > maximum {
1098 return Err(crate::policy::PolicyViolation::ResourceLimit {
1099 resource: crate::policy::resource_name::XML_NODES,
1100 maximum,
1101 actual,
1102 }
1103 .into());
1104 }
1105 Ok(())
1106}
1107
1108fn validate_content_key(algorithm: DataEncryptionAlgorithm, key: &[u8]) -> Result<(), XmlEncError> {
1109 if key.len() == algorithm.key_len() {
1110 Ok(())
1111 } else {
1112 Err(XmlEncError::InvalidKeySize {
1113 algorithm,
1114 expected: algorithm.key_len(),
1115 actual: key.len(),
1116 })
1117 }
1118}
1119
1120fn random_bytes(
1121 provider: &dyn crate::provider::CryptoProvider,
1122 len: usize,
1123) -> Result<Vec<u8>, XmlEncError> {
1124 provider.require_capability(crate::provider::ProviderCapability::Random)?;
1125 let mut bytes = vec![0_u8; len];
1126 provider.fill_random(&mut bytes)?;
1127 Ok(bytes)
1128}
1129
1130fn encrypt_content(
1131 provider: &dyn crate::provider::CryptoProvider,
1132 algorithm: DataEncryptionAlgorithm,
1133 key: &[u8],
1134 plaintext: &[u8],
1135) -> Result<Vec<u8>, XmlEncError> {
1136 provider.require_capability(crate::provider::ProviderCapability::Encrypt(algorithm))?;
1137 let ciphertext = provider.encrypt_data(algorithm, key, plaintext)?;
1138 super::types::validate_ciphertext_framing(algorithm, ciphertext.len())?;
1139 let expected = algorithm
1140 .ciphertext_len_for_plaintext(plaintext.len())
1141 .ok_or(crate::policy::PolicyViolation::ResourceLimit {
1142 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
1143 maximum: crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING,
1144 actual: plaintext.len(),
1145 })?;
1146 if ciphertext.len() != expected {
1147 return Err(crate::provider::ProviderError::InvalidOutputSize {
1148 operation: crate::provider::ProviderOperation::Encrypt,
1149 expected,
1150 actual: ciphertext.len(),
1151 }
1152 .into());
1153 }
1154 Ok(ciphertext)
1155}
1156
1157fn wrap_content_key(
1158 provider: &dyn crate::provider::CryptoProvider,
1159 recipient: &EncryptionRecipient,
1160 content_key: &[u8],
1161) -> Result<WrappedKey, XmlEncError> {
1162 match recipient {
1163 #[cfg(feature = "legacy-algorithms")]
1164 EncryptionRecipient::RsaPkcs1v15 {
1165 public_key,
1166 recipient,
1167 key_name,
1168 } => {
1169 provider.require_capability(crate::provider::ProviderCapability::Pkcs1v15Transport)?;
1170 let ciphertext = provider.transport_pkcs1v15(public_key.as_ref(), content_key)?;
1171 let expected = public_key.rsa_modulus().len();
1172 if ciphertext.len() != expected {
1173 return Err(XmlEncError::InvalidWrappedKeyLength {
1174 expected,
1175 actual: ciphertext.len(),
1176 });
1177 }
1178 Ok(WrappedKey {
1179 algorithm_uri: super::KeyTransportAlgorithm::RsaPkcs1v15.uri(),
1180 oaep: None,
1181 recipient: recipient.clone(),
1182 key_name: key_name.clone(),
1183 ciphertext,
1184 })
1185 }
1186 EncryptionRecipient::RsaOaep {
1187 public_key,
1188 parameters,
1189 recipient,
1190 key_name,
1191 } => Ok(WrappedKey {
1192 algorithm_uri: parameters.algorithm.uri(),
1193 oaep: Some(parameters.clone()),
1194 recipient: recipient.clone(),
1195 key_name: key_name.clone(),
1196 ciphertext: wrap_rsa_oaep(provider, public_key.as_ref(), parameters, content_key)?,
1197 }),
1198 EncryptionRecipient::AesKeyWrap {
1199 kek,
1200 algorithm,
1201 recipient,
1202 key_name,
1203 } => {
1204 provider
1205 .require_capability(crate::provider::ProviderCapability::KeyWrap(*algorithm))?;
1206 let wrapped = provider.wrap_key(*algorithm, kek, content_key)?;
1207 let expected = content_key.len() + algorithm.overhead();
1208 if wrapped.len() != expected {
1209 return Err(XmlEncError::InvalidWrappedKeyLength {
1210 expected,
1211 actual: wrapped.len(),
1212 });
1213 }
1214 Ok(WrappedKey {
1215 algorithm_uri: algorithm.uri(),
1216 oaep: None,
1217 recipient: recipient.clone(),
1218 key_name: key_name.clone(),
1219 ciphertext: wrapped,
1220 })
1221 }
1222 }
1223}
1224
1225fn wrap_rsa_oaep(
1226 provider: &dyn crate::provider::CryptoProvider,
1227 public_key: &dyn crate::provider::KeyTransportKey,
1228 parameters: &RsaOaepParameters,
1229 content_key: &[u8],
1230) -> Result<Vec<u8>, XmlEncError> {
1231 provider.require_capability(crate::provider::ProviderCapability::KeyTransport(
1232 parameters,
1233 ))?;
1234 let ciphertext = provider
1235 .transport_key(public_key, parameters, content_key)
1236 .map_err(|error| match error {
1237 crate::provider::ProviderError::Random(message) => XmlEncError::Rng(message),
1238 crate::provider::ProviderError::InvalidInput(reason) => {
1239 XmlEncError::InvalidEncryptionConfig(reason.to_string())
1240 }
1241 error => XmlEncError::RsaEncrypt(error.to_string()),
1242 })?;
1243 let expected = public_key.rsa_modulus().len();
1244 if ciphertext.len() != expected {
1245 return Err(XmlEncError::InvalidWrappedKeyLength {
1246 expected,
1247 actual: ciphertext.len(),
1248 });
1249 }
1250 Ok(ciphertext)
1251}
1252
1253struct EncryptionXml<'a> {
1254 algorithm: DataEncryptionAlgorithm,
1255 encrypted_type: Option<&'a EncryptedDataType>,
1256 id: Option<&'a str>,
1257 direct_key_name: Option<&'a str>,
1258 derivation_xml: Option<&'a str>,
1259 agreement_algorithm: Option<crate::policy::KeyAgreementAlgorithm>,
1260 encrypted_keys: &'a [WrappedKey],
1261 ciphertext: &'a [u8],
1262}
1263
1264fn render_encrypted_data(
1265 output: &EncryptionXml<'_>,
1266 maximum: usize,
1267) -> Result<String, XmlEncError> {
1268 let mut counter = Writer::new(EncryptionXmlSize { length: 0 });
1269 write_encrypted_data(&mut counter, output)?;
1270 let length = counter.into_inner().length;
1271 validate_document_len(length, maximum)?;
1272 let mut writer = Writer::new(Vec::with_capacity(length));
1273 write_encrypted_data(&mut writer, output)?;
1274 String::from_utf8(writer.into_inner())
1275 .map_err(|error| XmlEncError::XmlSerialize(error.to_string()))
1276}
1277
1278struct EncryptionXmlSize {
1279 length: usize,
1280}
1281
1282impl Write for EncryptionXmlSize {
1283 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
1284 self.length = self
1285 .length
1286 .checked_add(bytes.len())
1287 .ok_or_else(|| std::io::Error::other("encrypted XML size overflow"))?;
1288 Ok(bytes.len())
1289 }
1290
1291 fn flush(&mut self) -> std::io::Result<()> {
1292 Ok(())
1293 }
1294}
1295
1296fn write_encrypted_data<W: Write>(
1297 writer: &mut Writer<W>,
1298 output: &EncryptionXml<'_>,
1299) -> Result<(), XmlEncError> {
1300 let EncryptionXml {
1301 algorithm,
1302 encrypted_type,
1303 id,
1304 direct_key_name,
1305 derivation_xml,
1306 agreement_algorithm,
1307 encrypted_keys,
1308 ciphertext,
1309 } = *output;
1310 let encrypted_type_uri = encrypted_type.map(|value| match value {
1311 EncryptedDataType::Element => "http://www.w3.org/2001/04/xmlenc#Element",
1312 EncryptedDataType::Content => "http://www.w3.org/2001/04/xmlenc#Content",
1313 EncryptedDataType::Other(uri) => uri.as_str(),
1314 });
1315 let root_attributes = [
1316 ("xmlns:xenc", XMLENC_NS),
1317 ("xmlns:xenc11", XMLENC11_NS),
1318 ("xmlns:ds", XMLDSIG_NS),
1319 ]
1320 .into_iter()
1321 .chain(id.map(|value| ("Id", value)))
1322 .chain(encrypted_type_uri.map(|value| ("Type", value)));
1323 write_start(writer, "xenc:EncryptedData", root_attributes)?;
1324 write_empty_with_algorithm(writer, "xenc:EncryptionMethod", algorithm.uri())?;
1325
1326 if direct_key_name.is_some() || !encrypted_keys.is_empty() || derivation_xml.is_some() {
1327 write_start(writer, "ds:KeyInfo", [])?;
1328 if let Some(key_name) = direct_key_name {
1329 write_text_element(writer, "ds:KeyName", key_name)?;
1330 }
1331 if let Some(derivation_xml) = derivation_xml {
1332 if let Some(algorithm) = agreement_algorithm {
1333 write_start(
1338 writer,
1339 "xenc:AgreementMethod",
1340 [("Algorithm", algorithm.uri())],
1341 )?;
1342 } else {
1343 write_start(writer, "xenc11:DerivedKey", [])?;
1344 }
1345 writer
1346 .raw(derivation_xml)
1347 .map_err(|error| XmlEncError::XmlSerialize(error.to_string()))?;
1348 if let Some(name) = direct_key_name
1349 && agreement_algorithm.is_none()
1350 {
1351 write_text_element(writer, "xenc11:DerivedKeyName", name)?;
1352 }
1353 write_end(
1354 writer,
1355 if agreement_algorithm.is_some() {
1356 "xenc:AgreementMethod"
1357 } else {
1358 "xenc11:DerivedKey"
1359 },
1360 )?;
1361 }
1362 for encrypted_key in encrypted_keys {
1363 write_encrypted_key(writer, encrypted_key)?;
1364 }
1365 write_end(writer, "ds:KeyInfo")?;
1366 }
1367
1368 write_cipher_data(writer, ciphertext)?;
1369 write_end(writer, "xenc:EncryptedData")
1370}
1371
1372fn write_encrypted_key<W: Write>(
1373 writer: &mut Writer<W>,
1374 encrypted_key: &WrappedKey,
1375) -> Result<(), XmlEncError> {
1376 write_start(
1377 writer,
1378 "xenc:EncryptedKey",
1379 encrypted_key
1380 .recipient
1381 .as_deref()
1382 .map(|recipient| ("Recipient", recipient)),
1383 )?;
1384
1385 if let Some(parameters) = encrypted_key.oaep.as_ref() {
1386 write_start(
1387 writer,
1388 "xenc:EncryptionMethod",
1389 [("Algorithm", encrypted_key.algorithm_uri)],
1390 )?;
1391 if !parameters.label.is_empty() {
1392 write_text_element(
1393 writer,
1394 "xenc:OAEPparams",
1395 &STANDARD.encode(¶meters.label),
1396 )?;
1397 }
1398 write_empty_with_algorithm(writer, "ds:DigestMethod", parameters.digest.uri())?;
1399 if parameters.algorithm == super::KeyTransportAlgorithm::RsaOaep11
1400 || parameters.mgf_digest != super::OaepDigestAlgorithm::Sha1
1401 {
1402 write_empty_with_algorithm(writer, "xenc11:MGF", parameters.mgf_digest.mgf_uri())?;
1403 }
1404 write_end(writer, "xenc:EncryptionMethod")?;
1405 } else {
1406 write_empty_with_algorithm(writer, "xenc:EncryptionMethod", encrypted_key.algorithm_uri)?;
1407 }
1408
1409 if let Some(key_name) = encrypted_key.key_name.as_deref() {
1410 write_start(writer, "ds:KeyInfo", [])?;
1411 write_text_element(writer, "ds:KeyName", key_name)?;
1412 write_end(writer, "ds:KeyInfo")?;
1413 }
1414 write_cipher_data(writer, &encrypted_key.ciphertext)?;
1415 write_end(writer, "xenc:EncryptedKey")
1416}
1417
1418fn write_cipher_data<W: Write>(writer: &mut Writer<W>, value: &[u8]) -> Result<(), XmlEncError> {
1419 write_start(writer, "xenc:CipherData", [])?;
1420 write_start(writer, "xenc:CipherValue", [])?;
1421 let mut encoded = [0_u8; 4096];
1425 for chunk in value.chunks(3072) {
1426 let length = STANDARD
1427 .encode_slice(chunk, &mut encoded)
1428 .map_err(|error| XmlEncError::XmlSerialize(error.to_string()))?;
1429 let text = std::str::from_utf8(&encoded[..length])
1430 .map_err(|error| XmlEncError::XmlSerialize(error.to_string()))?;
1431 writer
1432 .raw(text)
1433 .map_err(|error| XmlEncError::XmlSerialize(error.to_string()))?;
1434 }
1435 write_end(writer, "xenc:CipherValue")?;
1436 write_end(writer, "xenc:CipherData")
1437}
1438
1439fn write_empty_with_algorithm<W: Write>(
1440 writer: &mut Writer<W>,
1441 name: &str,
1442 algorithm: &str,
1443) -> Result<(), XmlEncError> {
1444 writer
1445 .empty(name, [("Algorithm", algorithm)])
1446 .map_err(|error| XmlEncError::XmlSerialize(error.to_string()))
1447}
1448
1449fn write_text_element<W: Write>(
1450 writer: &mut Writer<W>,
1451 name: &str,
1452 text: &str,
1453) -> Result<(), XmlEncError> {
1454 write_start(writer, name, [])?;
1455 writer
1456 .text(text)
1457 .map_err(|error| XmlEncError::XmlSerialize(error.to_string()))?;
1458 write_end(writer, name)
1459}
1460
1461fn write_start<'a, W: Write>(
1462 writer: &mut Writer<W>,
1463 name: &str,
1464 attributes: impl IntoIterator<Item = (&'a str, &'a str)>,
1465) -> Result<(), XmlEncError> {
1466 writer
1467 .start(name, attributes)
1468 .map_err(|error| XmlEncError::XmlSerialize(error.to_string()))
1469}
1470
1471fn write_end<W: Write>(writer: &mut Writer<W>, name: &str) -> Result<(), XmlEncError> {
1472 writer
1473 .end(name)
1474 .map_err(|error| XmlEncError::XmlSerialize(error.to_string()))
1475}
1476
1477fn validate_xml_plaintext(
1478 xml: &str,
1479 encrypted_type: &EncryptedDataType,
1480 policy: &crate::policy::EncryptionPolicy,
1481 parse_budget: &XmlParseWorkBudget,
1482 backend: crate::XmlBackend,
1483) -> Result<(), XmlEncError> {
1484 let settings =
1485 DocumentParseSettings::from_policy(&policy.xml, &policy.resources).with_backend(backend);
1486 match encrypted_type {
1487 EncryptedDataType::Element => {
1488 let document =
1489 parse_borrowed_with_settings_and_budget(xml, settings, Some(parse_budget))
1490 .map_err(|error| map_document_error(error, settings))?;
1491 if !has_single_element_with_boundary_trivia(document.root()) {
1492 return Err(XmlEncError::InvalidStructure(
1493 "Element plaintext must contain exactly one element".into(),
1494 ));
1495 }
1496 Ok(())
1497 }
1498 EncryptedDataType::Content => {
1499 const WRAPPER_START: &str = "<xmlsec-content>";
1500 const WRAPPER_END: &str = "</xmlsec-content>";
1501
1502 policy.resources.validate_xml_document_len(xml.len())?;
1503 let wrapped = format!("{WRAPPER_START}{xml}{WRAPPER_END}");
1504 let wrapper_bytes = WRAPPER_START.len() + WRAPPER_END.len();
1505 let wrapped_settings = DocumentParseSettings {
1508 nodes_limit: settings.nodes_limit.saturating_add(1),
1509 depth_limit: settings.depth_limit.saturating_add(1),
1510 max_bytes: settings.max_bytes.saturating_add(wrapper_bytes),
1511 ..settings
1512 };
1513 parse_borrowed_with_settings_and_budget(&wrapped, wrapped_settings, Some(parse_budget))
1514 .map_err(|error| match error {
1515 XmlDocumentError::DocumentTooLarge { actual, .. } => {
1516 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
1517 resource: crate::policy::resource_name::XML_DOCUMENT,
1518 maximum: settings.max_bytes,
1519 actual: actual.saturating_sub(wrapper_bytes),
1520 })
1521 }
1522 XmlDocumentError::Parse(crate::xml::dom::ParseError::NodesLimitReached) => {
1523 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
1524 resource: crate::policy::resource_name::XML_NODES,
1525 maximum: settings.nodes_limit as usize,
1526 actual: settings.nodes_limit as usize + 1,
1527 })
1528 }
1529 XmlDocumentError::DocumentTooDeep { actual, .. } => {
1530 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
1531 resource: crate::policy::resource_name::XML_DEPTH,
1532 maximum: settings.depth_limit,
1533 actual: actual.saturating_sub(1),
1534 })
1535 }
1536 error => map_document_error(error, wrapped_settings),
1537 })?;
1538 Ok(())
1539 }
1540 EncryptedDataType::Other(_) => Err(XmlEncError::InvalidEncryptionConfig(
1541 "encrypt_xml requires Element or Content Type".into(),
1542 )),
1543 }
1544}
1545
1546fn select_encryption_target<'a, 'input>(
1547 document: &'a Document<'input>,
1548 id: Option<&str>,
1549) -> Result<Node<'a, 'input>, XmlEncError> {
1550 let Some(id) = id else {
1551 return Ok(document.root_element());
1552 };
1553 let mut matches = document.descendants().filter(|node| {
1554 node.is_element()
1555 && ["Id", "ID", "id"]
1556 .iter()
1557 .any(|name| node.attribute(*name) == Some(id))
1558 });
1559 let selected = matches
1560 .next()
1561 .ok_or(XmlEncError::EncryptionTargetNotFound)?;
1562 if matches.next().is_some() {
1563 return Err(XmlEncError::AmbiguousEncryptionTarget);
1564 }
1565 Ok(selected)
1566}
1567
1568fn element_content_boundaries(source: &str) -> Result<ContentBoundaries, XmlEncError> {
1569 let tag_end = find_start_tag_end(source)?;
1570 let before_end = source[..tag_end].trim_end_matches(XML_WHITESPACE);
1571 let self_closing = before_end.ends_with('/');
1572 let name_end = source[1..]
1573 .find(|character: char| character.is_ascii_whitespace() || matches!(character, '/' | '>'))
1574 .map(|index| index + 1)
1575 .ok_or_else(|| XmlEncError::InvalidStructure("source element has no name".into()))?;
1576 let qualified_name = source[1..name_end].to_owned();
1577 if self_closing {
1578 return Ok(ContentBoundaries {
1579 content: tag_end..tag_end,
1580 self_closing: true,
1581 qualified_name,
1582 start_tag_end: tag_end,
1583 });
1584 }
1585 let closing_start = source
1588 .rfind("</")
1589 .ok_or_else(|| XmlEncError::InvalidStructure("source element has no closing tag".into()))?;
1590 Ok(ContentBoundaries {
1591 content: tag_end + 1..closing_start,
1592 self_closing: false,
1593 qualified_name,
1594 start_tag_end: tag_end,
1595 })
1596}
1597
1598fn find_start_tag_end(source: &str) -> Result<usize, XmlEncError> {
1599 let mut quote = None;
1600 for (index, character) in source.char_indices() {
1601 match (quote, character) {
1602 (Some(expected), actual) if expected == actual => quote = None,
1603 (None, '\'' | '"') => quote = Some(character),
1604 (None, '>') => return Ok(index),
1605 _ => {}
1606 }
1607 }
1608 Err(XmlEncError::InvalidStructure(
1609 "source element start tag is unterminated".into(),
1610 ))
1611}
1612
1613#[cfg(test)]
1614mod tests {
1615 use std::sync::atomic::{AtomicUsize, Ordering};
1616
1617 use std::sync::Arc;
1618
1619 use crate::rsa_encoding::RsaPublicKeyEncoding as _;
1620 use getrandom::SysRng;
1621 use getrandom::rand_core::UnwrapErr;
1622 use rsa::{RsaPrivateKey, RsaPublicKey};
1623
1624 use super::*;
1625 use crate::hard_limits::{
1626 ENCRYPTION_METADATA_BYTE_CEILING as MAX_ENCRYPTION_METADATA_LEN,
1627 ENCRYPTION_PLAINTEXT_BYTE_CEILING as MAX_ENCRYPTION_PLAINTEXT_LEN,
1628 ENCRYPTION_RECIPIENT_CEILING as MAX_ENCRYPTION_RECIPIENTS,
1629 XML_DOCUMENT_BYTE_CEILING as MAX_ENCRYPTION_DOCUMENT_LEN,
1630 };
1631 use crate::xmlenc::{
1632 KekDecryptor, OaepDigestAlgorithm, PrivateKeyDecryptor, SymmetricKeyDecryptor, decrypt,
1633 decrypt_document, parse_encrypted_data,
1634 };
1635
1636 #[test]
1637 fn streamed_ciphertext_preserves_chunk_boundaries_and_padding() {
1638 for length in [0, 1, 2, 3, 3071, 3072, 3073, 3074, 6144, 6145] {
1641 let bytes: Vec<_> = (0..length).map(|index| index as u8).collect();
1642 let mut writer = Writer::new(Vec::new());
1643 writer
1644 .start("root", [("xmlns:xenc", XMLENC_NS)])
1645 .expect("bound namespace");
1646 write_cipher_data(&mut writer, &bytes).expect("streamed ciphertext");
1647 writer.end("root").expect("close wrapper");
1648 let output = String::from_utf8(writer.into_inner()).expect("base64 XML is UTF-8");
1649 assert_eq!(
1650 output,
1651 format!(
1652 "<root xmlns:xenc=\"{XMLENC_NS}\"><xenc:CipherData><xenc:CipherValue>{}</xenc:CipherValue></xenc:CipherData></root>",
1653 STANDARD.encode(bytes),
1654 )
1655 );
1656 }
1657 }
1658
1659 #[test]
1660 fn generated_xml_size_counts_escaping_and_checks_exact_boundary() {
1661 let render = |maximum| {
1664 render_encrypted_data(
1665 &EncryptionXml {
1666 algorithm: DataEncryptionAlgorithm::Aes128Gcm,
1667 encrypted_type: Some(&EncryptedDataType::Element),
1668 id: Some("id&value"),
1669 direct_key_name: Some("key<&\r"),
1670 derivation_xml: None,
1671 agreement_algorithm: None,
1672 encrypted_keys: &[],
1673 ciphertext: &[7; 28],
1674 },
1675 maximum,
1676 )
1677 };
1678 let output = render(MAX_ENCRYPTION_DOCUMENT_LEN).expect("generous output ceiling");
1679 assert_eq!(render(output.len()).expect("exact output ceiling"), output);
1680 assert!(matches!(render(output.len() - 1),
1681 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
1682 resource: crate::policy::resource_name::XML_DOCUMENT,
1683 maximum,
1684 actual,
1685 })) if maximum == output.len() - 1 && actual == output.len()
1686 ));
1687 let document = Document::parse(&output).expect("serialized XML");
1688 assert_eq!(document.root_element().attribute("Id"), Some("id&value"));
1689 let name = document
1690 .descendants()
1691 .find(|node| node.has_tag_name((XMLDSIG_NS, "KeyName")))
1692 .expect("serialized KeyName");
1693 assert_eq!(name.text(), Some("key<&\r"));
1694 }
1695
1696 #[test]
1697 fn document_encryption_requires_a_compiled_mutation_node() {
1698 assert!(matches!(
1699 require_encryption_mutation(None),
1700 Err(XmlEncError::OperationPlan(message))
1701 if message == "encryption mutation node is unavailable"
1702 ));
1703 }
1704
1705 #[derive(Debug)]
1706 struct OverridingOutputProvider {
1707 ciphertext: Option<Vec<u8>>,
1708 wrapped_key: Option<Vec<u8>>,
1709 transported_key: Option<Vec<u8>>,
1710 transport_calls: AtomicUsize,
1711 }
1712
1713 struct OpaqueTransportKey {
1714 modulus: Vec<u8>,
1715 exponent: Vec<u8>,
1716 }
1717
1718 impl crate::provider::KeyTransportKey for OpaqueTransportKey {
1719 fn rsa_modulus(&self) -> std::borrow::Cow<'_, [u8]> {
1720 std::borrow::Cow::Borrowed(&self.modulus)
1721 }
1722
1723 fn rsa_exponent(&self) -> std::borrow::Cow<'_, [u8]> {
1724 std::borrow::Cow::Borrowed(&self.exponent)
1725 }
1726
1727 fn transport_with_provider(
1728 &self,
1729 _provider: &dyn crate::provider::CryptoProvider,
1730 _parameters: &RsaOaepParameters,
1731 _plaintext: &[u8],
1732 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1733 panic!("custom provider must own transport for its opaque key")
1734 }
1735 }
1736
1737 impl crate::provider::CryptoProvider for OverridingOutputProvider {
1738 fn name(&self) -> &'static str {
1739 "overriding-output-test"
1740 }
1741
1742 fn supports(&self, capability: crate::provider::ProviderCapability<'_>) -> bool {
1743 crate::provider::CryptoProvider::supports(
1744 &crate::provider::RustCryptoProvider,
1745 capability,
1746 )
1747 }
1748
1749 fn fill_random(&self, output: &mut [u8]) -> Result<(), crate::provider::ProviderError> {
1750 crate::provider::CryptoProvider::fill_random(
1751 &crate::provider::RustCryptoProvider,
1752 output,
1753 )
1754 }
1755
1756 fn derive_key(
1757 &self,
1758 parameters: &crate::provider::KdfParameters<'_>,
1759 secret: &[u8],
1760 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1761 crate::provider::RustCryptoProvider.derive_key(parameters, secret)
1762 }
1763
1764 #[cfg(feature = "xmldsig")]
1765 fn digest(
1766 &self,
1767 algorithm: crate::xmldsig::DigestAlgorithm,
1768 data: &[u8],
1769 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1770 crate::provider::CryptoProvider::digest(
1771 &crate::provider::RustCryptoProvider,
1772 algorithm,
1773 data,
1774 )
1775 }
1776
1777 #[cfg(feature = "xmldsig")]
1778 fn sign(
1779 &self,
1780 key: &dyn crate::xmldsig::SigningKey,
1781 algorithm: crate::xmldsig::SignatureAlgorithm,
1782 data: &[u8],
1783 ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
1784 crate::provider::CryptoProvider::sign(
1785 &crate::provider::RustCryptoProvider,
1786 key,
1787 algorithm,
1788 data,
1789 )
1790 }
1791
1792 #[cfg(feature = "xmldsig")]
1793 fn verify(
1794 &self,
1795 key: &dyn crate::xmldsig::VerifyingKey,
1796 algorithm: crate::xmldsig::SignatureAlgorithm,
1797 data: &[u8],
1798 signature: &[u8],
1799 ) -> Result<bool, crate::xmldsig::DsigError> {
1800 crate::provider::CryptoProvider::verify(
1801 &crate::provider::RustCryptoProvider,
1802 key,
1803 algorithm,
1804 data,
1805 signature,
1806 )
1807 }
1808
1809 fn encrypt_data(
1810 &self,
1811 algorithm: DataEncryptionAlgorithm,
1812 key: &[u8],
1813 plaintext: &[u8],
1814 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1815 if let Some(ciphertext) = &self.ciphertext {
1816 return Ok(ciphertext.clone());
1817 }
1818 crate::provider::CryptoProvider::encrypt_data(
1819 &crate::provider::RustCryptoProvider,
1820 algorithm,
1821 key,
1822 plaintext,
1823 )
1824 }
1825
1826 fn decrypt_data(
1827 &self,
1828 algorithm: DataEncryptionAlgorithm,
1829 key: &[u8],
1830 ciphertext: &[u8],
1831 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1832 crate::provider::CryptoProvider::decrypt_data(
1833 &crate::provider::RustCryptoProvider,
1834 algorithm,
1835 key,
1836 ciphertext,
1837 )
1838 }
1839
1840 fn wrap_key(
1841 &self,
1842 algorithm: KeyWrapAlgorithm,
1843 kek: &[u8],
1844 key: &[u8],
1845 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1846 if let Some(wrapped_key) = &self.wrapped_key {
1847 return Ok(wrapped_key.clone());
1848 }
1849 crate::provider::CryptoProvider::wrap_key(
1850 &crate::provider::RustCryptoProvider,
1851 algorithm,
1852 kek,
1853 key,
1854 )
1855 }
1856
1857 fn unwrap_key(
1858 &self,
1859 algorithm: KeyWrapAlgorithm,
1860 kek: &[u8],
1861 wrapped: &[u8],
1862 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1863 crate::provider::CryptoProvider::unwrap_key(
1864 &crate::provider::RustCryptoProvider,
1865 algorithm,
1866 kek,
1867 wrapped,
1868 )
1869 }
1870
1871 fn transport_key(
1872 &self,
1873 key: &dyn crate::provider::KeyTransportKey,
1874 parameters: &RsaOaepParameters,
1875 plaintext: &[u8],
1876 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1877 self.transport_calls.fetch_add(1, Ordering::Relaxed);
1878 if let Some(transported_key) = &self.transported_key {
1879 return Ok(transported_key.clone());
1880 }
1881 crate::provider::CryptoProvider::transport_key(
1882 &crate::provider::RustCryptoProvider,
1883 key,
1884 parameters,
1885 plaintext,
1886 )
1887 }
1888
1889 fn recover_key(
1890 &self,
1891 key: &dyn crate::provider::KeyRecoveryKey,
1892 parameters: &RsaOaepParameters,
1893 ciphertext: &[u8],
1894 ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1895 crate::provider::CryptoProvider::recover_key(
1896 &crate::provider::RustCryptoProvider,
1897 key,
1898 parameters,
1899 ciphertext,
1900 )
1901 }
1902 }
1903
1904 #[test]
1905 fn direct_key_round_trips_every_content_algorithm() {
1906 for algorithm in [
1909 DataEncryptionAlgorithm::Aes128Cbc,
1910 DataEncryptionAlgorithm::Aes256Cbc,
1911 DataEncryptionAlgorithm::Aes128Gcm,
1912 DataEncryptionAlgorithm::Aes256Gcm,
1913 ] {
1914 for plaintext in [b"".as_slice(), b"sixteen-byte-msg", b"not aligned"] {
1915 let key = vec![0x31; algorithm.key_len()];
1916 let encrypted = EncryptedDataBuilder::new(algorithm)
1917 .direct_key(key.clone())
1918 .direct_key_name("content-key")
1919 .encrypt_binary(plaintext)
1920 .expect("supported direct encryption must succeed");
1921 assert_eq!(
1922 decrypt(
1923 &encrypted.encrypted_data_xml,
1924 &SymmetricKeyDecryptor::new(key)
1925 )
1926 .expect("generated ciphertext must decrypt"),
1927 super::super::DecryptedContent::Bytes(plaintext.to_vec())
1928 );
1929 }
1930 }
1931 }
1932
1933 #[test]
1934 fn aes_key_wrap_round_trips_and_preserves_recipient_metadata() {
1935 let kek = [0x44; 32];
1936 let encrypted = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
1937 .add_recipient(
1938 EncryptionRecipient::aes_key_wrap(kek, KeyWrapAlgorithm::AesKw256)
1939 .recipient("service-a")
1940 .key_name("shared-kek"),
1941 )
1942 .encrypt_xml("<secret>value</secret>")
1943 .expect("AES-KW encryption must succeed");
1944 let parsed = parse_encrypted_data(&encrypted.encrypted_data_xml)
1945 .expect("generated EncryptedData must parse");
1946 assert_eq!(
1947 parsed.encrypted_keys[0].recipient.as_deref(),
1948 Some("service-a")
1949 );
1950 assert_eq!(
1951 parsed.encrypted_keys[0].key_name.as_deref(),
1952 Some("shared-kek")
1953 );
1954 assert_eq!(
1955 decrypt(&encrypted.encrypted_data_xml, &KekDecryptor::new(kek))
1956 .expect("wrapped key must decrypt"),
1957 super::super::DecryptedContent::Xml("<secret>value</secret>".into())
1958 );
1959 }
1960
1961 #[test]
1962 fn aes_key_wrap_rejects_mismatched_kek_before_provider_dispatch() {
1963 let builder = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
1966 .recipient_aes_kw([0x44; 32], KeyWrapAlgorithm::AesKw128);
1967
1968 assert!(matches!(
1969 builder.validate_configuration_with_policy(&builder.policy),
1970 Err(XmlEncError::InvalidEncryptionConfig(_))
1971 ));
1972 }
1973
1974 #[test]
1975 fn rsa_oaep_round_trips_configurable_parameters() {
1976 let private = RsaPrivateKey::new(&mut UnwrapErr(SysRng), 2048)
1977 .expect("test RSA key generation must succeed");
1978 let public = RsaPublicKey::from(&private);
1979 let parameters =
1980 RsaOaepParameters::xmlenc11(OaepDigestAlgorithm::Sha256, OaepDigestAlgorithm::Sha512)
1981 .label(b"recipient-label".to_vec());
1982 let encrypted = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes256Gcm)
1983 .add_recipient(
1984 EncryptionRecipient::rsa_oaep(public)
1985 .oaep_parameters(parameters)
1986 .recipient("rsa-recipient"),
1987 )
1988 .encrypt_xml("<secret/>")
1989 .expect("RSA-OAEP encryption must succeed");
1990 assert_eq!(
1991 decrypt(
1992 &encrypted.encrypted_data_xml,
1993 &PrivateKeyDecryptor::new(private)
1994 )
1995 .expect("RSA recipient must recover content key"),
1996 super::super::DecryptedContent::Xml("<secret/>".into())
1997 );
1998 }
1999
2000 #[test]
2001 fn legacy_oaep_serializes_explicit_non_default_mgf() {
2002 let public = RsaPublicKey::from_public_key_pem(include_str!(
2005 "../../tests/fixtures/keys/rsa/rsa-2048-pubkey.pem"
2006 ))
2007 .expect("tracked RSA public key must parse");
2008 let parameters = RsaOaepParameters {
2009 algorithm: super::super::KeyTransportAlgorithm::RsaOaepMgf1p,
2010 digest: OaepDigestAlgorithm::Sha256,
2011 mgf_digest: OaepDigestAlgorithm::Sha256,
2012 label: Vec::new(),
2013 };
2014 let encrypted = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2015 .add_recipient(EncryptionRecipient::rsa_oaep(public).oaep_parameters(parameters))
2016 .encrypt_binary(b"legacy OAEP with explicit MGF")
2017 .expect("libxmlsec1-compatible legacy OAEP parameters must encrypt");
2018 assert!(encrypted.encrypted_data_xml.contains("xenc11:MGF"));
2019 }
2020
2021 #[test]
2022 fn encryption_policy_reports_the_rejected_mgf_uri() {
2023 let public = RsaPublicKey::from_public_key_pem(include_str!(
2026 "../../tests/fixtures/keys/rsa/rsa-2048-pubkey.pem"
2027 ))
2028 .expect("tracked RSA public key must parse");
2029 let parameters = RsaOaepParameters {
2030 algorithm: super::super::KeyTransportAlgorithm::RsaOaepMgf1p,
2031 digest: OaepDigestAlgorithm::Sha256,
2032 mgf_digest: OaepDigestAlgorithm::Sha384,
2033 label: Vec::new(),
2034 };
2035 let policy = crate::policy::EncryptionPolicy {
2036 key_transport_algorithms: Some(std::collections::HashSet::from([parameters.algorithm])),
2037 oaep_digests: Some(std::collections::HashSet::from([parameters.digest])),
2038 ..crate::policy::EncryptionPolicy::default()
2039 };
2040 let builder = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2041 .policy(policy)
2042 .add_recipient(EncryptionRecipient::rsa_oaep(public).oaep_parameters(parameters));
2043
2044 assert!(matches!(
2045 builder.validate_configuration_with_policy(&builder.policy),
2046 Err(XmlEncError::Policy(
2047 crate::policy::PolicyViolation::Algorithm { algorithm, .. }
2048 )) if algorithm == OaepDigestAlgorithm::Sha384.mgf_uri()
2049 ));
2050 }
2051
2052 #[test]
2053 fn encrypt_document_replaces_element_and_self_closing_content() {
2054 let key = [0x55; 16];
2055 let document =
2056 "<root><target ID=\"element\"><child/></target><empty ID=\"content\"/></root>";
2057 let encrypted_element = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2058 .direct_key(key)
2059 .encrypt_document(
2060 document,
2061 DocumentEncryptionOptions {
2062 element_id: Some("element"),
2063 },
2064 )
2065 .expect("element replacement must succeed");
2066 let decrypted_element =
2067 decrypt_document(&encrypted_element, None, &SymmetricKeyDecryptor::new(key))
2068 .expect("element replacement must round-trip");
2069 assert_eq!(decrypted_element, document);
2070
2071 let encrypted_content = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2072 .encryption_type(EncryptedDataType::Content)
2073 .direct_key(key)
2074 .encrypt_document(
2075 document,
2076 DocumentEncryptionOptions {
2077 element_id: Some("content"),
2078 },
2079 )
2080 .expect("self-closing content replacement must expand the element");
2081 assert!(encrypted_content.contains("<empty ID=\"content\"><xenc:EncryptedData"));
2082 let decrypted_content =
2083 decrypt_document(&encrypted_content, None, &SymmetricKeyDecryptor::new(key))
2084 .expect("empty content must decrypt");
2085 assert_eq!(
2086 decrypted_content,
2087 "<root><target ID=\"element\"><child/></target><empty ID=\"content\"></empty></root>"
2088 );
2089 }
2090
2091 #[test]
2092 fn invalid_configuration_and_bounds_fail_before_encryption() {
2093 let no_key = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2094 .encrypt_binary(b"data")
2095 .expect_err("missing key source must fail");
2096 assert!(matches!(no_key, XmlEncError::InvalidEncryptionConfig(_)));
2097
2098 assert!(
2099 validate_plaintext_len(MAX_ENCRYPTION_PLAINTEXT_LEN, MAX_ENCRYPTION_PLAINTEXT_LEN,)
2100 .is_ok()
2101 );
2102 assert!(matches!(
2103 validate_plaintext_len(
2104 MAX_ENCRYPTION_PLAINTEXT_LEN + 1,
2105 MAX_ENCRYPTION_PLAINTEXT_LEN,
2106 ),
2107 Err(XmlEncError::Policy(
2108 crate::policy::PolicyViolation::ResourceLimit {
2109 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
2110 ..
2111 }
2112 ))
2113 ));
2114
2115 assert!(matches!(
2116 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2117 .direct_key([0_u8; 15])
2118 .encrypt_binary(b"data"),
2119 Err(XmlEncError::InvalidKeySize { .. })
2120 ));
2121
2122 let too_many_recipients = (0..=MAX_ENCRYPTION_RECIPIENTS).fold(
2123 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm),
2124 |builder, _| builder.recipient_aes_kw([0_u8; 16], KeyWrapAlgorithm::AesKw128),
2125 );
2126 assert!(matches!(
2127 too_many_recipients.encrypt_binary(b"data"),
2128 Err(XmlEncError::Policy(
2129 crate::policy::PolicyViolation::ResourceLimit {
2130 resource: crate::policy::resource_name::ENCRYPTION_RECIPIENTS,
2131 ..
2132 }
2133 ))
2134 ));
2135
2136 let oversized_metadata = "x".repeat(MAX_ENCRYPTION_METADATA_LEN + 1);
2137 assert!(matches!(
2138 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2139 .direct_key([0_u8; 16])
2140 .id(oversized_metadata)
2141 .encrypt_binary(b"data"),
2142 Err(XmlEncError::Policy(
2143 crate::policy::PolicyViolation::ResourceLimit {
2144 resource: crate::policy::resource_name::ENCRYPTION_METADATA_BYTES,
2145 ..
2146 }
2147 ))
2148 ));
2149 }
2150
2151 #[test]
2152 fn oversized_xml_is_rejected_before_parsing() {
2153 let oversized_malformed = format!(
2156 "<child>{}</unclosed>",
2157 "x".repeat(MAX_ENCRYPTION_PLAINTEXT_LEN)
2158 );
2159
2160 assert!(matches!(
2161 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2162 .encryption_type(EncryptedDataType::Content)
2163 .direct_key([0_u8; 16])
2164 .encrypt_xml(&oversized_malformed),
2165 Err(XmlEncError::Policy(
2166 crate::policy::PolicyViolation::ResourceLimit {
2167 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
2168 ..
2169 }
2170 ))
2171 ));
2172 }
2173
2174 #[test]
2175 fn oversized_document_is_rejected_before_parsing() {
2176 let oversized_malformed = format!(
2179 "<root>{}</unclosed>",
2180 "x".repeat(MAX_ENCRYPTION_DOCUMENT_LEN)
2181 );
2182
2183 assert!(matches!(
2184 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2185 .direct_key([0_u8; 16])
2186 .encrypt_document(&oversized_malformed, DocumentEncryptionOptions::default()),
2187 Err(XmlEncError::Policy(
2188 crate::policy::PolicyViolation::ResourceLimit {
2189 resource: crate::policy::resource_name::XML_DOCUMENT,
2190 ..
2191 }
2192 ))
2193 ));
2194 }
2195
2196 #[test]
2197 fn encrypted_replacement_must_fit_document_policy() {
2198 for encrypted_type in [EncryptedDataType::Element, EncryptedDataType::Content] {
2201 let document = "<root><target ID=\"selected\">x</target></root>";
2202 let policy = crate::policy::EncryptionPolicy {
2203 resources: crate::policy::ResourcePolicy {
2204 max_xml_document_bytes: document.len(),
2205 ..crate::policy::ResourcePolicy::default()
2206 },
2207 ..crate::policy::EncryptionPolicy::default()
2208 };
2209
2210 assert!(matches!(
2211 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2212 .encryption_type(encrypted_type)
2213 .direct_key([0_u8; 16])
2214 .policy(policy)
2215 .encrypt_document(
2216 document,
2217 DocumentEncryptionOptions {
2218 element_id: Some("selected"),
2219 },
2220 ),
2221 Err(XmlEncError::Policy(
2222 crate::policy::PolicyViolation::ResourceLimit {
2223 resource: crate::policy::resource_name::XML_DOCUMENT,
2224 ..
2225 }
2226 ))
2227 ));
2228 }
2229 }
2230
2231 #[test]
2232 fn binary_encryption_preserves_an_opaque_type_hint() {
2233 let result = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2236 .encryption_type(EncryptedDataType::Other("urn:example:binary".into()))
2237 .direct_key([0x42_u8; 16])
2238 .encrypt_binary(b"opaque payload")
2239 .expect("opaque binary Type must be accepted");
2240
2241 assert!(
2242 result
2243 .encrypted_data_xml
2244 .contains("Type=\"urn:example:binary\"")
2245 );
2246 }
2247
2248 #[test]
2249 fn binary_encryption_bounds_an_opaque_type_hint() {
2250 let maximum = 64;
2253 let policy = crate::policy::EncryptionPolicy {
2254 resources: crate::policy::ResourcePolicy {
2255 max_encryption_metadata_bytes: maximum,
2256 ..crate::policy::ResourcePolicy::default()
2257 },
2258 ..crate::policy::EncryptionPolicy::default()
2259 };
2260 let encrypt = |uri: String| {
2261 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2262 .encryption_type(EncryptedDataType::Other(uri))
2263 .direct_key([0x42_u8; 16])
2264 .policy(policy.clone())
2265 .encrypt_binary(b"opaque payload")
2266 };
2267
2268 encrypt(format!("urn:{}", "x".repeat(maximum - 4)))
2269 .expect("metadata at the configured boundary must remain accepted");
2270 assert!(matches!(
2271 encrypt(format!("urn:{}", "x".repeat(maximum - 3))),
2272 Err(XmlEncError::Policy(
2273 crate::policy::PolicyViolation::ResourceLimit {
2274 resource: crate::policy::resource_name::ENCRYPTION_METADATA_BYTES,
2275 maximum: 64,
2276 actual: 65,
2277 }
2278 ))
2279 ));
2280 }
2281
2282 #[test]
2283 fn encryption_policy_bounds_xml_nodes_at_both_parse_entry_points() {
2284 let policy = crate::policy::EncryptionPolicy {
2287 resources: crate::policy::ResourcePolicy {
2288 max_xml_nodes: 4,
2289 ..crate::policy::ResourcePolicy::default()
2290 },
2291 ..crate::policy::EncryptionPolicy::default()
2292 };
2293 let builder = || {
2294 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2295 .direct_key([0_u8; 16])
2296 .policy(policy.clone())
2297 };
2298 let xml = "<root><a/><b/><c/><d/></root>";
2299
2300 assert!(matches!(
2301 builder().encrypt_xml(xml),
2302 Err(XmlEncError::Policy(
2303 crate::policy::PolicyViolation::ResourceLimit {
2304 resource: crate::policy::resource_name::XML_NODES,
2305 maximum: 4,
2306 actual: 5,
2307 }
2308 ))
2309 ));
2310 assert!(matches!(
2311 builder().encrypt_document(xml, DocumentEncryptionOptions::default()),
2312 Err(XmlEncError::Policy(
2313 crate::policy::PolicyViolation::ResourceLimit {
2314 resource: crate::policy::resource_name::XML_NODES,
2315 maximum: 4,
2316 actual: 5,
2317 }
2318 ))
2319 ));
2320 }
2321
2322 #[test]
2323 fn encryption_entry_points_enforce_policy_depth() {
2324 let xml = "<root><child><leaf/></child></root>";
2327 let policy = crate::policy::EncryptionPolicy {
2328 resources: crate::policy::ResourcePolicy {
2329 max_xml_depth: 2,
2330 ..crate::policy::ResourcePolicy::default()
2331 },
2332 ..crate::policy::EncryptionPolicy::default()
2333 };
2334 let builder = || {
2335 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2336 .direct_key([0_u8; 16])
2337 .policy(policy.clone())
2338 };
2339 let mut document = XmlDocument::parse(xml).expect("wide retained fixture must parse");
2340
2341 assert!(matches!(
2342 builder().encrypt_document(xml, DocumentEncryptionOptions::default()),
2343 Err(XmlEncError::Policy(
2344 crate::policy::PolicyViolation::ResourceLimit {
2345 resource: crate::policy::resource_name::XML_DEPTH,
2346 maximum: 2,
2347 actual: 3,
2348 }
2349 ))
2350 ));
2351 assert!(matches!(
2352 builder().encrypt_owned_document(&mut document, DocumentEncryptionOptions::default()),
2353 Err(XmlEncError::Policy(
2354 crate::policy::PolicyViolation::ResourceLimit {
2355 resource: crate::policy::resource_name::XML_DEPTH,
2356 maximum: 2,
2357 actual: 3,
2358 }
2359 ))
2360 ));
2361 }
2362
2363 #[test]
2364 fn encryption_plaintext_and_owned_mutations_use_the_active_depth() {
2365 let policy = crate::policy::EncryptionPolicy {
2368 resources: crate::policy::ResourcePolicy {
2369 max_xml_depth: 1,
2370 ..crate::policy::ResourcePolicy::default()
2371 },
2372 ..crate::policy::EncryptionPolicy::default()
2373 };
2374 let builder = || {
2375 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2376 .direct_key([0_u8; 16])
2377 .policy(policy.clone())
2378 };
2379
2380 assert!(matches!(
2381 builder().encrypt_xml("<root><child/></root>"),
2382 Err(XmlEncError::Policy(
2383 crate::policy::PolicyViolation::ResourceLimit {
2384 resource: crate::policy::resource_name::XML_DEPTH,
2385 maximum: 1,
2386 actual: 2,
2387 }
2388 ))
2389 ));
2390
2391 let generated = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2392 .direct_key([0_u8; 16])
2393 .encryption_type(EncryptedDataType::Content)
2394 .encrypt_xml("")
2395 .expect("generated content replacement must parse");
2396 let generated_document = Document::parse(&generated.encrypted_data_xml)
2397 .expect("generated replacement must be XML");
2398 let generated_depth = generated_document
2399 .descendants()
2400 .filter(|node| node.is_element())
2401 .map(|node| {
2402 node.ancestors()
2403 .filter(|ancestor| ancestor.is_element())
2404 .count()
2405 })
2406 .max()
2407 .expect("generated replacement has elements");
2408 let mutation_policy = crate::policy::EncryptionPolicy {
2409 resources: crate::policy::ResourcePolicy {
2410 max_xml_depth: generated_depth,
2411 ..crate::policy::ResourcePolicy::default()
2412 },
2413 ..crate::policy::EncryptionPolicy::default()
2414 };
2415 let mut document = XmlDocument::parse("<root/>").expect("shallow fixture must parse");
2416 let original = document.as_xml().to_owned();
2417 let generation = document.generation();
2418 assert!(matches!(
2419 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2420 .direct_key([0_u8; 16])
2421 .encryption_type(EncryptedDataType::Content)
2422 .policy(mutation_policy)
2423 .encrypt_owned_document(&mut document, DocumentEncryptionOptions::default()),
2424 Err(XmlEncError::Policy(
2425 crate::policy::PolicyViolation::ResourceLimit {
2426 resource: crate::policy::resource_name::XML_DEPTH,
2427 maximum,
2428 actual,
2429 }
2430 )) if maximum == generated_depth && actual == generated_depth + 1
2431 ));
2432 assert_eq!(document.as_xml(), original);
2433 assert_eq!(document.generation(), generation);
2434 }
2435
2436 #[test]
2437 fn owned_encryption_checks_active_node_limit_before_target_selection() {
2438 let policy = crate::policy::EncryptionPolicy {
2441 resources: crate::policy::ResourcePolicy {
2442 max_xml_nodes: 1,
2443 ..crate::policy::ResourcePolicy::default()
2444 },
2445 ..crate::policy::EncryptionPolicy::default()
2446 };
2447 let mut document = XmlDocument::parse("<root><first/><second/></root>")
2448 .expect("retained fixture must parse");
2449 let before = document.as_xml().to_owned();
2450
2451 let error = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2452 .direct_key([0_u8; 16])
2453 .policy(policy)
2454 .encrypt_owned_document(
2455 &mut document,
2456 DocumentEncryptionOptions {
2457 element_id: Some("missing"),
2458 },
2459 )
2460 .expect_err("active node ceiling must precede target selection");
2461
2462 assert!(matches!(
2463 error,
2464 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2465 resource: crate::policy::resource_name::XML_NODES,
2466 maximum: 1,
2467 actual,
2468 }) if actual > 1
2469 ));
2470 assert_eq!(document.as_xml(), before);
2471 assert_eq!(document.generation(), 0);
2472 }
2473
2474 #[test]
2475 fn document_encryption_initial_parse_uses_the_policy_work_budget() {
2476 let xml = "<root/>";
2479 let policy = crate::policy::EncryptionPolicy {
2480 resources: crate::policy::ResourcePolicy {
2481 max_xml_parse_work_bytes: 0,
2482 ..crate::policy::ResourcePolicy::default()
2483 },
2484 ..crate::policy::EncryptionPolicy::default()
2485 };
2486
2487 let error = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2488 .direct_key([0_u8; 16])
2489 .policy(policy)
2490 .encrypt_document(xml, DocumentEncryptionOptions::default())
2491 .expect_err("a zero parse-work budget must reject the input parse");
2492
2493 assert!(matches!(
2494 error,
2495 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2496 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
2497 maximum: 0,
2498 actual,
2499 }) if actual == xml.len()
2500 ));
2501 }
2502
2503 #[test]
2504 fn content_plaintext_node_limit_excludes_the_internal_wrapper() {
2505 let policy = |max_xml_nodes| crate::policy::EncryptionPolicy {
2506 resources: crate::policy::ResourcePolicy {
2507 max_xml_nodes,
2508 ..crate::policy::ResourcePolicy::default()
2509 },
2510 ..crate::policy::EncryptionPolicy::default()
2511 };
2512
2513 let policy_three = policy(3);
2514 let budget_three = XmlParseWorkBudget::from_resources(&policy_three.resources);
2515 validate_xml_plaintext(
2516 "<first/><second/>",
2517 &EncryptedDataType::Content,
2518 &policy_three,
2519 &budget_three,
2520 crate::XmlBackend::default(),
2521 )
2522 .expect("the caller root and two elements must fit a three-node policy");
2523 let policy_two = policy(2);
2524 let budget_two = XmlParseWorkBudget::from_resources(&policy_two.resources);
2525 assert!(matches!(
2526 validate_xml_plaintext(
2527 "<first/><second/>",
2528 &EncryptedDataType::Content,
2529 &policy_two,
2530 &budget_two,
2531 crate::XmlBackend::default(),
2532 ),
2533 Err(XmlEncError::Policy(
2534 crate::policy::PolicyViolation::ResourceLimit {
2535 resource: crate::policy::resource_name::XML_NODES,
2536 maximum: 2,
2537 actual: 3,
2538 }
2539 ))
2540 ));
2541 }
2542
2543 #[test]
2544 fn content_plaintext_byte_limit_excludes_only_the_internal_wrapper() {
2545 let xml = "<first/><second/>";
2549 let maximum = xml.len() - 1;
2550 let policy = crate::policy::EncryptionPolicy {
2551 resources: crate::policy::ResourcePolicy {
2552 max_xml_document_bytes: maximum,
2553 max_encryption_plaintext_bytes: xml.len(),
2554 ..crate::policy::ResourcePolicy::default()
2555 },
2556 ..crate::policy::EncryptionPolicy::default()
2557 };
2558 let budget = XmlParseWorkBudget::from_resources(&policy.resources);
2559
2560 assert!(matches!(
2561 validate_xml_plaintext(
2562 xml,
2563 &EncryptedDataType::Content,
2564 &policy,
2565 &budget,
2566 crate::XmlBackend::default(),
2567 ),
2568 Err(XmlEncError::Policy(
2569 crate::policy::PolicyViolation::ResourceLimit {
2570 resource: crate::policy::resource_name::XML_DOCUMENT,
2571 maximum: observed_maximum,
2572 actual,
2573 }
2574 )) if observed_maximum == maximum && actual == xml.len()
2575 ));
2576 assert_eq!(
2577 budget.consumed(),
2578 0,
2579 "oversized XML must fail before parsing"
2580 );
2581 }
2582
2583 #[test]
2584 fn generated_encrypted_data_parse_uses_the_active_node_limit() {
2585 let xml = "<EncryptedData><CipherData><CipherValue>AA==</CipherValue></CipherData></EncryptedData>";
2588 let policy = crate::policy::EncryptionPolicy {
2589 resources: crate::policy::ResourcePolicy {
2590 max_xml_nodes: 1,
2591 ..crate::policy::ResourcePolicy::default()
2592 },
2593 ..crate::policy::EncryptionPolicy::default()
2594 };
2595 let budget = XmlParseWorkBudget::from_resources(&policy.resources);
2596
2597 assert!(matches!(
2598 count_generated_encrypted_data_nodes(
2599 xml,
2600 &policy,
2601 &budget,
2602 crate::XmlBackend::default(),
2603 ),
2604 Err(XmlEncError::Policy(
2605 crate::policy::PolicyViolation::ResourceLimit {
2606 resource: crate::policy::resource_name::XML_NODES,
2607 maximum: 1,
2608 actual: 2,
2609 }
2610 ))
2611 ));
2612 }
2613
2614 #[test]
2615 fn document_encryption_bounds_projected_replacement_nodes() {
2616 fn policy(max_xml_nodes: usize) -> crate::policy::EncryptionPolicy {
2617 crate::policy::EncryptionPolicy {
2618 resources: crate::policy::ResourcePolicy {
2619 max_xml_nodes,
2620 ..crate::policy::ResourcePolicy::default()
2621 },
2622 ..crate::policy::EncryptionPolicy::default()
2623 }
2624 }
2625
2626 let generated_nodes = {
2627 let encrypted = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2628 .direct_key([0_u8; 16])
2629 .encrypt_binary(b"payload")
2630 .expect("default policy must permit generated EncryptedData");
2631 Document::parse(&encrypted.encrypted_data_xml)
2632 .expect("generated EncryptedData must parse")
2633 .root_element()
2634 .descendants()
2635 .count()
2636 };
2637
2638 let element_actual = match EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2642 .direct_key([0_u8; 16])
2643 .policy(policy(generated_nodes))
2644 .encrypt_document("<root/>", DocumentEncryptionOptions::default())
2645 {
2646 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2647 resource: "XML nodes",
2648 maximum,
2649 actual,
2650 })) if maximum == generated_nodes && actual > maximum => actual,
2651 result => panic!("expected projected element node bound, got {result:?}"),
2652 };
2653 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2654 .direct_key([0_u8; 16])
2655 .policy(policy(element_actual))
2656 .encrypt_document("<root/>", DocumentEncryptionOptions::default())
2657 .expect("the exact projected element node limit must be accepted");
2658
2659 let content_actual = match EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2663 .encryption_type(EncryptedDataType::Content)
2664 .direct_key([0_u8; 16])
2665 .policy(policy(generated_nodes))
2666 .encrypt_document("<root/>", DocumentEncryptionOptions::default())
2667 {
2668 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2669 resource: "XML nodes",
2670 maximum,
2671 actual,
2672 })) if maximum == generated_nodes && actual > maximum => actual,
2673 result => panic!("expected projected content node bound, got {result:?}"),
2674 };
2675 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2676 .encryption_type(EncryptedDataType::Content)
2677 .direct_key([0_u8; 16])
2678 .policy(policy(content_actual))
2679 .encrypt_document("<root/>", DocumentEncryptionOptions::default())
2680 .expect("the exact projected content node limit must be accepted");
2681 }
2682
2683 #[test]
2684 fn standalone_encryption_bounds_generated_xml_nodes() {
2685 fn policy(max_xml_nodes: usize) -> crate::policy::EncryptionPolicy {
2686 crate::policy::EncryptionPolicy {
2687 resources: crate::policy::ResourcePolicy {
2688 max_xml_nodes,
2689 ..crate::policy::ResourcePolicy::default()
2690 },
2691 ..crate::policy::EncryptionPolicy::default()
2692 }
2693 }
2694
2695 assert!(matches!(
2698 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2699 .direct_key([0_u8; 16])
2700 .policy(policy(1))
2701 .encrypt_binary(b"payload"),
2702 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2703 resource: "XML nodes",
2704 maximum: 1,
2705 actual,
2706 })) if actual > 1
2707 ));
2708
2709 let generated = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2710 .direct_key([0_u8; 16])
2711 .encrypt_binary(b"payload")
2712 .expect("default policy must permit generated EncryptedData");
2713 let document = Document::parse(&generated.encrypted_data_xml)
2714 .expect("generated EncryptedData must parse");
2715 let actual = document.root().descendants().count();
2716 assert!(matches!(
2717 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2718 .direct_key([0_u8; 16])
2719 .policy(policy(actual - 1))
2720 .encrypt_binary(b"payload"),
2721 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2722 resource: "XML nodes",
2723 maximum,
2724 actual: reported,
2725 })) if maximum == actual - 1 && reported == actual
2726 ));
2727 let exact = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2728 .direct_key([0_u8; 16])
2729 .policy(policy(actual))
2730 .encrypt_binary(b"payload")
2731 .expect("the exact generated node limit must be accepted");
2732 let exact_document =
2733 Document::parse(&exact.encrypted_data_xml).expect("exact-boundary output must parse");
2734 let decryption_policy = crate::policy::DecryptionPolicy {
2735 resources: crate::policy::ResourcePolicy {
2736 max_xml_nodes: actual,
2737 ..crate::policy::ResourcePolicy::default()
2738 },
2739 ..crate::policy::DecryptionPolicy::default()
2740 };
2741 super::super::parse::parse_encrypted_data_node_with_policy(
2742 exact_document.root_element(),
2743 &decryption_policy,
2744 )
2745 .expect("output accepted at the exact limit must be consumable at the same limit");
2746 }
2747
2748 #[test]
2749 fn document_dtd_is_controlled_only_by_operation_policy() {
2750 let document = "<!DOCTYPE root [<!ELEMENT root ANY>]><root/>";
2753 assert!(matches!(
2754 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2755 .direct_key([0_u8; 16])
2756 .encrypt_document(document, DocumentEncryptionOptions { element_id: None },),
2757 Err(XmlEncError::XmlParse(_))
2758 ));
2759 let mut policy = crate::policy::EncryptionPolicy::default();
2760 policy.xml.allow_internal_dtd = true;
2761 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2762 .direct_key([0_u8; 16])
2763 .policy(policy.clone())
2764 .encrypt_document(document, DocumentEncryptionOptions { element_id: None })
2765 .expect("the explicit operation policy should permit parsing");
2766 }
2767
2768 #[test]
2769 fn invalid_resource_policy_is_rejected_at_every_entry_point() {
2770 let mut policy = crate::policy::EncryptionPolicy::default();
2773 policy.resources.max_encryption_plaintext_bytes =
2774 crate::hard_limits::ENCRYPTION_PLAINTEXT_BYTE_CEILING + 1;
2775 let builder = || {
2776 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2777 .direct_key([0_u8; 16])
2778 .policy(policy.clone())
2779 };
2780
2781 assert!(matches!(
2782 builder().encrypt_xml("<broken>"),
2783 Err(XmlEncError::Policy(_))
2784 ));
2785 assert!(matches!(
2786 builder().encrypt_binary(b"x"),
2787 Err(XmlEncError::Policy(_))
2788 ));
2789 assert!(matches!(
2790 builder().encrypt_document("<broken>", DocumentEncryptionOptions::default()),
2791 Err(XmlEncError::Policy(_))
2792 ));
2793 }
2794
2795 #[test]
2796 fn standalone_encrypted_output_obeys_document_byte_ceiling() {
2797 let policy = |maximum| crate::policy::EncryptionPolicy {
2800 resources: crate::policy::ResourcePolicy {
2801 max_xml_document_bytes: maximum,
2802 ..crate::policy::ResourcePolicy::default()
2803 },
2804 ..crate::policy::EncryptionPolicy::default()
2805 };
2806
2807 let binary_len = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2808 .direct_key([0_u8; 16])
2809 .encrypt_binary(b"bounded binary")
2810 .expect("baseline binary encryption must succeed")
2811 .encrypted_data_xml
2812 .len();
2813 assert!(matches!(
2814 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2815 .direct_key([0_u8; 16])
2816 .policy(policy(binary_len - 1))
2817 .encrypt_binary(b"bounded binary"),
2818 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2819 resource: crate::policy::resource_name::XML_DOCUMENT,
2820 maximum,
2821 actual,
2822 }))
2823 if maximum == binary_len - 1 && actual == binary_len
2824 ));
2825 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2826 .direct_key([0_u8; 16])
2827 .policy(policy(binary_len))
2828 .encrypt_binary(b"bounded binary")
2829 .expect("the exact standalone binary output bound must be accepted");
2830
2831 let xml_len = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2832 .direct_key([0_u8; 16])
2833 .encrypt_xml("<secret>bounded XML</secret>")
2834 .expect("baseline XML encryption must succeed")
2835 .encrypted_data_xml
2836 .len();
2837 assert!(matches!(
2838 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2839 .direct_key([0_u8; 16])
2840 .policy(policy(xml_len - 1))
2841 .encrypt_xml("<secret>bounded XML</secret>"),
2842 Err(XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2843 resource: crate::policy::resource_name::XML_DOCUMENT,
2844 maximum,
2845 actual,
2846 }))
2847 if maximum == xml_len - 1 && actual == xml_len
2848 ));
2849 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2850 .direct_key([0_u8; 16])
2851 .policy(policy(xml_len))
2852 .encrypt_xml("<secret>bounded XML</secret>")
2853 .expect("the exact standalone XML output bound must be accepted");
2854 }
2855
2856 #[test]
2857 fn zero_resource_ceilings_allow_operations_that_consume_none() {
2858 let policy = crate::policy::EncryptionPolicy {
2861 resources: crate::policy::ResourcePolicy {
2862 max_encryption_plaintext_bytes: 0,
2863 max_encryption_recipients: 0,
2864 ..crate::policy::ResourcePolicy::default()
2865 },
2866 ..crate::policy::EncryptionPolicy::default()
2867 };
2868
2869 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2870 .direct_key([0_u8; 16])
2871 .policy(policy.clone())
2872 .encrypt_binary(&[])
2873 .expect("zero ceilings must allow resources the operation does not consume");
2874
2875 assert!(matches!(
2876 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2877 .direct_key([0_u8; 16])
2878 .policy(policy.clone())
2879 .encrypt_binary(b"x"),
2880 Err(XmlEncError::Policy(
2881 crate::policy::PolicyViolation::ResourceLimit {
2882 resource: crate::policy::resource_name::ENCRYPTION_PLAINTEXT_BYTES,
2883 maximum: 0,
2884 actual: 1
2885 }
2886 ))
2887 ));
2888 assert!(matches!(
2889 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2890 .recipient_aes_kw([0_u8; 16], KeyWrapAlgorithm::AesKw128)
2891 .policy(policy)
2892 .encrypt_binary(&[]),
2893 Err(XmlEncError::Policy(
2894 crate::policy::PolicyViolation::ResourceLimit {
2895 resource: crate::policy::resource_name::ENCRYPTION_RECIPIENTS,
2896 maximum: 0,
2897 actual: 1,
2898 }
2899 ))
2900 ));
2901 }
2902
2903 #[test]
2904 fn encryption_enforces_key_candidate_budget_before_inspection() {
2905 let deny_keys = crate::policy::EncryptionPolicy {
2908 resources: crate::policy::ResourcePolicy {
2909 max_key_candidates: 0,
2910 ..crate::policy::ResourcePolicy::default()
2911 },
2912 ..crate::policy::EncryptionPolicy::default()
2913 };
2914
2915 for error in [
2916 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2917 .direct_key([0_u8; 15])
2918 .policy(deny_keys.clone())
2919 .encrypt_binary(b"data")
2920 .expect_err("a direct key must consume one candidate"),
2921 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2922 .recipient_aes_kw([0_u8; 15], KeyWrapAlgorithm::AesKw128)
2923 .policy(deny_keys)
2924 .encrypt_binary(b"data")
2925 .expect_err("a recipient key must consume one candidate"),
2926 ] {
2927 assert!(matches!(
2928 error,
2929 XmlEncError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2930 resource: crate::policy::resource_name::KEY_CANDIDATES,
2931 maximum: 0,
2932 actual: 1,
2933 })
2934 ));
2935 }
2936
2937 let recipient =
2938 || EncryptionRecipient::aes_key_wrap([0_u8; 16], KeyWrapAlgorithm::AesKw128);
2939 let policy_with_candidate_limit = |maximum| crate::policy::EncryptionPolicy {
2940 resources: crate::policy::ResourcePolicy {
2941 max_key_candidates: maximum,
2942 ..crate::policy::ResourcePolicy::default()
2943 },
2944 ..crate::policy::EncryptionPolicy::default()
2945 };
2946 let builder = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2947 .add_recipient(recipient())
2948 .add_recipient(recipient());
2949
2950 assert!(matches!(
2951 builder
2952 .clone()
2953 .policy(policy_with_candidate_limit(1))
2954 .encrypt_binary(b"data"),
2955 Err(XmlEncError::Policy(
2956 crate::policy::PolicyViolation::ResourceLimit {
2957 resource: crate::policy::resource_name::KEY_CANDIDATES,
2958 maximum: 1,
2959 actual: 2,
2960 }
2961 ))
2962 ));
2963 builder
2964 .policy(policy_with_candidate_limit(2))
2965 .encrypt_binary(b"data")
2966 .expect("the exact key-candidate boundary must be accepted");
2967 }
2968
2969 #[test]
2970 fn element_plaintext_enforces_replacement_node_contract() {
2971 let builder =
2974 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm).direct_key([0_u8; 16]);
2975 builder
2976 .encrypt_xml("\n<!--before--><secret/><!--after-->\n")
2977 .expect("one element with boundary trivia must be accepted");
2978
2979 assert!(matches!(
2980 builder.encrypt_xml("<?target value?><secret/>"),
2981 Err(XmlEncError::InvalidStructure(_))
2982 ));
2983 }
2984
2985 #[test]
2986 fn empty_key_names_are_rejected_before_serialization() {
2987 assert!(matches!(
2990 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2991 .direct_key([0_u8; 16])
2992 .direct_key_name("")
2993 .encrypt_binary(b"data"),
2994 Err(XmlEncError::InvalidEncryptionConfig(_))
2995 ));
2996 assert!(matches!(
2997 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
2998 .add_recipient(
2999 EncryptionRecipient::aes_key_wrap([0_u8; 16], KeyWrapAlgorithm::AesKw128)
3000 .key_name("")
3001 )
3002 .encrypt_binary(b"data"),
3003 Err(XmlEncError::InvalidEncryptionConfig(_))
3004 ));
3005 }
3006
3007 #[test]
3008 fn xml_forbidden_metadata_characters_are_rejected() {
3009 assert!(matches!(
3012 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3013 .direct_key([0_u8; 16])
3014 .id("invalid\0id")
3015 .encrypt_binary(b"data"),
3016 Err(XmlEncError::InvalidEncryptionConfig(_))
3017 ));
3018 assert!(matches!(
3019 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3020 .direct_key([0_u8; 16])
3021 .direct_key_name("invalid\0name")
3022 .encrypt_binary(b"data"),
3023 Err(XmlEncError::InvalidEncryptionConfig(_))
3024 ));
3025 assert!(matches!(
3026 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3027 .add_recipient(
3028 EncryptionRecipient::aes_key_wrap([0_u8; 16], KeyWrapAlgorithm::AesKw128)
3029 .recipient("invalid\0recipient")
3030 )
3031 .encrypt_binary(b"data"),
3032 Err(XmlEncError::InvalidEncryptionConfig(_))
3033 ));
3034 assert!(matches!(
3035 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3036 .add_recipient(
3037 EncryptionRecipient::aes_key_wrap([0_u8; 16], KeyWrapAlgorithm::AesKw128)
3038 .key_name("invalid\0name")
3039 )
3040 .encrypt_binary(b"data"),
3041 Err(XmlEncError::InvalidEncryptionConfig(_))
3042 ));
3043 }
3044
3045 #[test]
3046 fn encrypted_data_id_must_be_an_xml_ncname() {
3047 for invalid in ["bad id", "1leading", "qualified:name", ""] {
3050 assert!(matches!(
3051 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3052 .direct_key([0_u8; 16])
3053 .id(invalid)
3054 .encrypt_binary(b"data"),
3055 Err(XmlEncError::InvalidEncryptionConfig(_))
3056 ));
3057 }
3058
3059 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3060 .direct_key([0_u8; 16])
3061 .id("Δοκιμή")
3062 .encrypt_binary(b"data")
3063 .expect("Unicode XML NCNames must remain valid identifiers");
3064 }
3065
3066 #[test]
3067 fn rejects_malformed_custom_provider_ciphertext_before_serialization() {
3068 for (algorithm, ciphertext) in [
3071 (DataEncryptionAlgorithm::Aes128Gcm, vec![0_u8; 27]),
3072 (DataEncryptionAlgorithm::Aes128Cbc, vec![0_u8; 31]),
3073 (DataEncryptionAlgorithm::Aes256Cbc, vec![0_u8; 33]),
3074 ] {
3075 let error = EncryptedDataBuilder::new(algorithm)
3076 .provider(Arc::new(OverridingOutputProvider {
3077 ciphertext: Some(ciphertext),
3078 wrapped_key: None,
3079 transported_key: None,
3080 transport_calls: AtomicUsize::new(0),
3081 }))
3082 .direct_key(vec![0_u8; algorithm.key_len()])
3083 .encrypt_binary(b"data")
3084 .expect_err("malformed provider output must fail before XML serialization");
3085 assert!(matches!(
3086 error,
3087 XmlEncError::DataTooShort { .. } | XmlEncError::InvalidCbcCiphertextLength { .. }
3088 ));
3089 }
3090 }
3091
3092 #[test]
3093 fn rejects_overlong_custom_provider_ciphertext_before_serialization() {
3094 for (algorithm, expected, actual) in [
3097 (DataEncryptionAlgorithm::Aes128Gcm, 32, 33),
3098 (DataEncryptionAlgorithm::Aes128Cbc, 32, 48),
3099 ] {
3100 let error = EncryptedDataBuilder::new(algorithm)
3101 .provider(Arc::new(OverridingOutputProvider {
3102 ciphertext: Some(vec![0_u8; actual]),
3103 wrapped_key: None,
3104 transported_key: None,
3105 transport_calls: AtomicUsize::new(0),
3106 }))
3107 .direct_key(vec![0_u8; algorithm.key_len()])
3108 .encrypt_binary(b"data")
3109 .expect_err("overlong provider output must fail before XML serialization");
3110 assert!(matches!(
3111 error,
3112 XmlEncError::Provider(crate::provider::ProviderError::InvalidOutputSize {
3113 operation: crate::provider::ProviderOperation::Encrypt,
3114 expected: observed_expected,
3115 actual: observed_actual,
3116 }) if observed_expected == expected && observed_actual == actual
3117 ));
3118 }
3119 }
3120
3121 #[test]
3122 fn rejects_malformed_custom_provider_wrapped_keys_before_serialization() {
3123 for wrapped_key in [vec![], vec![0_u8; 23], vec![0_u8; 25]] {
3126 let result = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3127 .provider(Arc::new(OverridingOutputProvider {
3128 ciphertext: None,
3129 wrapped_key: Some(wrapped_key),
3130 transported_key: None,
3131 transport_calls: AtomicUsize::new(0),
3132 }))
3133 .recipient_aes_kw([0_u8; 16], KeyWrapAlgorithm::AesKw128)
3134 .encrypt_binary(b"data");
3135 assert!(matches!(
3136 result,
3137 Err(XmlEncError::InvalidWrappedKeyLength { expected: 24, actual })
3138 if actual != 24
3139 ));
3140 }
3141
3142 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3143 .provider(Arc::new(OverridingOutputProvider {
3144 ciphertext: None,
3145 wrapped_key: Some(vec![0_u8; 24]),
3146 transported_key: None,
3147 transport_calls: AtomicUsize::new(0),
3148 }))
3149 .recipient_aes_kw([0_u8; 16], KeyWrapAlgorithm::AesKw128)
3150 .encrypt_binary(b"data")
3151 .expect("exact RFC 3394 wrapped-key length must remain accepted");
3152 }
3153
3154 #[test]
3155 fn rejects_malformed_custom_provider_rsa_transport_before_serialization() {
3156 let private_key = RsaPrivateKey::new(&mut UnwrapErr(SysRng), 2048)
3159 .expect("RSA key generation should succeed");
3160 let public_key = RsaPublicKey::from(&private_key);
3161 for transported_key in [vec![], vec![0_u8; 255], vec![0_u8; 257]] {
3162 let result = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3163 .provider(Arc::new(OverridingOutputProvider {
3164 ciphertext: None,
3165 wrapped_key: None,
3166 transported_key: Some(transported_key),
3167 transport_calls: AtomicUsize::new(0),
3168 }))
3169 .recipient_rsa_oaep(public_key.clone())
3170 .encrypt_binary(b"data");
3171 assert!(matches!(
3172 result,
3173 Err(XmlEncError::InvalidWrappedKeyLength {
3174 expected: 256,
3175 actual,
3176 }) if actual != 256
3177 ));
3178 }
3179
3180 EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3181 .provider(Arc::new(OverridingOutputProvider {
3182 ciphertext: None,
3183 wrapped_key: None,
3184 transported_key: Some(vec![0_u8; 256]),
3185 transport_calls: AtomicUsize::new(0),
3186 }))
3187 .recipient_rsa_oaep(public_key)
3188 .encrypt_binary(b"data")
3189 .expect("modulus-sized RSA transport output must remain accepted");
3190 }
3191
3192 #[test]
3193 fn custom_provider_encrypts_with_an_opaque_transport_key() {
3194 let provider = Arc::new(OverridingOutputProvider {
3197 ciphertext: None,
3198 wrapped_key: None,
3199 transported_key: Some(vec![0x5a; 256]),
3200 transport_calls: AtomicUsize::new(0),
3201 });
3202 let key = Arc::new(OpaqueTransportKey {
3203 modulus: vec![0x80; 256],
3204 exponent: vec![0x01, 0x00, 0x01],
3205 });
3206
3207 let encrypted = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3208 .provider(provider.clone())
3209 .recipient_key_transport(key)
3210 .encrypt_binary(b"opaque provider key")
3211 .expect("custom provider must accept its opaque transport key");
3212
3213 assert_eq!(provider.transport_calls.load(Ordering::Relaxed), 1);
3214 assert!(encrypted.encrypted_data_xml.contains("rsa-oaep"));
3215 }
3216
3217 #[test]
3218 fn opaque_transport_preflight_rejects_weak_rsa_metadata() {
3219 let key = OpaqueTransportKey {
3222 modulus: vec![0x80; 128],
3223 exponent: vec![0x01, 0x00, 0x01],
3224 };
3225 assert!(matches!(
3226 validate_key_transport_recipient(&key, &crate::policy::EncryptionPolicy::default()),
3227 Err(XmlEncError::Policy(
3228 crate::policy::PolicyViolation::KeySize { .. }
3229 ))
3230 ));
3231 }
3232
3233 #[test]
3234 fn encryption_policy_rejects_weak_rsa_recipient_before_provider_dispatch() {
3235 let private_key = RsaPrivateKey::new(&mut UnwrapErr(SysRng), 1024)
3237 .expect("test RSA key generation should succeed");
3238 let provider = Arc::new(OverridingOutputProvider {
3239 ciphertext: None,
3240 wrapped_key: None,
3241 transported_key: Some(vec![0_u8; 128]),
3242 transport_calls: AtomicUsize::new(0),
3243 });
3244
3245 let result = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3246 .provider(provider.clone())
3247 .recipient_rsa_oaep(RsaPublicKey::from(&private_key))
3248 .encrypt_binary(b"data");
3249
3250 assert!(matches!(
3251 result,
3252 Err(XmlEncError::Policy(
3253 crate::policy::PolicyViolation::KeySize {
3254 operation: "encryption",
3255 minimum_bits: 2048,
3256 maximum_bits: 8192,
3257 actual_bits: 1024,
3258 ..
3259 }
3260 ))
3261 ));
3262 assert_eq!(provider.transport_calls.load(Ordering::Relaxed), 0);
3263 }
3264
3265 #[test]
3266 fn debug_output_redacts_symmetric_key_material() {
3267 let direct_key = b"direct-key-secret".to_vec();
3268 let kek = b"key-wrap-secret!".to_vec();
3269 let builder = EncryptedDataBuilder::new(DataEncryptionAlgorithm::Aes128Gcm)
3270 .direct_key(direct_key.clone());
3271 let recipient = EncryptionRecipient::aes_key_wrap(kek.clone(), KeyWrapAlgorithm::AesKw128);
3272
3273 let builder_debug = format!("{builder:?}");
3274 let recipient_debug = format!("{recipient:?}");
3275 assert!(builder_debug.contains("[REDACTED]"));
3276 assert!(recipient_debug.contains("[REDACTED]"));
3277 assert!(!builder_debug.contains(&format!("{direct_key:?}")));
3278 assert!(!recipient_debug.contains(&format!("{kek:?}")));
3279 }
3280}