Conditional export for certificates

This commit is contained in:
Armin Friedl 2020-01-19 11:31:33 +01:00
parent d2a113e5a4
commit aa1c835186
Signed by: armin
GPG key ID: 48C726EEE7FBCBC8
15 changed files with 103 additions and 81 deletions

7
Cargo.lock generated
View file

@ -131,6 +131,7 @@ dependencies = [
"coffer-common 0.1.0", "coffer-common 0.1.0",
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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)", "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)", "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]] [[package]]
name = "humantime" name = "humantime"
version = "1.3.0" 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 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 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 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 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 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" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"

View file

@ -42,3 +42,27 @@
- All server requests are sealed by the server's public and client's private - 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 key. No tampered requests can be sent or communication data collected except
the private keys are compromised. 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

View file

@ -19,12 +19,9 @@
** TODO Secure Communication ** TODO Secure Communication
* Coffer Companion * Coffer Companion
** TODO Add Subcommands ** TODO Add Subcommands
- [X] Generate master key - [X] Generate certificate
- [ ] Generate client key - [ ] Seal secrets with certificate
- [X] Encrypt secrets with master key - [ ] Open secrets with certificate
- [ ] Decrypt secrets with master key
- [ ] Encrypt key request with client key
- [ ] Decrypt key request with client key
- [ ] Generate trampolin sh from dockerfile - [ ] Generate trampolin sh from dockerfile
* Docker * Docker
** TODO Create Dockerfile for server ** TODO Create Dockerfile for server

View file

@ -1,12 +1,13 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use std::net::SocketAddr;
use env_logger; use env_logger;
use structopt::StructOpt; use structopt::StructOpt;
use std::fs::File; use std::fs::File;
use std::error::Error; use std::error::Error;
use std::net::TcpStream; use std::net::TcpStream;
use std::net::ToSocketAddrs;
use std::path::PathBuf; use std::path::PathBuf;
use std::io::BufRead; use std::io::BufRead;
use std::io::BufReader; use std::io::BufReader;
@ -14,16 +15,12 @@ use std::io::Write;
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
struct Args { struct Args {
/// The port secsrv is listening on /// Address of the coffer server
#[structopt(short, long, env = "SECSRV_PORT", default_value = "9187")] #[structopt(short, long, parse(try_from_str), env = "COFFER_SERVER_ADDRESS", default_value = "127.0.0.1:9187")]
port: u16, server_address: SocketAddr,
/// The address secsrv binds to /// Path to the request file sent to the server
#[structopt(short, long, env = "SECSRV_HOST", default_value = "127.0.0.1")] #[structopt(parse(from_os_str), env = "COFFER_REQUEST", hide_env_values = true)]
host: String,
/// Path to the keys file
#[structopt(parse(from_os_str), env = "SECSRV_SECRETS", hide_env_values = true)]
secrets: PathBuf, secrets: PathBuf,
/// The subcommand spawned by coffer-client /// The subcommand spawned by coffer-client
@ -38,8 +35,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let args = Args::from_args(); let args = Args::from_args();
info!{"Connecting to coffer server"} info!{"Connecting to coffer server"}
let mut addr = (&*args.host, args.port).to_socket_addrs()?; let stream: TcpStream = TcpStream::connect(args.server_address)?;
let stream: TcpStream = TcpStream::connect(addr.next().unwrap())?;
info!{"Parsing key requests"} info!{"Parsing key requests"}
let keys = parse_from_path(&args.secrets)?; let keys = parse_from_path(&args.secrets)?;

View file

@ -6,6 +6,11 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # 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] [dependencies]
# Base tools # Base tools
log = "^0.4" log = "^0.4"

View file

@ -76,6 +76,7 @@ impl Certificate {
Ok(Certificate{inner}) Ok(Certificate{inner})
} }
#[cfg(feature = "export")]
pub fn to_cbor(&self) -> Result<Vec<u8>, CertificateError> { pub fn to_cbor(&self) -> Result<Vec<u8>, CertificateError> {
let inner_cert = &*self.inner.read(); let inner_cert = &*self.inner.read();
let cbor = serde_cbor::to_vec(inner_cert)?; let cbor = serde_cbor::to_vec(inner_cert)?;

View file

@ -12,7 +12,7 @@ quick_error! {
pub enum CofferError { pub enum CofferError {
Msg(err: &'static str) { Msg(err: &'static str) {
from(err) from(err)
display("{}", err) display("{}", err)
} }
Other(err: Box<dyn std::error::Error>) { Other(err: Box<dyn std::error::Error>) {
cause(&**err) cause(&**err)
@ -42,7 +42,7 @@ pub trait Coffer {
/// Put `value` at `path`. Errors if there is already a value at `path`. /// Put `value` at `path`. Errors if there is already a value at `path`.
fn put(&mut self, path: CofferPath, value: CofferValue) -> CofferResult<()>; 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); fn push(&mut self, path: CofferPath, value: CofferValue);
/// Retrieve `value` at path. Errors if there is no `value` at path. /// Retrieve `value` at path. Errors if there is no `value` at path.

View file

@ -19,4 +19,4 @@ serde = { version = "1.0", features = ["derive"]}
serde_cbor = "0.10.2" serde_cbor = "0.10.2"
serde_yaml = "0.8" serde_yaml = "0.8"
coffer-common = { path = "../coffer-common" } coffer-common = { path = "../coffer-common", features = ["export"]}

View file

@ -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
}

View file

@ -12,6 +12,6 @@ pub fn generate_key(out: PathBuf) {
let mut writer = File::create(&out) let mut writer = File::create(&out)
.expect(&format!{"Could not create out file {}", &out.display()}); .expect(&format!{"Could not create out file {}", &out.display()});
writer.write_all(&cert); writer.write_all(&cert).unwrap();
} }

View file

@ -6,7 +6,7 @@ mod encrypt;
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
enum Args { enum Args {
Generate { Certificate {
#[structopt(short, long, parse(from_os_str))] #[structopt(short, long, parse(from_os_str))]
out: PathBuf out: PathBuf
}, },
@ -16,7 +16,7 @@ enum Args {
#[structopt(short, long, parse(from_os_str))] #[structopt(short, long, parse(from_os_str))]
out: PathBuf, out: PathBuf,
#[structopt(short, long, parse(from_os_str))] #[structopt(short, long, parse(from_os_str))]
masterkey: PathBuf, certificate: PathBuf,
} }
} }
@ -24,7 +24,7 @@ fn main() {
let args: Args = Args::from_args(); let args: Args = Args::from_args();
match args { match args {
Args::Generate {out} => generate::generate_key(out), Args::Certificate {out} => generate::generate_key(out),
Args::Encrypt {yaml, out, masterkey} => {} Args::Encrypt {yaml, out, certificate} => {}
} }
} }

View file

@ -14,6 +14,7 @@ lazy_static = "^1.4"
# Key management/Cryptography # Key management/Cryptography
sodiumoxide = "^0.2" sodiumoxide = "^0.2"
hex = "^0.4"
# Communication # Communication
tokio = { version="^0.2.9", features = ["full"]} tokio = { version="^0.2.9", features = ["full"]}
serde = { version = "^1.0", features = ["derive"]} serde = { version = "^1.0", features = ["derive"]}

View file

@ -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<dyn std::error::Error>) {
cause(&**err)
}
}
}
enum Command {
None
}
struct CommandParser<T>
where T: AsyncRead {
reader: T
}
impl Stream for CommandParser {
type Item = Command;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Option<Self::Item>> {
Poll::Ready(Some(Command::None))
}
}

View file

@ -23,12 +23,12 @@ struct Args {
#[structopt(short, long, parse(from_os_str), env = "COFFER_SERVER_CERTIFICATE", hide_env_values = true)] #[structopt(short, long, parse(from_os_str), env = "COFFER_SERVER_CERTIFICATE", hide_env_values = true)]
certificate: Option<PathBuf>, certificate: Option<PathBuf>,
/// 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 /// 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)] #[structopt(short, long, parse(from_os_str), env = "COFFER_SERVER_SECRETS", hide_env_values = true)]
secrets: Option<PathBuf>, secrets: Option<PathBuf>,
/// 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")] #[structopt(short, long, parse(try_from_str), env = "COFFER_SERVER_ADDRESS", default_value = "127.0.0.1:9187")]
address: SocketAddr, address: SocketAddr,
} }

View file

@ -2,11 +2,12 @@
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use std::sync::Arc; use std::sync::Arc;
use std::convert::TryInto; use std::convert::{TryFrom, TryInto};
use std::convert::TryFrom;
use std::net::Shutdown; use std::net::Shutdown;
use tokio::prelude::*; use tokio::io::{AsyncRead,
AsyncReadExt,
AsyncWriteExt};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -14,10 +15,11 @@ use serde_cbor;
use quick_error::quick_error; use quick_error::quick_error;
use coffer_common::coffer::Coffer; use coffer_common::coffer::{CofferValue,
use coffer_common::coffer::CofferValue; CofferPath,
use coffer_common::coffer::CofferPath; Coffer};
use coffer_common::keyring::Keyring; use coffer_common::keyring::Keyring;
use hex;
quick_error! { quick_error! {
#[derive(Debug)] #[derive(Debug)]
@ -68,13 +70,13 @@ where C: Coffer
keyring: Arc<RwLock<Keyring>> keyring: Arc<RwLock<Keyring>>
) -> Protocol<C> ) -> Protocol<C>
{ {
let state = State::Start; let state = State::Start;
let client = None; let client = None;
Protocol {stream, coffer, keyring, client, state} Protocol {stream, coffer, keyring, client, state}
} }
pub async fn run(mut self) { pub async fn run(mut self)
{
while self.state != State::End while self.state != State::End
{ {
debug!{"In state: {:?}", self.state} debug!{"In state: {:?}", self.state}
@ -85,7 +87,8 @@ where C: Coffer
self.stream.shutdown(Shutdown::Both).unwrap(); 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(); let (mut reader, _writer) = self.stream.split();
// TODO restrict msg_size more, otherwise bad client could bring server // TODO restrict msg_size more, otherwise bad client could bring server
@ -163,7 +166,8 @@ where C: Coffer
Some(message) Some(message)
} }
async fn transit(&mut self, event: Request) { async fn transit(&mut self, event: Request)
{
match (&self.state, event) { match (&self.state, event) {
(State::Start, Request::Hello(pk)) => { (State::Start, Request::Hello(pk)) => {
debug!{"Reading public key"} debug!{"Reading public key"}
@ -176,11 +180,14 @@ where C: Coffer
(State::Link, Request::Get(req)) => { (State::Link, Request::Get(req)) => {
debug!{"Writing response"} debug!{"Writing response"}
let req = serde_cbor::from_slice( let mut req: CofferPath =
&self.keyring.read().await serde_cbor::from_slice(
.open(&req) &self.keyring.read().await
.unwrap() .open(&req)
).unwrap(); .unwrap()
).unwrap();
req.0.insert(0, hex::encode(self.client.as_ref().unwrap()));
let res = self.coffer.read().await let res = self.coffer.read().await
.get(req) .get(req)
@ -203,13 +210,18 @@ where C: Coffer
(State::Link, Request::Put(put)) => { (State::Link, Request::Put(put)) => {
debug!{"Putting secrets"} debug!{"Putting secrets"}
let put: Vec<(CofferPath, CofferValue)> = let mut put: Vec<(CofferPath, CofferValue)> =
serde_cbor::from_slice( serde_cbor::from_slice(
&self.keyring.read().await &self.keyring.read().await
.open(&put) .open(&put)
.unwrap() .unwrap()
).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 { for (coffer_path, coffer_value) in put {
self.coffer.write().await self.coffer.write().await
.put(coffer_path, coffer_value) .put(coffer_path, coffer_value)
@ -227,7 +239,8 @@ where C: Coffer
} }
} }
async fn framed(msg_type: u8, data: Vec<u8>) -> Vec<u8> { async fn framed(msg_type: u8, data: Vec<u8>) -> Vec<u8>
{
trace!{"Creating frame for type: {:?}, data: {:?}", msg_type, data} trace!{"Creating frame for type: {:?}, data: {:?}", msg_type, data}
// TODO magic number // TODO magic number