Allow key set derivation from download URL, start on download logic

This commit is contained in:
Tim Visée 2018-03-20 11:56:22 +01:00
parent e7e0d8c1ff
commit 8101bb3d19
No known key found for this signature in database
GPG key ID: A28432A0AE6E6306
4 changed files with 116 additions and 72 deletions

View file

@ -22,38 +22,82 @@ use reader::{
ProgressReader, ProgressReader,
ProgressReporter, ProgressReporter,
}; };
use file::file::File as SendFile; use file::file::DownloadFile;
use file::metadata::{Metadata, XFileMetadata}; use file::metadata::{Metadata, XFileMetadata};
pub type Result<T> = ::std::result::Result<T, DownloadError>; pub type Result<T> = ::std::result::Result<T, DownloadError>;
/// The name of the header that is used for the authentication nonce.
const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
/// A file upload action to a Send server. /// A file upload action to a Send server.
pub struct Download { pub struct Download<'a> {
/// The Send host to upload the file to. /// The Send file to download.
host: Url, file: &DownloadFile,
/// The file to upload.
path: PathBuf,
} }
impl Download { impl<'a> Download<'a> {
/// Construct a new upload action. /// Construct a new download action for the given file.
pub fn new(host: Url, path: PathBuf) -> Self { pub fn new(file: &'a DownloadFile) -> Self {
Self { Self {
host, file,
path,
} }
} }
/// Invoke the upload action. /// Invoke the download action.
pub fn invoke( pub fn invoke(
self, self,
client: &Client, client: &Client,
reporter: Arc<Mutex<ProgressReporter>>,
) -> Result<SendFile> { ) -> Result<SendFile> {
// Create file data, generate a key // Create a key set for the file
let file = FileData::from(Box::new(&self.path))?; let key = KeySet::from(self.file);
let key = KeySet::generate(true);
// 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,
// };
// 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(HEADER_AUTH_NONCE)
.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
// Crpate metadata and a file reader // Crpate metadata and a file reader
let metadata = self.create_metadata(&key, &file)?; let metadata = self.create_metadata(&key, &file)?;

View file

@ -1,5 +1,6 @@
use openssl::symm::Cipher; use openssl::symm::Cipher;
use file::file::DownloadFile;
use super::{b64, rand_bytes}; use super::{b64, rand_bytes};
use super::hdkf::{derive_auth_key, derive_file_key, derive_meta_key}; use super::hdkf::{derive_auth_key, derive_file_key, derive_meta_key};
@ -51,56 +52,6 @@ impl KeySet {
// Derive all keys // Derive all keys
set.derive(); 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 set
} }
@ -131,6 +82,7 @@ impl KeySet {
} }
/// Derive a file, authentication and metadata key. /// Derive a file, authentication and metadata key.
// TODO: add support for deriving with a password and URL
pub fn derive(&mut self) { pub fn derive(&mut self) {
self.file_key = Some(derive_file_key(&self.secret)); self.file_key = Some(derive_file_key(&self.secret));
self.auth_key = Some(derive_auth_key(&self.secret, None, None)); self.auth_key = Some(derive_auth_key(&self.secret, None, None));

View file

@ -85,6 +85,12 @@ impl File {
/// Get the raw secret. /// Get the raw secret.
pub fn secret_raw(&self) -> &Vec<u8> { pub fn secret_raw(&self) -> &Vec<u8> {
// A secret must have been set
if !self.has_secret() {
// TODO: don't panic, return an error instead
panic!("missing secret");
}
&self.secret &self.secret
} }
@ -93,11 +99,22 @@ impl File {
b64::encode(self.secret_raw()) b64::encode(self.secret_raw())
} }
/// Get the download URL of the file, with the secret key included. /// Check whether a file secret is set.
pub fn download_url(&self) -> Url { /// This secret must be set to decrypt a downloaded Send file.
pub fn has_secret(&self) -> bool {
!self.secret.is_empty()
}
/// Get the download URL of the file.
/// Set `secret` to `true`, to include it in the URL if known.
pub fn download_url(&self, secret: bool) -> Url {
// Get the download URL, and add the secret fragment // Get the download URL, and add the secret fragment
let mut url = self.url.clone(); let mut url = self.url.clone();
if secret && self.has_secret() {
url.set_fragment(Some(&self.secret())); url.set_fragment(Some(&self.secret()));
} else {
url.set_fragment(None);
}
url url
} }
@ -160,6 +177,7 @@ impl DownloadFile {
let re_path = Regex::new(DOWNLOAD_PATH_PATTERN).unwrap(); let re_path = Regex::new(DOWNLOAD_PATH_PATTERN).unwrap();
let id = re_path.captures(url.path()) let id = re_path.captures(url.path())
.ok_or(FileParseError::InvalidDownloadUrl)?[1] .ok_or(FileParseError::InvalidDownloadUrl)?[1]
.trim()
.to_owned(); .to_owned();
// Get the file secret // Get the file secret
@ -170,7 +188,7 @@ impl DownloadFile {
.ok_or(FileParseError::InvalidSecret)? .ok_or(FileParseError::InvalidSecret)?
.get(1) .get(1)
{ {
secret = b64::decode(raw.as_str()) secret = b64::decode(raw.as_str().trim())
.map_err(|_| FileParseError::InvalidSecret)? .map_err(|_| FileParseError::InvalidSecret)?
} }
} }
@ -184,6 +202,22 @@ impl DownloadFile {
)) ))
} }
/// Get the raw secret.
pub fn secret_raw(&self) -> &Vec<u8> {
// A secret must have been set
if !self.has_secret() {
// TODO: don't panic, return an error instead
panic!("missing secret");
}
&self.secret
}
/// Get the secret as base64 encoded string.
pub fn secret(&self) -> String {
b64::encode(self.secret_raw())
}
/// Check whether a file secret is set. /// Check whether a file secret is set.
/// This secret must be set to decrypt a downloaded Send file. /// This secret must be set to decrypt a downloaded Send file.
pub fn has_secret(&self) -> bool { pub fn has_secret(&self) -> bool {
@ -195,6 +229,20 @@ impl DownloadFile {
pub fn set_secret(&mut self, secret: Vec<u8>) { pub fn set_secret(&mut self, secret: Vec<u8>) {
self.secret = secret; self.secret = secret;
} }
/// Get the download URL of the file.
/// Set `secret` to `true`, to include it in the URL if known.
pub fn download_url(&self, secret: bool) -> Url {
// Get the download URL, and add the secret fragment
let mut url = self.url.clone();
if secret && self.has_secret() {
url.set_fragment(Some(&self.secret()));
} else {
url.set_fragment(None);
}
url
}
} }
pub enum FileParseError { pub enum FileParseError {

View file

@ -41,7 +41,7 @@ impl<'a> Upload<'a> {
let file = ApiUpload::new(host, path).invoke(&client, bar).unwrap(); let file = ApiUpload::new(host, path).invoke(&client, bar).unwrap();
// Get the download URL, and report it in the console // Get the download URL, and report it in the console
let url = file.download_url(); let url = file.download_url(true);
println!("Download URL: {}", url); println!("Download URL: {}", url);
// Open the URL in the browser // Open the URL in the browser