diff --git a/src/main.rs b/src/main.rs index af89188..6d8a4e6 100644 --- a/src/main.rs +++ b/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 { + // 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 = vec![0u8; digest.output_bytes()]; + hkdf_extract(digest, salt, ikm, &mut pkr); + + let mut okm: Vec = vec![0u8; length]; + hkdf_expand(digest, &pkr, info, &mut okm); + + okm +} + +fn derive_file_key(secret: &[u8]) -> Vec { + hkdf(16, secret, None, Some(b"encryption")) +} + +fn derive_auth_key(secret: &[u8], password: Option, url: Option) -> Vec { + if password.is_none() { + hkdf(64, secret, None, Some(b"authentication")) + } else { + // TODO: implement this + unimplemented!(); + } +} + +fn derive_meta_key(secret: &[u8]) -> Vec { + 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 { + // 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, +/// The response from the server after a file has been uploaded. +/// This response contains the file ID and owner key, to manage the file. +/// +/// 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, } -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 { - // 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. +impl UploadResponse { + /// Get the download URL, including the secret. /// - /// 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() + /// 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 { - hkdf(16, secret, None, Some(b"encryption")) -} - -fn derive_auth_key(secret: &[u8], password: Option, url: Option) -> Vec { - if password.is_none() { - hkdf(64, secret, None, Some(b"authentication")) - } else { - // TODO: implement this - unimplemented!(); - } -} - -fn derive_meta_key(secret: &[u8]) -> Vec { - hkdf(16, secret, None, Some(b"metadata")) -} - -fn hkdf<'a>( - length: usize, - ikm: &[u8], - salt: Option<&[u8]>, - info: Option<&[u8]> -) -> Vec { - // 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 = vec![0u8; digest.output_bytes()]; - hkdf_extract(digest, salt, ikm, &mut pkr); - - let mut okm: Vec = vec![0u8; length]; - hkdf_expand(digest, &pkr, info, &mut okm); - - okm -}