[common] Add documentation
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Armin Friedl 2020-02-09 12:33:06 +01:00
parent e7fbb4e47c
commit 1e43bd5a7b
Signed by: armin
GPG key ID: 48C726EEE7FBCBC8
4 changed files with 114 additions and 35 deletions

View file

@ -1,11 +1,11 @@
//! A keypair contianer providing functionality for signing, encryption and //! A keypair container providing functionality for signing, encryption and
//! decryption //! decryption
//! //!
//! # Base libraries //! # Base libraries
//! The cryptographic operations exposed by this module are based on the //! The cryptographic operations exposed by this module are based on the
//! [NaCl](http://nacl.cr.yp.to/) fork [libsodium](https://libsodium.org) as //! [NaCl](http://nacl.cr.yp.to/) fork [libsodium](https://libsodium.org) as
//! exposed by the rust bindings [sodiumoxide](https://crates.io/crates/sodiumoxide). //! exposed by the rust bindings [sodiumoxide](https://crates.io/crates/sodiumoxide).
//!
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
@ -19,8 +19,7 @@ use std::{
use quick_error::quick_error; use quick_error::quick_error;
use seckey::SecKey; use seckey::SecKey;
use sodiumoxide::crypto::box_; use sodiumoxide::crypto::{box_, sealedbox};
use sodiumoxide::crypto::sealedbox;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use serde_cbor; use serde_cbor;

View file

@ -1,17 +1,85 @@
//! A storage container for client data //! 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)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use std::fmt::Debug; use std::{
use std::path::Path; fmt::Debug,
use std::fs::File; fs::File,
use std::io::{BufReader, Read}; io::{BufReader, Read},
path::Path,
use toml::Value as TomlValue; };
use serde::{Serialize, Deserialize};
use quick_error::quick_error; use quick_error::quick_error;
use toml::Value as TomlValue;
use serde::{Serialize, Deserialize};
quick_error! { quick_error! {
#[derive(Debug)] #[derive(Debug)]
@ -28,7 +96,7 @@ quick_error! {
pub type CofferResult<T> = Result<T, CofferError>; pub type CofferResult<T> = Result<T, CofferError>;
/// Values supported by a `Coffer` /// Values supported by `Coffer`
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum CofferValue { pub enum CofferValue {
/// A UTF-8 encoded string /// A UTF-8 encoded string
@ -37,35 +105,38 @@ pub enum CofferValue {
Integer(i32), Integer(i32),
/// A 32-bit float /// A 32-bit float
Float(f32), Float(f32),
// A boolean /// A boolean value
Boolean(bool) Boolean(bool)
} }
/// A `CofferKey` defining the shard and the key into the kv-store
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct CofferKey { pub struct CofferKey {
pub shard: String, pub shard: String,
pub key: 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)>); pub struct CofferShard(pub Vec<(String, CofferValue)>);
/// Interface for interacting with a `Coffer` /// Trait for `Coffer`
pub trait 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<()>; 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); fn push(&mut self, key: CofferKey, value: CofferValue);
/// Retrieve `value` at path. Errors if there is no `value` at path. /// Retrieve `value` at path. `None` if there is no `value` for `key`.
fn get(&self, key: &CofferKey) -> CofferResult<CofferValue>; fn get(&self, key: &CofferKey) -> Option<CofferValue>;
/// Retrieve `value` at path. Errors if there is no `value` at path. /// Retrieve an entire shard. `None` if there is no `CofferShard` for `shard`.
fn get_shard<T>(&self, shard: T) -> CofferResult<CofferShard> fn get_shard<T>(&self, shard: T) -> Option<CofferShard>
where T: AsRef<str>; where T: AsRef<str>;
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 where Self: Coffer + Default
{ {
// read the secrets file into a temporary string // read the secrets file into a temporary string
@ -135,4 +206,5 @@ pub trait Coffer {
self.put(CofferKey{shard, key}, value).unwrap(); self.put(CofferKey{shard, key}, value).unwrap();
} }
} }
} }

View file

@ -1,3 +1,4 @@
//! Certificate of the keyring owner plus known and trusted public keys
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; 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 { pub struct Keyring {
certificate: Certificate, certificate: Certificate,
known_keys: HashMap<Vec<u8>, box_::PublicKey> known_keys: HashMap<Vec<u8>, box_::PublicKey>
} }
impl Keyring { impl Keyring {
/// Create a new keyring
pub fn new(certificate: Certificate) -> Keyring { pub fn new(certificate: Certificate) -> Keyring {
Keyring { Keyring {
certificate, certificate,
@ -49,6 +55,7 @@ impl Keyring {
} }
} }
/// Deserialize a keyring from a file in [cbor](https://cbor.io) format
pub fn new_from_path<T>(certificate_path: T) -> Keyring pub fn new_from_path<T>(certificate_path: T) -> Keyring
where T: AsRef<Path> where T: AsRef<Path>
{ {
@ -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> { pub fn add_known_keys_toml(&mut self, toml: &str) -> Result<(), KeyringError> {
// parse the string into a toml Table // parse the string into a toml Table
let clients: toml::value::Table = match toml.parse::<TomlValue>().unwrap() { let clients: toml::value::Table = match toml.parse::<TomlValue>().unwrap() {
@ -100,11 +109,21 @@ impl Keyring {
Ok(()) Ok(())
} }
/// Open a sealed message with the keyring owner's certificate
pub fn open(&self, message: &[u8]) -> Result<Vec<u8>, KeyringError> { pub fn open(&self, message: &[u8]) -> Result<Vec<u8>, KeyringError> {
self.certificate.open(message) self.certificate.open(message)
.map_err(KeyringError::from) .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<Vec<u8>, KeyringError> { pub fn seal(&self, client: &[u8], message: &[u8]) -> Result<Vec<u8>, KeyringError> {
let client_key = self.known_keys.get(client) let client_key = self.known_keys.get(client)
.ok_or(KeyringError::UnkownClientKey)?; .ok_or(KeyringError::UnkownClientKey)?;

View file

@ -1,16 +1,5 @@
//! Common base for coffer binaries //! Common traits and function for coffer implementations
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
pub mod certificate; pub mod certificate;
pub mod coffer; pub mod coffer;
pub mod keyring; pub mod keyring;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}