Compare commits
No commits in common. "7b1908b6766590f45e7e6682aaf953614877146d" and "e44ee83e74629cbe36534ffff663475302e8d846" have entirely different histories.
7b1908b676
...
e44ee83e74
@ -3,10 +3,6 @@ name = "vault"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[features]
|
|
||||||
native = ["kv/native"]
|
|
||||||
wasm = ["kv/web"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
getrandom = { version = "0.3.3", features = ["wasm_js"] }
|
getrandom = { version = "0.3.3", features = ["wasm_js"] }
|
||||||
rand = "0.9.1"
|
rand = "0.9.1"
|
||||||
@ -17,4 +13,3 @@ serde_json = "1.0.140"
|
|||||||
chacha20poly1305 = "0.10.1"
|
chacha20poly1305 = "0.10.1"
|
||||||
k256 = { version = "0.13.4", features = ["ecdh"] }
|
k256 = { version = "0.13.4", features = ["ecdh"] }
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
kv = { git = "https://git.ourworld.tf/samehabouelsaad/sal-modular", package = "kvstore", rev = "9dce815daa" }
|
|
||||||
|
@ -1,29 +1,3 @@
|
|||||||
#[derive(Debug)]
|
|
||||||
/// Errors encountered while using the vault
|
|
||||||
pub enum Error {
|
|
||||||
/// An error during cryptographic operations
|
|
||||||
Crypto(CryptoError),
|
|
||||||
/// An error while performing an I/O operation
|
|
||||||
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 {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
match self {
|
|
||||||
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}")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::error::Error for Error {}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Errors generated by the vault or keys.
|
/// Errors generated by the vault or keys.
|
||||||
///
|
///
|
||||||
@ -44,7 +18,7 @@ pub enum CryptoError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl core::fmt::Display for CryptoError {
|
impl core::fmt::Display for CryptoError {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
CryptoError::InvalidKeySize => f.write_str("provided key is not the correct size"),
|
CryptoError::InvalidKeySize => f.write_str("provided key is not the correct size"),
|
||||||
CryptoError::EncryptionFailed => f.write_str("encryption failure"),
|
CryptoError::EncryptionFailed => f.write_str("encryption failure"),
|
||||||
@ -59,21 +33,3 @@ impl core::fmt::Display for CryptoError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl core::error::Error for CryptoError {}
|
impl core::error::Error for CryptoError {}
|
||||||
|
|
||||||
impl From<CryptoError> for Error {
|
|
||||||
fn from(value: CryptoError) -> Self {
|
|
||||||
Self::Crypto(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
|
||||||
fn from(value: std::io::Error) -> Self {
|
|
||||||
Self::IOError(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<kv::error::KVError> for Error {
|
|
||||||
fn from(value: kv::error::KVError) -> Self {
|
|
||||||
Self::KV(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
// #[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"))]
|
|
||||||
use kv::native::NativeStore;
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use kv::wasm::WasmStore;
|
|
||||||
|
|
||||||
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,
|
|
||||||
#[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<String, Key>,
|
|
||||||
/// The encryption key used to encrypt/decrypt this keyspace.
|
|
||||||
encryption_key: SymmetricKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wasm32 constructor
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
impl KeySpace {}
|
|
||||||
|
|
||||||
/// Non-wasm constructor
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
impl KeySpace {
|
|
||||||
/// Open the keyspace at the provided path using the given key for encryption.
|
|
||||||
pub fn open(path: &Path, encryption_key: SymmetricKey) -> Result<Self, Error> {
|
|
||||||
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<Self, Error> {
|
|
||||||
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.
|
|
||||||
pub async fn get(&self, key: &str) -> Result<Option<Key>, Error> {
|
|
||||||
Ok(self.keys.get(key).cloned())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Store a [`Key`] under the provided name.
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
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
|
|
||||||
pub async fn iter(&self) -> Result<impl Iterator<Item = (&String, &Key)>, 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!();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
use std::{collections::HashMap, io::Write, path::PathBuf};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
error::Error,
|
|
||||||
key::{Key, symmetric::SymmetricKey},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Magic value used as header in decrypted keyspace files.
|
|
||||||
const KEYSPACE_MAGIC: [u8; 14] = [
|
|
||||||
118, 97, 117, 108, 116, 95, 107, 101, 121, 115, 112, 97, 99, 101,
|
|
||||||
]; //"vault_keyspace"
|
|
||||||
|
|
||||||
/// A KeySpace using the filesystem as storage
|
|
||||||
pub struct KeySpace {
|
|
||||||
/// Path to file on disk
|
|
||||||
path: PathBuf,
|
|
||||||
/// Decrypted keys held in the store
|
|
||||||
keystore: HashMap<String, Key>,
|
|
||||||
/// The encryption key used to encrypt/decrypt the storage.
|
|
||||||
encryption_key: SymmetricKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeySpace {
|
|
||||||
/// Opens the `KeySpace`. If it does not exist, it will be created. The provided encryption key
|
|
||||||
/// will be used for Encrypting and Decrypting the content of the KeySpace.
|
|
||||||
async fn open(path: PathBuf, encryption_key: SymmetricKey) -> Result<Self, Error> {
|
|
||||||
/// If the path does not exist, create it first and write the encrypted magic header
|
|
||||||
if !path.exists() {
|
|
||||||
// Since we checked path does not exist, the only errors here can be actual IO errors
|
|
||||||
// (unless something else creates the same file at the same time).
|
|
||||||
let mut file = std::fs::File::create_new(path)?;
|
|
||||||
let content = encryption_key.encrypt(&KEYSPACE_MAGIC)?;
|
|
||||||
file.write_all(&content)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load file, try to decrypt, verify magic header, deserialize keystore
|
|
||||||
let mut file = std::fs::File::open(path)?;
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
file.read_to_end(&mut buffer)?;
|
|
||||||
if buffer.len() < KEYSPACE_MAGIC.len() {
|
|
||||||
return Err(Error::CorruptKeyspace);
|
|
||||||
}
|
|
||||||
|
|
||||||
if buffer[..KEYSPACE_MAGIC.len()] != KEYSPACE_MAGIC {
|
|
||||||
return Err(Error::CorruptKeyspace);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Actual deserialization
|
|
||||||
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a [`Key`] previously stored under the provided name.
|
|
||||||
async fn get(&self, key: &str) -> Result<Option<Key>, Error> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Store a [`Key`] under the provided name.
|
|
||||||
async fn set(&self, key: &str, value: Key) -> Result<(), Error> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete the [`Key`] stored under the provided name.
|
|
||||||
async fn delete(&self, key: &str) -> Result<(), Error> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate over all stored [`keys`](Key) in the KeySpace
|
|
||||||
async fn iter(&self) -> Result<impl Iterator<Item = (String, Key)>, Error> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
use crate::{error::Error, key::Key};
|
|
||||||
|
|
||||||
/// KeySpace represents an IndexDB keyspace
|
|
||||||
pub struct KeySpace {}
|
|
||||||
|
|
||||||
impl KeySpace {
|
|
||||||
/// Get a [`Key`] previously stored under the provided name.
|
|
||||||
async fn get(&self, key: &str) -> Result<Option<Key>, Error> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Store a [`Key`] under the provided name.
|
|
||||||
async fn set(&self, key: &str, value: Key) -> Result<(), Error> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete the [`Key`] stored under the provided name.
|
|
||||||
async fn delete(&self, key: &str) -> Result<(), Error> {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate over all stored [`keys`](Key) in the KeySpace
|
|
||||||
async fn iter(&self) -> Result<impl Iterator<Item = (String, Key)>, Error> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod key;
|
pub mod key;
|
||||||
pub mod keyspace;
|
|
||||||
pub mod kvs;
|
pub mod kvs;
|
||||||
|
Loading…
Reference in New Issue
Block a user