1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
pub use self::model::*;
use data_encoding::HEXLOWER;
use sha2::{Digest, Sha256};
use std::sync::Arc;
use url::Url;
#[cfg(feature = "dropbox")]
pub mod dropbox;
mod error;
mod local_dir;
mod memory;
mod model;
#[cfg(feature = "sled")]
mod sled;
pub mod sync;
#[cfg(test)]
mod tests;
pub use self::error::{StoreError, StoreResult};
use crate::memguard::weak::ZeroingWords;
type RingId = (String, u64);
type RingContent = (u64, ZeroingWords);
/// Common interface of all block stores
///
/// In terms of persistence t-rust-less thinks in collections of blocks. Whereas a
/// block is just a chunk of bytes (i.e. Vec<u8>) stored on some sort of medium that
/// may or may not be available to the public (!!!).
///
/// To put it even more bluntly: All data inside a block has to be protected in a way
/// that the underlying medium might as well be twitter or facebook (not that I actually
/// suggest or plan to write an implementation of that sort). The blcok store in itself
/// is not responsible providing this sort of protection ... is just stores and organizes
/// blocks.
///
/// As a rule a block store is supposed to be distributed among multiple clients, each able
/// to asynchronously create additions to it.
///
/// All implementation are supposed to be thread-safe. Any kind of internal state has to be
/// protected accordingly.
///
pub trait BlockStore: std::fmt::Debug + Send + Sync {
/// Get the current node id.
///
/// Each accessor to a distributed store should have a unique id.
///
fn node_id(&self) -> &str;
/// Get list of ring block identifiers.
///
/// A store may contain any number of secrets rings. Usually associated with identities/users
/// that may access the store.
fn list_ring_ids(&self) -> StoreResult<Vec<RingId>>;
/// Get/read the ring block by its id.
///
/// Every identities/user should have a ring block containing all the relevant key material to
/// encrypt/descript all the other blocks.
///
/// Theses block should be protected by some sort of passphrase/key-derivation
///
fn get_ring(&self, ring_id: &str) -> StoreResult<RingContent>;
/// Set/write a ring block.
///
/// Implementors should ensure a sort of backup in case this operation fails, since
/// loosing the (private) ring will render the entire store useless to a user
///
fn store_ring(&self, ring_id: &str, version: u64, raw: &[u8]) -> StoreResult<()>;
/// Get all the change logs of the store.
///
/// The store has to keep track of all commits (see below).
///
fn change_logs(&self) -> StoreResult<Vec<ChangeLog>>;
/// Get the index block of a specific client/user.
///
/// An index block contains any sort of searchable index data referencing the
/// underlying data blocks. As the index might contain sensible data this block
/// has to be protected similar to a regular data block.
///
/// Index blocks should not be shared among clients or user. I.e. every client/user
/// should have its own set index blocks.
///
fn get_index(&self, index_id: &str) -> StoreResult<Option<ZeroingWords>>;
/// Store the index block of a specific client/user.
///
fn store_index(&self, index_id: &str, raw: &[u8]) -> StoreResult<()>;
/// Add a new data block to the store.
///
/// Data blocks contain the secret data shared between clients and should be
/// protected by the keys inside the ring block.
///
/// The result of an add operation is a unique key of the data block.
///
fn add_block(&self, raw: &[u8]) -> StoreResult<String>;
/// Get a block by its id.
///
fn get_block(&self, block: &str) -> StoreResult<ZeroingWords>;
/// Commit a set of changes to the store.
///
/// After adding one or more blocks to the store every client has to
/// commit its changes. This will create an entry in the `change_log` so that
/// other clients will notice the new data blocks.
///
fn commit(&self, changes: &[Change]) -> StoreResult<()>;
/// Update changelog of other nodes.
///
/// This is intended for store synchronization only.
fn update_change_log(&self, change_log: ChangeLog) -> StoreResult<()>;
}
pub fn open_block_store(url: &str, node_id: &str) -> StoreResult<Arc<dyn BlockStore>> {
let store_url = Url::parse(url)?;
match store_url.scheme() {
"file" => Ok(Arc::new(local_dir::LocalDirBlockStore::new(
store_url.to_file_path().unwrap(),
node_id,
)?)),
"memory" => Ok(Arc::new(memory::MemoryBlockStore::new(node_id))),
#[cfg(feature = "sled")]
"sled" => Ok(Arc::new(sled::SledBlockStore::new(
store_url.to_file_path().unwrap(),
node_id,
)?)),
#[cfg(feature = "dropbox")]
"dropbox" => Ok(Arc::new(dropbox::DropboxBlockStore::new(
store_url.username(),
store_url.host_str().unwrap(),
node_id,
)?)),
_ => Err(StoreError::InvalidStoreUrl(url.to_string())),
}
}
pub fn generate_block_id(data: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(data);
HEXLOWER.encode(&hasher.finalize())
}