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
|
//! 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;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue