1use std::{fmt, sync::Arc};
4
5use rsa::RsaPublicKey;
6
7pub const XMLENC_NS: &str = "http://www.w3.org/2001/04/xmlenc#";
9pub const XMLENC11_NS: &str = "http://www.w3.org/2009/xmlenc11#";
11pub const XMLDSIG_NS: &str = "http://www.w3.org/2000/09/xmldsig#";
13
14pub const MAX_CIPHER_VALUE_BASE64_LEN: usize =
16 crate::hard_limits::ENCRYPTION_CIPHER_VALUE_BASE64_BYTE_CEILING;
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum EncryptedDataType {
20 Element,
22 Content,
24 Other(String),
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub enum DataEncryptionAlgorithm {
31 #[cfg(feature = "legacy-algorithms")]
33 TripleDesCbc,
34 #[cfg(feature = "legacy-algorithms")]
36 Aes192Cbc,
37 #[cfg(feature = "legacy-algorithms")]
39 Aes192Gcm,
40 Aes128Cbc,
42 Aes256Cbc,
44 Aes128Gcm,
46 Aes256Gcm,
48}
49
50impl DataEncryptionAlgorithm {
51 pub const fn key_kind(self) -> crate::key_manager::SymmetricKeyKind {
54 #[cfg(feature = "legacy-algorithms")]
55 if matches!(self, Self::TripleDesCbc) {
56 return crate::key_manager::SymmetricKeyKind::Des;
57 }
58 crate::key_manager::SymmetricKeyKind::Aes
59 }
60 pub fn from_uri(uri: &str) -> Result<Self, XmlEncError> {
62 match uri {
63 #[cfg(feature = "legacy-algorithms")]
64 "http://www.w3.org/2001/04/xmlenc#tripledes-cbc" => Ok(Self::TripleDesCbc),
65 #[cfg(feature = "legacy-algorithms")]
66 "http://www.w3.org/2001/04/xmlenc#aes192-cbc" => Ok(Self::Aes192Cbc),
67 #[cfg(feature = "legacy-algorithms")]
68 "http://www.w3.org/2009/xmlenc11#aes192-gcm" => Ok(Self::Aes192Gcm),
69 "http://www.w3.org/2001/04/xmlenc#aes128-cbc" => Ok(Self::Aes128Cbc),
70 "http://www.w3.org/2001/04/xmlenc#aes256-cbc" => Ok(Self::Aes256Cbc),
71 "http://www.w3.org/2009/xmlenc11#aes128-gcm" => Ok(Self::Aes128Gcm),
72 "http://www.w3.org/2009/xmlenc11#aes256-gcm" => Ok(Self::Aes256Gcm),
73 _ => Err(XmlEncError::UnsupportedAlgorithm(uri.to_owned())),
74 }
75 }
76
77 pub const fn key_len(self) -> usize {
79 match self {
80 #[cfg(feature = "legacy-algorithms")]
81 Self::TripleDesCbc | Self::Aes192Cbc | Self::Aes192Gcm => 24,
82 Self::Aes128Cbc | Self::Aes128Gcm => 16,
83 Self::Aes256Cbc | Self::Aes256Gcm => 32,
84 }
85 }
86
87 pub const fn uri(self) -> &'static str {
89 match self {
90 #[cfg(feature = "legacy-algorithms")]
91 Self::TripleDesCbc => "http://www.w3.org/2001/04/xmlenc#tripledes-cbc",
92 #[cfg(feature = "legacy-algorithms")]
93 Self::Aes192Cbc => "http://www.w3.org/2001/04/xmlenc#aes192-cbc",
94 #[cfg(feature = "legacy-algorithms")]
95 Self::Aes192Gcm => "http://www.w3.org/2009/xmlenc11#aes192-gcm",
96 Self::Aes128Cbc => "http://www.w3.org/2001/04/xmlenc#aes128-cbc",
97 Self::Aes256Cbc => "http://www.w3.org/2001/04/xmlenc#aes256-cbc",
98 Self::Aes128Gcm => "http://www.w3.org/2009/xmlenc11#aes128-gcm",
99 Self::Aes256Gcm => "http://www.w3.org/2009/xmlenc11#aes256-gcm",
100 }
101 }
102
103 pub(crate) const fn minimum_ciphertext_len(self) -> usize {
105 match self.cbc_block_len() {
106 Some(block) => block * 2,
107 None => 28,
108 }
109 }
110
111 pub const fn requires_explicit_permission(self) -> bool {
114 match self {
115 #[cfg(feature = "legacy-algorithms")]
116 Self::TripleDesCbc | Self::Aes192Cbc | Self::Aes192Gcm => true,
117 _ => false,
118 }
119 }
120
121 pub(crate) const fn cbc_block_len(self) -> Option<usize> {
122 match self {
126 Self::Aes128Cbc | Self::Aes256Cbc => Some(16),
127 #[cfg(feature = "legacy-algorithms")]
128 Self::Aes192Cbc => Some(16),
129 #[cfg(feature = "legacy-algorithms")]
130 Self::TripleDesCbc => Some(8),
131 _ => None,
132 }
133 }
134
135 pub(crate) fn ciphertext_len_for_plaintext(self, plaintext_len: usize) -> Option<usize> {
137 match self.cbc_block_len() {
138 Some(block) => (plaintext_len / block)
139 .checked_add(1)?
140 .checked_mul(block)?
141 .checked_add(block),
142 None => plaintext_len.checked_add(28),
143 }
144 }
145}
146
147pub(crate) fn validate_ciphertext_framing(
148 algorithm: DataEncryptionAlgorithm,
149 ciphertext_len: usize,
150) -> Result<(), XmlEncError> {
151 let minimum = algorithm.minimum_ciphertext_len();
152 if ciphertext_len < minimum {
153 let algorithm_name = match algorithm.cbc_block_len() {
154 Some(8) => "Triple DES CBC",
155 Some(_) => "AES-CBC",
156 None => "AES-GCM",
157 };
158 return Err(XmlEncError::DataTooShort {
159 algorithm: algorithm_name,
160 minimum,
161 actual: ciphertext_len,
162 });
163 }
164 if let Some(block) = algorithm.cbc_block_len()
165 && !(ciphertext_len - block).is_multiple_of(block)
166 {
167 return Err(XmlEncError::InvalidCbcCiphertextLength {
168 algorithm,
169 block,
170 actual: ciphertext_len - block,
171 });
172 }
173 Ok(())
174}
175
176impl KeyTransportAlgorithm {
177 pub const fn requires_explicit_permission(self) -> bool {
179 match self {
180 #[cfg(feature = "legacy-algorithms")]
181 Self::RsaPkcs1v15 => true,
182 _ => false,
183 }
184 }
185 pub fn from_uri(uri: &str) -> Result<Self, XmlEncError> {
187 match uri {
188 "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" => Ok(Self::RsaOaepMgf1p),
189 "http://www.w3.org/2009/xmlenc11#rsa-oaep" => Ok(Self::RsaOaep11),
190 #[cfg(feature = "legacy-algorithms")]
191 "http://www.w3.org/2001/04/xmlenc#rsa-1_5" => Ok(Self::RsaPkcs1v15),
192 _ => Err(XmlEncError::UnsupportedAlgorithm(uri.to_owned())),
193 }
194 }
195
196 pub const fn uri(self) -> &'static str {
198 match self {
199 Self::RsaOaepMgf1p => "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p",
200 Self::RsaOaep11 => "http://www.w3.org/2009/xmlenc11#rsa-oaep",
201 #[cfg(feature = "legacy-algorithms")]
202 Self::RsaPkcs1v15 => "http://www.w3.org/2001/04/xmlenc#rsa-1_5",
203 }
204 }
205}
206
207impl KeyWrapAlgorithm {
208 pub const fn key_kind(self) -> crate::key_manager::SymmetricKeyKind {
210 #[cfg(feature = "legacy-algorithms")]
211 if matches!(self, Self::TripleDes) {
212 return crate::key_manager::SymmetricKeyKind::Des;
213 }
214 crate::key_manager::SymmetricKeyKind::Aes
215 }
216 pub fn from_uri(uri: &str) -> Result<Self, XmlEncError> {
218 match uri {
219 #[cfg(feature = "legacy-algorithms")]
220 "http://www.w3.org/2001/04/xmlenc#kw-aes192" => Ok(Self::AesKw192),
221 #[cfg(feature = "legacy-algorithms")]
222 "http://www.w3.org/2001/04/xmlenc#kw-tripledes" => Ok(Self::TripleDes),
223 "http://www.w3.org/2001/04/xmlenc#kw-aes128" => Ok(Self::AesKw128),
224 "http://www.w3.org/2001/04/xmlenc#kw-aes256" => Ok(Self::AesKw256),
225 _ => Err(XmlEncError::UnsupportedAlgorithm(uri.to_owned())),
226 }
227 }
228
229 pub const fn key_len(self) -> usize {
231 match self {
232 #[cfg(feature = "legacy-algorithms")]
233 Self::AesKw192 | Self::TripleDes => 24,
234 Self::AesKw128 => 16,
235 Self::AesKw256 => 32,
236 }
237 }
238
239 pub const fn uri(self) -> &'static str {
241 match self {
242 #[cfg(feature = "legacy-algorithms")]
243 Self::AesKw192 => "http://www.w3.org/2001/04/xmlenc#kw-aes192",
244 #[cfg(feature = "legacy-algorithms")]
245 Self::TripleDes => "http://www.w3.org/2001/04/xmlenc#kw-tripledes",
246 Self::AesKw128 => "http://www.w3.org/2001/04/xmlenc#kw-aes128",
247 Self::AesKw256 => "http://www.w3.org/2001/04/xmlenc#kw-aes256",
248 }
249 }
250
251 pub const fn requires_explicit_permission(self) -> bool {
253 match self {
254 #[cfg(feature = "legacy-algorithms")]
255 Self::AesKw192 | Self::TripleDes => true,
256 _ => false,
257 }
258 }
259
260 pub(crate) const fn overhead(self) -> usize {
261 match self {
265 #[cfg(feature = "legacy-algorithms")]
266 Self::TripleDes => 16,
267 _ => 8,
268 }
269 }
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
274pub enum KeyTransportAlgorithm {
275 #[cfg(feature = "legacy-algorithms")]
277 RsaPkcs1v15,
278 RsaOaepMgf1p,
283 RsaOaep11,
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
289pub enum KeyWrapAlgorithm {
290 #[cfg(feature = "legacy-algorithms")]
292 AesKw192,
293 #[cfg(feature = "legacy-algorithms")]
295 TripleDes,
296 AesKw128,
298 AesKw256,
300}
301
302#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
304pub enum OaepDigestAlgorithm {
305 Sha1,
307 Sha256,
309 Sha384,
311 Sha512,
313}
314
315impl OaepDigestAlgorithm {
316 pub fn from_uri(uri: &str) -> Option<Self> {
318 match uri {
319 "http://www.w3.org/2000/09/xmldsig#sha1" => Some(Self::Sha1),
320 "http://www.w3.org/2001/04/xmlenc#sha256" => Some(Self::Sha256),
321 "http://www.w3.org/2001/04/xmlenc#sha384"
322 | "http://www.w3.org/2001/04/xmldsig-more#sha384" => Some(Self::Sha384),
323 "http://www.w3.org/2001/04/xmlenc#sha512" => Some(Self::Sha512),
324 _ => None,
325 }
326 }
327
328 pub fn from_mgf_uri(uri: &str) -> Option<Self> {
330 match uri {
331 "http://www.w3.org/2009/xmlenc11#mgf1sha1" => Some(Self::Sha1),
332 "http://www.w3.org/2009/xmlenc11#mgf1sha256" => Some(Self::Sha256),
333 "http://www.w3.org/2009/xmlenc11#mgf1sha384" => Some(Self::Sha384),
334 "http://www.w3.org/2009/xmlenc11#mgf1sha512" => Some(Self::Sha512),
335 _ => None,
336 }
337 }
338
339 pub const fn uri(self) -> &'static str {
341 match self {
342 Self::Sha1 => "http://www.w3.org/2000/09/xmldsig#sha1",
343 Self::Sha256 => "http://www.w3.org/2001/04/xmlenc#sha256",
344 Self::Sha384 => "http://www.w3.org/2001/04/xmlenc#sha384",
345 Self::Sha512 => "http://www.w3.org/2001/04/xmlenc#sha512",
346 }
347 }
348
349 pub const fn mgf_uri(self) -> &'static str {
351 match self {
352 Self::Sha1 => "http://www.w3.org/2009/xmlenc11#mgf1sha1",
353 Self::Sha256 => "http://www.w3.org/2009/xmlenc11#mgf1sha256",
354 Self::Sha384 => "http://www.w3.org/2009/xmlenc11#mgf1sha384",
355 Self::Sha512 => "http://www.w3.org/2009/xmlenc11#mgf1sha512",
356 }
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::{OaepDigestAlgorithm, XmlEncError};
363
364 #[test]
365 fn operation_plan_failures_have_a_distinct_error_class() {
366 let error = XmlEncError::from(crate::operation::OperationPlanError::StaleResourceIdentity);
367 assert!(matches!(error, XmlEncError::OperationPlan(_)));
368 }
369
370 #[test]
371 fn oaep_sha384_accepts_both_interoperable_digest_uris() {
372 for uri in [
375 "http://www.w3.org/2001/04/xmlenc#sha384",
376 "http://www.w3.org/2001/04/xmldsig-more#sha384",
377 ] {
378 assert_eq!(
379 OaepDigestAlgorithm::from_uri(uri),
380 Some(OaepDigestAlgorithm::Sha384)
381 );
382 }
383 }
384
385 #[test]
386 fn oaep_mgf_uris_round_trip() {
387 for algorithm in [
388 OaepDigestAlgorithm::Sha1,
389 OaepDigestAlgorithm::Sha256,
390 OaepDigestAlgorithm::Sha384,
391 OaepDigestAlgorithm::Sha512,
392 ] {
393 assert_eq!(
394 OaepDigestAlgorithm::from_mgf_uri(algorithm.mgf_uri()),
395 Some(algorithm)
396 );
397 }
398 assert_eq!(
399 OaepDigestAlgorithm::from_mgf_uri("urn:unsupported-mgf"),
400 None
401 );
402 }
403}
404
405#[derive(Debug, Clone, PartialEq, Eq)]
407pub struct RsaOaepParameters {
408 pub algorithm: KeyTransportAlgorithm,
410 pub digest: OaepDigestAlgorithm,
412 pub mgf_digest: OaepDigestAlgorithm,
414 pub label: Vec<u8>,
416}
417
418impl RsaOaepParameters {
419 pub fn legacy() -> Self {
421 Self {
422 algorithm: KeyTransportAlgorithm::RsaOaepMgf1p,
423 digest: OaepDigestAlgorithm::Sha1,
424 mgf_digest: OaepDigestAlgorithm::Sha1,
425 label: Vec::new(),
426 }
427 }
428
429 pub fn xmlenc11(digest: OaepDigestAlgorithm, mgf_digest: OaepDigestAlgorithm) -> Self {
431 Self {
432 algorithm: KeyTransportAlgorithm::RsaOaep11,
433 digest,
434 mgf_digest,
435 label: Vec::new(),
436 }
437 }
438
439 pub fn label(mut self, label: impl Into<Vec<u8>>) -> Self {
441 self.label = label.into();
442 self
443 }
444}
445
446impl Default for RsaOaepParameters {
447 fn default() -> Self {
448 Self::xmlenc11(OaepDigestAlgorithm::Sha256, OaepDigestAlgorithm::Sha256)
449 }
450}
451
452#[derive(Clone)]
454pub enum EncryptionRecipient {
455 #[cfg(feature = "legacy-algorithms")]
457 RsaPkcs1v15 {
458 public_key: Arc<dyn crate::provider::KeyTransportKey>,
460 recipient: Option<String>,
462 key_name: Option<String>,
464 },
465 RsaOaep {
467 public_key: Arc<dyn crate::provider::KeyTransportKey>,
469 parameters: RsaOaepParameters,
471 recipient: Option<String>,
473 key_name: Option<String>,
475 },
476 AesKeyWrap {
478 kek: Vec<u8>,
480 algorithm: KeyWrapAlgorithm,
482 recipient: Option<String>,
484 key_name: Option<String>,
486 },
487}
488
489impl fmt::Debug for EncryptionRecipient {
490 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
491 match self {
492 #[cfg(feature = "legacy-algorithms")]
493 Self::RsaPkcs1v15 {
494 recipient,
495 key_name,
496 ..
497 } => formatter
498 .debug_struct("EncryptionRecipient::RsaPkcs1v15")
499 .field("public_key", &"[PUBLIC KEY]")
500 .field("recipient", recipient)
501 .field("key_name", key_name)
502 .finish(),
503 Self::RsaOaep {
504 parameters,
505 recipient,
506 key_name,
507 ..
508 } => formatter
509 .debug_struct("EncryptionRecipient::RsaOaep")
510 .field("public_key", &"[PUBLIC KEY]")
511 .field("parameters", parameters)
512 .field("recipient", recipient)
513 .field("key_name", key_name)
514 .finish(),
515 Self::AesKeyWrap {
516 algorithm,
517 recipient,
518 key_name,
519 ..
520 } => formatter
521 .debug_struct("EncryptionRecipient::AesKeyWrap")
522 .field("kek", &"[REDACTED]")
523 .field("algorithm", algorithm)
524 .field("recipient", recipient)
525 .field("key_name", key_name)
526 .finish(),
527 }
528 }
529}
530
531impl EncryptionRecipient {
532 #[cfg(feature = "legacy-algorithms")]
534 pub fn rsa_pkcs1v15(public_key: RsaPublicKey) -> Self {
535 Self::provider_pkcs1v15(Arc::new(crate::provider::RustCryptoRsaPublicKey::new(
536 public_key,
537 )))
538 }
539
540 #[cfg(feature = "legacy-algorithms")]
542 pub fn provider_pkcs1v15(public_key: Arc<dyn crate::provider::KeyTransportKey>) -> Self {
543 Self::RsaPkcs1v15 {
544 public_key,
545 recipient: None,
546 key_name: None,
547 }
548 }
549 pub fn rsa_oaep(public_key: RsaPublicKey) -> Self {
555 Self::provider_key_transport(Arc::new(crate::provider::RustCryptoRsaPublicKey::new(
556 public_key,
557 )))
558 }
559
560 pub fn provider_key_transport(public_key: Arc<dyn crate::provider::KeyTransportKey>) -> Self {
562 Self::RsaOaep {
563 public_key,
564 parameters: RsaOaepParameters::default(),
565 recipient: None,
566 key_name: None,
567 }
568 }
569
570 pub fn aes_key_wrap(kek: impl Into<Vec<u8>>, algorithm: KeyWrapAlgorithm) -> Self {
572 Self::AesKeyWrap {
573 kek: kek.into(),
574 algorithm,
575 recipient: None,
576 key_name: None,
577 }
578 }
579
580 pub fn oaep_parameters(mut self, parameters: RsaOaepParameters) -> Self {
582 if let Self::RsaOaep {
583 parameters: current,
584 ..
585 } = &mut self
586 {
587 *current = parameters;
588 }
589 self
590 }
591
592 pub fn recipient(mut self, value: impl Into<String>) -> Self {
594 match &mut self {
595 #[cfg(feature = "legacy-algorithms")]
596 Self::RsaPkcs1v15 { recipient, .. } => *recipient = Some(value.into()),
597 Self::RsaOaep { recipient, .. } | Self::AesKeyWrap { recipient, .. } => {
598 *recipient = Some(value.into());
599 }
600 }
601 self
602 }
603
604 pub fn key_name(mut self, value: impl Into<String>) -> Self {
606 match &mut self {
607 #[cfg(feature = "legacy-algorithms")]
608 Self::RsaPkcs1v15 { key_name, .. } => *key_name = Some(value.into()),
609 Self::RsaOaep { key_name, .. } | Self::AesKeyWrap { key_name, .. } => {
610 *key_name = Some(value.into());
611 }
612 }
613 self
614 }
615}
616
617#[derive(Debug, Clone, Copy, PartialEq, Eq)]
619pub enum ReplacementMode {
620 ReplaceElement,
622 ReplaceContent,
624}
625
626#[derive(Debug, Clone, PartialEq, Eq)]
628pub struct EncryptionResult {
629 pub encrypted_data_xml: String,
631 pub replacement: ReplacementMode,
633}
634
635#[derive(Debug, Clone, Copy, Default)]
637pub struct DocumentEncryptionOptions<'a> {
638 pub element_id: Option<&'a str>,
640}
641
642#[derive(Debug, Clone, PartialEq, Eq)]
644pub struct EncryptionMethod {
645 pub algorithm: String,
647 pub key_size_bits: Option<usize>,
649 pub oaep_digest: Option<String>,
651 pub mgf_algorithm: Option<String>,
653 pub oaep_params: Option<Vec<u8>>,
655}
656
657impl EncryptionMethod {
658 pub(crate) fn validate_structure(&self) -> Result<(), XmlEncError> {
663 if self.key_size_bits == Some(0) {
664 return Err(XmlEncError::InvalidStructure(
665 "KeySize must be a positive integer".into(),
666 ));
667 }
668 let is_legacy_oaep = self.algorithm == KeyTransportAlgorithm::RsaOaepMgf1p.uri();
669 let is_oaep11 = self.algorithm == KeyTransportAlgorithm::RsaOaep11.uri();
670 if (self.oaep_params.is_some()
671 || self.oaep_digest.is_some()
672 || self.mgf_algorithm.is_some())
673 && !is_legacy_oaep
674 && !is_oaep11
675 {
676 return Err(XmlEncError::InvalidStructure(
677 "OAEP parameters are only valid for RSA-OAEP EncryptionMethod".into(),
678 ));
679 }
680 if let (Some(actual), Some(expected)) =
681 (self.key_size_bits, fixed_aes_key_size(&self.algorithm))
682 && actual != expected
683 {
684 return Err(XmlEncError::InvalidStructure(format!(
685 "EncryptionMethod {} requires KeySize {expected}, got {actual}",
686 self.algorithm
687 )));
688 }
689 Ok(())
690 }
691}
692
693fn fixed_aes_key_size(algorithm: &str) -> Option<usize> {
694 let key_len = DataEncryptionAlgorithm::from_uri(algorithm)
695 .map(DataEncryptionAlgorithm::key_len)
696 .or_else(|_| KeyWrapAlgorithm::from_uri(algorithm).map(KeyWrapAlgorithm::key_len))
697 .ok()?;
698 Some(key_len * 8)
699}
700
701#[derive(Debug, Clone, PartialEq, Eq)]
703pub enum CipherData {
704 Value {
706 value: String,
708 },
709 Reference {
711 uri: String,
713 transforms: Vec<crate::xmldsig::transforms::Transform>,
715 },
716 Bytes(Vec<u8>),
718}
719
720impl CipherData {
721 pub(super) fn octets(&self) -> Result<std::borrow::Cow<'_, [u8]>, XmlEncError> {
722 use base64::Engine as _;
723 match self {
724 Self::Value { value } => base64::engine::general_purpose::STANDARD
725 .decode(value)
726 .map(std::borrow::Cow::Owned)
727 .map_err(|error| XmlEncError::Base64(error.to_string())),
728 Self::Bytes(bytes) => Ok(std::borrow::Cow::Borrowed(bytes)),
729 Self::Reference { .. } => Err(XmlEncError::InvalidStructure(
730 "CipherReference requires its original document context".into(),
731 )),
732 }
733 }
734
735 pub fn inline_value(&self) -> Option<&str> {
737 match self {
738 Self::Value { value } => Some(value),
739 Self::Reference { .. } | Self::Bytes(_) => None,
740 }
741 }
742
743 pub fn inline_value_mut(&mut self) -> Option<&mut String> {
745 match self {
746 Self::Value { value } => Some(value),
747 Self::Reference { .. } | Self::Bytes(_) => None,
748 }
749 }
750}
751
752#[derive(Debug, Clone, PartialEq, Eq)]
754pub struct EncryptedKey {
755 pub sources: EncryptionKeySources,
758 pub id: Option<String>,
760 pub recipient: Option<String>,
762 pub key_name: Option<String>,
764 pub encryption_method: EncryptionMethod,
766 pub cipher_data: CipherData,
768 pub reference_list: Option<ReferenceList>,
770 pub carried_key_name: Option<String>,
772}
773
774#[derive(Debug, Clone, PartialEq, Eq, Default)]
777pub struct EncryptionKeySources {
778 pub encapsulation_methods: Vec<crate::key_establishment::EncapsulationMechanism>,
780 pub encrypted_keys: Vec<EncryptedKey>,
782 pub derived_keys: Vec<super::DerivedKey>,
784 pub agreement_methods: Vec<super::AgreementMethod>,
786}
787
788impl EncryptionKeySources {
789 pub(super) fn is_empty(&self) -> bool {
790 self.encrypted_keys.is_empty()
791 && self.encapsulation_methods.is_empty()
792 && self.derived_keys.is_empty()
793 && self.agreement_methods.is_empty()
794 }
795}
796
797#[derive(Debug, Clone, PartialEq, Eq)]
799pub struct ReferenceList {
800 pub data_references: Vec<String>,
802 pub key_references: Vec<String>,
804}
805
806#[derive(Debug, Clone, PartialEq, Eq)]
808pub struct EncryptedData {
809 pub encapsulation_methods: Vec<crate::key_establishment::EncapsulationMechanism>,
811 pub id: Option<String>,
813 pub encrypted_type: Option<EncryptedDataType>,
815 pub key_name: Option<String>,
817 pub encryption_method: EncryptionMethod,
819 pub encrypted_keys: Vec<EncryptedKey>,
821 pub derived_keys: Vec<super::DerivedKey>,
823 pub agreement_methods: Vec<super::AgreementMethod>,
825 pub cipher_data: CipherData,
827}
828
829#[derive(Debug, Clone, PartialEq, Eq)]
831pub enum DecryptedContent {
832 Xml(String),
834 Bytes(Vec<u8>),
836}
837
838#[derive(Debug, thiserror::Error)]
840#[non_exhaustive]
841pub enum XmlEncError {
842 #[error(transparent)]
844 KeyEstablishment(#[from] crate::key_establishment::KeyEstablishmentError),
845 #[error("ciphertext reference error: {0}")]
847 Transform(#[from] crate::xmldsig::TransformError),
848 #[error("XML Encryption policy violation: {0}")]
850 Policy(#[from] crate::policy::PolicyViolation),
851
852 #[error("cryptographic provider error: {0}")]
854 Provider(#[from] crate::provider::ProviderError),
855
856 #[error("XML parsing error: {0}")]
858 XmlParse(#[from] crate::xml::dom::ParseError),
859 #[error("XML document error: {0}")]
861 Document(#[from] crate::document::XmlDocumentError),
862 #[error("missing required {0}")]
864 MissingRequired(&'static str),
865 #[error("invalid encrypted structure: {0}")]
867 InvalidStructure(String),
868 #[error("invalid XML Encryption operation plan: {0}")]
870 OperationPlan(String),
871 #[error("selected node ID is missing or ambiguous: {id}")]
873 SelectedNodeUnavailable {
874 id: String,
876 },
877 #[error("unsupported encryption algorithm: {0}")]
879 UnsupportedAlgorithm(String),
880 #[error("invalid base64 data: {0}")]
882 Base64(String),
883 #[error("{algorithm} ciphertext is too short: need at least {minimum} bytes, got {actual}")]
885 DataTooShort {
886 algorithm: &'static str,
888 minimum: usize,
890 actual: usize,
892 },
893 #[error(
895 "{algorithm} ciphertext length must be a non-zero multiple of {block} bytes, got {actual}"
896 )]
897 InvalidCbcCiphertextLength {
898 algorithm: DataEncryptionAlgorithm,
900 block: usize,
902 actual: usize,
904 },
905 #[error("invalid XMLEnc padding")]
910 InvalidPadding,
911 #[error("AES-GCM authentication failed")]
913 AeadAuthenticationFailed,
914 #[error("{algorithm:?} requires a {expected}-byte key, got {actual}")]
916 InvalidKeySize {
917 algorithm: DataEncryptionAlgorithm,
919 expected: usize,
921 actual: usize,
923 },
924 #[error(
926 "{algorithm:?} cannot safely select among {actual} unordered decryption key candidates"
927 )]
928 AmbiguousKeyCandidates {
929 algorithm: DataEncryptionAlgorithm,
931 actual: usize,
933 },
934 #[error("{algorithm:?} requires a {expected}-byte KEK, got {actual}")]
936 InvalidKekSize {
937 algorithm: KeyWrapAlgorithm,
939 expected: usize,
941 actual: usize,
943 },
944 #[error("wrapped-key value must be {expected} bytes, got {actual}")]
946 InvalidWrappedKeyLength {
947 expected: usize,
949 actual: usize,
951 },
952 #[error("invalid encryption configuration: {0}")]
954 InvalidEncryptionConfig(String),
955 #[error("no suitable decryption key was resolved")]
957 KeyNotFound,
958 #[error("no matching EncryptedData element was found")]
960 EncryptedDataNotFound,
961 #[error("more than one EncryptedData element matched; select one by Id")]
963 AmbiguousEncryptedData,
964 #[error("no matching element was found for encryption")]
966 EncryptionTargetNotFound,
967 #[error("more than one element matched the encryption target")]
969 AmbiguousEncryptionTarget,
970 #[error("EncryptedData must declare Element or Content Type for document replacement")]
972 ReplacementRequiresXml,
973 #[error("RSA-OAEP key unwrap failed: {0}")]
975 Rsa(String),
976 #[error("RSA-OAEP key wrap failed: {0}")]
978 RsaEncrypt(String),
979 #[error("AES key unwrap failed integrity validation")]
981 KeyWrapIntegrity,
982 #[error("operating-system random number generation failed: {0}")]
984 Rng(String),
985 #[error("XML encryption serialization failed: {0}")]
987 XmlSerialize(String),
988 #[error("decrypted XML is not valid UTF-8: {0}")]
990 Utf8(#[from] std::string::FromUtf8Error),
991}
992
993impl From<crate::operation::OperationPlanError> for XmlEncError {
994 fn from(error: crate::operation::OperationPlanError) -> Self {
995 Self::OperationPlan(error.to_string())
996 }
997}
998
999impl fmt::Display for DataEncryptionAlgorithm {
1000 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1001 formatter.write_str(match self {
1002 #[cfg(feature = "legacy-algorithms")]
1003 Self::TripleDesCbc => "Triple DES CBC",
1004 #[cfg(feature = "legacy-algorithms")]
1005 Self::Aes192Cbc => "AES-192-CBC",
1006 #[cfg(feature = "legacy-algorithms")]
1007 Self::Aes192Gcm => "AES-192-GCM",
1008 Self::Aes128Cbc => "AES-128-CBC",
1009 Self::Aes256Cbc => "AES-256-CBC",
1010 Self::Aes128Gcm => "AES-128-GCM",
1011 Self::Aes256Gcm => "AES-256-GCM",
1012 })
1013 }
1014}
1015
1016#[cfg(test)]
1017mod framing_tests {
1018 use super::*;
1019
1020 #[test]
1021 fn cbc_framing_error_names_the_selected_cipher() {
1022 let error = validate_ciphertext_framing(DataEncryptionAlgorithm::Aes128Cbc, 33)
1025 .expect_err("AES ciphertext body is not block-aligned");
1026 assert_eq!(
1027 error.to_string(),
1028 "AES-128-CBC ciphertext length must be a non-zero multiple of 16 bytes, got 17"
1029 );
1030 #[cfg(feature = "legacy-algorithms")]
1031 {
1032 let error = validate_ciphertext_framing(DataEncryptionAlgorithm::TripleDesCbc, 17)
1033 .expect_err("3DES ciphertext body is not block-aligned");
1034 assert_eq!(
1035 error.to_string(),
1036 "Triple DES CBC ciphertext length must be a non-zero multiple of 8 bytes, got 9"
1037 );
1038 }
1039 }
1040}