Browse Source

Use dynamic vector as secret type

Tim Visée 7 years ago
parent
commit
e7e0d8c1ff
3 changed files with 83 additions and 7 deletions
  1. 76 4
      api/src/crypto/key_set.rs
  2. 6 2
      api/src/file/file.rs
  3. 1 1
      cli/src/progress.rs

+ 76 - 4
api/src/crypto/key_set.rs

@@ -3,12 +3,15 @@ use openssl::symm::Cipher;
 use super::{b64, rand_bytes};
 use super::hdkf::{derive_auth_key, derive_file_key, derive_meta_key};
 
+/// The length of an input vector.
+const KEY_IV_LEN: usize = 12;
+
 pub struct KeySet {
     /// A secret.
-    secret: [u8; 16],
+    secret: Vec<u8>,
 
     /// Input vector.
-    iv: [u8; 12],
+    iv: [u8; KEY_IV_LEN],
 
     /// A derived file encryption key.
     file_key: Option<Vec<u8>>,
@@ -22,7 +25,7 @@ pub struct KeySet {
 
 impl KeySet {
     /// Construct a new key, with the given `secret` and `iv`.
-    pub fn new(secret: [u8; 16], iv: [u8; 12]) -> Self {
+    pub fn new(secret: Vec<u8>, iv: [u8; 12]) -> Self {
         Self {
             secret,
             iv,
@@ -32,13 +35,82 @@ impl KeySet {
         }
     }
 
+    /// Create a key set from the given file ID and secret.
+    /// This method may be used to create a key set based on a Send download
+    /// URL.
+    // TODO: add a parameter for the password and URL
+    // TODO: return a result?
+    // TODO: supply a client instance as parameter
+    pub fn from(file: &DownloadFile) -> Self {
+        // Create a new key set instance
+        let mut set = Self::new(
+            file.secret_raw().clone(),
+            [0; 12],
+        );
+
+        // Derive all keys
+        set.derive();
+
+        // Build the meta cipher
+        // let mut metadata_tag = vec![0u8; 16];
+        // let mut meta_cipher = match encrypt_aead(
+        //     KeySet::cipher(),
+        //     self.meta_key().unwrap(),
+        //     self.iv,
+        //     &[],
+        //     &metadata,
+        //     &mut metadata_tag,
+        // ) {
+        //     Ok(cipher) => cipher,
+        //     Err(_) => // TODO: return error here,
+        // };
+
+        // Create a reqwest client
+        let client = Client::new();
+
+        // Get the download url, and parse the nonce
+        // TODO: do not unwrap here, return error
+        let download_url = file.download_url(false);
+        let response = client.get(download_url)
+            .send()
+            .expect("failed to get nonce, failed to send file request");
+
+        // Validate the status code
+        // TODO: allow redirects here?
+        if !response.status().is_success() {
+            // TODO: return error here
+            panic!("failed to get nonce, request status is not successful");
+        }
+
+        // Get the authentication nonce
+        // TODO: don't unwrap here, return an error
+        let nonce = b64::decode(
+            response.headers()
+                .get_raw("WWW-Authenticate")
+                .expect("missing authenticate header") 
+                .one()
+                .map(|line| String::from_utf8(line.to_vec())
+                    .expect("invalid authentication header contents")
+                )
+                .expect("authentication header is empty")
+                .split_terminator(" ")
+                .skip(1)
+                .next()
+                .expect("missing authentication nonce")
+        );
+
+        // TODO: set the input vector
+
+        set
+    }
+
     /// 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 secret = vec![0u8; 16];
         let mut iv = [0u8; 12];
 
         // Generate the secrets

+ 6 - 2
api/src/file/file.rs

@@ -14,7 +14,7 @@ use crypto::b64;
 // TODO: match any sub-path?
 // TODO: match URL-safe base64 chars for the file ID?
 // TODO: constrain the ID length?
-const DOWNLOAD_PATH_PATTERN: &'static str = r"$/?download/([[:alnum:]]+={0,3})/?^";
+const DOWNLOAD_PATH_PATTERN: &'static str = r"$/?download/([[:alnum:]]{8,}={0,3})/?^";
 
 /// A pattern for Send download URL fragments, capturing the file secret.
 // TODO: constrain the secret length?
@@ -140,7 +140,8 @@ impl DownloadFile {
     /// this does not check whether the host is a valid and online Send host.
     ///
     /// If the URL fragmet contains a file secret, it is also parsed.
-    /// If it does not, the secret is left empty and must be specified manually.
+    /// If it does not, the secret is left empty and must be specified
+    /// manually.
     pub fn parse_url(url: String) -> Result<DownloadFile, FileParseError> {
         // Try to parse as an URL
         let url = Url::parse(&url)
@@ -152,6 +153,9 @@ impl DownloadFile {
         host.set_query(None);
         host.set_path("");
 
+        // TODO: remove this after debugging
+        println!("DEBUG: Extracted host: {}", host);
+
         // Validate the path, get the file ID
         let re_path = Regex::new(DOWNLOAD_PATH_PATTERN).unwrap();
         let id = re_path.captures(url.path())

+ 1 - 1
cli/src/progress.rs

@@ -27,7 +27,7 @@ impl ProgressReporter for ProgressBar {
         // Initialize the progress bar
         let mut bar = Pbr::new(total);
         bar.set_units(Units::Bytes);
-        bar.message("Upload/encrypt ");
+        bar.message("Encrypt & Upload ");
 
         self.bar = Some(bar);
     }