Browse Source

Extract crypto keys to a key set module

timvisee 7 năm trước cách đây
mục cha
commit
a4ccf395b9
8 tập tin đã thay đổi với 122 bổ sung26 xóa
  1. 0 1
      Cargo.lock
  2. 0 1
      Cargo.toml
  3. 3 1
      src/action/upload.rs
  4. 3 0
      src/crypto.rs
  5. 10 20
      src/main.rs
  6. 4 3
      src/send/file.rs
  7. 101 0
      src/send/key_set.rs
  8. 1 0
      src/send/mod.rs

+ 0 - 1
Cargo.lock

@@ -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)",

+ 0 - 1
Cargo.toml

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

+ 3 - 1
src/action/upload.rs

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

+ 3 - 0
src/crypto.rs

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

+ 10 - 20
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

+ 4 - 3
src/send/file.rs

@@ -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 - 0
src/send/key_set.rs

@@ -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()
+    }
+}

+ 1 - 0
src/send/mod.rs

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