Skip to main content

bitwarden_crypto/safe/
symmetric_key_envelope.rs

1//! Wrapped symmetric key envelope for sealing a symmetric key with another symmetric key.
2
3use std::str::FromStr;
4
5use bitwarden_encoding::{B64, FromStrVisitor};
6use coset::{CborSerializable, CoseEncrypt0Builder, HeaderBuilder};
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9#[cfg(feature = "wasm")]
10use wasm_bindgen::convert::FromWasmAbi;
11
12use crate::{
13    ContentFormat, EncodedSymmetricKey, KeySlotIds, KeyStoreContext, SymmetricCryptoKey,
14    XAes256GcmKey,
15    cose::{
16        ContentNamespace, SafeObjectNamespace,
17        symmetric::{
18            CoseAlgorithmPolicy, CoseContentEncryptionAlgorithm, decrypt_cose0, encrypt_cose0,
19        },
20    },
21    keys::KeyId,
22    safe::{
23        DecodeSealedKeyError, decode_sealed_symmetric_key, extract_key_id,
24        helpers::{debug_fmt, set_safe_namespaces, validate_safe_namespaces},
25        set_contained_key_id,
26    },
27};
28
29/// Errors that can occur when sealing or unsealing a symmetric key with envelope operations.
30#[derive(Debug, Error)]
31pub enum SymmetricKeyEnvelopeError {
32    /// The wrapping key provided is incorrect or the envelope was tampered with
33    #[error("Wrong key")]
34    WrongKey,
35    /// The envelope could not be parsed correctly
36    #[error("Parsing error {0}")]
37    Parsing(String),
38    /// There is no key for the provided key id in the key store
39    #[error("Key missing error")]
40    KeyMissing,
41    /// The key store could not be written to, for example due to being read-only
42    #[error("Could not write to key store")]
43    KeyStore,
44    /// A wrapping or contained key has an unsupported type
45    #[error("Wrong key type")]
46    WrongKeyType,
47    /// The symmetric key envelope namespace is invalid.
48    #[error("Invalid namespace")]
49    InvalidNamespace,
50}
51
52/// A symmetric key protected by an XAES-256-GCM wrapping key.
53pub struct SymmetricKeyEnvelope {
54    cose_encrypt0: coset::CoseEncrypt0,
55}
56
57impl SymmetricKeyEnvelope {
58    /// Seals a symmetric key with an XAES-256-GCM key from the key store.
59    pub fn seal<Ids: KeySlotIds>(
60        key_to_seal: Ids::Symmetric,
61        sealing_key: Ids::Symmetric,
62        namespace: SymmetricKeyEnvelopeNamespace,
63        ctx: &KeyStoreContext<Ids>,
64    ) -> Result<Self, SymmetricKeyEnvelopeError> {
65        // Get the keys from the key store.
66        let key_to_seal = ctx
67            .get_symmetric_key(key_to_seal)
68            .map_err(|_| SymmetricKeyEnvelopeError::KeyMissing)?;
69        let wrapping_key = ctx
70            .get_symmetric_key(sealing_key)
71            .map_err(|_| SymmetricKeyEnvelopeError::KeyMissing)?;
72
73        let wrapping_key: &XAes256GcmKey = match wrapping_key {
74            SymmetricCryptoKey::XAes256GcmKey(key) => key,
75            _ => return Err(SymmetricKeyEnvelopeError::WrongKeyType),
76        };
77
78        let (content_format, key_bytes) = match key_to_seal.to_encoded_raw() {
79            EncodedSymmetricKey::BitwardenLegacyKey(key_bytes) => {
80                (ContentFormat::BitwardenLegacyKey, key_bytes.to_vec())
81            }
82            EncodedSymmetricKey::CoseKey(key_bytes) => (ContentFormat::CoseKey, key_bytes.to_vec()),
83        };
84
85        let mut protected_header = HeaderBuilder::from(content_format).build();
86
87        set_contained_key_id(&mut protected_header, key_to_seal.key_id());
88
89        set_safe_namespaces(
90            &mut protected_header,
91            SafeObjectNamespace::SymmetricKeyEnvelope,
92            namespace,
93        );
94        protected_header.key_id = wrapping_key.key_id.as_slice().into();
95
96        let cose_encrypt0 = encrypt_cose0(
97            CoseContentEncryptionAlgorithm::XAes256Gcm,
98            CoseEncrypt0Builder::new(),
99            protected_header,
100            &key_bytes,
101            wrapping_key.enc_key.as_slice(),
102        )
103        .map_err(|_| SymmetricKeyEnvelopeError::WrongKeyType)?;
104
105        Ok(SymmetricKeyEnvelope { cose_encrypt0 })
106    }
107
108    /// Unseals a symmetric key with an XAES-256-GCM key and stores it in the key store context.
109    pub fn unseal<Ids: KeySlotIds>(
110        &self,
111        wrapping_key: Ids::Symmetric,
112        namespace: SymmetricKeyEnvelopeNamespace,
113        ctx: &mut KeyStoreContext<Ids>,
114    ) -> Result<Ids::Symmetric, SymmetricKeyEnvelopeError> {
115        let wrapping_key_ref = ctx
116            .get_symmetric_key(wrapping_key)
117            .map_err(|_| SymmetricKeyEnvelopeError::KeyMissing)?;
118
119        let wrapping_key_inner = match wrapping_key_ref {
120            SymmetricCryptoKey::XAes256GcmKey(key) => key,
121            _ => return Err(SymmetricKeyEnvelopeError::WrongKeyType),
122        };
123
124        validate_safe_namespaces(
125            &self.cose_encrypt0.protected.header,
126            SafeObjectNamespace::SymmetricKeyEnvelope,
127            namespace,
128        )
129        .map_err(|_| SymmetricKeyEnvelopeError::InvalidNamespace)?;
130
131        // The wrapping key is independently typed as XAES-256-GCM, so require the protected
132        // content-encryption algorithm to match it before attempting decryption.
133        let key_bytes = decrypt_cose0(
134            &self.cose_encrypt0,
135            CoseAlgorithmPolicy::Exactly(CoseContentEncryptionAlgorithm::XAes256Gcm),
136            wrapping_key_inner.enc_key.as_slice(),
137        )
138        .map_err(|_| SymmetricKeyEnvelopeError::WrongKey)?;
139
140        let key = decode_sealed_symmetric_key(&self.cose_encrypt0.protected.header, key_bytes)
141            .map_err(|e| match e {
142                DecodeSealedKeyError::InvalidContentFormat => {
143                    SymmetricKeyEnvelopeError::Parsing("Invalid content format".to_string())
144                }
145                DecodeSealedKeyError::UnsupportedContentFormat
146                | DecodeSealedKeyError::InvalidKey => SymmetricKeyEnvelopeError::WrongKeyType,
147            })?;
148
149        Ok(ctx.add_local_symmetric_key(key))
150    }
151
152    /// Get the key ID of the contained key.
153    pub fn contained_key_id(&self) -> Result<Option<KeyId>, SymmetricKeyEnvelopeError> {
154        extract_key_id(&self.cose_encrypt0.protected.header)
155            .map_err(|_| SymmetricKeyEnvelopeError::Parsing("Invalid contained key id".to_string()))
156    }
157}
158
159impl From<&SymmetricKeyEnvelope> for Vec<u8> {
160    fn from(val: &SymmetricKeyEnvelope) -> Self {
161        val.cose_encrypt0
162            .clone()
163            .to_vec()
164            .expect("Serialization to cose should not fail")
165    }
166}
167
168impl TryFrom<&Vec<u8>> for SymmetricKeyEnvelope {
169    type Error = coset::CoseError;
170
171    fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
172        let cose_encrypt0 = coset::CoseEncrypt0::from_slice(value)?;
173        Ok(SymmetricKeyEnvelope { cose_encrypt0 })
174    }
175}
176
177impl std::fmt::Debug for SymmetricKeyEnvelope {
178    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179        let mut s = f.debug_struct("SymmetricKeyEnvelope");
180
181        if !self.cose_encrypt0.protected.header.key_id.is_empty() {
182            s.field(
183                "sealing_key_id",
184                &self.cose_encrypt0.protected.header.key_id,
185            );
186        }
187
188        debug_fmt::<SymmetricKeyEnvelopeNamespace>(&mut s, &self.cose_encrypt0.protected.header);
189
190        if let Ok(Some(key_id)) = self.contained_key_id() {
191            s.field("contained_key_id", &key_id);
192        }
193
194        s.finish()
195    }
196}
197
198impl FromStr for SymmetricKeyEnvelope {
199    type Err = SymmetricKeyEnvelopeError;
200
201    fn from_str(s: &str) -> Result<Self, Self::Err> {
202        let data = B64::try_from(s).map_err(|_| {
203            SymmetricKeyEnvelopeError::Parsing(
204                "Invalid WrappedSymmetricKey Base64 encoding".to_string(),
205            )
206        })?;
207        Self::try_from(&data.into_bytes()).map_err(|_| {
208            SymmetricKeyEnvelopeError::Parsing("Failed to parse SymmetricKeyEnvelope".to_string())
209        })
210    }
211}
212
213impl From<SymmetricKeyEnvelope> for String {
214    fn from(val: SymmetricKeyEnvelope) -> Self {
215        let serialized: Vec<u8> = (&val).into();
216        B64::from(serialized).to_string()
217    }
218}
219
220impl<'de> Deserialize<'de> for SymmetricKeyEnvelope {
221    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
222    where
223        D: serde::Deserializer<'de>,
224    {
225        deserializer.deserialize_str(FromStrVisitor::new())
226    }
227}
228
229impl Serialize for SymmetricKeyEnvelope {
230    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
231    where
232        S: serde::Serializer,
233    {
234        let serialized: Vec<u8> = self.into();
235        serializer.serialize_str(&B64::from(serialized).to_string())
236    }
237}
238
239#[cfg(feature = "wasm")]
240#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
241const TS_CUSTOM_TYPES: &'static str = r#"
242export type SymmetricKeyEnvelope = Tagged<string, "SymmetricKeyEnvelope">;
243"#;
244
245#[cfg(feature = "wasm")]
246impl wasm_bindgen::describe::WasmDescribe for SymmetricKeyEnvelope {
247    fn describe() {
248        <String as wasm_bindgen::describe::WasmDescribe>::describe();
249    }
250}
251
252#[cfg(feature = "wasm")]
253impl FromWasmAbi for SymmetricKeyEnvelope {
254    type Abi = <String as FromWasmAbi>::Abi;
255
256    unsafe fn from_abi(abi: Self::Abi) -> Self {
257        use wasm_bindgen::UnwrapThrowExt;
258        let string = unsafe { String::from_abi(abi) };
259        SymmetricKeyEnvelope::from_str(&string).unwrap_throw()
260    }
261}
262
263/// Content namespace for the symmetric key envelope
264#[allow(clippy::enum_variant_names)]
265#[derive(Debug, Copy, Clone, PartialEq)]
266pub enum SymmetricKeyEnvelopeNamespace {
267    /// A key used for re-hydration of the SDK
268    SessionKey = 1,
269    #[cfg(test)]
270    /// Example namespace for testing purposes.
271    ExampleNamespace = -3,
272    #[cfg(test)]
273    /// Another example namespace for testing purposes.
274    ExampleNamespace2 = -4,
275}
276
277impl SymmetricKeyEnvelopeNamespace {
278    /// Returns the numeric value of the namespace.
279    pub fn as_i64(&self) -> i64 {
280        *self as i64
281    }
282}
283
284impl TryFrom<i128> for SymmetricKeyEnvelopeNamespace {
285    type Error = SymmetricKeyEnvelopeError;
286
287    fn try_from(value: i128) -> Result<Self, Self::Error> {
288        match value {
289            1 => Ok(SymmetricKeyEnvelopeNamespace::SessionKey),
290            #[cfg(test)]
291            -3 => Ok(SymmetricKeyEnvelopeNamespace::ExampleNamespace),
292            #[cfg(test)]
293            -4 => Ok(SymmetricKeyEnvelopeNamespace::ExampleNamespace2),
294            _ => Err(SymmetricKeyEnvelopeError::InvalidNamespace),
295        }
296    }
297}
298
299impl TryFrom<i64> for SymmetricKeyEnvelopeNamespace {
300    type Error = SymmetricKeyEnvelopeError;
301
302    fn try_from(value: i64) -> Result<Self, Self::Error> {
303        Self::try_from(i128::from(value))
304    }
305}
306
307impl From<SymmetricKeyEnvelopeNamespace> for i128 {
308    fn from(value: SymmetricKeyEnvelopeNamespace) -> Self {
309        value.as_i64().into()
310    }
311}
312
313impl ContentNamespace for SymmetricKeyEnvelopeNamespace {}
314
315#[cfg(test)]
316mod tests {
317    use super::*;
318    use crate::{KeyStore, SymmetricKeyAlgorithm, traits::tests::TestIds};
319
320    const TEST_VECTOR_SEALING_KEY: &str = "pQEEAlAJiRm3TVKQVUpm9gqA4tm6AzoAARF5BIQDBAUGIFggQyO5bN7Uto3hpXUyqluuArn+zppBmhdnahDRJ6p4s84B";
321    const TEST_VECTOR_KEY_TO_SEAL: &str = "pQEEAlDpQoswNPD5xaz7sYLHZXXXAzoAARFvBIQDBAUGIFgg2YO7eUZhb9WSxxsGvdURTunDOBV0W4FRk9E4TV7c00QB";
322    const TEST_VECTOR_ENVELOPE: &str = "g1g+pgE6AAEReQMYZQRQCYkZt01SkFVKZvYKgOLZujoAARVcUOlCizA08PnFrPuxgsdlddc6AAE4gQM6AAE4gCKhBVgYLLX2iYq+Ko+2Hp2fByDfGd5AC0Lw4+zxWFS9n8fBbBKHfiarETj6Q3uRwZYpDS5p29MBEJW7WxOaf5Az51+Qtr8ugnoocUJhb8l6vmkDxFy1blU6iHuoBvpd+3MSFOeCdiuMLbKrf469fmUlDn0=";
323
324    #[test]
325    #[ignore = "Manual test to verify debug format"]
326    fn test_debug() {
327        let key_store = KeyStore::<TestIds>::default();
328        let mut ctx = key_store.context_mut();
329        let key1 = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
330        let key2 = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
331
332        let envelope = SymmetricKeyEnvelope::seal(
333            key1,
334            key2,
335            SymmetricKeyEnvelopeNamespace::ExampleNamespace,
336            &ctx,
337        );
338        println!("{:?}", envelope);
339    }
340
341    #[test]
342    fn test_seal_unseal_symmetric() {
343        let key_store = KeyStore::<TestIds>::default();
344        let mut ctx = key_store.context_mut();
345
346        let key_to_seal = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
347        let wrapping_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
348
349        let envelope = SymmetricKeyEnvelope::seal(
350            key_to_seal,
351            wrapping_key,
352            SymmetricKeyEnvelopeNamespace::ExampleNamespace,
353            &ctx,
354        )
355        .unwrap();
356
357        assert_eq!(
358            envelope.cose_encrypt0.protected.header.alg,
359            Some(coset::Algorithm::PrivateUse(crate::cose::XAES_256_GCM))
360        );
361
362        let unsealed_key = envelope
363            .unseal(
364                wrapping_key,
365                SymmetricKeyEnvelopeNamespace::ExampleNamespace,
366                &mut ctx,
367            )
368            .unwrap();
369
370        let unsealed_key_ref = ctx
371            .get_symmetric_key(unsealed_key)
372            .expect("Key should exist in the key store");
373
374        let original_key_ref = ctx
375            .get_symmetric_key(key_to_seal)
376            .expect("Key should exist in the key store");
377
378        assert_eq!(unsealed_key_ref, original_key_ref);
379    }
380
381    #[test]
382    fn test_contained_key_id_symmetric() {
383        let key_store = KeyStore::<TestIds>::default();
384        let mut ctx = key_store.context_mut();
385
386        let key_to_seal = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
387        let wrapping_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
388
389        let envelope = SymmetricKeyEnvelope::seal(
390            key_to_seal,
391            wrapping_key,
392            SymmetricKeyEnvelopeNamespace::ExampleNamespace,
393            &ctx,
394        )
395        .unwrap();
396
397        let key_to_seal_ref = ctx
398            .get_symmetric_key(key_to_seal)
399            .expect("Key should exist in the key store");
400
401        let contained_key_id = envelope.contained_key_id().unwrap();
402
403        assert_eq!(key_to_seal_ref.key_id(), contained_key_id);
404    }
405
406    #[test]
407    fn test_string_serialization() {
408        let key_store = KeyStore::<TestIds>::default();
409        let mut ctx = key_store.context_mut();
410
411        let key_to_seal = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
412        let wrapping_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
413
414        let envelope = SymmetricKeyEnvelope::seal(
415            key_to_seal,
416            wrapping_key,
417            SymmetricKeyEnvelopeNamespace::ExampleNamespace,
418            &ctx,
419        )
420        .unwrap();
421
422        let serialized: String = envelope.into();
423        let deserialized = SymmetricKeyEnvelope::from_str(&serialized).unwrap();
424
425        let unsealed_key = deserialized
426            .unseal(
427                wrapping_key,
428                SymmetricKeyEnvelopeNamespace::ExampleNamespace,
429                &mut ctx,
430            )
431            .unwrap();
432
433        let unsealed_key_ref = ctx
434            .get_symmetric_key(unsealed_key)
435            .expect("Key should exist in the key store");
436
437        let original_key_ref = ctx
438            .get_symmetric_key(key_to_seal)
439            .expect("Key should exist in the key store");
440
441        assert_eq!(unsealed_key_ref, original_key_ref);
442    }
443
444    #[test]
445    fn test_wrong_key() {
446        let key_store = KeyStore::<TestIds>::default();
447        let mut ctx = key_store.context_mut();
448
449        let key_to_seal = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
450        let wrapping_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
451        let wrong_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
452
453        let envelope = SymmetricKeyEnvelope::seal(
454            key_to_seal,
455            wrapping_key,
456            SymmetricKeyEnvelopeNamespace::ExampleNamespace,
457            &ctx,
458        )
459        .unwrap();
460
461        assert!(matches!(
462            envelope.unseal(
463                wrong_key,
464                SymmetricKeyEnvelopeNamespace::ExampleNamespace,
465                &mut ctx
466            ),
467            Err(SymmetricKeyEnvelopeError::WrongKey)
468        ));
469    }
470
471    #[test]
472    fn test_rejects_non_xaes_wrapping_keys() {
473        let key_store = KeyStore::<TestIds>::default();
474        let mut ctx = key_store.context_mut();
475
476        let key_to_seal = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
477        let wrapping_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
478        let envelope = SymmetricKeyEnvelope::seal(
479            key_to_seal,
480            wrapping_key,
481            SymmetricKeyEnvelopeNamespace::ExampleNamespace,
482            &ctx,
483        )
484        .unwrap();
485
486        let unsupported_wrapping_keys = [
487            ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac),
488            ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256Gcm),
489            ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305),
490        ];
491
492        for unsupported_wrapping_key in unsupported_wrapping_keys {
493            assert!(matches!(
494                SymmetricKeyEnvelope::seal(
495                    key_to_seal,
496                    unsupported_wrapping_key,
497                    SymmetricKeyEnvelopeNamespace::ExampleNamespace,
498                    &ctx,
499                ),
500                Err(SymmetricKeyEnvelopeError::WrongKeyType)
501            ));
502            assert!(matches!(
503                envelope.unseal(
504                    unsupported_wrapping_key,
505                    SymmetricKeyEnvelopeNamespace::ExampleNamespace,
506                    &mut ctx,
507                ),
508                Err(SymmetricKeyEnvelopeError::WrongKeyType)
509            ));
510        }
511    }
512
513    #[test]
514    fn test_wrong_namespace() {
515        let key_store = KeyStore::<TestIds>::default();
516        let mut ctx = key_store.context_mut();
517
518        let key_to_seal = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
519        let wrapping_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
520
521        let envelope = SymmetricKeyEnvelope::seal(
522            key_to_seal,
523            wrapping_key,
524            SymmetricKeyEnvelopeNamespace::ExampleNamespace,
525            &ctx,
526        )
527        .unwrap();
528
529        assert!(matches!(
530            envelope.unseal(
531                wrapping_key,
532                SymmetricKeyEnvelopeNamespace::ExampleNamespace2,
533                &mut ctx
534            ),
535            Err(SymmetricKeyEnvelopeError::InvalidNamespace)
536        ));
537    }
538
539    #[test]
540    #[ignore]
541    fn generate_test_vectors() {
542        let key_store = KeyStore::<TestIds>::default();
543        let mut ctx = key_store.context_mut();
544
545        let key_to_seal = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
546        let wrapping_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
547
548        let envelope = SymmetricKeyEnvelope::seal(
549            key_to_seal,
550            wrapping_key,
551            SymmetricKeyEnvelopeNamespace::ExampleNamespace,
552            &ctx,
553        )
554        .unwrap();
555
556        println!(
557            "const TEST_VECTOR_SEALING_KEY: &str = \"{}\";",
558            bitwarden_encoding::B64::from(
559                ctx.get_symmetric_key(wrapping_key)
560                    .unwrap()
561                    .to_encoded()
562                    .to_vec()
563                    .as_slice()
564            )
565        );
566        println!(
567            "const TEST_VECTOR_KEY_TO_SEAL: &str = \"{}\";",
568            bitwarden_encoding::B64::from(
569                ctx.get_symmetric_key(key_to_seal)
570                    .unwrap()
571                    .to_encoded()
572                    .to_vec()
573                    .as_slice()
574            )
575        );
576        let serialized: String = envelope.into();
577        println!("const TEST_VECTOR_ENVELOPE: &str = \"{}\";", serialized);
578    }
579
580    #[test]
581    fn decrypt_test_vectors() {
582        let key_store = KeyStore::<TestIds>::default();
583        let mut ctx = key_store.context_mut();
584
585        let sealing_key = SymmetricCryptoKey::try_from(TEST_VECTOR_SEALING_KEY.to_string())
586            .expect("Failed to parse sealing key from test vector");
587        let sealed_key_test_vector =
588            SymmetricCryptoKey::try_from(TEST_VECTOR_KEY_TO_SEAL.to_string())
589                .expect("Failed to parse key to seal from test vector");
590
591        let sealing_key_id = ctx.add_local_symmetric_key(sealing_key);
592
593        let envelope = SymmetricKeyEnvelope::from_str(TEST_VECTOR_ENVELOPE).unwrap();
594
595        let unsealed_key_id = envelope
596            .unseal(
597                sealing_key_id,
598                SymmetricKeyEnvelopeNamespace::ExampleNamespace,
599                &mut ctx,
600            )
601            .unwrap();
602        let unsealed_key = ctx.get_symmetric_key(unsealed_key_id).unwrap();
603        assert_eq!(unsealed_key.to_owned(), sealed_key_test_vector.to_owned());
604    }
605}