1use std::str::FromStr;
2
3use bitwarden_encoding::{B64, FromStrVisitor, NotB64EncodedError};
4#[allow(unused_imports)]
5use coset::{CborSerializable, ProtectedHeader, RegisteredLabel, iana::CoapContentFormat};
6use serde::{Deserialize, Serialize, de::DeserializeOwned};
7use thiserror::Error;
8#[cfg(feature = "wasm")]
9use wasm_bindgen::convert::FromWasmAbi;
10
11use crate::{
12 Aes256GcmKey, CONTENT_TYPE_PADDED_CBOR, CoseEncrypt0Bytes, CoseKeyView, CryptoError, EncString,
13 EncodingError, KeyId, KeySlotIds, SerializedMessage, SymmetricCryptoKey,
14 cose::{
15 ContentNamespace, SafeObjectNamespace,
16 symmetric::{
17 CoseAlgorithmPolicy, CoseContentEncryptionAlgorithm, decrypt_cose0, encrypt_cose0,
18 },
19 },
20 safe::helpers::{debug_fmt, set_safe_namespaces, validate_safe_namespaces},
21 utils::pad_bytes,
22};
23
24pub(crate) const DATA_ENVELOPE_PADDING_SIZE: usize = 64;
25
26pub trait SealableVersionedData: Serialize + DeserializeOwned {
30 const NAMESPACE: DataEnvelopeNamespace;
32}
33
34pub trait SealableData: Serialize + DeserializeOwned {}
48
49#[derive(Clone)]
60pub struct DataEnvelope {
61 envelope_data: CoseEncrypt0Bytes,
62}
63
64impl DataEnvelope {
65 pub fn seal<Ids: KeySlotIds, T>(
68 data: T,
69 ctx: &mut crate::store::KeyStoreContext<Ids>,
70 ) -> Result<(Self, Ids::Symmetric), DataEnvelopeError>
71 where
72 T: Serialize + SealableVersionedData,
73 {
74 let (envelope, cek) = Self::seal_ref(&data, T::NAMESPACE)?;
75 let cek_id = ctx.generate_symmetric_key();
76 ctx.set_symmetric_key_internal(cek_id, SymmetricCryptoKey::Aes256GcmKey(cek))
77 .map_err(|_| DataEnvelopeError::KeyStore)?;
78 Ok((envelope, cek_id))
79 }
80
81 pub fn seal_with_wrapping_key<Ids: KeySlotIds, T>(
84 data: T,
85 wrapping_key: &Ids::Symmetric,
86 ctx: &mut crate::store::KeyStoreContext<Ids>,
87 ) -> Result<(Self, EncString), DataEnvelopeError>
88 where
89 T: Serialize + SealableVersionedData,
90 {
91 let (envelope, cek) = Self::seal(data, ctx)?;
92
93 let wrapped_cek = ctx
94 .wrap_symmetric_key(*wrapping_key, cek)
95 .map_err(|_| DataEnvelopeError::Encryption)?;
96
97 Ok((envelope, wrapped_cek))
98 }
99
100 fn seal_ref<T>(
103 data: &T,
104 namespace: DataEnvelopeNamespace,
105 ) -> Result<(DataEnvelope, Aes256GcmKey), DataEnvelopeError>
106 where
107 T: Serialize + SealableVersionedData,
108 {
109 let mut cek = Aes256GcmKey::make();
110
111 let serialized_message =
113 SerializedMessage::encode(&data).map_err(|_| DataEnvelopeError::Encoding)?;
114 if serialized_message.content_type() != coset::iana::CoapContentFormat::Cbor {
115 return Err(DataEnvelopeError::UnsupportedContentFormat);
116 }
117
118 let serialized_and_padded_message =
119 pad_cbor(serialized_message.as_bytes()).map_err(|_| DataEnvelopeError::Encoding)?;
120
121 let mut protected_header = coset::HeaderBuilder::new()
123 .key_id(cek.key_id.as_slice().to_vec())
124 .content_type(CONTENT_TYPE_PADDED_CBOR.to_string())
125 .build();
126 set_safe_namespaces(
127 &mut protected_header,
128 SafeObjectNamespace::DataEnvelope,
129 namespace,
130 );
131
132 let encrypt0 = encrypt_cose0(
136 CoseContentEncryptionAlgorithm::Aes256Gcm,
137 coset::CoseEncrypt0Builder::new(),
138 protected_header,
139 &serialized_and_padded_message,
140 cek.enc_key.as_slice(),
141 )
142 .map_err(|_| DataEnvelopeError::Encoding)?;
143
144 let envelope_data = encrypt0
146 .to_vec()
147 .map(CoseEncrypt0Bytes::from)
148 .map_err(|_| DataEnvelopeError::Encoding)?;
149
150 cek.disable_key_operation(coset::iana::KeyOperation::Encrypt)
152 .disable_key_operation(coset::iana::KeyOperation::WrapKey)
153 .disable_key_operation(coset::iana::KeyOperation::UnwrapKey);
154
155 Ok((DataEnvelope { envelope_data }, cek))
156 }
157
158 pub fn unseal<Ids: KeySlotIds, T>(
161 &self,
162 cek_keyslot: Ids::Symmetric,
163 ctx: &mut crate::store::KeyStoreContext<Ids>,
164 ) -> Result<T, DataEnvelopeError>
165 where
166 T: DeserializeOwned + SealableVersionedData,
167 {
168 let cek = ctx
169 .get_symmetric_key(cek_keyslot)
170 .map_err(|_| DataEnvelopeError::KeyStore)?;
171
172 let view = cek
176 .as_cose_key_view()
177 .ok_or(DataEnvelopeError::UnsupportedContentFormat)?;
178 self.unseal_ref(T::NAMESPACE, view)
179 }
180
181 pub fn unseal_with_wrapping_key<Ids: KeySlotIds, T>(
183 &self,
184 wrapping_key: &Ids::Symmetric,
185 wrapped_cek: &EncString,
186 ctx: &mut crate::store::KeyStoreContext<Ids>,
187 ) -> Result<T, DataEnvelopeError>
188 where
189 T: DeserializeOwned + SealableVersionedData,
190 {
191 let cek = ctx
192 .unwrap_symmetric_key(*wrapping_key, wrapped_cek)
193 .map_err(|_| DataEnvelopeError::Decryption)?;
194 self.unseal(cek, ctx)
195 }
196
197 fn unseal_ref<T>(
200 &self,
201 namespace: DataEnvelopeNamespace,
202 cek: CoseKeyView,
203 ) -> Result<T, DataEnvelopeError>
204 where
205 T: DeserializeOwned + SealableVersionedData,
206 {
207 let msg = coset::CoseEncrypt0::from_slice(self.envelope_data.as_ref())
209 .map_err(|_| DataEnvelopeError::CoseDecoding)?;
210 let content_format =
211 content_format(&msg.protected).map_err(|_| DataEnvelopeError::Decoding)?;
212
213 if msg.protected.header.key_id != cek.key_id().as_slice() {
215 return Err(DataEnvelopeError::WrongKey);
216 }
217
218 validate_safe_namespaces(
219 &msg.protected.header,
220 SafeObjectNamespace::DataEnvelope,
221 namespace,
222 )
223 .map_err(|_| DataEnvelopeError::InvalidNamespace)?;
224
225 if content_format != CONTENT_TYPE_PADDED_CBOR {
226 return Err(DataEnvelopeError::UnsupportedContentFormat);
227 }
228
229 let decrypted_message = decrypt_cose0(
232 &msg,
233 CoseAlgorithmPolicy::Exactly(cek.algorithm()),
234 cek.key_bytes(),
235 )
236 .map_err(|_| DataEnvelopeError::Decryption)?;
237
238 let unpadded_message =
239 unpad_cbor(&decrypted_message).map_err(|_| DataEnvelopeError::Decryption)?;
240
241 let serialized_message =
243 SerializedMessage::from_bytes(unpadded_message, CoapContentFormat::Cbor);
244 serialized_message
245 .decode()
246 .map_err(|_| DataEnvelopeError::Decoding)
247 }
248}
249
250pub(super) fn content_format(protected_header: &ProtectedHeader) -> Result<String, EncodingError> {
254 protected_header
255 .header
256 .content_type
257 .as_ref()
258 .and_then(|ct| match ct {
259 RegisteredLabel::Text(content_format) => Some(content_format.clone()),
260 _ => None,
261 })
262 .ok_or(EncodingError::InvalidCoseEncoding)
263}
264
265impl From<&DataEnvelope> for Vec<u8> {
266 fn from(val: &DataEnvelope) -> Self {
267 val.envelope_data.to_vec()
268 }
269}
270
271impl From<Vec<u8>> for DataEnvelope {
272 fn from(data: Vec<u8>) -> Self {
273 DataEnvelope {
274 envelope_data: CoseEncrypt0Bytes::from(data),
275 }
276 }
277}
278
279impl std::fmt::Debug for DataEnvelope {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 let mut s = f.debug_struct("DataEnvelope");
282 if let Ok(msg) = coset::CoseEncrypt0::from_slice(self.envelope_data.as_ref()) {
283 debug_fmt::<DataEnvelopeNamespace>(&mut s, &msg.protected.header);
284 if let Ok(encrypted_by) = KeyId::try_from(msg.protected.header.key_id.as_slice()) {
285 s.field("encrypted_by", &encrypted_by);
286 }
287 }
288 s.finish()
289 }
290}
291
292impl FromStr for DataEnvelope {
293 type Err = NotB64EncodedError;
294
295 fn from_str(s: &str) -> Result<Self, Self::Err> {
296 let data = B64::try_from(s)?;
297 Ok(Self::from(data.into_bytes()))
298 }
299}
300
301impl From<DataEnvelope> for String {
302 fn from(val: DataEnvelope) -> Self {
303 let serialized: Vec<u8> = (&val).into();
304 B64::from(serialized).to_string()
305 }
306}
307
308impl<'de> Deserialize<'de> for DataEnvelope {
309 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
310 where
311 D: serde::Deserializer<'de>,
312 {
313 deserializer.deserialize_str(FromStrVisitor::new())
314 }
315}
316
317impl Serialize for DataEnvelope {
318 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
319 where
320 S: serde::Serializer,
321 {
322 let serialized: Vec<u8> = self.into();
323 serializer.serialize_str(&B64::from(serialized).to_string())
324 }
325}
326
327impl std::fmt::Display for DataEnvelope {
328 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329 let serialized: Vec<u8> = self.into();
330 write!(f, "{}", B64::from(serialized))
331 }
332}
333
334#[derive(Debug, Error)]
336pub enum DataEnvelopeError {
337 #[error("Unsupported content format")]
339 UnsupportedContentFormat,
340 #[error("Failed to decode COSE message")]
342 CoseDecoding,
343 #[error("Failed to decode the content of the envelope")]
345 Decoding,
346 #[error("Encoding error")]
348 Encoding,
349 #[error("KeyStore error")]
351 KeyStore,
352 #[error("Decryption error")]
354 Decryption,
355 #[error("Encryption error")]
357 Encryption,
358 #[error("Parsing error: {0}")]
360 Parsing(String),
361 #[error("Invalid namespace")]
363 InvalidNamespace,
364 #[error("Wrong key used for decryption")]
366 WrongKey,
367}
368
369#[cfg(feature = "wasm")]
370#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
371const TS_CUSTOM_TYPES: &'static str = r#"
372export type DataEnvelope = Tagged<string, "DataEnvelope">;
373"#;
374
375#[cfg(feature = "wasm")]
376impl wasm_bindgen::describe::WasmDescribe for DataEnvelope {
377 fn describe() {
378 <String as wasm_bindgen::describe::WasmDescribe>::describe();
379 }
380}
381
382#[cfg(feature = "wasm")]
383impl FromWasmAbi for DataEnvelope {
384 type Abi = <String as FromWasmAbi>::Abi;
385
386 unsafe fn from_abi(abi: Self::Abi) -> Self {
387 use wasm_bindgen::UnwrapThrowExt;
388
389 let s = unsafe { String::from_abi(abi) };
390 Self::from_str(&s).unwrap_throw()
391 }
392}
393
394fn pad_cbor(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
395 let mut data = data.to_vec();
396 pad_bytes(&mut data, DATA_ENVELOPE_PADDING_SIZE).map_err(|_| CryptoError::InvalidPadding)?;
397 Ok(data)
398}
399
400fn unpad_cbor(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
401 let unpadded = crate::utils::unpad_bytes(data).map_err(|_| CryptoError::InvalidPadding)?;
402 Ok(unpadded.to_vec())
403}
404
405#[macro_export]
440macro_rules! generate_versioned_sealable {
441 (
442 $enum_name:ident,
444 $namespace:path,
446 [ $( $variant_ty:ident => $rename:literal ),+ $(,)? ]
448 ) => {
449 #[derive(Serialize, Deserialize, Debug, PartialEq)]
451 #[serde(tag = "version", content = "content")]
452 enum $enum_name {
453 $(
454 #[serde(rename = $rename)]
455 $variant_ty($variant_ty),
457 )+
458 }
459
460 impl SealableVersionedData for $enum_name
462 where
463 $( $variant_ty: SealableData ),+
464 {
465 const NAMESPACE: DataEnvelopeNamespace = $namespace;
467 }
468
469 $(
471 impl From<$variant_ty> for $enum_name {
472 fn from(value: $variant_ty) -> Self {
473 Self::$variant_ty(value)
474 }
475 }
476 )+
477 };
478}
479
480#[derive(Debug, Clone, Copy, PartialEq, Eq)]
487pub enum DataEnvelopeNamespace {
488 VaultItem = 1,
490 #[cfg(test)]
492 ExampleNamespace = -1,
493 #[cfg(test)]
495 ExampleNamespace2 = -2,
496}
497
498impl DataEnvelopeNamespace {
499 fn as_i64(&self) -> i64 {
501 *self as i64
502 }
503}
504
505impl TryFrom<i128> for DataEnvelopeNamespace {
506 type Error = DataEnvelopeError;
507
508 fn try_from(value: i128) -> Result<Self, Self::Error> {
509 match value {
510 1 => Ok(DataEnvelopeNamespace::VaultItem),
511 #[cfg(test)]
512 -1 => Ok(DataEnvelopeNamespace::ExampleNamespace),
513 #[cfg(test)]
514 -2 => Ok(DataEnvelopeNamespace::ExampleNamespace2),
515 _ => Err(DataEnvelopeError::InvalidNamespace),
516 }
517 }
518}
519
520impl TryFrom<i64> for DataEnvelopeNamespace {
521 type Error = DataEnvelopeError;
522
523 fn try_from(value: i64) -> Result<Self, Self::Error> {
524 Self::try_from(i128::from(value))
525 }
526}
527
528impl From<DataEnvelopeNamespace> for i128 {
529 fn from(val: DataEnvelopeNamespace) -> Self {
530 val.as_i64().into()
531 }
532}
533
534impl ContentNamespace for DataEnvelopeNamespace {}
535
536#[cfg(test)]
537mod tests {
538 use serde::Deserialize;
539
540 use super::*;
541 use crate::{SymmetricKeyAlgorithm, traits::tests::TestIds};
542
543 #[derive(Serialize, Deserialize, Debug, PartialEq)]
544 struct TestDataV1 {
545 field: u32,
546 }
547 impl SealableData for TestDataV1 {}
548
549 generate_versioned_sealable!(
550 TestData,
551 DataEnvelopeNamespace::ExampleNamespace,
552 [
553 TestDataV1 => "1",
554 ]
555 );
556
557 const TEST_VECTOR_CEK: &str =
560 "pQEEAlB5RTKA0xXdA7C4iQE4QfVUAzoAARFvBIEEIFggQYqnsrAfeFFTaXGXB54YrksB6eQcctMpnaZ8rG6rMJ0B";
561 const TEST_VECTOR_ENVELOPE: &str = "g1hLpQE6AAERbwN4I2FwcGxpY2F0aW9uL3guYml0d2FyZGVuLmNib3ItcGFkZGVkBFB5RTKA0xXdA7C4iQE4QfVUOgABOIECOgABOIAgoQVYGLfQrYHVWxRxO6A8m/yp5DPbBIn3h8nijlhQj4jFwDLWfFz7le1Oy8dTls5vdEFg/FjjsPvXicI2bdb5KDdJCz/YkEu0kqjpQwdCcALpJLVJwgQQeKIeU2klBHEPZjnlLpRRXeCUp5c5BYQ=";
562
563 const TEST_VECTOR_AES_GCM_CEK: &str =
566 "pQEEAlDLIx+izSLk9h9sVjHFzpKoAwMEgQQgWCDWM3iwTX2/LHTIaXS0cPIKCYFZethtKyD6Pucdt4fkGQQEBAQ=";
567 const TEST_VECTOR_AES_GCM_ENVELOPE: &str = "g1hHpQEDA3gjYXBwbGljYXRpb24veC5iaXR3YXJkZW4uY2Jvci1wYWRkZWQEUMsjH6LNIuT2H2xWMcXOkqg6AAE4gQI6AAE4gCChBUyoF/oGEm+lJYrjjgdYUGIH5LnQjqFMWo2BJORVPYH2+hEWkxIn3tRgAMHNwIr0nTXMVD1EyVGZOsHSDMPqn2HaYrDeR5s+Rg0ezZ2WLh8n2FbdC44A/ExOms4IHcyT";
568
569 fn unseal_with_cek<T>(
571 envelope: &DataEnvelope,
572 namespace: DataEnvelopeNamespace,
573 cek: &Aes256GcmKey,
574 ) -> Result<T, DataEnvelopeError>
575 where
576 T: serde::de::DeserializeOwned + SealableVersionedData,
577 {
578 envelope.unseal_ref(namespace, CoseKeyView::Aes256Gcm(cek))
579 }
580
581 #[test]
582 #[ignore = "Manual test to verify debug format"]
583 fn test_debug() {
584 let data: TestData = TestDataV1 { field: 42 }.into();
585 let (envelope, _cek) =
586 DataEnvelope::seal_ref(&data, DataEnvelopeNamespace::ExampleNamespace).unwrap();
587 println!("{:?}", envelope);
588 }
589
590 #[test]
591 #[ignore]
592 fn generate_aes_gcm_test_vectors() {
593 let data: TestData = TestDataV1 { field: 123 }.into();
594 let (envelope, cek) =
595 DataEnvelope::seal_ref(&data, DataEnvelopeNamespace::ExampleNamespace).unwrap();
596 let unsealed_data: TestData =
597 unseal_with_cek(&envelope, DataEnvelopeNamespace::ExampleNamespace, &cek).unwrap();
598 assert_eq!(unsealed_data, data);
599 println!(
600 "const TEST_VECTOR_AES_GCM_CEK: &str = \"{}\";",
601 B64::from(SymmetricCryptoKey::Aes256GcmKey(cek).to_encoded())
602 );
603 println!(
604 "const TEST_VECTOR_AES_GCM_ENVELOPE: &str = \"{}\";",
605 String::from(envelope)
606 );
607 }
608
609 #[test]
610 fn test_data_envelope_legacy_xchacha20_test_vector() {
611 let cek = SymmetricCryptoKey::try_from(B64::try_from(TEST_VECTOR_CEK).unwrap()).unwrap();
612 let SymmetricCryptoKey::XChaCha20Poly1305Key(ref cek) = cek else {
613 panic!("Invalid CEK type");
614 };
615
616 let envelope: DataEnvelope = TEST_VECTOR_ENVELOPE.parse().unwrap();
617 let unsealed_data: TestData = envelope
618 .unseal_ref(
619 DataEnvelopeNamespace::ExampleNamespace,
620 CoseKeyView::XChaCha20Poly1305(cek),
621 )
622 .unwrap();
623 assert_eq!(unsealed_data, TestDataV1 { field: 123 }.into());
624 }
625
626 #[test]
627 fn test_data_envelope_aes_gcm_test_vector() {
628 let cek =
629 SymmetricCryptoKey::try_from(B64::try_from(TEST_VECTOR_AES_GCM_CEK).unwrap()).unwrap();
630 let SymmetricCryptoKey::Aes256GcmKey(ref cek) = cek else {
631 panic!("Invalid CEK type");
632 };
633
634 let envelope: DataEnvelope = TEST_VECTOR_AES_GCM_ENVELOPE.parse().unwrap();
635 let unsealed_data: TestData =
636 unseal_with_cek(&envelope, DataEnvelopeNamespace::ExampleNamespace, cek).unwrap();
637 assert_eq!(unsealed_data, TestDataV1 { field: 123 }.into());
638 }
639
640 #[test]
641 fn test_data_envelope_uses_aes_gcm() {
642 let data: TestData = TestDataV1 { field: 42 }.into();
643 let (envelope, _cek) =
644 DataEnvelope::seal_ref(&data, DataEnvelopeNamespace::ExampleNamespace).unwrap();
645
646 let msg = coset::CoseEncrypt0::from_slice(envelope.envelope_data.as_ref()).unwrap();
648 assert_eq!(
649 msg.protected.header.alg,
650 Some(coset::Algorithm::Assigned(coset::iana::Algorithm::A256GCM))
651 );
652 }
653
654 #[test]
655 fn test_data_envelope() {
656 let data: TestData = TestDataV1 { field: 42 }.into();
658
659 let (envelope, cek) =
661 DataEnvelope::seal_ref(&data, DataEnvelopeNamespace::ExampleNamespace).unwrap();
662 let unsealed_data: TestData =
663 unseal_with_cek(&envelope, DataEnvelopeNamespace::ExampleNamespace, &cek).unwrap();
664
665 assert_eq!(unsealed_data, data);
667 }
668
669 #[test]
670 fn test_data_envelope_with_keystore_roundtrip() {
671 let data: TestData = TestDataV1 { field: 7 }.into();
672 let key_store = crate::store::KeyStore::<TestIds>::default();
673 let mut ctx = key_store.context_mut();
674
675 let (envelope, cek_id) = DataEnvelope::seal(data, &mut ctx).unwrap();
676
677 assert_eq!(
679 ctx.get_symmetric_key_algorithm(cek_id).unwrap(),
680 SymmetricKeyAlgorithm::Aes256Gcm
681 );
682
683 let unsealed: TestData = envelope.unseal(cek_id, &mut ctx).unwrap();
684 assert_eq!(unsealed, TestDataV1 { field: 7 }.into());
685 }
686
687 #[test]
688 fn test_data_envelope_wrapping_key_roundtrip() {
689 let data: TestData = TestDataV1 { field: 99 }.into();
690 let key_store = crate::store::KeyStore::<TestIds>::default();
691 let mut ctx = key_store.context_mut();
692
693 let wrapping_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
694
695 let (envelope, wrapped_cek) =
698 DataEnvelope::seal_with_wrapping_key(data, &wrapping_key, &mut ctx).unwrap();
699 let unsealed: TestData = envelope
700 .unseal_with_wrapping_key(&wrapping_key, &wrapped_cek, &mut ctx)
701 .unwrap();
702 assert_eq!(unsealed, TestDataV1 { field: 99 }.into());
703 }
704
705 #[test]
706 fn test_namespace_validation_success() {
707 let data: TestData = TestDataV1 { field: 123 }.into();
708
709 let (envelope1, cek1) =
711 DataEnvelope::seal_ref(&data, DataEnvelopeNamespace::ExampleNamespace).unwrap();
712 let unsealed_data1: TestData =
713 unseal_with_cek(&envelope1, DataEnvelopeNamespace::ExampleNamespace, &cek1).unwrap();
714 assert_eq!(unsealed_data1, data);
715
716 let (envelope2, cek2) =
718 DataEnvelope::seal_ref(&data, DataEnvelopeNamespace::ExampleNamespace2).unwrap();
719 let unsealed_data2: TestData =
720 unseal_with_cek(&envelope2, DataEnvelopeNamespace::ExampleNamespace2, &cek2).unwrap();
721 assert_eq!(unsealed_data2, data);
722 }
723
724 #[test]
725 fn test_namespace_validation_failure() {
726 let data: TestData = TestDataV1 { field: 456 }.into();
727
728 let (envelope, cek) =
730 DataEnvelope::seal_ref(&data, DataEnvelopeNamespace::ExampleNamespace).unwrap();
731
732 let result: Result<TestData, DataEnvelopeError> =
734 unseal_with_cek(&envelope, DataEnvelopeNamespace::ExampleNamespace2, &cek);
735 assert!(matches!(result, Err(DataEnvelopeError::InvalidNamespace)));
736
737 let unsealed_data: TestData =
739 unseal_with_cek(&envelope, DataEnvelopeNamespace::ExampleNamespace, &cek).unwrap();
740 assert_eq!(unsealed_data, data);
741 }
742
743 #[test]
744 fn test_namespace_validation_with_keystore() {
745 let data: TestData = TestDataV1 { field: 789 }.into();
746 let key_store = crate::store::KeyStore::<TestIds>::default();
747 let mut ctx = key_store.context_mut();
748
749 let (envelope, cek) =
751 DataEnvelope::seal_ref(&data, DataEnvelopeNamespace::ExampleNamespace2).unwrap();
752 ctx.set_symmetric_key_internal(
753 crate::traits::tests::TestSymmKey::A(0),
754 SymmetricCryptoKey::Aes256GcmKey(cek),
755 )
756 .unwrap();
757
758 let result: Result<TestData, DataEnvelopeError> =
760 envelope.unseal(crate::traits::tests::TestSymmKey::A(0), &mut ctx);
761 assert!(matches!(result, Err(DataEnvelopeError::InvalidNamespace)));
762 }
763
764 #[test]
765 fn test_namespace_cross_contamination_protection() {
766 let data1: TestData = TestDataV1 { field: 111 }.into();
767 let data2: TestData = TestDataV1 { field: 222 }.into();
768
769 let (envelope1, cek1) =
771 DataEnvelope::seal_ref(&data1, DataEnvelopeNamespace::ExampleNamespace).unwrap();
772 let (envelope2, cek2) =
773 DataEnvelope::seal_ref(&data2, DataEnvelopeNamespace::ExampleNamespace2).unwrap();
774
775 let unsealed1: TestData =
777 unseal_with_cek(&envelope1, DataEnvelopeNamespace::ExampleNamespace, &cek1).unwrap();
778 assert_eq!(unsealed1, data1);
779
780 let unsealed2: TestData =
781 unseal_with_cek(&envelope2, DataEnvelopeNamespace::ExampleNamespace2, &cek2).unwrap();
782 assert_eq!(unsealed2, data2);
783
784 assert!(matches!(
786 unseal_with_cek::<TestData>(
787 &envelope1,
788 DataEnvelopeNamespace::ExampleNamespace2,
789 &cek1
790 ),
791 Err(DataEnvelopeError::InvalidNamespace)
792 ));
793 assert!(matches!(
794 unseal_with_cek::<TestData>(&envelope2, DataEnvelopeNamespace::ExampleNamespace, &cek2),
795 Err(DataEnvelopeError::InvalidNamespace)
796 ));
797 }
798}