This commit is contained in:
parent
e7fbb4e47c
commit
1e43bd5a7b
4 changed files with 114 additions and 35 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<T> = Result<T, CofferError>;
|
||||
|
||||
/// 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<CofferValue>;
|
||||
/// Retrieve `value` at path. `None` if there is no `value` for `key`.
|
||||
fn get(&self, key: &CofferKey) -> Option<CofferValue>;
|
||||
|
||||
/// Retrieve `value` at path. Errors if there is no `value` at path.
|
||||
fn get_shard<T>(&self, shard: T) -> CofferResult<CofferShard>
|
||||
/// Retrieve an entire shard. `None` if there is no `CofferShard` for `shard`.
|
||||
fn get_shard<T>(&self, shard: T) -> Option<CofferShard>
|
||||
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
|
||||
{
|
||||
// read the secrets file into a temporary string
|
||||
|
@ -135,4 +206,5 @@ pub trait Coffer {
|
|||
self.put(CofferKey{shard, key}, value).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Vec<u8>, 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<T>(certificate_path: T) -> Keyring
|
||||
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> {
|
||||
// parse the string into a toml Table
|
||||
let clients: toml::value::Table = match toml.parse::<TomlValue>().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<Vec<u8>, 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<Vec<u8>, KeyringError> {
|
||||
let client_key = self.known_keys.get(client)
|
||||
.ok_or(KeyringError::UnkownClientKey)?;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue