mirror of
https://github.com/timvisee/ffsend.git
synced 2025-10-03 09:39:15 +02:00
Extract crypto keys to a key set module
This commit is contained in:
parent
8ffea0410d
commit
a4ccf395b9
8 changed files with 122 additions and 26 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
30
src/main.rs
30
src/main.rs
|
@ -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
|
||||
|
|
|
@ -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
101
src/send/key_set.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -4,3 +4,4 @@
|
|||
//! to and from a secure Firefox Send server.
|
||||
|
||||
pub mod file;
|
||||
pub mod key_set;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue