Prechádzať zdrojové kódy

Simplify API download action, replace metadata logic with metadata action

timvisee 7 rokov pred
rodič
commit
887e09ebd2
1 zmenil súbory, kde vykonal 26 pridanie a 269 odobranie
  1. 26 269
      api/src/action/download.rs

+ 26 - 269
api/src/action/download.rs

@@ -9,26 +9,19 @@ use std::io::{
 use std::path::PathBuf;
 use std::sync::{Arc, Mutex};
 
-use failure::Error as FailureError;
-use openssl::symm::decrypt_aead;
 use reqwest::{Client, Response, StatusCode};
 use reqwest::header::Authorization;
 use reqwest::header::ContentLength;
-use serde_json;
 
-use crypto::b64;
 use crypto::key_set::KeySet;
 use crypto::sig::signature_encoded;
 use ext::status_code::StatusCodeExt;
-use file::metadata::Metadata;
 use file::remote_file::RemoteFile;
 use reader::{EncryptedFileWriter, ProgressReporter, ProgressWriter};
-
-/// The name of the header that is used for the authentication nonce.
-const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
-
-/// The HTTP status code that is returned for expired files.
-const FILE_EXPIRED_STATUS: StatusCode = StatusCode::NotFound;
+use super::metadata::{
+    Error as MetadataError,
+    Metadata as MetadataAction,
+};
 
 /// A file upload action to a Send server.
 pub struct Download<'a> {
@@ -65,18 +58,17 @@ impl<'a> Download<'a> {
         // Create a key set for the file
         let mut key = KeySet::from(self.file, self.password.as_ref());
 
-        // Fetch the authentication nonce
-        let auth_nonce = self.fetch_auth_nonce(client)?;
-
-        // Fetch the meta data, apply the derived input vector
-        let (metadata, meta_nonce) = self.fetch_metadata_apply_iv(
-            &client,
-            &mut key,
-            auth_nonce,
-        )?;
+        // Fetch the file metadata, update the input vector in the key set
+        let metadata = MetadataAction::new(self.file, self.password.clone())
+            .invoke(&client)
+            .map_err(|err| match err {
+                MetadataError::Expired => Error::Expired,
+                _ => err.into(),
+            })?;
+        key.set_iv(metadata.metadata().iv());
 
         // Decide what actual file target to use
-        let path = self.decide_path(metadata.name());
+        let path = self.decide_path(metadata.metadata().name());
         let path_str = path.to_str().unwrap_or("?").to_owned();
 
         // Open the file we will write to
@@ -89,7 +81,11 @@ impl<'a> Download<'a> {
             ))?;
 
         // Create the file reader for downloading
-        let (reader, len) = self.create_file_reader(&key, meta_nonce, &client)?;
+        let (reader, len) = self.create_file_reader(
+            &key,
+            metadata.nonce().to_vec(),
+            &client,
+        )?;
 
         // Create the file writer
         let writer = self.create_file_writer(
@@ -108,125 +104,6 @@ impl<'a> Download<'a> {
         Ok(())
     }
 
-    /// Fetch the authentication nonce for the file from the Send server.
-    fn fetch_auth_nonce(&self, client: &Client)
-        -> Result<Vec<u8>, Error>
-    {
-        // Get the download url, and parse the nonce
-        let download_url = self.file.download_url(false);
-        let response = client.get(download_url)
-            .send()
-            .map_err(|_| AuthError::NonceReq)?;
-
-        // Validate the status code
-        let status = response.status();
-        if !status.is_success() {
-            // Handle expired files
-            if status == FILE_EXPIRED_STATUS {
-                return Err(Error::Expired);
-            } else {
-                return Err(AuthError::NonceReqStatus(status, status.err_text()).into());
-            }
-        }
-
-        // Get the authentication nonce
-        b64::decode(
-            response.headers()
-                .get_raw(HEADER_AUTH_NONCE)
-                .ok_or(AuthError::NoNonceHeader)?
-                .one()
-                .ok_or(AuthError::MalformedNonce)
-                .and_then(|line| String::from_utf8(line.to_vec())
-                    .map_err(|_| AuthError::MalformedNonce)
-                )?
-                .split_terminator(" ")
-                .skip(1)
-                .next()
-                .ok_or(AuthError::MalformedNonce)?
-        ).map_err(|_| AuthError::MalformedNonce.into())
-    }
-
-
-    /// Create a metadata nonce, and fetch the metadata for the file from the
-    /// server.
-    ///
-    /// The key set, along with the authentication nonce must be given.
-    ///
-    /// The metadata, with the meta nonce is returned.
-    ///
-    /// This method is similar to `fetch_metadata`, and additionally applies
-    /// the derived input vector to the given key set.
-    fn fetch_metadata_apply_iv(
-        &self,
-        client: &Client,
-        key: &mut KeySet,
-        auth_nonce: Vec<u8>,
-    ) -> Result<(Metadata, Vec<u8>), MetaError> {
-        // Fetch the metadata and the nonce
-        let data = self.fetch_metadata(client, key, auth_nonce)?;
-
-        // Set the input vector bas
-        key.set_iv(data.0.iv());
-
-        Ok(data)
-    }
-
-    /// Create a metadata nonce, and fetch the metadata for the file from the
-    /// Send server.
-    ///
-    /// The key set, along with the authentication nonce must be given.
-    ///
-    /// The metadata, with the meta nonce is returned.
-    fn fetch_metadata(
-        &self,
-        client: &Client,
-        key: &KeySet,
-        auth_nonce: Vec<u8>,
-    ) -> Result<(Metadata, Vec<u8>), MetaError> {
-        // Compute the cryptographic signature for authentication
-        let sig = signature_encoded(key.auth_key().unwrap(), &auth_nonce)
-            .map_err(|_| MetaError::ComputeSignature)?;
-
-        // Build the request, fetch the encrypted metadata
-        let mut response = client.get(self.file.api_meta_url())
-            .header(Authorization(
-                format!("send-v1 {}", sig)
-            ))
-            .send()
-            .map_err(|_| MetaError::NonceReq)?;
-
-        // Validate the status code
-        let status = response.status();
-        if !status.is_success() {
-            return Err(MetaError::NonceReqStatus(status, status.err_text()));
-        }
-
-        // Get the metadata nonce
-        let nonce = b64::decode(
-            response.headers()
-                .get_raw(HEADER_AUTH_NONCE)
-                .ok_or(MetaError::NoNonceHeader)?
-                .one()
-                .ok_or(MetaError::MalformedNonce)
-                .and_then(|line| String::from_utf8(line.to_vec())
-                    .map_err(|_| MetaError::MalformedNonce)
-                )?
-                .split_terminator(" ")
-                .skip(1)
-                .next()
-                .ok_or(MetaError::MalformedNonce)?
-        ).map_err(|_| MetaError::MalformedNonce)?;
-
-        // Parse the metadata response, and decrypt it
-        Ok((
-            response.json::<MetadataResponse>()
-                .map_err(|_| MetaError::Malformed)?
-                .decrypt_metadata(&key)
-                .map_err(|_| MetaError::Decrypt)?,
-            nonce,
-        ))
-    }
-
     /// Decide what path we will download the file to.
     ///
     /// A target file or directory, and a file name hint must be given.
@@ -353,54 +230,13 @@ impl<'a> Download<'a> {
     }
 }
 
-/// The metadata response from the server, when fetching the data through
-/// the API.
-///
-/// This metadata is required to successfully download and decrypt the
-/// corresponding file.
-#[derive(Debug, Deserialize)]
-struct MetadataResponse {
-    /// The encrypted metadata.
-    #[serde(rename = "metadata")]
-    meta: String,
-}
-
-impl MetadataResponse {
-    /// Get and decrypt the metadata, based on the raw data in this response.
-    ///
-    /// The decrypted data is verified using an included tag.
-    /// If verification failed, an error is returned.
-    pub fn decrypt_metadata(&self, key_set: &KeySet) -> Result<Metadata, FailureError> {
-        // Decode the metadata
-        let raw = b64::decode(&self.meta)?;
-
-        // Get the encrypted metadata, and it's tag
-        let (encrypted, tag) = raw.split_at(raw.len() - 16);
-        // TODO: is the tag length correct, remove assert if it is
-        assert_eq!(tag.len(), 16);
-
-        // Decrypt the metadata
-		let meta = decrypt_aead(
-			KeySet::cipher(),
-			key_set.meta_key().unwrap(),
-			Some(key_set.iv()),
-			&[],
-			encrypted,
-			&tag,
-		)?;
-
-        // Parse the metadata, and return
-        Ok(serde_json::from_slice(&meta)?)
-    }
-}
-
 #[derive(Fail, Debug)]
 pub enum Error {
-    /// A general error occurred while requesting the file data.
-    /// This may be because authentication failed, because decrypting the
-    /// file metadata didn't succeed, or due to some other reason.
-    #[fail(display = "Failed to request file data")]
-    Request(#[cause] RequestError),
+    /// An error occurred while fetching the metadata of the file.
+    /// This step is required in order to succsessfully decrypt the
+    /// file that will be downloaded.
+    #[fail(display = "Failed to fetch file metadata")]
+    Meta(#[cause] MetadataError),
 
     /// The given Send file has expired, or did never exist in the first place.
     /// Therefore the file could not be downloaded.
@@ -421,15 +257,9 @@ pub enum Error {
     File(String, #[cause] FileError),
 }
 
-impl From<AuthError> for Error {
-    fn from(err: AuthError) -> Error {
-        Error::Request(RequestError::Auth(err))
-    }
-}
-
-impl From<MetaError> for Error {
-    fn from(err: MetaError) -> Error {
-        Error::Request(RequestError::Meta(err))
+impl From<MetadataError> for Error {
+    fn from(err: MetadataError) -> Error {
+        Error::Meta(err)
     }
 }
 
@@ -439,79 +269,6 @@ impl From<DownloadError> for Error {
     }
 }
 
-#[derive(Fail, Debug)]
-pub enum RequestError {
-    /// Failed authenticating, in order to fetch the file data.
-    #[fail(display = "Failed to authenticate")]
-    Auth(#[cause] AuthError),
-
-    /// Failed to retrieve the file metadata.
-    #[fail(display = "Failed to retrieve file metadata")]
-    Meta(#[cause] MetaError),
-}
-
-#[derive(Fail, Debug)]
-pub enum AuthError {
-    /// Sending the request to gather the authentication encryption nonce
-    /// failed.
-    #[fail(display = "Failed to request authentication nonce")]
-    NonceReq,
-
-    /// The response for fetching the authentication encryption nonce
-    /// indicated an error and wasn't successful.
-    #[fail(display = "Bad HTTP response '{}' while requesting authentication nonce", _1)]
-    NonceReqStatus(StatusCode, String),
-
-    /// No authentication encryption nonce was included in the response
-    /// from the server, it was missing.
-    #[fail(display = "Missing authentication nonce in server response")]
-    NoNonceHeader,
-
-    /// The authentication encryption nonce from the response malformed or
-    /// empty.
-    /// Maybe the server responded with a new format that isn't supported yet
-    /// by this client.
-    #[fail(display = "Received malformed authentication nonce")]
-    MalformedNonce,
-}
-
-#[derive(Fail, Debug)]
-pub enum MetaError {
-    /// An error occurred while computing the cryptographic signature used for
-    /// decryption.
-    #[fail(display = "Failed to compute cryptographic signature")]
-    ComputeSignature,
-
-    /// Sending the request to gather the metadata encryption nonce failed.
-    #[fail(display = "Failed to request metadata nonce")]
-    NonceReq,
-
-    /// The response for fetching the metadata encryption nonce indicated an
-    /// error and wasn't successful.
-    #[fail(display = "Bad HTTP response '{}' while requesting metadata nonce", _1)]
-    NonceReqStatus(StatusCode, String),
-
-    /// No metadata encryption nonce was included in the response from the
-    /// server, it was missing.
-    #[fail(display = "Missing metadata nonce in server response")]
-    NoNonceHeader,
-
-    /// The metadata encryption nonce from the response malformed or empty.
-    /// Maybe the server responded with a new format that isn't supported yet
-    /// by this client.
-    #[fail(display = "Received malformed metadata nonce")]
-    MalformedNonce,
-
-    /// The received metadata is malformed, and couldn't be decoded or
-    /// interpreted.
-    #[fail(display = "Received malformed metadata")]
-    Malformed,
-
-    /// Failed to decrypt the received metadata.
-    #[fail(display = "Failed to decrypt received metadata")]
-    Decrypt,
-}
-
 #[derive(Fail, Debug)]
 pub enum DownloadError {
     /// An error occurred while computing the cryptographic signature used for