mirror of
https://github.com/timvisee/ffsend.git
synced 2025-10-03 09:39:15 +02:00
First working implementation, nicly handle upload response, cleanup
This commit is contained in:
parent
9753f1e3cc
commit
0786ed5917
1 changed files with 138 additions and 105 deletions
243
src/main.rs
243
src/main.rs
|
@ -31,6 +31,8 @@ use reqwest::header::{
|
|||
use reqwest::mime::APPLICATION_OCTET_STREAM;
|
||||
use reqwest::multipart::Part;
|
||||
|
||||
const TAG_LEN: usize = 16;
|
||||
|
||||
fn main() {
|
||||
// TODO: a fixed path for now, as upload test
|
||||
let path = Path::new("/home/timvisee/Pictures/Avatar/1024x1024/Avatar.png");
|
||||
|
@ -88,13 +90,121 @@ fn main() {
|
|||
.send()
|
||||
.unwrap();
|
||||
|
||||
let text = res.text().unwrap();
|
||||
// Parse the response
|
||||
let upload_res: UploadResponse = res.json().unwrap();
|
||||
|
||||
// TODO: remove after debugging
|
||||
println!("TEXT: {}", text);
|
||||
// Print the response
|
||||
println!("Response: {:#?}", upload_res);
|
||||
println!("Secret key: {}", base64::encode(&secret));
|
||||
println!("Download URL: {}", upload_res.download_url(&secret));
|
||||
}
|
||||
|
||||
const TAG_LEN: usize = 16;
|
||||
fn hkdf<'a>(
|
||||
length: usize,
|
||||
ikm: &[u8],
|
||||
salt: Option<&[u8]>,
|
||||
info: Option<&[u8]>
|
||||
) -> Vec<u8> {
|
||||
// Get the salt and info parameters, use defaults if undefined
|
||||
let salt = salt.unwrap_or(b"");
|
||||
let info = info.unwrap_or(b"");
|
||||
|
||||
// Define the digest to use
|
||||
let digest = Sha256::new();
|
||||
|
||||
let mut pkr: Vec<u8> = vec![0u8; digest.output_bytes()];
|
||||
hkdf_extract(digest, salt, ikm, &mut pkr);
|
||||
|
||||
let mut okm: Vec<u8> = vec![0u8; length];
|
||||
hkdf_expand(digest, &pkr, info, &mut okm);
|
||||
|
||||
okm
|
||||
}
|
||||
|
||||
fn derive_file_key(secret: &[u8]) -> Vec<u8> {
|
||||
hkdf(16, secret, None, Some(b"encryption"))
|
||||
}
|
||||
|
||||
fn derive_auth_key(secret: &[u8], password: Option<String>, url: Option<String>) -> Vec<u8> {
|
||||
if password.is_none() {
|
||||
hkdf(64, secret, None, Some(b"authentication"))
|
||||
} else {
|
||||
// TODO: implement this
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_meta_key(secret: &[u8]) -> Vec<u8> {
|
||||
hkdf(16, secret, None, Some(b"metadata"))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Metadata {
|
||||
/// The input vector
|
||||
iv: String,
|
||||
|
||||
/// The file name
|
||||
name: String,
|
||||
|
||||
/// The file mimetype
|
||||
#[serde(rename="type")]
|
||||
mime: String,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
/// Construct metadata from the given properties.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * iv: initialisation vector
|
||||
/// * name: file name
|
||||
/// * mime: file mimetype
|
||||
pub fn from(iv: &[u8], name: String, mime: Mime) -> Self {
|
||||
Metadata {
|
||||
iv: base64::encode(iv),
|
||||
name,
|
||||
mime: mime.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this structure to a JSON string.
|
||||
pub fn to_json(&self) -> String {
|
||||
serde_json::to_string(&self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct XFileMetadata {
|
||||
/// The metadata, as a base64 encoded string.
|
||||
metadata: String,
|
||||
}
|
||||
|
||||
impl XFileMetadata {
|
||||
pub fn new(metadata: String) -> Self {
|
||||
XFileMetadata {
|
||||
metadata,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from(bytes: &[u8]) -> Self {
|
||||
XFileMetadata::new(base64::encode(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Header for XFileMetadata {
|
||||
fn header_name() -> &'static str {
|
||||
"X-File-Metadata"
|
||||
}
|
||||
|
||||
fn parse_header(_raw: &Raw) -> Result<Self, HyperError> {
|
||||
// TODO: implement this some time
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn fmt_header(&self, f: &mut HeaderFormatter) -> fmt::Result {
|
||||
// TODO: is this encoding base64 for us?
|
||||
f.fmt_line(&self.metadata)
|
||||
}
|
||||
}
|
||||
|
||||
/// A file reader, that encrypts the file with the given cipher, and appends
|
||||
/// the raw cipher tag.
|
||||
|
@ -150,109 +260,32 @@ impl Read for EncryptedFileReaderTagged {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct XFileMetadata {
|
||||
/// The metadata, as a base64 encoded string.
|
||||
metadata: String,
|
||||
}
|
||||
|
||||
impl XFileMetadata {
|
||||
pub fn new(metadata: String) -> Self {
|
||||
XFileMetadata {
|
||||
metadata,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from(bytes: &[u8]) -> Self {
|
||||
XFileMetadata::new(base64::encode(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Header for XFileMetadata {
|
||||
fn header_name() -> &'static str {
|
||||
"X-File-Metadata"
|
||||
}
|
||||
|
||||
fn parse_header(_raw: &Raw) -> Result<Self, HyperError> {
|
||||
// TODO: implement this some time
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn fmt_header(&self, f: &mut HeaderFormatter) -> fmt::Result {
|
||||
// TODO: is this encoding base64 for us?
|
||||
f.fmt_line(&self.metadata)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Metadata {
|
||||
/// The input vector
|
||||
iv: String,
|
||||
|
||||
/// The file name
|
||||
name: String,
|
||||
|
||||
/// The file mimetype
|
||||
#[serde(rename="type")]
|
||||
mime: String,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
/// Construct metadata from the given properties.
|
||||
/// The response from the server after a file has been uploaded.
|
||||
/// This response contains the file ID and owner key, to manage the file.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * iv: initialisation vector
|
||||
/// * name: file name
|
||||
/// * mime: file mimetype
|
||||
pub fn from(iv: &[u8], name: String, mime: Mime) -> Self {
|
||||
Metadata {
|
||||
iv: base64::encode(iv),
|
||||
name,
|
||||
mime: mime.to_string(),
|
||||
}
|
||||
/// It also contains the download URL, although an additional secret is
|
||||
/// required.
|
||||
///
|
||||
/// The download URL can be generated using `download_url()` which will
|
||||
/// include the required secret in the URL.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct UploadResponse {
|
||||
/// The URL the file is reachable at.
|
||||
/// This includes the file ID, but does not include the secret.
|
||||
url: String,
|
||||
|
||||
/// The owner key, used to do further file modifications.
|
||||
owner: String,
|
||||
|
||||
/// The file ID.
|
||||
id: String,
|
||||
}
|
||||
|
||||
/// Convert this structure to a JSON string.
|
||||
pub fn to_json(&self) -> String {
|
||||
serde_json::to_string(&self).unwrap()
|
||||
impl UploadResponse {
|
||||
/// Get the download URL, including the secret.
|
||||
///
|
||||
/// The secret bytes must be passed to `secret`.
|
||||
pub fn download_url(&self, secret: &[u8]) -> String {
|
||||
format!("{}#{}", self.url, base64::encode(secret))
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_file_key(secret: &[u8]) -> Vec<u8> {
|
||||
hkdf(16, secret, None, Some(b"encryption"))
|
||||
}
|
||||
|
||||
fn derive_auth_key(secret: &[u8], password: Option<String>, url: Option<String>) -> Vec<u8> {
|
||||
if password.is_none() {
|
||||
hkdf(64, secret, None, Some(b"authentication"))
|
||||
} else {
|
||||
// TODO: implement this
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_meta_key(secret: &[u8]) -> Vec<u8> {
|
||||
hkdf(16, secret, None, Some(b"metadata"))
|
||||
}
|
||||
|
||||
fn hkdf<'a>(
|
||||
length: usize,
|
||||
ikm: &[u8],
|
||||
salt: Option<&[u8]>,
|
||||
info: Option<&[u8]>
|
||||
) -> Vec<u8> {
|
||||
// Get the salt and info parameters, use defaults if undefined
|
||||
let salt = salt.unwrap_or(b"");
|
||||
let info = info.unwrap_or(b"");
|
||||
|
||||
// Define the digest to use
|
||||
let digest = Sha256::new();
|
||||
|
||||
let mut pkr: Vec<u8> = vec![0u8; digest.output_bytes()];
|
||||
hkdf_extract(digest, salt, ikm, &mut pkr);
|
||||
|
||||
let mut okm: Vec<u8> = vec![0u8; length];
|
||||
hkdf_expand(digest, &pkr, info, &mut okm);
|
||||
|
||||
okm
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue