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

View file

@ -1,5 +1,6 @@
use openssl::symm::Cipher;
use file::file::DownloadFile;
use super::{b64, rand_bytes};
use super::hdkf::{derive_auth_key, derive_file_key, derive_meta_key};
@ -51,56 +52,6 @@ impl KeySet {
// 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
}
@ -131,6 +82,7 @@ impl KeySet {
}
/// Derive a file, authentication and metadata key.
// TODO: add support for deriving with a password and URL
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));

View file

@ -85,6 +85,12 @@ impl File {
/// 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
}
@ -93,11 +99,22 @@ impl File {
b64::encode(self.secret_raw())
}
/// Get the download URL of the file, with the secret key included.
pub fn download_url(&self) -> Url {
/// Check whether a file secret is set.
/// 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
let mut url = self.url.clone();
if secret && self.has_secret() {
url.set_fragment(Some(&self.secret()));
} else {
url.set_fragment(None);
}
url
}
@ -160,6 +177,7 @@ impl DownloadFile {
let re_path = Regex::new(DOWNLOAD_PATH_PATTERN).unwrap();
let id = re_path.captures(url.path())
.ok_or(FileParseError::InvalidDownloadUrl)?[1]
.trim()
.to_owned();
// Get the file secret
@ -170,7 +188,7 @@ impl DownloadFile {
.ok_or(FileParseError::InvalidSecret)?
.get(1)
{
secret = b64::decode(raw.as_str())
secret = b64::decode(raw.as_str().trim())
.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.
/// This secret must be set to decrypt a downloaded Send file.
pub fn has_secret(&self) -> bool {
@ -195,6 +229,20 @@ impl DownloadFile {
pub fn set_secret(&mut self, secret: Vec<u8>) {
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 {

View file

@ -41,7 +41,7 @@ impl<'a> Upload<'a> {
let file = ApiUpload::new(host, path).invoke(&client, bar).unwrap();
// 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);
// Open the URL in the browser