From aa1c83518607febee325258821d518b00d026e2d Mon Sep 17 00:00:00 2001 From: Armin Friedl Date: Sun, 19 Jan 2020 11:31:33 +0100 Subject: [PATCH] Conditional export for certificates --- Cargo.lock | 7 +++++ Design.org | 24 +++++++++++++++ TODO.org | 9 ++---- coffer-client/src/main.rs | 20 +++++------- coffer-common/Cargo.toml | 5 +++ coffer-common/src/certificate.rs | 1 + coffer-common/src/coffer.rs | 4 +-- coffer-companion/Cargo.toml | 2 +- coffer-companion/src/encrypt.rs | 14 +++++++++ coffer-companion/src/generate.rs | 2 +- coffer-companion/src/main.rs | 8 ++--- coffer-server/Cargo.toml | 1 + coffer-server/src/command_parser.rs | 36 ---------------------- coffer-server/src/main.rs | 4 +-- coffer-server/src/protocol.rs | 47 ++++++++++++++++++----------- 15 files changed, 103 insertions(+), 81 deletions(-) delete mode 100644 coffer-server/src/command_parser.rs diff --git a/Cargo.lock b/Cargo.lock index fad3ade..0be444c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,6 +131,7 @@ dependencies = [ "coffer-common 0.1.0", "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -343,6 +344,11 @@ dependencies = [ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hex" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "humantime" version = "1.3.0" @@ -957,6 +963,7 @@ dependencies = [ "checksum half 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ff54597ea139063f4225f1ec47011b03c9de4a486957ff3fc506881dac951d0" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" +"checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e" "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" diff --git a/Design.org b/Design.org index f26d61f..00494d8 100644 --- a/Design.org +++ b/Design.org @@ -42,3 +42,27 @@ - All server requests are sealed by the server's public and client's private key. No tampered requests can be sent or communication data collected except the private keys are compromised. + +* Coffer YAML +** Secrets Definition + Encrypted with: SK of coffer-companion, PK of coffer-server + + #+BEGIN_SRC yaml + # Names for ids (public keys) of clients + [clients] + file = "AAAA-AAAA-AAAA-AAAA" + bin = "FFFF-FFFF-FFFF-FFFF" + + # Secrets for a named client (defined in clients) + [file] + secretkey = "secret value" + secretkey2 = "secret value2" + #+END_SRC + +** Secret Response + file client executes GET to server + + #+BEGIN_SRC yaml + secretkey = "secret value" + secretkey2 = "secret value2" + #+END_SRC diff --git a/TODO.org b/TODO.org index 6009367..c659c65 100644 --- a/TODO.org +++ b/TODO.org @@ -19,12 +19,9 @@ ** TODO Secure Communication * Coffer Companion ** TODO Add Subcommands - - [X] Generate master key - - [ ] Generate client key - - [X] Encrypt secrets with master key - - [ ] Decrypt secrets with master key - - [ ] Encrypt key request with client key - - [ ] Decrypt key request with client key + - [X] Generate certificate + - [ ] Seal secrets with certificate + - [ ] Open secrets with certificate - [ ] Generate trampolin sh from dockerfile * Docker ** TODO Create Dockerfile for server diff --git a/coffer-client/src/main.rs b/coffer-client/src/main.rs index 4f78201..b7f9728 100644 --- a/coffer-client/src/main.rs +++ b/coffer-client/src/main.rs @@ -1,12 +1,13 @@ #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; +use std::net::SocketAddr; + use env_logger; use structopt::StructOpt; use std::fs::File; use std::error::Error; use std::net::TcpStream; -use std::net::ToSocketAddrs; use std::path::PathBuf; use std::io::BufRead; use std::io::BufReader; @@ -14,16 +15,12 @@ use std::io::Write; #[derive(StructOpt, Debug)] struct Args { - /// The port secsrv is listening on - #[structopt(short, long, env = "SECSRV_PORT", default_value = "9187")] - port: u16, + /// Address of the coffer server + #[structopt(short, long, parse(try_from_str), env = "COFFER_SERVER_ADDRESS", default_value = "127.0.0.1:9187")] + server_address: SocketAddr, - /// The address secsrv binds to - #[structopt(short, long, env = "SECSRV_HOST", default_value = "127.0.0.1")] - host: String, - - /// Path to the keys file - #[structopt(parse(from_os_str), env = "SECSRV_SECRETS", hide_env_values = true)] + /// Path to the request file sent to the server + #[structopt(parse(from_os_str), env = "COFFER_REQUEST", hide_env_values = true)] secrets: PathBuf, /// The subcommand spawned by coffer-client @@ -38,8 +35,7 @@ fn main() -> Result<(), Box> { let args = Args::from_args(); info!{"Connecting to coffer server"} - let mut addr = (&*args.host, args.port).to_socket_addrs()?; - let stream: TcpStream = TcpStream::connect(addr.next().unwrap())?; + let stream: TcpStream = TcpStream::connect(args.server_address)?; info!{"Parsing key requests"} let keys = parse_from_path(&args.secrets)?; diff --git a/coffer-common/Cargo.toml b/coffer-common/Cargo.toml index 82f58d8..a22ad42 100644 --- a/coffer-common/Cargo.toml +++ b/coffer-common/Cargo.toml @@ -6,6 +6,11 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +# Make certificates exportable +# Otherwise secret keys are not accessible other than via the Certificate API +export = [] + [dependencies] # Base tools log = "^0.4" diff --git a/coffer-common/src/certificate.rs b/coffer-common/src/certificate.rs index b453786..e73274b 100644 --- a/coffer-common/src/certificate.rs +++ b/coffer-common/src/certificate.rs @@ -76,6 +76,7 @@ impl Certificate { Ok(Certificate{inner}) } + #[cfg(feature = "export")] pub fn to_cbor(&self) -> Result, CertificateError> { let inner_cert = &*self.inner.read(); let cbor = serde_cbor::to_vec(inner_cert)?; diff --git a/coffer-common/src/coffer.rs b/coffer-common/src/coffer.rs index 24c650d..010245c 100644 --- a/coffer-common/src/coffer.rs +++ b/coffer-common/src/coffer.rs @@ -12,7 +12,7 @@ quick_error! { pub enum CofferError { Msg(err: &'static str) { from(err) - display("{}", err) + display("{}", err) } Other(err: Box) { cause(&**err) @@ -42,7 +42,7 @@ pub trait Coffer { /// Put `value` at `path`. Errors if there is already a value at `path`. fn put(&mut self, path: CofferPath, value: CofferValue) -> CofferResult<()>; - /// Push `value` to `path`. Replaces existing values silently. + /// Push `value` to `path`. Replaces existing values. fn push(&mut self, path: CofferPath, value: CofferValue); /// Retrieve `value` at path. Errors if there is no `value` at path. diff --git a/coffer-companion/Cargo.toml b/coffer-companion/Cargo.toml index 1830a46..6fa99b3 100644 --- a/coffer-companion/Cargo.toml +++ b/coffer-companion/Cargo.toml @@ -19,4 +19,4 @@ serde = { version = "1.0", features = ["derive"]} serde_cbor = "0.10.2" serde_yaml = "0.8" -coffer-common = { path = "../coffer-common" } \ No newline at end of file +coffer-common = { path = "../coffer-common", features = ["export"]} \ No newline at end of file diff --git a/coffer-companion/src/encrypt.rs b/coffer-companion/src/encrypt.rs index e69de29..a1ef249 100644 --- a/coffer-companion/src/encrypt.rs +++ b/coffer-companion/src/encrypt.rs @@ -0,0 +1,14 @@ +use coffer_common::certificate::Certificate; + +use std::path::PathBuf; +use std::fs::File; +use std::io::Write; + +use serde::Deserialize; +use serde_yaml; + +pub fn encrypt_yaml(yaml:PathBuf, out: PathBuf, certificate: PathBuf) { + let cert = Certificate::new_from_cbor(certificate).unwrap(); + + let +} diff --git a/coffer-companion/src/generate.rs b/coffer-companion/src/generate.rs index 0501aaf..3f2452c 100644 --- a/coffer-companion/src/generate.rs +++ b/coffer-companion/src/generate.rs @@ -12,6 +12,6 @@ pub fn generate_key(out: PathBuf) { let mut writer = File::create(&out) .expect(&format!{"Could not create out file {}", &out.display()}); - writer.write_all(&cert); + writer.write_all(&cert).unwrap(); } diff --git a/coffer-companion/src/main.rs b/coffer-companion/src/main.rs index 404bc58..b885924 100644 --- a/coffer-companion/src/main.rs +++ b/coffer-companion/src/main.rs @@ -6,7 +6,7 @@ mod encrypt; #[derive(StructOpt, Debug)] enum Args { - Generate { + Certificate { #[structopt(short, long, parse(from_os_str))] out: PathBuf }, @@ -16,7 +16,7 @@ enum Args { #[structopt(short, long, parse(from_os_str))] out: PathBuf, #[structopt(short, long, parse(from_os_str))] - masterkey: PathBuf, + certificate: PathBuf, } } @@ -24,7 +24,7 @@ fn main() { let args: Args = Args::from_args(); match args { - Args::Generate {out} => generate::generate_key(out), - Args::Encrypt {yaml, out, masterkey} => {} + Args::Certificate {out} => generate::generate_key(out), + Args::Encrypt {yaml, out, certificate} => {} } } diff --git a/coffer-server/Cargo.toml b/coffer-server/Cargo.toml index 01fece2..cc36e39 100644 --- a/coffer-server/Cargo.toml +++ b/coffer-server/Cargo.toml @@ -14,6 +14,7 @@ lazy_static = "^1.4" # Key management/Cryptography sodiumoxide = "^0.2" +hex = "^0.4" # Communication tokio = { version="^0.2.9", features = ["full"]} serde = { version = "^1.0", features = ["derive"]} diff --git a/coffer-server/src/command_parser.rs b/coffer-server/src/command_parser.rs deleted file mode 100644 index 0bc663d..0000000 --- a/coffer-server/src/command_parser.rs +++ /dev/null @@ -1,36 +0,0 @@ -#[allow(unused_imports)] -use log::{debug, error, info, trace, warn}; - -use tokio::io::AsyncRead; - -use quick_error::quick_error; - -quick_error! { - #[derive(Debug)] - pub enum CommandParserError { - Msg(err: &'static str) { - from(err) - display("{}", err) - } - Other(err: Box) { - cause(&**err) - } - } -} - -enum Command { - None -} - -struct CommandParser -where T: AsyncRead { - reader: T -} - -impl Stream for CommandParser { - type Item = Command; - - fn poll_next(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { - Poll::Ready(Some(Command::None)) - } -} diff --git a/coffer-server/src/main.rs b/coffer-server/src/main.rs index 18f9272..9a80847 100644 --- a/coffer-server/src/main.rs +++ b/coffer-server/src/main.rs @@ -23,12 +23,12 @@ struct Args { #[structopt(short, long, parse(from_os_str), env = "COFFER_SERVER_CERTIFICATE", hide_env_values = true)] certificate: Option, - /// Path to an initial secrets file. Will be deleted after processing. + /// Path to secrets file. Will be deleted after processing. /// Must be sealed by the public key of the server certificate #[structopt(short, long, parse(from_os_str), env = "COFFER_SERVER_SECRETS", hide_env_values = true)] secrets: Option, - /// Address the coffer server should bind to + /// Address, the coffer server should bind to #[structopt(short, long, parse(try_from_str), env = "COFFER_SERVER_ADDRESS", default_value = "127.0.0.1:9187")] address: SocketAddr, } diff --git a/coffer-server/src/protocol.rs b/coffer-server/src/protocol.rs index 90ea500..7134724 100644 --- a/coffer-server/src/protocol.rs +++ b/coffer-server/src/protocol.rs @@ -2,11 +2,12 @@ use log::{debug, error, info, trace, warn}; use std::sync::Arc; -use std::convert::TryInto; -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::net::Shutdown; -use tokio::prelude::*; +use tokio::io::{AsyncRead, + AsyncReadExt, + AsyncWriteExt}; use tokio::net::TcpStream; use tokio::sync::RwLock; @@ -14,10 +15,11 @@ use serde_cbor; use quick_error::quick_error; -use coffer_common::coffer::Coffer; -use coffer_common::coffer::CofferValue; -use coffer_common::coffer::CofferPath; +use coffer_common::coffer::{CofferValue, + CofferPath, + Coffer}; use coffer_common::keyring::Keyring; +use hex; quick_error! { #[derive(Debug)] @@ -68,13 +70,13 @@ where C: Coffer keyring: Arc> ) -> Protocol { - let state = State::Start; let client = None; Protocol {stream, coffer, keyring, client, state} } - pub async fn run(mut self) { + pub async fn run(mut self) + { while self.state != State::End { debug!{"In state: {:?}", self.state} @@ -85,7 +87,8 @@ where C: Coffer self.stream.shutdown(Shutdown::Both).unwrap(); } - async fn event(&mut self) -> Request { + async fn event(&mut self) -> Request + { let (mut reader, _writer) = self.stream.split(); // TODO restrict msg_size more, otherwise bad client could bring server @@ -163,7 +166,8 @@ where C: Coffer Some(message) } - async fn transit(&mut self, event: Request) { + async fn transit(&mut self, event: Request) + { match (&self.state, event) { (State::Start, Request::Hello(pk)) => { debug!{"Reading public key"} @@ -176,11 +180,14 @@ where C: Coffer (State::Link, Request::Get(req)) => { debug!{"Writing response"} - let req = serde_cbor::from_slice( - &self.keyring.read().await - .open(&req) - .unwrap() - ).unwrap(); + let mut req: CofferPath = + serde_cbor::from_slice( + &self.keyring.read().await + .open(&req) + .unwrap() + ).unwrap(); + + req.0.insert(0, hex::encode(self.client.as_ref().unwrap())); let res = self.coffer.read().await .get(req) @@ -203,13 +210,18 @@ where C: Coffer (State::Link, Request::Put(put)) => { debug!{"Putting secrets"} - let put: Vec<(CofferPath, CofferValue)> = + let mut put: Vec<(CofferPath, CofferValue)> = serde_cbor::from_slice( &self.keyring.read().await .open(&put) .unwrap() ).unwrap(); + let key_string = hex::encode(self.client.as_ref().unwrap()); + + put.iter_mut().map( |(cp, _cv)| &mut cp.0) + .for_each(|cp| cp.insert(0, key_string.clone())); + for (coffer_path, coffer_value) in put { self.coffer.write().await .put(coffer_path, coffer_value) @@ -227,7 +239,8 @@ where C: Coffer } } - async fn framed(msg_type: u8, data: Vec) -> Vec { + async fn framed(msg_type: u8, data: Vec) -> Vec + { trace!{"Creating frame for type: {:?}, data: {:?}", msg_type, data} // TODO magic number