mirror of
https://github.com/timvisee/ffsend.git
synced 2025-10-03 09:39:15 +02:00
Merge branch 'master' of github.com:timvisee/ffsend
This commit is contained in:
commit
9a1c480564
5 changed files with 119 additions and 90 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -184,6 +184,7 @@ dependencies = [
|
||||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sha2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sha2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"version-compare 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -949,6 +950,11 @@ name = "vec_map"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version-compare"
|
||||||
|
version = "0.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -1107,6 +1113,7 @@ dependencies = [
|
||||||
"checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
|
"checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22"
|
||||||
"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
|
"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
|
||||||
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
|
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
|
||||||
|
"checksum version-compare 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "78068add8bf1e4d37d13fa5867182fe4c03f8e525c831053733f83aaba942d37"
|
||||||
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
|
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
|
||||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||||
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
||||||
|
|
|
@ -17,3 +17,4 @@ serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
sha2 = "0.7"
|
sha2 = "0.7"
|
||||||
|
version-compare = "0.0"
|
||||||
|
|
19
src/b64.rs
Normal file
19
src/b64.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
//! A simple module for encoding or decoding a base64 string from or to a
|
||||||
|
//! byte array.
|
||||||
|
//!
|
||||||
|
//! This module uses an URL-safe scheme, and doesn't add additional padding
|
||||||
|
//! to the encoded strings.
|
||||||
|
|
||||||
|
extern crate base64;
|
||||||
|
|
||||||
|
use self::base64::DecodeError;
|
||||||
|
|
||||||
|
/// Encode the given byte slice using base64, in an URL-safe manner.
|
||||||
|
pub fn encode(input: &[u8]) -> String {
|
||||||
|
base64::encode_config(input, base64::URL_SAFE_NO_PAD)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode the given string as base64, in an URL-safe manner.
|
||||||
|
pub fn decode(input: &str) -> Result<Vec<u8>, DecodeError> {
|
||||||
|
base64::decode_config(input, base64::URL_SAFE_NO_PAD)
|
||||||
|
}
|
97
src/main.rs
97
src/main.rs
|
@ -1,4 +1,3 @@
|
||||||
extern crate base64;
|
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
extern crate hkdf;
|
extern crate hkdf;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
@ -9,32 +8,26 @@ extern crate rand;
|
||||||
extern crate reqwest;
|
extern crate reqwest;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate serde_json;
|
|
||||||
extern crate sha2;
|
extern crate sha2;
|
||||||
|
|
||||||
|
mod b64;
|
||||||
|
mod metadata;
|
||||||
mod reader;
|
mod reader;
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
use hyper::error::Error as HyperError;
|
|
||||||
use mime_guess::Mime;
|
|
||||||
use openssl::symm::{Cipher, encrypt_aead};
|
use openssl::symm::{Cipher, encrypt_aead};
|
||||||
use rand::{Rng, thread_rng};
|
use rand::{Rng, thread_rng};
|
||||||
use reqwest::header::{
|
use reqwest::header::Authorization;
|
||||||
Authorization,
|
|
||||||
Formatter as HeaderFormatter,
|
|
||||||
Header,
|
|
||||||
Raw
|
|
||||||
};
|
|
||||||
use reqwest::mime::APPLICATION_OCTET_STREAM;
|
use reqwest::mime::APPLICATION_OCTET_STREAM;
|
||||||
use reqwest::multipart::Part;
|
use reqwest::multipart::Part;
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
|
|
||||||
|
use metadata::{Metadata, XFileMetadata};
|
||||||
use reader::EncryptedFileReaderTagged;
|
use reader::EncryptedFileReaderTagged;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -124,7 +117,7 @@ fn main() {
|
||||||
|
|
||||||
// Make the request
|
// Make the request
|
||||||
let mut res = client.post("http://localhost:8080/api/upload")
|
let mut res = client.post("http://localhost:8080/api/upload")
|
||||||
.header(Authorization(format!("send-v1 {}", base64_encode(&auth_key))))
|
.header(Authorization(format!("send-v1 {}", b64::encode(&auth_key))))
|
||||||
.header(XFileMetadata::from(&metadata))
|
.header(XFileMetadata::from(&metadata))
|
||||||
.multipart(form)
|
.multipart(form)
|
||||||
.send()
|
.send()
|
||||||
|
@ -136,7 +129,7 @@ fn main() {
|
||||||
// Print the response
|
// Print the response
|
||||||
let url = upload_res.download_url(&secret);
|
let url = upload_res.download_url(&secret);
|
||||||
println!("Response: {:#?}", upload_res);
|
println!("Response: {:#?}", upload_res);
|
||||||
println!("Secret key: {}", base64_encode(&secret));
|
println!("Secret key: {}", b64::encode(&secret));
|
||||||
println!("Download URL: {}", url);
|
println!("Download URL: {}", url);
|
||||||
|
|
||||||
// Open the URL in the browser
|
// Open the URL in the browser
|
||||||
|
@ -188,77 +181,6 @@ fn derive_meta_key(secret: &[u8]) -> Vec<u8> {
|
||||||
hkdf(16, secret, Some(b"metadata"))
|
hkdf(16, secret, Some(b"metadata"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// File metadata, which is send to the server.
|
|
||||||
#[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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A X-File-Metadata header for reqwest, that is used to pass encrypted
|
|
||||||
/// metadata to the server.
|
|
||||||
///
|
|
||||||
/// The encrypted metadata (bytes) is base64 encoded when constructing this
|
|
||||||
/// header using `from`.
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct XFileMetadata {
|
|
||||||
/// The metadata, as a base64 encoded string.
|
|
||||||
metadata: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XFileMetadata {
|
|
||||||
/// Construct the header from the given encrypted metadata.
|
|
||||||
pub fn from(bytes: &[u8]) -> Self {
|
|
||||||
XFileMetadata {
|
|
||||||
metadata: 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The response from the server after a file has been uploaded.
|
/// The response from the server after a file has been uploaded.
|
||||||
/// This response contains the file ID and owner key, to manage the file.
|
/// This response contains the file ID and owner key, to manage the file.
|
||||||
///
|
///
|
||||||
|
@ -285,11 +207,6 @@ impl UploadResponse {
|
||||||
///
|
///
|
||||||
/// The secret bytes must be passed to `secret`.
|
/// The secret bytes must be passed to `secret`.
|
||||||
pub fn download_url(&self, secret: &[u8]) -> String {
|
pub fn download_url(&self, secret: &[u8]) -> String {
|
||||||
format!("{}#{}", self.url, base64_encode(secret))
|
format!("{}#{}", self.url, b64::encode(secret))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the given byte slice using base64, in an URL-safe manner.
|
|
||||||
fn base64_encode(input: &[u8]) -> String {
|
|
||||||
base64::encode_config(input, base64::URL_SAFE_NO_PAD)
|
|
||||||
}
|
|
||||||
|
|
85
src/metadata.rs
Normal file
85
src/metadata.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use hyper::error::Error as HyperError;
|
||||||
|
use mime_guess::Mime;
|
||||||
|
use reqwest::header::{
|
||||||
|
Formatter as HeaderFormatter,
|
||||||
|
Header,
|
||||||
|
Raw,
|
||||||
|
};
|
||||||
|
|
||||||
|
use b64;
|
||||||
|
|
||||||
|
/// File metadata, which is send to the server.
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub 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: b64::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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A X-File-Metadata header for reqwest, that is used to pass encrypted
|
||||||
|
/// metadata to the server.
|
||||||
|
///
|
||||||
|
/// The encrypted metadata (bytes) is base64 encoded when constructing this
|
||||||
|
/// header using `from`.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct XFileMetadata {
|
||||||
|
/// The metadata, as a base64 encoded string.
|
||||||
|
metadata: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XFileMetadata {
|
||||||
|
/// Construct the header from the given encrypted metadata.
|
||||||
|
pub fn from(bytes: &[u8]) -> Self {
|
||||||
|
XFileMetadata {
|
||||||
|
metadata: b64::encode(bytes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make this struct usable as reqwest header.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue