From 7b1908b6766590f45e7e6682aaf953614877146d Mon Sep 17 00:00:00 2001 From: Lee Smet Date: Wed, 14 May 2025 19:21:02 +0200 Subject: [PATCH] Use kvstore as backing Signed-off-by: Lee Smet --- vault/Cargo.toml | 5 +++ vault/src/error.rs | 9 ++++ vault/src/keyspace.rs | 100 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 95 insertions(+), 19 deletions(-) diff --git a/vault/Cargo.toml b/vault/Cargo.toml index 4033186..e847f9f 100644 --- a/vault/Cargo.toml +++ b/vault/Cargo.toml @@ -3,6 +3,10 @@ name = "vault" version = "0.1.0" edition = "2024" +[features] +native = ["kv/native"] +wasm = ["kv/web"] + [dependencies] getrandom = { version = "0.3.3", features = ["wasm_js"] } rand = "0.9.1" @@ -13,3 +17,4 @@ serde_json = "1.0.140" chacha20poly1305 = "0.10.1" k256 = { version = "0.13.4", features = ["ecdh"] } sha2 = "0.10.9" +kv = { git = "https://git.ourworld.tf/samehabouelsaad/sal-modular", package = "kvstore", rev = "9dce815daa" } diff --git a/vault/src/error.rs b/vault/src/error.rs index 1c2366b..5613f7e 100644 --- a/vault/src/error.rs +++ b/vault/src/error.rs @@ -7,6 +7,8 @@ pub enum Error { IOError(std::io::Error), /// A corrupt keyspace is returned if a keyspace can't be decrypted CorruptKeyspace, + /// An error in the used key value store + KV(kv::error::KVError), } impl core::fmt::Display for Error { @@ -15,6 +17,7 @@ impl core::fmt::Display for Error { Error::Crypto(e) => f.write_fmt(format_args!("crypto: {e}")), Error::IOError(e) => f.write_fmt(format_args!("io: {e}")), Error::CorruptKeyspace => f.write_str("corrupt keyspace"), + Error::KV(e) => f.write_fmt(format_args!("kv: {e}")), } } } @@ -68,3 +71,9 @@ impl From for Error { Self::IOError(value) } } + +impl From for Error { + fn from(value: kv::error::KVError) -> Self { + Self::KV(value) + } +} diff --git a/vault/src/keyspace.rs b/vault/src/keyspace.rs index 86208a6..775b400 100644 --- a/vault/src/keyspace.rs +++ b/vault/src/keyspace.rs @@ -1,21 +1,39 @@ -#[cfg(target_arch = "wasm32")] -mod wasm; +// #[cfg(not(target_arch = "wasm32"))] +// mod fallback; +// #[cfg(target_arch = "wasm32")] +// mod wasm; + +use std::{collections::HashMap, path::Path}; + +use crate::{ + error::Error, + key::{Key, symmetric::SymmetricKey}, +}; + +// #[cfg(not(target_arch = "wasm32"))] +// use fallback::KeySpace as Ks; +// #[cfg(target_arch = "wasm32")] +// use wasm::KeySpace as Ks; #[cfg(not(target_arch = "wasm32"))] -mod fallback; - +use kv::native::NativeStore; #[cfg(target_arch = "wasm32")] -use wasm::KeySpace as Ks; +use kv::wasm::WasmStore; -#[cfg(not(target_arch = "wasm32"))] -use fallback::KeySpace as Ks; - -use crate::{error::Error, key::Key}; +const KEYSPACE_NAME: &str = "vault_keyspace"; /// A keyspace represents a group of stored cryptographic keys. The storage is encrypted, a /// password must be provided when opening the KeySpace to decrypt the keys. pub struct KeySpace { - store: Ks, + // store: Ks, + #[cfg(not(target_arch = "wasm32"))] + store: NativeStore, + #[cfg(target_arch = "wasm32")] + store: WasmStore, + /// A collection of all keys stored in the KeySpace, in decrypted form. + keys: HashMap, + /// The encryption key used to encrypt/decrypt this keyspace. + encryption_key: SymmetricKey, } /// Wasm32 constructor @@ -24,27 +42,71 @@ impl KeySpace {} /// Non-wasm constructor #[cfg(not(target_arch = "wasm32"))] -impl KeySpace {} +impl KeySpace { + /// Open the keyspace at the provided path using the given key for encryption. + pub fn open(path: &Path, encryption_key: SymmetricKey) -> Result { + let store = NativeStore::open(&path.display().to_string())?; + let mut ks = Self { + store, + keys: HashMap::new(), + encryption_key, + }; + ks.load_keyspace()?; + Ok(ks) + } +} + +#[cfg(target_arch = "wasm32")] +impl KeySpace { + pub async fn open(name: &str, encryption_key: SymmetricKey) -> Result { + let store = WasmStore::open(name).await?; + todo!(); + // Ok(Self { store }) + } +} /// Exposed methods, platform independant impl KeySpace { /// Get a [`Key`] previously stored under the provided name. - async fn get(&self, key: &str) -> Result, Error> { - todo!(); + pub async fn get(&self, key: &str) -> Result, Error> { + Ok(self.keys.get(key).cloned()) } /// Store a [`Key`] under the provided name. - async fn set(&self, key: &str, value: Key) -> Result<(), Error> { - todo!(); + /// + /// This overwrites the existing key if one is already stored with the same name. + pub async fn set(&mut self, key: String, value: Key) -> Result<(), Error> { + self.keys.insert(key, value); + self.save_keyspace() } /// Delete the [`Key`] stored under the provided name. - async fn delete(&self, key: &str) -> Result<(), Error> { - todo!(); + pub async fn delete(&mut self, key: &str) -> Result<(), Error> { + self.keys.remove(key); + self.save_keyspace() } /// Iterate over all stored [`keys`](Key) in the KeySpace - async fn iter(&self) -> Result, Error> { - todo!() + pub async fn iter(&self) -> Result, Error> { + Ok(self.keys.iter()) + } + + /// Encrypt all keys and save them to the underlying store + async fn save_keyspace(&self) -> Result<(), Error> { + // Bincode encode keys + // + // Put in store + } + + /// Loads the encrypted keyspace from the underlying storage + async fn load_keyspace(&mut self) -> Result<(), Error> { + let Some(ks) = self.store.get(KEYSPACE_NAME).await? else { + // Keyspace doesn't exist yet, nothing to do here + return Ok(()); + }; + + // TODO: bincode decode + + todo!(); } }