1use 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#[derive(Serialize, Deserialize)]
77pub enum KeyPair {
78 P256 {
80 private: <DhP256HkdfSha256 as Kem>::PrivateKey,
82 public: <DhP256HkdfSha256 as Kem>::PublicKey,
84 },
85}
86
87impl KeyPair {
88 pub fn to_bytes(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
90 Ok(Zeroizing::new(bincode::serialize(self)?))
91 }
92
93 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
95 bincode::deserialize(bytes).map_err(Error::from)
96 }
97}
98
99#[derive(Serialize, Deserialize)]
101pub enum PublicKey {
102 P256(<DhP256HkdfSha256 as Kem>::PublicKey),
104}
105
106impl PublicKey {
107 pub fn to_bytes(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
109 Ok(Zeroizing::new(bincode::serialize(self)?))
110 }
111
112 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#[derive(thiserror::Error, Debug)]
147pub enum Error {
148 #[error("error with encryption: {0}")]
150 Encryption(String),
151 #[error("error from vault key storage: {0}")]
153 VaultKeyStorage(String),
154 #[error("error occurred while initializing: {0}")]
156 Initializing(String),
157 #[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 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 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 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 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
383pub trait VaultKeyStorage: Send + Sync + Debug + 'static {
385 type Error: Display;
387 fn set_vault_key_for(&self, storage_id: StorageId, key: KeyPair) -> Result<(), Self::Error>;
389
390 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 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
492pub trait AnyVaultKeyStorage: Send + Sync + Debug + 'static {
496 fn vault_key_for(&self, storage_id: StorageId) -> Result<Option<KeyPair>, Error>;
498
499 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#[derive(Debug, Clone)]
534pub struct LocalVaultKeyStorage {
535 directory: PathBuf,
536}
537
538impl LocalVaultKeyStorage {
539 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#[derive(thiserror::Error, Debug)]
553pub enum LocalVaultKeyStorageError {
554 #[error("io error: {0}")]
556 Io(#[from] std::io::Error),
557
558 #[error("serialization error: {0}")]
560 Serialization(#[from] bincode::Error),
561
562 #[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 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}