diff --git a/coffer-common/src/certificate.rs b/coffer-common/src/certificate.rs index 9431184..69e975d 100644 --- a/coffer-common/src/certificate.rs +++ b/coffer-common/src/certificate.rs @@ -1,11 +1,11 @@ -//! A keypair contianer providing functionality for signing, encryption and +//! A keypair container providing functionality for signing, encryption and //! decryption //! //! # Base libraries //! The cryptographic operations exposed by this module are based on the //! [NaCl](http://nacl.cr.yp.to/) fork [libsodium](https://libsodium.org) as //! exposed by the rust bindings [sodiumoxide](https://crates.io/crates/sodiumoxide). - +//! #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; @@ -19,8 +19,7 @@ use std::{ use quick_error::quick_error; use seckey::SecKey; -use sodiumoxide::crypto::box_; -use sodiumoxide::crypto::sealedbox; +use sodiumoxide::crypto::{box_, sealedbox}; use serde::{Serialize, Deserialize}; use serde_cbor; diff --git a/coffer-common/src/coffer.rs b/coffer-common/src/coffer.rs index b43ac17..3f86581 100644 --- a/coffer-common/src/coffer.rs +++ b/coffer-common/src/coffer.rs @@ -1,17 +1,85 @@ //! A storage container for client data +//! +//! Coffer supports separated client data by `CofferShard`s. Content of a shard +//! is a key-value store with typed values. +//! +//! # Coffer files +//! A `Coffer` can be read from a [toml](https://github.com/toml-lang/) +//! file in a specific format. +//! +//! ## Shards +//! A `CofferShard` is identified by a toml table with a field `id` containing +//! the unique shard id. +//! +//! Tables can be nested for grouping shards together. The grouping is not +//! necessarily reflected in the deserialized `Coffer`, as shards can be +//! uniquely identified by their id. +//! +//! Shards (tables with an id) cannot be nested. +//! +//! A simple shard with no data +//! ```toml +//! [app] +//! id = "1" +//! ``` +//! +//! Grouped shard +//! ```toml +//! [app] +//! [app.frontend] +//! id = "1" +//! +//! [app.backend] +//! id = "2" +//! ``` +//! +//! Nested shards (invalid) +//! ```toml +//! [app] +//! id = "1" # app is a shard since it has an id +//! [app.frontend] # invalid, can't nest shards inside other shards +//! id = "2" +//! ``` +//! +//! ## Values +//! Shards can contain a subset of toml values. The currently supported toml +//! values are: +//! - [String](https://github.com/toml-lang/toml#user-content-string) +//! - [Integer](https://github.com/toml-lang/toml#user-content-integer) +//! - [Float](https://github.com/toml-lang/toml#user-content-float) +//! - [Boolean](https://github.com/toml-lang/toml#user-content-boolean) +//! +//! ## Example +//! ```toml +//! [app] +//! [app.frontend] +//! id = "1" +//! password = "admin" +//! font_size = 1.4 +//! +//! [app.backend] +//! id = "2" +//! cors = true +//! +//! [database] +//! id = "0" +//! user = "root" +//! passwort = "toor" +//! ``` +//! #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; -use std::fmt::Debug; -use std::path::Path; -use std::fs::File; -use std::io::{BufReader, Read}; - -use toml::Value as TomlValue; - -use serde::{Serialize, Deserialize}; +use std::{ + fmt::Debug, + fs::File, + io::{BufReader, Read}, + path::Path, +}; use quick_error::quick_error; +use toml::Value as TomlValue; +use serde::{Serialize, Deserialize}; quick_error! { #[derive(Debug)] @@ -28,7 +96,7 @@ quick_error! { pub type CofferResult = Result; -/// Values supported by a `Coffer` +/// Values supported by `Coffer` #[derive(Clone, Debug, Serialize, Deserialize)] pub enum CofferValue { /// A UTF-8 encoded string @@ -37,35 +105,38 @@ pub enum CofferValue { Integer(i32), /// A 32-bit float Float(f32), - // A boolean + /// A boolean value Boolean(bool) } +/// A `CofferKey` defining the shard and the key into the kv-store #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct CofferKey { pub shard: String, pub key: String } -#[derive(Debug, Serialize, Deserialize)] +/// A key-value store for client data +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct CofferShard(pub Vec<(String, CofferValue)>); -/// Interface for interacting with a `Coffer` +/// Trait for `Coffer` pub trait Coffer { - /// Put `value` at `path`. Errors if there is already a value at `path`. + /// Put `value` at `key`. Errors if there is already a value for `key`. fn put(&mut self, key: CofferKey, value: CofferValue) -> CofferResult<()>; - /// Push `value` to `path`. Replaces existing values. + /// Push `value` to `key`. Replaces existing values. fn push(&mut self, key: CofferKey, value: CofferValue); - /// Retrieve `value` at path. Errors if there is no `value` at path. - fn get(&self, key: &CofferKey) -> CofferResult; + /// Retrieve `value` at path. `None` if there is no `value` for `key`. + fn get(&self, key: &CofferKey) -> Option; - /// Retrieve `value` at path. Errors if there is no `value` at path. - fn get_shard(&self, shard: T) -> CofferResult + /// Retrieve an entire shard. `None` if there is no `CofferShard` for `shard`. + fn get_shard(&self, shard: T) -> Option where T: AsRef; - fn from_toml_file(toml_path: &Path) -> Self + /// Deserializes a `Coffer` from a toml file + fn from_toml_path(toml_path: &Path) -> Self where Self: Coffer + Default { // read the secrets file into a temporary string @@ -135,4 +206,5 @@ pub trait Coffer { self.put(CofferKey{shard, key}, value).unwrap(); } } + } diff --git a/coffer-common/src/keyring.rs b/coffer-common/src/keyring.rs index 87e50dd..4a29986 100644 --- a/coffer-common/src/keyring.rs +++ b/coffer-common/src/keyring.rs @@ -1,3 +1,4 @@ +//! Certificate of the keyring owner plus known and trusted public keys #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; @@ -36,12 +37,17 @@ quick_error! { } } +/// Keyring container +/// +/// A keyring constists of the owner's certificate and +/// the known and trusted public keys of the keyring owner pub struct Keyring { certificate: Certificate, known_keys: HashMap, box_::PublicKey> } impl Keyring { + /// Create a new keyring pub fn new(certificate: Certificate) -> Keyring { Keyring { certificate, @@ -49,6 +55,7 @@ impl Keyring { } } + /// Deserialize a keyring from a file in [cbor](https://cbor.io) format pub fn new_from_path(certificate_path: T) -> Keyring where T: AsRef { @@ -58,6 +65,8 @@ impl Keyring { } } + /// Add the table ids of a `Coffer` in toml format as known keys to the keyring + // TODO: This needs to be refactored. Keyring shouldn't be that tightly bound to coffer format pub fn add_known_keys_toml(&mut self, toml: &str) -> Result<(), KeyringError> { // parse the string into a toml Table let clients: toml::value::Table = match toml.parse::().unwrap() { @@ -100,11 +109,21 @@ impl Keyring { Ok(()) } + /// Open a sealed message with the keyring owner's certificate pub fn open(&self, message: &[u8]) -> Result, KeyringError> { self.certificate.open(message) .map_err(KeyringError::from) } + /// Seal a message for a client in the keyring + // TODO: Does this make sense? + // - What is a client in context of a keyring? + // - Why do we need to store trusted public keys in a keyring and not just + // encrypt for a pub key. Sealing does not need certificate. + // => We need authenticated encryption here. Sealed boxes can be tampered with by a MITM + // https://download.libsodium.org/doc/public-key_cryptography/authenticated_encryption + // I.e. a client could retrieve forged secrets by a attacker-controlled server, even if the + // real server's certificate was not exposed. pub fn seal(&self, client: &[u8], message: &[u8]) -> Result, KeyringError> { let client_key = self.known_keys.get(client) .ok_or(KeyringError::UnkownClientKey)?; diff --git a/coffer-common/src/lib.rs b/coffer-common/src/lib.rs index 36b33b7..7984de1 100644 --- a/coffer-common/src/lib.rs +++ b/coffer-common/src/lib.rs @@ -1,16 +1,5 @@ -//! Common base for coffer binaries - -#[allow(unused_imports)] -use log::{debug, error, info, trace, warn}; +//! Common traits and function for coffer implementations pub mod certificate; pub mod coffer; pub mod keyring; - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -}