Extract crypto keys to a key set module

This commit is contained in:
timvisee 2018-03-08 21:36:50 +01:00
parent 8ffea0410d
commit a4ccf395b9
No known key found for this signature in database
GPG key ID: 109CBA0BF74036C2
8 changed files with 122 additions and 26 deletions

1
Cargo.lock generated
View file

@ -189,7 +189,6 @@ dependencies = [
"mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
"open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -13,7 +13,6 @@ lazy_static = "1.0"
mime_guess = "2.0.0-alpha.2"
open = "1"
openssl = "0.10"
rand = "0.4"
reqwest = "0.8"
serde = "1.0"
serde_derive = "1.0"

View file

@ -1,3 +1,5 @@
use super::super::url::Url;
use super::super::send::file::File;
/// The response from the server after a file has been uploaded.
@ -25,7 +27,7 @@ impl UploadResponse {
/// Convert this response into a file object.
///
/// The `host` and `secret` must be given.
pub fn into_file(self, host: String, secret: Vec<u8>) -> File {
pub fn into_file(self, host: Url, secret: Vec<u8>) -> File {
File::new_now(
self.id,
host,

View file

@ -4,6 +4,9 @@ extern crate sha2;
use self::hkdf::Hkdf;
use self::sha2::Sha256;
// Reexport the cryptographically secure random bytes generator
pub use super::openssl::rand::rand_bytes;
/// Derive a HKDF key.
///
/// No _salt_ bytes are used in this function.

View file

@ -3,7 +3,6 @@ extern crate lazy_static;
extern crate mime_guess;
extern crate open;
extern crate openssl;
extern crate rand;
extern crate reqwest;
#[macro_use]
extern crate serde_derive;
@ -24,7 +23,6 @@ use std::io::BufReader;
use std::path::Path;
use openssl::symm::{Cipher, encrypt_aead};
use rand::{Rng, thread_rng};
use reqwest::header::Authorization;
use reqwest::mime::APPLICATION_OCTET_STREAM;
use reqwest::multipart::Part;
@ -32,9 +30,9 @@ use reqwest::multipart::Part;
use action::upload::UploadResponse;
use cmd::Handler;
use cmd::cmd_upload::CmdUpload;
use crypto::{derive_auth_key, derive_file_key, derive_meta_key};
use metadata::{Metadata, XFileMetadata};
use reader::EncryptedFileReaderTagged;
use send::key_set::KeySet;
/// Application entrypoint.
fn main() {
@ -79,22 +77,14 @@ fn action_upload(cmd_upload: &CmdUpload) {
// Create a new reqwest client
let client = reqwest::Client::new();
// Generate a secret and iv
let mut secret = [0u8; 16];
let mut iv = [0u8; 12];
thread_rng().fill_bytes(&mut secret);
thread_rng().fill_bytes(&mut iv);
// Derive keys
let encrypt_key = derive_file_key(&secret);
let auth_key = derive_auth_key(&secret, None, None);
let meta_key = derive_meta_key(&secret);
// Generate a key
let key = KeySet::generate(true);
// Guess the mimetype of the file
let file_mime = mime_guess::get_mime_type(file_ext);
// Construct the metadata
let metadata = Metadata::from(&iv, file_name.clone(), file_mime);
let metadata = Metadata::from(key.iv(), file_name.clone(), file_mime);
// Convert the metadata to JSON bytes
let metadata = metadata.to_json().into_bytes();
@ -106,7 +96,7 @@ fn action_upload(cmd_upload: &CmdUpload) {
let mut metadata_tag = vec![0u8; 16];
let mut metadata = encrypt_aead(
cipher,
&meta_key,
key.meta_key().unwrap(),
Some(&[0u8; 12]),
&[],
&metadata,
@ -119,8 +109,8 @@ fn action_upload(cmd_upload: &CmdUpload) {
let reader = EncryptedFileReaderTagged::new(
file,
cipher,
&encrypt_key,
&iv,
key.file_key().unwrap(),
key.iv(),
).unwrap();
// Buffer the encrypted reader, and determine the length
@ -138,7 +128,7 @@ fn action_upload(cmd_upload: &CmdUpload) {
// TODO: properly format an URL here
let url = host.join("api/upload").expect("invalid host");
let mut res = client.post(url.as_str())
.header(Authorization(format!("send-v1 {}", b64::encode(&auth_key))))
.header(Authorization(format!("send-v1 {}", key.auth_key_encoded().unwrap())))
.header(XFileMetadata::from(&metadata))
.multipart(form)
.send()
@ -148,10 +138,10 @@ fn action_upload(cmd_upload: &CmdUpload) {
let upload_res: UploadResponse = res.json().unwrap();
// Print the response
let file = upload_res.into_file(host.into_string(), secret.to_vec());
let file = upload_res.into_file(host, key.secret().to_vec());
let url = file.download_url();
println!("File: {:#?}", file);
println!("Secret key: {}", b64::encode(&secret));
println!("Secret key: {}", key.secret_encoded());
println!("Download URL: {}", url);
// Open the URL in the browser

View file

@ -1,6 +1,7 @@
extern crate chrono;
use self::chrono::{DateTime, Utc};
use super::super::url::Url;
use super::super::b64;
@ -17,7 +18,7 @@ pub struct File {
time: DateTime<Utc>,
/// The host the file was uploaded to.
host: String,
host: Url,
/// The file URL that was provided by the server.
url: String,
@ -34,7 +35,7 @@ impl File {
pub fn new(
id: String,
time: DateTime<Utc>,
host: String,
host: Url,
url: String,
secret: Vec<u8>,
owner_key: String,
@ -52,7 +53,7 @@ impl File {
/// Construct a new file, that was created at this exact time.
pub fn new_now(
id: String,
host: String,
host: Url,
url: String,
secret: Vec<u8>,
owner_key: String,

101
src/send/key_set.rs Normal file
View file

@ -0,0 +1,101 @@
use b64;
use crypto::{derive_auth_key, derive_file_key, derive_meta_key, rand_bytes};
pub struct KeySet {
/// A secret.
secret: [u8; 16],
/// Input vector.
iv: [u8; 12],
/// A derived file encryption key.
file_key: Option<Vec<u8>>,
/// A derived authentication key.
auth_key: Option<Vec<u8>>,
/// A derived metadata key.
meta_key: Option<Vec<u8>>,
}
impl KeySet {
/// Construct a new key, with the given `secret` and `iv`.
pub fn new(secret: [u8; 16], iv: [u8; 12]) -> Self {
Self {
secret,
iv,
file_key: None,
auth_key: None,
meta_key: None,
}
}
/// Generate a secure new key.
///
/// If `derive` is `true`, file, authentication and metadata keys will be
/// derived from the generated secret.
pub fn generate(derive: bool) -> Self {
// Allocate two keys
let mut secret = [0u8; 16];
let mut iv = [0u8; 12];
// Generate the secrets
rand_bytes(&mut secret)
.expect("failed to generate crypto secure random secret");
rand_bytes(&mut iv)
.expect("failed to generate crypto secure random input vector");
// Create the key
let mut key = Self::new(secret, iv);
// Derive
if derive {
key.derive();
}
key
}
/// Derive a file, authentication and metadata key.
pub fn derive(&mut self) {
self.file_key = Some(derive_file_key(&self.secret));
self.auth_key = Some(derive_auth_key(&self.secret, None, None));
self.meta_key = Some(derive_meta_key(&self.secret));
}
/// Get the secret key.
pub fn secret(&self) -> &[u8] {
&self.secret
}
/// Get the secret key as URL-safe base64 encoded string.
pub fn secret_encoded(&self) -> String {
b64::encode(self.secret())
}
/// Get the input vector.
pub fn iv(&self) -> &[u8] {
&self.iv
}
/// Get the file encryption key, if derived.
pub fn file_key(&self) -> Option<&Vec<u8>> {
self.file_key.as_ref()
}
/// Get the authentication encryption key, if derived.
pub fn auth_key(&self) -> Option<&Vec<u8>> {
self.auth_key.as_ref()
}
/// Get the authentication encryption key, if derived,
/// as URL-safe base64 encoded string.
pub fn auth_key_encoded(&self) -> Option<String> {
self.auth_key().map(|key| b64::encode(key))
}
/// Get the metadata encryption key, if derived.
pub fn meta_key(&self) -> Option<&Vec<u8>> {
self.meta_key.as_ref()
}
}

View file

@ -4,3 +4,4 @@
//! to and from a secure Firefox Send server.
pub mod file;
pub mod key_set;