Pārlūkot izejas kodu

First working implementation, nicly handle upload response, cleanup

timvisee 7 gadi atpakaļ
vecāks
revīzija
0786ed5917
1 mainītis faili ar 136 papildinājumiem un 103 dzēšanām
  1. 136 103
      src/main.rs

+ 136 - 103
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,65 +90,85 @@ 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"");
 
-/// A file reader, that encrypts the file with the given cipher, and appends
-/// the raw cipher tag.
-///
-/// This reader is lazy, and reads/encrypts the file on the fly.
-///
-/// TODO: the current implementation is not lazy
-struct EncryptedFileReaderTagged {
-    /// A cursor that reads encrypted file data.
-    data: Cursor<Vec<u8>>,
+    // Define the digest to use
+    let digest = Sha256::new();
 
-    /// A tag cursor that reads the tag to append.
-    tag: Cursor<Vec<u8>>,
-}
+    let mut pkr: Vec<u8> = vec![0u8; digest.output_bytes()];
+    hkdf_extract(digest, salt, ikm, &mut pkr);
 
-impl EncryptedFileReaderTagged {
-    /// Construct a new reader.
-    pub fn new(mut file: File, mut cipher: AesGcm<'static>) -> Self {
-        // Get the file length
-        let len = file.metadata().unwrap().len() as usize;
+    let mut okm: Vec<u8> = vec![0u8; length];
+    hkdf_expand(digest, &pkr, info, &mut okm);
 
-        // Create a file data buffer and an encrypted buffer
-        let mut data = Vec::with_capacity(len);
-        file.read_to_end(&mut data).unwrap();
-        let mut encrypted = vec![0u8; data.len()];
+    okm
+}
 
-        // Encrypt the data, get the tag
-        let mut tag = vec![0u8; TAG_LEN];
-        cipher.encrypt(&data, &mut encrypted, &mut tag);
+fn derive_file_key(secret: &[u8]) -> Vec<u8> {
+    hkdf(16, secret, None, Some(b"encryption"))
+}
 
-        // Construct the reader and return
-        EncryptedFileReaderTagged {
-            data: Cursor::new(encrypted),
-            tag: Cursor::new(tag),
-        }
+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!();
     }
 }
 
-impl Read for EncryptedFileReaderTagged {
-    fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
-        // Read the data if we haven't started with the tag yet
-        if self.tag.position() == 0 {
-            // Read and return if something was read
-            let result = self.data.read(buf);
-            match result {
-                Ok(len) if len > 0 => return result,
-                _ => {},
-            }
+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(),
         }
+    }
 
-        // Read the tag if it's empty
-        self.tag.read(buf)
+    /// Convert this structure to a JSON string.
+    pub fn to_json(&self) -> String {
+        serde_json::to_string(&self).unwrap()
     }
 }
 
@@ -184,75 +206,86 @@ impl Header for XFileMetadata {
     }
 }
 
-#[derive(Serialize)]
-struct Metadata {
-    /// The input vector
-    iv: String,
-
-    /// The file name
-    name: String,
+/// A file reader, that encrypts the file with the given cipher, and appends
+/// the raw cipher tag.
+///
+/// This reader is lazy, and reads/encrypts the file on the fly.
+///
+/// TODO: the current implementation is not lazy
+struct EncryptedFileReaderTagged {
+    /// A cursor that reads encrypted file data.
+    data: Cursor<Vec<u8>>,
 
-    /// The file mimetype
-    #[serde(rename="type")]
-    mime: String,
+    /// A tag cursor that reads the tag to append.
+    tag: Cursor<Vec<u8>>,
 }
 
-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(),
-        }
-    }
+impl EncryptedFileReaderTagged {
+    /// Construct a new reader.
+    pub fn new(mut file: File, mut cipher: AesGcm<'static>) -> Self {
+        // Get the file length
+        let len = file.metadata().unwrap().len() as usize;
 
-    /// Convert this structure to a JSON string.
-    pub fn to_json(&self) -> String {
-        serde_json::to_string(&self).unwrap()
+        // Create a file data buffer and an encrypted buffer
+        let mut data = Vec::with_capacity(len);
+        file.read_to_end(&mut data).unwrap();
+        let mut encrypted = vec![0u8; data.len()];
+
+        // Encrypt the data, get the tag
+        let mut tag = vec![0u8; TAG_LEN];
+        cipher.encrypt(&data, &mut encrypted, &mut tag);
+
+        // Construct the reader and return
+        EncryptedFileReaderTagged {
+            data: Cursor::new(encrypted),
+            tag: Cursor::new(tag),
+        }
     }
 }
 
-fn derive_file_key(secret: &[u8]) -> Vec<u8> {
-    hkdf(16, secret, None, Some(b"encryption"))
-}
+impl Read for EncryptedFileReaderTagged {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
+        // Read the data if we haven't started with the tag yet
+        if self.tag.position() == 0 {
+            // Read and return if something was read
+            let result = self.data.read(buf);
+            match result {
+                Ok(len) if len > 0 => return result,
+                _ => {},
+            }
+        }
 
-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!();
+        // Read the tag if it's empty
+        self.tag.read(buf)
     }
 }
 
-fn derive_meta_key(secret: &[u8]) -> Vec<u8> {
-    hkdf(16, secret, None, Some(b"metadata"))
+/// 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,
 }
 
-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
+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))
+    }
 }