...
 
Commits (631)
......@@ -3,9 +3,9 @@ stages:
- build
- publish
# Build the Rust root on nightly
build:beta:
image: instrumentisto/rust:1.39.0-beta
# Build the Rust root on a pinned stable
build:
image: rust:1.42.0-buster
script:
- cargo test
- cargo build
......@@ -13,7 +13,7 @@ build:beta:
# Run test suite
test_webgui:
stage: test
image: circleci/node:10-browsers
image: circleci/node:12-browsers
cache:
key: yarn
paths:
......@@ -22,10 +22,12 @@ test_webgui:
- cd webgui
- yarn install --pure-lockfile --cache-folder .yarn
- yarn run test
- yarn run lint:hbs
- yarn run lint:js
build_webgui:
stage: build
image: node:10
image: node:12
cache:
key: yarn
paths:
......
This source diff could not be displayed because it is too large. You can view the blob instead.
[workspace]
members = [
# Main storage database
"alexandria",
# qaul.net service library
# main qaul.net service library
"libqaul",
# libqaul additions
"libqaul/http-api",
"libqaul/http",
"libqaul/ipc",
"libqaul/rpc",
"libqaul/ws",
# qaul.net main services
"libqaul/service/messaging",
"libqaul/service/chat",
# "libqaul/service/feed",
"libqaul/service/files",
"libqaul/service/voices",
# decentralised routing protocol
"ratman",
"ratman/harness",
"ratman/identity",
"ratman/netmod",
"ratman/configure",
# A high-level network simulator
# "service-sim",
"visn",
# various utility crates
"async-notify",
"clockctrl",
"permute",
# Available netmod drivers
"visn",
# available netmod drivers
"netmod-mem",
"netmod-tcp",
"netmod-udp",
"netmod-wd",
# android build support
"librobot",
# test binaries
"clients/multinode-test",
"clients/linux-http-test",
"clients/linux-voice-test",
"clients/mini-tcp",
# Client specific targets
"clients/linux",
# Actual target binaries
"clients/linux-cli",
# "clients/http-test"
"clients/hubd",
]
![http://qaul.net/](https://raw.githubusercontent.com/qaul/qaul.net/release-1.0.0/doc/qaul-net.png)
![http://qaul.net/](https://git.open-communication.net/qaul/qaul.net/raw/release-1.0.0/doc/qaul-net.png)
# qaul.net [![pipeline status](https://git.open-communication.net/qaul/qaul.net/badges/master/pipeline.svg)](https://git.open-communication.net/qaul/qaul.net/commits/master)
......@@ -17,36 +17,52 @@ what so ever.
The project is currently being re-written for a more modular and
portable approach. The new Release will be qaul.net 2.0. Please check
our milestones & issues to get an idea of the development plan and
status. If you want to get involved, please [get in
touch][contributors-guide]!
our [milestones] & [issues] to get an idea of the development plan and
status. If you want to get involved, see how to [participate] and read
the [contributors-guide].
For the latest stable release, check the [`release-1.0.0`][release]
branch.
[contributors-guide]: /contributors/social/_intro.html
[release]: https://github.com/qaul/qaul.net/tree/release-1.0.0
[milestones]: https://git.open-communication.net/groups/qaul/-/milestones
[issues]: https://git.open-communication.net/qaul/qaul.net/issues
[participate]: https://qaul.net/#participation
[contributors-guide]: https://docs.qaul.net/contributors/
[release]: https://git.open-communication.net/qaul/qaul.net/tree/release-1.0.0
## Build Instructions
The project is being re-written in Rust, thus using [cargo][cargo] as
a build system. If you don't have Rust installed, you can get it
[here](https://rustup.sh) or via your OS.
The qaul.net project has many libraries and clients, for different
platforms. Check the "clients" directory for instructions on how to
build them. Because some platforms require some bootstrapping you may
have to build different parts in sequence: we don't currently have an
overarching build system for this.
[cargo]: https://crates.io/
To build the rust libraries for most platforms, simply run `cargo
build --release` (for release mode). To build android, check the
[`build.sh`](./clients/android/build.sh) in that client. The web UI
is built with emberJS and con be found [here](webgui).
To build the web stack on Linux, you can build the ember UI with
`ember dist`, then move the output to `libqaul/http/ui`, so that they
can be included in the web server, which will then serve them via
`clients/linux-http-test` or `clients/android`.
The repo has a `shell.nix` if you want to use nix to get dependencies,
however this doesn't actually build the project.
## Documentation
Documentation is avaliable [here](https://docs.qaul.net).
Documentation is available on [docs.qaul.net](https://docs.qaul.net).
## License
qaul.net is free and open source software licensed under the [GNU
Affero General Public License version 3 or
later](licenses/gpl-3.0.md).
later](licenses/agpl-3.0.md).
**Additional Permissions:** For Submission to the Apple App Store:
Provided that you are otherwise in compliance with the AGPLv3 for each
......
/target
**/*.rs.bk
Cargo.lock
\ No newline at end of file
stages:
- build
- test
build:nightly:
image: rustlang/rust:nightly
script:
- cargo build
test:stable:
image: rustlang/rust:nightly
script:
- cargo test --all
\ No newline at end of file
[package]
name = "alexandria"
description = "An encrypted document-oriented database with tag based query support"
version = "0.2.0"
authors = ["Katharina Fey <kookie@spacekookie.de>"]
repository = "https://git.open-communication.net/qaul/alexandria"
documentation = "https://docs.rs/alexandria"
license = "GPL-3.0-or-later"
edition = "2018"
[dependencies]
id = { version = "0.4", path = "../ratman/identity", features = ["digest", "random", "aligned"], package = "ratman-identity" }
async-std = { version = "1.0", features = ["unstable", "attributes"] }
bincode = "1.0"
failure = "0.1"
hex = "0.4"
keybob = "0.3"
serde = { version = "1.0", features = ["derive", "rc"] }
sodiumoxide = "0.2.5"
tracing = "0.1"
tracing-futures = "0.2"
[dev-dependencies]
bincode = "1.0"
ed25519-dalek = "1.0.0-pre.3"
rand = "0.7"
serde_json = "1.0"
tempfile = "3.0"
This diff is collapsed.
# alexandria 📚 [![][irc-badge]][irc-url]
[irc-badge]: https://img.shields.io/badge/IRC-%23qaul.net-1e72ff.svg
[irc-url]: https://www.irccloud.com/invite?channel=%23qaul.net&hostname=irc.freenode.org&port=6697&ssl=1
Strongly typed, embedded record database with seemless encryption at
rest storage. Supports key-value Diff transactions, as well as
externally loaded binary payloads. Supports encrypted metadata
without extra configuration.
Alexandria has the following features:
- Store data on internal db path
- Query the database by path or dynamic search tags
- Subscribe to events based on query
- Iterate over query dynamically
- Store data in session or global namespaces
**Notice:** alexandria should be considered experimental and not used
in production systems where data loss is unacceptable.
## How to use
Alexandria requires `rustc` 1.42 to compile.
```rust
use alexandria::{Library, Builder};
use tempfile::tempdir();
let dir = tempdir().unwrap();
let lib = Builder::new()
.offset(dir.path())
.root_sec("car horse battery staple")
.build()?
```
Alexandria is developed as part of [qaul.net][website]. We have a
[mailing list][list] and an [IRC channel][irc]! Please come by and ask
us questions! (the issue tracker is a bad place to ask questions)
[website]: https://qaul.net
[list]: https://lists.sr.ht/~qaul/community/
[irc]: https://irccloud.com/freenode/#qaul.net
## License
Alexandria is free software and part of [qaul.net][qaul.net]. You
are free to use, modify and redistribute the source code under the
terms of the GNU General Public License 3.0 or (at your choice) any
later version. For a full copy of the license, see `LICENSE` in the
source directory attached.
**Additional Permissions:** For Submission to the Apple App Store:
Provided that you are otherwise in compliance with the GPLv3 for each
covered work you convey (including without limitation making the
Corresponding Source available in compliance with Section 6 of the
GPLv3), the qaul.net developers also grant you the additional
permission to convey through the Apple App Store non-source executable
versions of the Program as incorporated into each applicable covered
work as Executable Versions only under the Mozilla Public License
version 2.0.
A copy of both the GPL-3.0 and MPL-2.0 license texts are included in
this repository.
//! Alexandria internal caching system
//!
//! The caches are divided into a hot cache, which is in active
//! rotation and <user-id>-<zone>-<record> indexed, and a cold cache
//! which can be used to pre-validate a set of changes, which is
//! <delta-id> indexed. Because each transaction is assigned a new
//! delta id,
use crate::{
crypto::{asym::KeyPair, DetachedKey, EncryptedMap},
notify::{Lock, LockNotify, Notify},
Id,
};
use async_std::{sync::Arc, task};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{
cmp::{Ord, Ordering as Order /* slurred yelling */, PartialOrd},
hash::Hash,
path::PathBuf,
};
/// A key that expresses an (id, zone) tuple
#[derive(Serialize, Deserialize, Eq, PartialEq, Hash)]
pub(crate) struct CombKey {
pub(crate) id: Id,
pub(crate) zone: String,
}
impl Ord for CombKey {
fn cmp(&self, other: &Self) -> Order {
self.id.cmp(&other.id).then(self.zone.cmp(&other.zone))
}
}
impl PartialOrd for CombKey {
fn partial_cmp(&self, other: &Self) -> Option<Order> {
self.id.partial_cmp(&other.id).and_then(|id| {
self.zone
.partial_cmp(&other.zone)
.and_then(|zone| Some(id.then(zone)))
})
}
}
impl ToString for CombKey {
fn to_string(&self) -> String {
format!("{}/{}", self.id, self.zone)
}
}
/// An Arc reference to a cache
pub(crate) type CacheRef<K, V> = Arc<Cache<K, V>>;
pub(crate) struct Cache<K, V>
where
K: Serialize + DeserializeOwned + Ord + PartialOrd + Hash + ToString,
V: DetachedKey<KeyPair> + Serialize + DeserializeOwned,
{
/// Cache from K -> V with an asymmetric encryption key
cache: LockNotify<EncryptedMap<K, Notify<V>, KeyPair>>,
/// The path the cache is written to
path: Option<PathBuf>,
}
impl<K, V> Cache<K, V>
where
K: Serialize + DeserializeOwned + Ord + PartialOrd + Hash + ToString,
V: DetachedKey<KeyPair> + Serialize + DeserializeOwned,
{
/// Create a new in-memory cache
pub(crate) fn new<P>(path: P) -> CacheRef<K, V>
where
P: Into<Option<PathBuf>>,
{
Arc::new(Self {
cache: Notify::new(Lock::new(EncryptedMap::new())),
path: path.into(),
})
}
/// Set the cache to hot, enabling write-through caching
///
/// Reversing this option is not possible. A hot cache does
/// write-through caching to disk, meaning that changes are
/// mirrored to disk immediately to avoid data loss when crashing.
/// By default the cache can be used to improve in-memory lookups,
/// but will not be persistent across reboots.
pub(crate) fn hot(self: Arc<Self>) {
let path = self.path.as_ref().expect("Can't set cache without path to 'hot'");
task::spawn(async move {});
}
}
This diff is collapsed.
use crate::{
dir::Dirs,
error::Result,
meta::{tags::TagCache, users::UserTable},
query::SubHub,
store::Store,
Library,
};
use async_std::sync::{Arc, RwLock};
use std::{path::Path, result::Result as StdResult};
/// A utility to configure and initialise an alexandria database
///
/// To load an existing database from disk, look at
/// [`Library::load()`][load]!
///
/// [load]: struct.Library.html#load
///
/// ```
/// # use alexandria::{Builder, Library, error::Result};
/// # use tempfile::tempdir;
/// # fn test() -> Result<()> {
/// let dir = tempdir().unwrap();
/// let lib = Builder::new()
/// .offset(dir.path())
/// .root_sec("car horse battery staple")
/// .build()?;
/// # drop(lib);
/// # Ok(()) }
/// ```
#[derive(Default)]
pub struct Builder {
/// The main offset path
offset: Option<String>,
}
impl Builder {
pub fn new() -> Self {
Self::default()
}
/// Inspect a path to load an existing alexandria library
///
/// If no library exists at the path yet (or the path doesn't
/// exist), the `Err(_)` variant is a new builder with an
/// initialised `offset` that can then be used to create a new
/// database.
pub fn inspect_path<'tmp, P, S>(offset: P, _: S) -> StdResult<Arc<Library>, Self>
where
P: Into<&'tmp Path>,
S: Into<String>,
{
let p: &Path = offset.into();
// If the path doesn't exist it can't be a database
if !p.exists() {
return Err(Self::new().offset(p));
}
// TODO: Check for a magic file here
// TODO: load database with provided root secret
let root = Dirs::new(p);
let users = RwLock::new(UserTable::new());
let tag_cache = RwLock::new(TagCache::new());
let store = RwLock::new(Store::new());
let subs = SubHub::new();
Ok(Arc::new(Library {
root,
users,
tag_cache,
store,
subs,
}))
}
/// Specify a normal path offset
///
/// This will act as the root metadata store. On multi-user
/// devices it needs to be a directory that's accessibly from the
/// daemon that owns the alexandria scope.
pub fn offset<'tmp, P: Into<&'tmp Path>>(self, offset: P) -> Self {
let p: &Path = offset.into();
let offset = p.to_str().map(|s| s.to_string());
Self { offset, ..self }
}
/// Some secret that will be used for the root namespace
///
/// When loading a library from disk in a future session, this
/// secret will have to be provided to [`Library::load()`][load]
///
/// [load]: struct.Library.html#load
pub fn root_sec<S: Into<String>>(self, _: S) -> Self {
self
}
/// Consume the builder and create a Library
pub fn build(self) -> Result<Arc<Library>> {
let root = Dirs::new(
self.offset
.expect("Builder without `offset` cannot be built"),
);
let users = RwLock::new(UserTable::new());
let tag_cache = RwLock::new(TagCache::new());
let store = RwLock::new(Store::new());
let subs = SubHub::new();
Library {
root,
users,
tag_cache,
store,
subs,
}
.init()
.map(|l| Arc::new(l))
}
}
//! Fundamental API types
mod sessions;
pub use sessions::{Session, SessionsApi, GLOBAL};
mod builder;
pub use builder::Builder;
mod api;
pub use api::Library;
//! User management API scope
use crate::{core::Library, error::Result, utils::Id};
use serde::{Deserialize, Serialize};
/// Represents a database session
///
/// A session is either bound to the global scope (meaning the
/// lifetime of the database in memory), or a specific id, which you
/// can yield via `id()`. To learn more about sessions, have a look
/// at the [`SessionsApi`][api]!
///
/// [api]: struct.SessionsApi.html
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Session {
/// The global session, accessed on `load()`
Global,
/// A user-specific session with separate key tree
Id(Id),
}
impl Session {
/// Get the inner Id, if applicable
pub(crate) fn id(&self) -> Option<Id> {
match self {
Self::Id(id) => Some(*id),
Self::Global => None,
}
}
}
impl From<Id> for Session {
fn from(id: Id) -> Self {
Self::Id(id)
}
}
/// Convenience type to represent the global namespace
pub const GLOBAL: Session = Session::Global;
/// Api scope wrapper for database sessions
///
/// A session is some random Id which is used as a namespace
/// identifier. Each session namespace is encrypted independently,
/// with a unique key, and record tree. This means that two paths
/// (say `/foo:bar`) can point to two separate records in the
/// database, if they belong to different sessions.
///
/// An important distiction to make here: a session is not ephemeral
/// but a long living namespace Id, similar to a user in a traditional
/// database.
pub struct SessionsApi<'alex> {
pub(crate) inner: &'alex Library,
}
impl<'alex> SessionsApi<'alex> {
/// List available sessions in this database
pub async fn list(&self) -> Vec<Id> {
vec![]
}
/// Open a previously created session
pub async fn open(&self, id: Id, pw: &str) -> Result<Session> {
let ref mut u = self.inner.users.write().await;
u.open(id, pw).map(|_| Session::Id(id))
}
/// Close an active session
pub async fn close(&self, id: Session) -> Result<()> {
if let Some(id) = id.id() {
let ref mut u = self.inner.users.write().await;
u.close(id)
} else {
Ok(())
}
}
/// Create a new session with a unique encryption key
pub async fn create(&self, id: Id, pw: &str) -> Result<Session> {
let ref mut u = self.inner.users.write().await;
u.insert(id, pw).map(|_| Session::Id(id))
}
/// Remove a session Id and corresponding data from the database
pub async fn destroy(&self, id: Session) -> Result<()> {
if let Some(id) = id.id() {
let ref mut u = self.inner.users.write().await;
u.delete(id)
} else {
Ok(())
}
}
}
//! Symmetric cipher utilities
//!
//! These functions are only used to bootstrap the unlocking process
//! for the database user table. For all other cryptographic
//! operations, see the `asym` module instead.
use crate::{
crypto::{CipherText, Encrypter},
error::{Error, Result},
wire::Encoder,
};
use keybob::{Key as KeyBuilder, KeyType};
use serde::{de::DeserializeOwned, Serialize};
use sodiumoxide::crypto::secretbox::{gen_nonce, open, seal, Nonce};
// Make it easier for alexandria internals to use this type
pub(crate) use sodiumoxide::crypto::secretbox::Key;
pub(crate) trait Constructor {
/// Create an AES symmetric key from a user password and salt
fn from_pw(pw: &str, salt: &str) -> Self;
}
impl Constructor for Key {
fn from_pw(pw: &str, salt: &str) -> Self {
let kb = KeyBuilder::from_pw(KeyType::Aes128, pw, salt);
Self::from_slice(kb.as_slice()).unwrap()
}
}
impl<T> Encrypter<T> for Key
where
T: Encoder<T> + Serialize + DeserializeOwned,
{
fn seal(&self, data: &T) -> Result<CipherText> {
let nonce = gen_nonce();
let encoded = data.encode()?;
let data = seal(&encoded, &nonce, self);
Ok(CipherText {
nonce: nonce.0.iter().cloned().collect(),
data,
})
}
fn open(&self, data: &CipherText) -> Result<T> {
let CipherText {
ref nonce,
ref data,
} = data;
let nonce = Nonce::from_slice(nonce.as_slice()).ok_or(Error::InternalError {
msg: "Failed to read nonce!".into(),
})?;
let clear = open(data.as_slice(), &nonce, self).map_err(|_| Error::InternalError {
msg: "Failed to decrypt data".into(),
})?;
Ok(T::decode(&clear)?)
}
}
#[test]
fn key_is_key() {
let k1 = KeyBuilder::from_pw(KeyType::Aes128, "password", "salt");
let k2 = KeyBuilder::from_pw(KeyType::Aes128, "password", "salt");
assert_eq!(k1, k2);
}
//! Asymmetric cryto utilities
use crate::{
crypto::{CipherText, Encrypter},
error::{Error, Result},
wire::Encoder,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use sodiumoxide::crypto::box_::{self, Nonce, PublicKey, SecretKey};
pub(crate) type SharedKey = KeyPair;
/// Both public and private keys for a user
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct KeyPair {
pub_: PublicKey,
sec: SecretKey,
}
impl KeyPair {
/// Create a new tree of keys
pub(crate) fn new() -> Self {
let (pub_, sec) = box_::gen_keypair();
Self { pub_, sec }
}
}
impl<T> Encrypter<T> for KeyPair
where
T: Encoder<T> + Serialize + DeserializeOwned,
{
fn seal(&self, data: &T) -> Result<CipherText> {
let non = box_::gen_nonce();
let enc = data.encode()?;
let data = box_::seal(&enc, &non, &self.pub_, &self.sec);
let nonce = non.0.iter().cloned().collect();
Ok(CipherText { nonce, data })
}
fn open(&self, data: &CipherText) -> Result<T> {
let CipherText {
ref nonce,
ref data,
} = data;
let nonce = Nonce::from_slice(nonce.as_slice()).ok_or(Error::InternalError {
msg: "Failed to read nonce!".into(),
})?;
let clear = box_::open(data.as_slice(), &nonce, &self.pub_, &self.sec).map_err(|_| {
Error::InternalError {
msg: "Failed to decrypt data".into(),
}
})?;
Ok(T::decode(&clear)?)
}
}
#[test]
fn sign_and_encrypt() {
use ed25519_dalek::Keypair as DKP;
use rand::rngs::OsRng;
let mut rng = OsRng {};
let DKP { secret, public } = DKP::generate(&mut rng);
let nacl_pair = KeyPair {
sec: SecretKey::from_slice(secret.as_bytes()).unwrap(),
pub_: PublicKey::from_slice(public.as_bytes()).unwrap(),
};
let dalek_pair = DKP { secret, public };
let message = "this can be signed and encrypted!";
// Try to sign data
let sign = dalek_pair.sign(message.as_bytes());
// Encrypt the message
let ctext = nacl_pair
.seal(&message.as_bytes().iter().cloned().collect::<Vec<u8>>())
.unwrap();
// Verify signature
dalek_pair.verify(message.as_bytes(), &sign).unwrap();
// Decrypt secret
nacl_pair.open(&ctext).unwrap()
}
//! An encrypted map datastructure
use crate::{
crypto::{DetachedKey, Encrypted, Encrypter},
error::{Error, Result},
};
use async_std::sync::Arc;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{
collections::BTreeMap,
fmt::Debug,
hash::Hash,
ops::{Deref, DerefMut},
};
/// A mapper around encrypted data in a map
#[derive(Serialize, Deserialize)]
pub(crate) struct EncryptedMap<K, V, Q>
where
K: Serialize + DeserializeOwned + Ord + PartialOrd + Hash + ToString,
V: Clone + DetachedKey<Q> + Serialize + DeserializeOwned,
Q: Encrypter<V>,
{
#[serde(bound(deserialize = "V: DeserializeOwned"))]
inner: BTreeMap<K, Encrypted<V, Q>>,
}
impl<K, V, Q> EncryptedMap<K, V, Q>
where
K: Serialize + DeserializeOwned + Ord + PartialOrd + Hash + ToString,
V: Clone + DetachedKey<Q> + Serialize + DeserializeOwned,
Q: Encrypter<V>,
{
/// Create a new encrypted map
pub(crate) fn new() -> Self {
Self {
inner: Default::default(),
}
}
/// Open an entry in the map with a key
pub(crate) fn open(&mut self, id: K, key: &Q) -> Result<()> {
match self.inner.get_mut(&id) {
Some(entry) => Ok(entry.open(key)?),
None => Err(Error::UnlockFailed { id: id.to_string() }),
}
}
/// Close an entry in the map
pub(crate) fn close<P>(&mut self, id: K, key: P) -> Result<()>
where
P: Into<Option<Arc<Q>>>,
{
let key = key.into();
match self.inner.get_mut(&id) {
Some(entry) => {
let key = entry
.key()
.or(key)
.expect("No key provided for `open` operation");
Ok(entry.close(key)?)
}
None => Err(Error::UnlockFailed { id: id.to_string() }),
}
}
/// Get a reference to the mapped value, if opened
pub(crate) fn get(&self, id: K) -> Result<&V> {
match self.inner.get(&id) {
Some(Encrypted::Open(ref data)) => Ok(data),
Some(Encrypted::Closed(_)) => Err(Error::InternalError {
msg: "Tried reading ::Closed variant".into(),
}),
Some(Encrypted::Never(_)) => unreachable!(),
None => Err(Error::InternalError {
msg: "No data for key".into(),
}),
}
}
/// Get a mutable reference to the mapped value, if opened
pub(crate) fn get_mut(&mut self, id: K) -> Result<&mut V> {
match self.inner.get_mut(&id) {
Some(Encrypted::Open(ref mut data)) => Ok(data),
Some(Encrypted::Closed(_)) => Err(Error::InternalError {
msg: "Tried reading ::Closed variant".into(),
}),
Some(Encrypted::Never(_)) => unreachable!(),
None => Err(Error::InternalError {
msg: "No data for key".into(),
}),
}
}
}
impl<K, V, Q> Deref for EncryptedMap<K, V, Q>
where
K: Serialize + DeserializeOwned + Ord + PartialOrd + Hash + ToString,
V: Clone + DetachedKey<Q> + Serialize + DeserializeOwned,
Q: Encrypter<V> + Debug,
{
type Target = BTreeMap<K, Encrypted<V, Q>>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<K, V, Q> DerefMut for EncryptedMap<K, V, Q>
where
K: Serialize + DeserializeOwned + Ord + PartialOrd + Hash + ToString,
V: Clone + DetachedKey<Q> + Serialize + DeserializeOwned,
Q: Encrypter<V> + Debug,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
//! Provides more convenient crypto wrappers
#![allow(unused)]
pub(crate) mod aes;
pub(crate) mod asym;
mod map;
pub(crate) use map::EncryptedMap;
use crate::{
error::{Error, Result},
utils::Id,
wire::Encoder,
};
use async_std::sync::Arc;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{fmt::Debug, marker::PhantomData};
/// An encrypted piece of data
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub(crate) struct CipherText {
/// Number only used once
nonce: Vec<u8>,
/// Data buffer
data: Vec<u8>,
}
/// A trait that encrypts data on an associated key
pub(crate) trait Encrypter<T>
where
T: Encoder<T>,
{
fn seal(&self, data: &T) -> Result<CipherText>;
fn open(&self, data: &CipherText) -> Result<T>;
}
/// A type that can provide an out-of-band key
///
/// Sometimes a type that is stored inside the `Encrypted` can bring
/// it's own key, to avoid having to have a second control-structure
/// for the keys.
pub(crate) trait DetachedKey<K> {
fn key(&self) -> Option<Arc<K>> {
None