bonsaidb_local/
vault.rs

1//! Encryption and secret management.
2//!
3//! BonsaiDb's vault is the core of encryption and secret management. To offer
4//! this security, BonsaiDb relies on external [`VaultKeyStorage`] to provide
5//! the key needed to decrypt the master keys. After the master keys have been
6//! decrypted, the vault is able to function without [`VaultKeyStorage`]. This
7//! design ensures that if a copy of a database was stolen, the data that is
8//! stored at-rest cannot be decrypted without gaining access to
9//! [`VaultKeyStorage`].
10//!
11//! ## At-Rest Encryption
12//!
13//! At-rest encryption only ensures that if the database files are stolen that
14//! an attacker cannot access the data without the encryption key. BonsaiDb
15//! will regularly decrypt data to process it, and while the data is in-memory,
16//! it is subject to the security of the machine running it. If using BonsaiDb
17//! over a network, the network transport layer's encryption is what ensures
18//! your data's safety.
19//!
20//! ## Security Best Practices
21//!
22//! ### Vault Key Storage
23//!
24//! In most situations, do not use [`LocalVaultKeyStorage`] in a production
25//! environment. If you store the vault keys on the same disk as the database,
26//! it's similar to hiding a key to your house under your doormat. It might stop
27//! the casual person from entering your house, but give any attacker a few
28//! minutes, and they'll find the key.
29//!
30//! Instead, you should use a storage location that provides authentication and
31//! encryption. Our recommendation for production enviroments is to find an
32//! Amazon S3-compatible storage service and use
33//! [`S3VaultKeyStorage`](https://dev.bonsaidb.io/main/docs/bonsaidb_keystorage_s3/struct.S3VaultKeyStorage.html).
34//! Eventually, other BonsaiDb servers will be able to operate as key storage
35//! for each other.
36//!
37//! ## Encryption Algorithms Used
38//!
39//! BonsaiDb uses the [`hpke`](https://github.com/rozbb/rust-hpke) crate to
40//! provide Hybrid Public Key Encryption (HPKE) when public key encryption is
41//! being used. This is currently only utilized for encrypting the master keys
42//! with the vault key. Our HPKE uses `P256+HKDF-SHA256+ChaCha20Poly1305`.
43//! Long term, we plan to offer public key encryption APIs on top of these same
44//! choices.
45//!
46//! For at-rest data encryption, the [`AEAD`
47//! `XChaCha20Poly1305`](https://github.com/RustCrypto/AEADs) implementation is
48//! used directly. This variant of `ChaCha20Poly1305` extends the nonce from 12
49//! bytes to 24 bytes, which allows for random nonces to be used.
50
51use std::borrow::Cow;
52use std::collections::HashMap;
53use std::fmt::{Debug, Display};
54use std::fs::{self, File};
55use std::io::{Read, Write};
56use std::path::{Path, PathBuf};
57use std::sync::Arc;
58
59use bonsaidb_core::arc_bytes::serde::Bytes;
60use bonsaidb_core::document::KeyId;
61use bonsaidb_core::permissions::bonsai::{encryption_key_resource_name, EncryptionKeyAction};
62use bonsaidb_core::permissions::Permissions;
63use chacha20poly1305::aead::generic_array::GenericArray;
64use chacha20poly1305::aead::{Aead, Payload};
65use chacha20poly1305::{KeyInit, XChaCha20Poly1305};
66use hpke::aead::{AeadTag, ChaCha20Poly1305};
67use hpke::kdf::HkdfSha256;
68use hpke::kem::DhP256HkdfSha256;
69use hpke::{self, Deserializable, Kem, OpModeS, Serializable};
70use lockedbox::LockedBox;
71use rand::{thread_rng, Rng};
72use serde::{Deserialize, Serialize};
73use zeroize::{Zeroize, Zeroizing};
74
75/// A private encryption key.
76#[derive(Serialize, Deserialize)]
77pub enum KeyPair {
78    /// A P256 keypair.
79    P256 {
80        /// The private key.
81        private: <DhP256HkdfSha256 as Kem>::PrivateKey,
82        /// The public key.
83        public: <DhP256HkdfSha256 as Kem>::PublicKey,
84    },
85}
86
87impl KeyPair {
88    /// Serializes the private key into bytes.
89    pub fn to_bytes(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
90        Ok(Zeroizing::new(bincode::serialize(self)?))
91    }
92
93    /// Deserializes the private key.
94    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
95        bincode::deserialize(bytes).map_err(Error::from)
96    }
97}
98
99/// A public key corresponding to a [`KeyPair`].
100#[derive(Serialize, Deserialize)]
101pub enum PublicKey {
102    /// A P256 public key.
103    P256(<DhP256HkdfSha256 as Kem>::PublicKey),
104}
105
106impl PublicKey {
107    /// Serializes the public key into bytes.
108    pub fn to_bytes(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
109        Ok(Zeroizing::new(bincode::serialize(self)?))
110    }
111
112    /// Deserializes the public key.
113    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
114        bincode::deserialize(bytes).map_err(Error::from)
115    }
116}
117
118impl<'a> From<&'a KeyPair> for PublicKey {
119    fn from(key: &'a KeyPair) -> Self {
120        match key {
121            KeyPair::P256 { public, .. } => PublicKey::P256(public.clone()),
122        }
123    }
124}
125
126use crate::storage::StorageId;
127
128pub(crate) struct Vault {
129    _vault_public_key: PublicKey,
130    master_keys: HashMap<u32, EncryptionKey>,
131    current_master_key_id: u32,
132    master_key_storage: Arc<dyn AnyVaultKeyStorage>,
133}
134
135impl Debug for Vault {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        f.debug_struct("Vault")
138            .field("master_keys", &self.master_keys)
139            .field("current_master_key_id", &self.current_master_key_id)
140            .field("master_key_storage", &self.master_key_storage)
141            .finish_non_exhaustive()
142    }
143}
144
145/// Errors relating to encryption and/or secret storage.
146#[derive(thiserror::Error, Debug)]
147pub enum Error {
148    /// An error occurred during encryption or decryption.
149    #[error("error with encryption: {0}")]
150    Encryption(String),
151    /// An error occurred within the vault key storage.
152    #[error("error from vault key storage: {0}")]
153    VaultKeyStorage(String),
154    /// An error occurred initializing the vault.
155    #[error("error occurred while initializing: {0}")]
156    Initializing(String),
157    /// A previously initialized vault was found, but the vault key storage
158    /// doesn't contain the key.
159    #[error("vault key not found")]
160    VaultKeyNotFound,
161}
162
163impl From<chacha20poly1305::aead::Error> for Error {
164    fn from(err: chacha20poly1305::aead::Error) -> Self {
165        Self::Encryption(err.to_string())
166    }
167}
168
169impl From<hpke::HpkeError> for Error {
170    fn from(err: hpke::HpkeError) -> Self {
171        Self::Encryption(err.to_string())
172    }
173}
174
175impl From<bincode::Error> for Error {
176    fn from(err: bincode::Error) -> Self {
177        Self::Initializing(err.to_string())
178    }
179}
180
181impl Vault {
182    pub fn initialize(
183        server_id: StorageId,
184        server_directory: &Path,
185        master_key_storage: Arc<dyn AnyVaultKeyStorage>,
186    ) -> Result<Self, Error> {
187        let master_keys_path = server_directory.join("master-keys");
188        if master_keys_path.exists() {
189            Self::unseal(&master_keys_path, server_id, master_key_storage)
190        } else {
191            Self::initialize_vault_key_storage(&master_keys_path, server_id, master_key_storage)
192        }
193    }
194
195    fn initialize_vault_key_storage(
196        master_keys_path: &Path,
197        server_id: StorageId,
198        master_key_storage: Arc<dyn AnyVaultKeyStorage>,
199    ) -> Result<Self, Error> {
200        let master_key = EncryptionKey::random();
201        let (private, public) = DhP256HkdfSha256::gen_keypair(&mut thread_rng());
202
203        master_key_storage
204            .set_vault_key_for(
205                server_id,
206                KeyPair::P256 {
207                    private,
208                    public: public.clone(),
209                },
210            )
211            .map_err(|err| Error::VaultKeyStorage(err.to_string()))?;
212        let mut master_keys = HashMap::new();
213        master_keys.insert(0_u32, master_key);
214        // Beacuse this is such a critical step, let's verify that we can
215        // retrieve the key before we store the sealing key.
216        let retrieved = master_key_storage
217            .vault_key_for(server_id)
218            .map_err(|err| Error::VaultKeyStorage(err.to_string()))?;
219        let expected_public_key_bytes = PublicKey::P256(public.clone()).to_bytes().unwrap();
220        let retrieved_key_matches = retrieved
221            .map(|r| PublicKey::from(&r).to_bytes().ok() == Some(expected_public_key_bytes))
222            .unwrap_or_default();
223        if retrieved_key_matches {
224            let mut serialized_master_keys = bincode::serialize(&master_keys)?;
225
226            let (encapsulated_key, aead_tag) = hpke::single_shot_seal_in_place_detached::<
227                ChaCha20Poly1305,
228                HkdfSha256,
229                DhP256HkdfSha256,
230                _,
231            >(
232                &OpModeS::Base,
233                &public,
234                b"",
235                &mut serialized_master_keys,
236                b"",
237                &mut thread_rng(),
238            )?;
239            let mut tag = [0_u8; 16];
240            tag.copy_from_slice(&aead_tag.to_bytes());
241
242            let encrypted_master_keys_payload = bincode::serialize(&HpkePayload {
243                encryption: PublicKeyEncryption::DhP256HkdfSha256ChaCha20,
244                payload: Bytes::from(serialized_master_keys),
245                encapsulated_key,
246                tag,
247            })?;
248
249            File::create(master_keys_path)
250                .and_then(move |mut file| file.write_all(&encrypted_master_keys_payload))
251                .map_err(|err| Error::Initializing(format!("error saving vault key: {err:?}")))?;
252
253            Ok(Self {
254                _vault_public_key: PublicKey::P256(public),
255                master_keys,
256                current_master_key_id: 0,
257                master_key_storage,
258            })
259        } else {
260            Err(Error::VaultKeyStorage(String::from(
261                "vault key storage failed to return the same stored key during initialization",
262            )))
263        }
264    }
265
266    fn unseal(
267        master_keys_path: &Path,
268        server_id: StorageId,
269        master_key_storage: Arc<dyn AnyVaultKeyStorage>,
270    ) -> Result<Self, Error> {
271        // The vault has been initilized previously. Do not overwrite this file voluntarily.
272        let encrypted_master_keys = std::fs::read(master_keys_path)
273            .map_err(|err| Error::Initializing(format!("error reading master keys: {err:?}")))?;
274        let mut encrypted_master_keys =
275            bincode::deserialize::<HpkePayload>(&encrypted_master_keys)?;
276        let PublicKeyEncryption::DhP256HkdfSha256ChaCha20 = &encrypted_master_keys.encryption;
277        if let Some(vault_key) = master_key_storage
278            .vault_key_for(server_id)
279            .map_err(|err| Error::VaultKeyStorage(err.to_string()))?
280        {
281            let master_keys = match &vault_key {
282                KeyPair::P256 { private, .. } => {
283                    let mut decryption_context =
284                        hpke::setup_receiver::<ChaCha20Poly1305, HkdfSha256, DhP256HkdfSha256>(
285                            &hpke::OpModeR::Base,
286                            private,
287                            &encrypted_master_keys.encapsulated_key,
288                            b"",
289                        )
290                        .unwrap();
291
292                    decryption_context
293                        .open_in_place_detached(
294                            &mut encrypted_master_keys.payload.0,
295                            b"",
296                            &AeadTag::<ChaCha20Poly1305>::from_bytes(&encrypted_master_keys.tag)
297                                .unwrap(),
298                        )
299                        .unwrap();
300
301                    bincode::deserialize::<HashMap<u32, EncryptionKey>>(
302                        &encrypted_master_keys.payload,
303                    )?
304                }
305            };
306
307            let current_master_key_id = *master_keys.keys().max().unwrap();
308            Ok(Self {
309                _vault_public_key: PublicKey::from(&vault_key),
310                master_keys,
311                current_master_key_id,
312                master_key_storage,
313            })
314        } else {
315            Err(Error::VaultKeyNotFound)
316        }
317    }
318
319    fn current_master_key(&self) -> &EncryptionKey {
320        self.master_keys.get(&self.current_master_key_id).unwrap()
321    }
322
323    pub fn encrypt_payload(
324        &self,
325        key_id: &KeyId,
326        payload: &[u8],
327        permissions: Option<&Permissions>,
328    ) -> Result<Vec<u8>, crate::Error> {
329        if let Some(permissions) = permissions {
330            permissions.check(
331                encryption_key_resource_name(key_id),
332                &EncryptionKeyAction::Encrypt,
333            )?;
334        }
335
336        let (key, version) = match key_id {
337            KeyId::Master => (self.current_master_key(), self.current_master_key_id),
338            KeyId::Id(_) => todo!(),
339            KeyId::None => unreachable!(),
340        };
341        let payload = key.encrypt_payload(key_id.clone(), version, payload);
342        Ok(payload.to_vec())
343    }
344
345    pub fn decrypt_payload(
346        &self,
347        payload: &[u8],
348        permissions: Option<&Permissions>,
349    ) -> Result<Vec<u8>, crate::Error> {
350        if let Ok(payload) = VaultPayload::from_slice(payload).map_err(|err| {
351            Error::Encryption(format!("error deserializing encrypted payload: {err:?}"))
352        }) {
353            self.decrypt(&payload, permissions)
354        } else {
355            // If we can't parse it as a VaultPayload, it might have been stored
356            // decrypted originally.
357            Ok(payload.to_vec())
358        }
359    }
360
361    fn decrypt(
362        &self,
363        payload: &VaultPayload<'_>,
364        permissions: Option<&Permissions>,
365    ) -> Result<Vec<u8>, crate::Error> {
366        if let Some(permissions) = permissions {
367            permissions.check(
368                encryption_key_resource_name(&payload.key_id),
369                &EncryptionKeyAction::Decrypt,
370            )?;
371        }
372
373        // TODO handle key version
374        let key = match &payload.key_id {
375            KeyId::Master => self.current_master_key(),
376            KeyId::Id(_) => todo!(),
377            KeyId::None => unreachable!(),
378        };
379        Ok(key.decrypt_payload(payload)?)
380    }
381}
382
383/// Stores encrypted keys for a vault.
384pub trait VaultKeyStorage: Send + Sync + Debug + 'static {
385    /// The error type that the functions return.
386    type Error: Display;
387    /// Store a key. Each server id should have unique storage.
388    fn set_vault_key_for(&self, storage_id: StorageId, key: KeyPair) -> Result<(), Self::Error>;
389
390    /// Retrieve all previously stored vault key for a given storage id.
391    fn vault_key_for(&self, storage_id: StorageId) -> Result<Option<KeyPair>, Self::Error>;
392}
393
394struct EncryptionKey(LockedBox<[u8; 32]>);
395
396impl EncryptionKey {
397    pub fn new(secret: [u8; 32]) -> Self {
398        Self(LockedBox::new(secret))
399    }
400
401    pub fn key(&self) -> &[u8] {
402        &*self.0
403    }
404
405    pub fn random() -> Self {
406        Self::new(thread_rng().gen())
407    }
408
409    pub fn encrypt_payload(
410        &self,
411        key_id: KeyId,
412        key_version: u32,
413        payload: &[u8],
414    ) -> VaultPayload<'static> {
415        let mut rng = thread_rng();
416        let nonce: [u8; 24] = rng.gen();
417        self.encrypt_payload_with_nonce(key_id, key_version, payload, &nonce)
418    }
419
420    pub fn encrypt_payload_with_nonce(
421        &self,
422        key_id: KeyId,
423        key_version: u32,
424        payload: &[u8],
425        nonce: &[u8],
426    ) -> VaultPayload<'static> {
427        let encrypted = XChaCha20Poly1305::new(GenericArray::from_slice(self.key()))
428            .encrypt(
429                GenericArray::from_slice(nonce),
430                Payload {
431                    msg: payload,
432                    aad: b"",
433                },
434            )
435            .unwrap();
436        VaultPayload {
437            key_id,
438            encryption: Encryption::XChaCha20Poly1305,
439            payload: Cow::Owned(encrypted),
440            nonce: Cow::Owned(nonce.to_vec()),
441            key_version,
442        }
443    }
444
445    pub fn decrypt_payload(&self, payload: &VaultPayload<'_>) -> Result<Vec<u8>, Error> {
446        // This is a no-op, but it will cause a compiler error if we introduce additional encryption methods
447        let encrypted = match payload.encryption {
448            Encryption::XChaCha20Poly1305 => {
449                XChaCha20Poly1305::new(GenericArray::from_slice(self.key())).decrypt(
450                    GenericArray::from_slice(&payload.nonce),
451                    Payload {
452                        msg: &payload.payload,
453                        aad: b"",
454                    },
455                )?
456            }
457        };
458        Ok(encrypted)
459    }
460}
461
462impl Drop for EncryptionKey {
463    fn drop(&mut self) {
464        self.0.zeroize();
465    }
466}
467
468impl Debug for EncryptionKey {
469    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
470        f.debug_struct("PrivateKey").finish_non_exhaustive()
471    }
472}
473
474impl Serialize for EncryptionKey {
475    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
476    where
477        S: serde::Serializer,
478    {
479        self.0.serialize(serializer)
480    }
481}
482
483impl<'de> Deserialize<'de> for EncryptionKey {
484    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
485    where
486        D: serde::Deserializer<'de>,
487    {
488        Ok(Self(LockedBox::new(<[u8; 32]>::deserialize(deserializer)?)))
489    }
490}
491
492/// A [`VaultKeyStorage`] trait that wraps the Error type before returning. This
493/// type is used to allow the Vault to operate without any generic parameters.
494/// This trait is auto-implemented for all [`VaultKeyStorage`] implementors.
495pub trait AnyVaultKeyStorage: Send + Sync + Debug + 'static {
496    /// Retrieve all previously stored master keys for a given storage id.
497    fn vault_key_for(&self, storage_id: StorageId) -> Result<Option<KeyPair>, Error>;
498
499    /// Store a key. Each server id should have unique storage. The keys are
500    /// uniquely encrypted per storage id and can only be decrypted by keys
501    /// contained in the storage itself.
502    fn set_vault_key_for(&self, storage_id: StorageId, key: KeyPair) -> Result<(), Error>;
503}
504
505impl<T> AnyVaultKeyStorage for T
506where
507    T: VaultKeyStorage + 'static,
508{
509    fn vault_key_for(&self, server_id: StorageId) -> Result<Option<KeyPair>, Error> {
510        VaultKeyStorage::vault_key_for(self, server_id)
511            .map_err(|err| Error::VaultKeyStorage(err.to_string()))
512    }
513
514    fn set_vault_key_for(&self, server_id: StorageId, key: KeyPair) -> Result<(), Error> {
515        VaultKeyStorage::set_vault_key_for(self, server_id, key)
516            .map_err(|err| Error::VaultKeyStorage(err.to_string()))
517    }
518}
519
520/// Stores vault key locally on disk. This is in general considered insecure,
521/// and shouldn't be used without careful consideration.
522///
523/// The primary goal of encryption within BonsaiDb is to offer limited
524/// encryption at-rest. Within these goals, the primary attack vector being
525/// protected against is an attacker being able to copy the data off of the
526/// disks, either by physically gaining access to the drives or having
527/// filesystem access. By storing the vault key on the same physical media, the
528/// encryption should be considered insecure because if you can gain access to
529/// the data, you have access to the keys as well.
530///
531/// For production environments, it is much more secure to store the vault key
532/// in a separate location. We recommand any S3-compatible backend.
533#[derive(Debug, Clone)]
534pub struct LocalVaultKeyStorage {
535    directory: PathBuf,
536}
537
538impl LocalVaultKeyStorage {
539    /// Creates a new file-based vaultr key storage, storing files within
540    /// `path`. The path provided shouod be a directory. If it doesn't exist, it
541    /// will be created.
542    pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, std::io::Error> {
543        let directory = path.as_ref().to_owned();
544        if !directory.exists() {
545            fs::create_dir_all(&directory)?;
546        }
547        Ok(Self { directory })
548    }
549}
550
551/// Errors from local vault key storage.
552#[derive(thiserror::Error, Debug)]
553pub enum LocalVaultKeyStorageError {
554    /// An error interacting with the filesystem.
555    #[error("io error: {0}")]
556    Io(#[from] std::io::Error),
557
558    /// An error serializing or deserializing the keys.
559    #[error("serialization error: {0}")]
560    Serialization(#[from] bincode::Error),
561
562    /// The file was not the correct size.
563    #[error("file was not the correct size")]
564    InvalidFile,
565}
566
567impl VaultKeyStorage for LocalVaultKeyStorage {
568    type Error = LocalVaultKeyStorageError;
569
570    fn vault_key_for(&self, server_id: StorageId) -> Result<Option<KeyPair>, Self::Error> {
571        let server_file = self.directory.join(server_id.to_string());
572        if !server_file.exists() {
573            return Ok(None);
574        }
575        let mut contents = File::open(server_file).and_then(|mut f| {
576            let mut bytes = Vec::new();
577            f.read_to_end(&mut bytes).map(|_| bytes)
578        })?;
579
580        let key = bincode::deserialize::<KeyPair>(&contents)?;
581        contents.zeroize();
582
583        Ok(Some(key))
584    }
585
586    fn set_vault_key_for(&self, server_id: StorageId, key: KeyPair) -> Result<(), Self::Error> {
587        let server_file = self.directory.join(server_id.to_string());
588        let bytes = bincode::serialize(&key)?;
589        File::create(server_file).and_then(|mut file| file.write_all(&bytes))?;
590        Ok(())
591    }
592}
593
594#[derive(Serialize, Deserialize)]
595struct VaultPayload<'a> {
596    // TODO make key_id be the additional data
597    key_id: KeyId,
598    key_version: u32,
599    encryption: Encryption,
600    payload: Cow<'a, [u8]>,
601    nonce: Cow<'a, [u8]>,
602}
603
604impl<'a> VaultPayload<'a> {
605    fn from_slice(bytes: &'a [u8]) -> Result<Self, Error> {
606        bincode::deserialize(bytes).map_err(|err| {
607            Error::Encryption(format!("error deserializing encrypted payload: {err:?}"))
608        })
609    }
610
611    fn to_vec(&self) -> Vec<u8> {
612        bincode::serialize(self).unwrap()
613    }
614}
615
616#[derive(Serialize, Deserialize)]
617struct HpkePayload {
618    encryption: PublicKeyEncryption,
619    payload: Bytes,
620    tag: [u8; 16],
621    encapsulated_key: <DhP256HkdfSha256 as Kem>::EncappedKey,
622}
623
624#[derive(Serialize, Deserialize)]
625enum Encryption {
626    XChaCha20Poly1305,
627}
628
629#[derive(Serialize, Deserialize)]
630enum PublicKeyEncryption {
631    DhP256HkdfSha256ChaCha20,
632}
633
634#[cfg(test)]
635mod tests {
636    use super::*;
637
638    #[derive(Debug)]
639    struct NullKeyStorage;
640    impl VaultKeyStorage for NullKeyStorage {
641        type Error = anyhow::Error;
642
643        fn set_vault_key_for(
644            &self,
645            _storage_id: StorageId,
646            _key: KeyPair,
647        ) -> Result<(), Self::Error> {
648            unreachable!()
649        }
650
651        fn vault_key_for(&self, _storage_id: StorageId) -> Result<Option<KeyPair>, Self::Error> {
652            unreachable!()
653        }
654    }
655
656    fn random_null_vault() -> Vault {
657        let mut master_keys = HashMap::new();
658        master_keys.insert(0, EncryptionKey::random());
659
660        let (_, public_key) = <DhP256HkdfSha256 as Kem>::gen_keypair(&mut thread_rng());
661
662        Vault {
663            _vault_public_key: PublicKey::P256(public_key),
664            master_keys,
665            current_master_key_id: 0,
666            master_key_storage: Arc::new(NullKeyStorage),
667        }
668    }
669
670    #[test]
671    fn vault_encryption_test() {
672        let vault = random_null_vault();
673        let encrypted = vault
674            .encrypt_payload(&KeyId::Master, b"hello", None)
675            .unwrap();
676        let decrypted = vault.decrypt_payload(&encrypted, None).unwrap();
677
678        assert_eq!(decrypted, b"hello");
679    }
680
681    #[test]
682    fn vault_permissions_test() {
683        let vault = random_null_vault();
684        assert!(matches!(
685            vault.encrypt_payload(&KeyId::Master, b"hello", Some(&Permissions::default()),),
686            Err(crate::Error::Core(bonsaidb_core::Error::PermissionDenied(
687                _
688            )))
689        ));
690        let encrypted = vault
691            .encrypt_payload(&KeyId::Master, b"hello", None)
692            .unwrap();
693        assert!(matches!(
694            vault.decrypt_payload(&encrypted, Some(&Permissions::default())),
695            Err(crate::Error::Core(bonsaidb_core::Error::PermissionDenied(
696                _
697            )))
698        ));
699    }
700}