mirror of
https://github.com/timvisee/ffsend.git
synced 2025-10-06 02:29:57 +02:00
Allow key set derivation from download URL, start on download logic
This commit is contained in:
parent
e7e0d8c1ff
commit
8101bb3d19
4 changed files with 116 additions and 72 deletions
|
@ -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)?;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue