diff --git a/Cargo.lock b/Cargo.lock index 29d1db4..6b0c628 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,17 @@ version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" +[[package]] +name = "argon2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34f8cda1a0ecf6f19d2bf64b9349d86900fa9bf98c979e655347a9e9dbe588c1" +dependencies = [ + "base64ct", + "blake2", + "password-hash", +] + [[package]] name = "async-trait" version = "0.1.51" @@ -103,6 +114,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64ct" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" + [[package]] name = "bincode" version = "1.3.3" @@ -149,6 +166,17 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +dependencies = [ + "crypto-mac", + "digest", + "opaque-debug", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -336,6 +364,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "digest" version = "0.9.0" @@ -922,17 +960,19 @@ dependencies = [ name = "omegaupload-common" version = "0.1.0" dependencies = [ + "argon2", "base64", "bytes", "chacha20poly1305", "chrono", + "gloo-console", "headers", "http", "lazy_static", "rand", "serde", - "sha2", "thiserror", + "typenum", "url", "web-sys", ] @@ -1001,6 +1041,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "password-hash" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -1377,19 +1428,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "sha2" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest", - "opaque-debug", -] - [[package]] name = "sharded-slab" version = "0.1.4" diff --git a/cli/src/main.rs b/cli/src/main.rs index c8b1092..99fed23 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -6,9 +6,9 @@ use std::io::{Read, Write}; use anyhow::{anyhow, bail, Context, Result}; use atty::Stream; use clap::Parser; -use omegaupload_common::crypto::{gen_key_nonce, open_in_place, seal_in_place, Key}; +use omegaupload_common::crypto::{open_in_place, seal_in_place}; use omegaupload_common::{ - base64, hash, Expiration, ParsedUrl, Url, API_ENDPOINT, EXPIRATION_HEADER_NAME, + base64, Expiration, ParsedUrl, Url, API_ENDPOINT, EXPIRATION_HEADER_NAME, }; use reqwest::blocking::Client; use reqwest::header::EXPIRES; @@ -65,27 +65,13 @@ fn handle_upload( bail!("This tool requires non interactive CLI. Pipe something in!"); } - let (data, nonce, key, pw_used) = { - let (enc_key, nonce) = gen_key_nonce(); + let (data, key) = { let mut container = Vec::new(); std::io::stdin().read_to_end(&mut container)?; - seal_in_place(&mut container, &nonce, &enc_key) - .map_err(|_| anyhow!("Failed to encrypt data"))?; - - let pw_used = if let Some(password) = password { - let pw_hash = hash(password.expose_secret().as_bytes()); - let pw_key = Key::from_slice(pw_hash.as_ref()); - seal_in_place(&mut container, &nonce.increment(), pw_key) - .map_err(|_| anyhow!("Failed to encrypt data"))?; - true - } else { - false - }; - + let password = password.as_ref().map(|v| v.expose_secret().as_ref()); + let enc_key = seal_in_place(&mut container, password)?; let key = base64::encode(&enc_key); - let nonce = base64::encode(&nonce); - - (container, nonce, key, pw_used) + (container, key) }; let mut res = Client::new().post(url.as_ref()); @@ -104,9 +90,9 @@ fn handle_upload( .map_err(|_| anyhow!("Failed to get base URL"))? .extend(std::iter::once(res.text()?)); - let mut fragment = format!("key:{}!nonce:{}", key, nonce); + let mut fragment = format!("key:{}", key); - if pw_used { + if password.is_some() { fragment.push_str("!pw"); } @@ -141,6 +127,7 @@ fn handle_download(mut url: ParsedUrl) -> Result<()> { let mut data = res.bytes()?.as_ref().to_vec(); + let mut password = None; if url.needs_password { // Only print prompt on interactive, else it messes with output if atty::is(Stream::Stdout) { @@ -150,16 +137,10 @@ fn handle_download(mut url: ParsedUrl) -> Result<()> { let mut input = String::new(); std::io::stdin().read_line(&mut input)?; input.pop(); // last character is \n, we need to drop it. - - let pw_hash = hash(input.as_bytes()); - let pw_key = Key::from_slice(pw_hash.as_ref()); - - open_in_place(&mut data, &url.nonce.increment(), pw_key) - .map_err(|_| anyhow!("Failed to decrypt data. Incorrect password?"))?; + password = Some(input); } - open_in_place(&mut data, &url.nonce, &url.decryption_key) - .map_err(|_| anyhow!("Failed to decrypt data. Incorrect decryption key?"))?; + open_in_place(&mut data, &url.decryption_key, &password)?; if atty::is(Stream::Stdout) { if let Ok(data) = String::from_utf8(data) { diff --git a/common/Cargo.toml b/common/Cargo.toml index 84fa933..46d2497 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -8,18 +8,21 @@ edition = "2021" [dependencies] base64 = "0.13" bytes = { version = "*", features = ["serde"] } -chacha20poly1305 = "0.9" +chacha20poly1305 = { version = "0.9", features = ["stream"] } chrono = { version = "0.4", features = ["serde"] } headers = "*" lazy_static = "1" rand = "0.8" serde = { version = "1", features = ["derive"] } -sha2 = "0.9" thiserror = "1" +typenum = "1" url = "2" +argon2 = "0.3.1" -web-sys = { version = "0.3", features = ["Headers"], optional = true } +# Wasm features +gloo-console = { version = "0.1", optional = true } http = { version = "0.2", optional = true } +web-sys = { version = "0.3", features = ["Headers"], optional = true } [features] -wasm = ["web-sys", "http"] \ No newline at end of file +wasm = ["gloo-console", "http", "web-sys"] \ No newline at end of file diff --git a/common/src/base64.rs b/common/src/base64.rs new file mode 100644 index 0000000..ebc9e9e --- /dev/null +++ b/common/src/base64.rs @@ -0,0 +1,11 @@ +use base64::{DecodeError, URL_SAFE}; + +/// URL-safe Base64 encoding. +pub fn encode(input: impl AsRef<[u8]>) -> String { + base64::encode_config(input, URL_SAFE) +} + +/// URL-safe Base64 decoding. +pub fn decode(input: impl AsRef<[u8]>) -> Result, DecodeError> { + base64::decode_config(input, URL_SAFE) +} diff --git a/common/src/crypto.rs b/common/src/crypto.rs new file mode 100644 index 0000000..d36782b --- /dev/null +++ b/common/src/crypto.rs @@ -0,0 +1,196 @@ +use std::fmt::Display; +use std::ops::{Deref, DerefMut}; + +use argon2::Argon2; +use chacha20poly1305::aead::generic_array::sequence::GenericSequence; +use chacha20poly1305::aead::generic_array::GenericArray; +use chacha20poly1305::aead::{AeadInPlace, NewAead}; +use chacha20poly1305::XChaCha20Poly1305; +use chacha20poly1305::XNonce; +use rand::{thread_rng, Rng}; +use typenum::Unsigned; + +pub use chacha20poly1305::Key; + +#[derive(Debug)] +pub enum Error { + ChaCha20Poly1305(chacha20poly1305::aead::Error), + Argon2(argon2::Error), +} + +impl From for Error { + fn from(err: chacha20poly1305::aead::Error) -> Self { + Error::ChaCha20Poly1305(err) + } +} + +impl From for Error { + fn from(err: argon2::Error) -> Self { + Error::Argon2(err) + } +} + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::ChaCha20Poly1305(_) => write!(f, "Decryption failed"), + Error::Argon2(_) => write!(f, "KDF failed"), + } + } +} + +/// Seals the provided message with an optional message. The resulting sealed +/// message has the nonce used to encrypt the message appended to it as well as +/// a salt string used to derive the key. In other words, the modified buffer is +/// one of the following to possibilities, depending if there was a password +/// provided: +/// +/// ``` +/// modified = C(message, rng_key, nonce) || nonce +/// ``` +/// or +/// ``` +/// modified = C(C(message, rng_key, nonce), kdf(pw, salt), nonce + 1) || nonce || salt +/// ``` +/// +/// Where: +/// - `C(message, key, nonce)` represents encrypting a provided message with +/// XChaCha20Poly1305. +/// - `rng_key` represents a randomly generated key. +/// - `kdf(pw, salt)` represents a key derived from Argon2. +pub fn seal_in_place(message: &mut Vec, pw: Option<&str>) -> Result { + let (key, nonce) = gen_key_nonce(); + let cipher = XChaCha20Poly1305::new(&key); + cipher.encrypt_in_place(&nonce, &[], message)?; + + let mut maybe_salt_string = None; + if let Some(password) = pw { + let (key, salt_string) = kdf(&password)?; + maybe_salt_string = Some(salt_string); + let cipher = XChaCha20Poly1305::new(&key); + cipher.encrypt_in_place(&nonce.increment(), &[], message)?; + } + + message.extend_from_slice(nonce.as_slice()); + if let Some(maybe_salted_string) = maybe_salt_string { + message.extend_from_slice(maybe_salted_string.as_ref()); + } + Ok(key) +} + +pub fn open_in_place(data: &mut Vec, key: &Key, password: Option<&str>) -> Result<(), Error> { + let buffer_len = data.len(); + let pw_key = if let Some(password) = password { + let salt_buf = data.split_off(buffer_len - Salt::SIZE); + let argon = Argon2::default(); + let mut pw_key = Key::default(); + argon.hash_password_into(password.as_bytes(), &salt_buf, &mut pw_key)?; + Some(pw_key) + } else { + None + }; + + let nonce = Nonce::from_slice(&data.split_off(Nonce::SIZE)); + + // At this point we should have a buffer that's only the ciphertext. + + if let Some(key) = pw_key { + let cipher = XChaCha20Poly1305::new(&key); + cipher.decrypt_in_place(&nonce.increment(), &[], data)?; + } + + let cipher = XChaCha20Poly1305::new(&key); + cipher.decrypt_in_place(&nonce, &[], data)?; + + Ok(()) +} + +/// Securely generates a random key and nonce. +#[must_use] +fn gen_key_nonce() -> (Key, Nonce) { + let mut rng = thread_rng(); + let mut key: Key = GenericArray::default(); + rng.fill(key.as_mut_slice()); + let mut nonce = Nonce::default(); + rng.fill(nonce.as_mut_slice()); + (key, nonce) +} + +// Type alias; to ensure that we're consistent on what the inner impl is. +type NonceImpl = XNonce; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +struct Nonce(NonceImpl); + +impl Default for Nonce { + fn default() -> Self { + Self(GenericArray::default()) + } +} + +impl Deref for Nonce { + type Target = NonceImpl; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Nonce { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsRef<[u8]> for Nonce { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl Nonce { + const SIZE: usize = >::Length::USIZE; + + #[must_use] + pub fn increment(&self) -> Self { + let mut inner = self.0; + inner.as_mut_slice()[0] += 1; + Self(inner) + } + + #[must_use] + pub fn from_slice(slice: &[u8]) -> Self { + Self(*NonceImpl::from_slice(slice)) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +struct Salt([u8; Salt::SIZE]); + +impl Salt { + const SIZE: usize = argon2::password_hash::Salt::RECOMMENDED_LENGTH; + + fn random() -> Self { + let mut salt = [0u8; Salt::SIZE]; + thread_rng().fill(&mut salt); + Self(salt) + } +} + +impl AsRef<[u8]> for Salt { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +/// Hashes an input to output a usable key. +fn kdf(password: &str) -> Result<(Key, Salt), argon2::Error> { + let salt = Salt::random(); + let hasher = Argon2::default(); + let mut key = Key::default(); + hasher.hash_password_into(password.as_ref(), salt.as_ref(), &mut key)?; + + Ok((*Key::from_slice(&key), salt)) +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 36ed24c..b1777f9 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -10,130 +10,25 @@ use chrono::{DateTime, Duration, Utc}; use headers::{Header, HeaderName, HeaderValue}; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; use thiserror::Error; pub use url::Url; -use crate::crypto::{Key, Nonce}; +use crate::crypto::Key; + +pub mod base64; +pub mod crypto; pub const API_ENDPOINT: &str = "/api"; -pub mod base64 { - /// URL-safe Base64 encoding. - pub fn encode(input: impl AsRef<[u8]>) -> String { - base64::encode_config(input, base64::URL_SAFE) - } - - /// URL-safe Base64 decoding. - pub fn decode(input: impl AsRef<[u8]>) -> Result, base64::DecodeError> { - base64::decode_config(input, base64::URL_SAFE) - } -} - -/// Hashes an input to output a usable key. -pub fn hash(data: impl AsRef<[u8]>) -> crypto::Key { - let mut hasher = Sha256::new(); - hasher.update(data); - hasher.finalize() -} - -pub mod crypto { - use std::ops::{Deref, DerefMut}; - - use chacha20poly1305::aead::generic_array::GenericArray; - use chacha20poly1305::aead::{Aead, AeadInPlace, Buffer, Error, NewAead}; - use chacha20poly1305::XChaCha20Poly1305; - use chacha20poly1305::XNonce; - use rand::{thread_rng, Rng}; - - pub use chacha20poly1305::Key; - - /// Securely generates a random key and nonce. - #[must_use] - pub fn gen_key_nonce() -> (Key, Nonce) { - let mut rng = thread_rng(); - let mut key: Key = GenericArray::default(); - rng.fill(key.as_mut_slice()); - let mut nonce = Nonce::default(); - rng.fill(nonce.as_mut_slice()); - (key, nonce) - } - - pub fn seal(plaintext: &[u8], nonce: &Nonce, key: &Key) -> Result, Error> { - let cipher = XChaCha20Poly1305::new(key); - cipher.encrypt(nonce, plaintext) - } - - pub fn seal_in_place(buffer: &mut impl Buffer, nonce: &Nonce, key: &Key) -> Result<(), Error> { - let cipher = XChaCha20Poly1305::new(key); - cipher.encrypt_in_place(nonce, &[], buffer) - } - - pub fn open(encrypted: &[u8], nonce: &Nonce, key: &Key) -> Result, Error> { - let cipher = XChaCha20Poly1305::new(key); - cipher.decrypt(nonce, encrypted) - } - - pub fn open_in_place(buffer: &mut impl Buffer, nonce: &Nonce, key: &Key) -> Result<(), Error> { - let cipher = XChaCha20Poly1305::new(key); - cipher.decrypt_in_place(nonce, &[], buffer) - } - - #[derive(Clone, Copy, PartialEq, Eq, Debug)] - pub struct Nonce(XNonce); - - impl Default for Nonce { - fn default() -> Self { - Self(GenericArray::default()) - } - } - - impl Deref for Nonce { - type Target = XNonce; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl DerefMut for Nonce { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - - impl AsRef<[u8]> for Nonce { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } - } - - impl Nonce { - #[must_use] - pub fn increment(&self) -> Self { - let mut inner = self.0; - inner.as_mut_slice()[0] += 1; - Self(inner) - } - - #[must_use] - pub fn from_slice(slice: &[u8]) -> Self { - Self(*XNonce::from_slice(slice)) - } - } -} - pub struct ParsedUrl { pub sanitized_url: Url, pub decryption_key: Key, - pub nonce: Nonce, pub needs_password: bool, } #[derive(Default)] pub struct PartialParsedUrl { pub decryption_key: Option, - pub nonce: Option, pub needs_password: bool, } @@ -150,7 +45,6 @@ impl From<&str> for PartialParsedUrl { let mut decryption_key = None; let mut needs_password = false; - let mut nonce = None; for (key, value) in args { match (key, value) { @@ -160,16 +54,12 @@ impl From<&str> for PartialParsedUrl { ("pw", _) => { needs_password = true; } - ("nonce", Some(value)) => { - nonce = base64::decode(value).as_deref().map(Nonce::from_slice).ok(); - } _ => (), } } Self { decryption_key, - nonce, needs_password, } } @@ -200,23 +90,19 @@ impl FromStr for ParsedUrl { let PartialParsedUrl { decryption_key, needs_password, - nonce, } = PartialParsedUrl::from(fragment); url.set_fragment(None); - let (decryption_key, nonce) = match (&decryption_key, nonce) { - (None, None) => Err(ParseUrlError::NeedKeyAndNonce), - (None, Some(_)) => Err(ParseUrlError::NeedKey), - (Some(_), None) => Err(ParseUrlError::NeedNonce), - (Some(k), Some(v)) => Ok((*k, v)), + let decryption_key = match &decryption_key { + Some(k) => Ok(*k), + None => Err(ParseUrlError::NeedKey), }?; Ok(Self { sanitized_url: url, decryption_key, needs_password, - nonce, }) } } diff --git a/web/src/decrypt.rs b/web/src/decrypt.rs index f43fe5e..89e7d79 100644 --- a/web/src/decrypt.rs +++ b/web/src/decrypt.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use gloo_console::log; use js_sys::{Array, Uint8Array}; -use omegaupload_common::crypto::{open_in_place, Key, Nonce}; +use omegaupload_common::crypto::{open_in_place, Key}; use serde::Serialize; use wasm_bindgen::JsCast; use web_sys::{Blob, BlobPropertyBag}; @@ -36,31 +36,10 @@ fn now() -> f64 { pub fn decrypt( mut container: Vec, key: Key, - nonce: Nonce, - maybe_password: Option, + maybe_password: Option<&str>, ) -> Result { - log!("Stage 1 decryption started."); - let start = now(); - - if let Some(password) = maybe_password { - crate::render_message("Decrypting Stage 1...".into()); - open_in_place(&mut container, &nonce.increment(), &password).map_err(|_| { - crate::render_message("Unable to decrypt paste with the provided password.".into()); - PasteCompleteConstructionError::StageOneFailure - })?; - } - log!(format!("Stage 1 completed in {}ms", now() - start)); - - log!("Stage 2 decryption started."); - let start = now(); - crate::render_message("Decrypting Stage 2...".into()); - open_in_place(&mut container, &nonce, &key).map_err(|_| { - crate::render_message( - "Unable to decrypt paste with the provided encryption key and nonce.".into(), - ); - PasteCompleteConstructionError::StageTwoFailure - })?; - log!(format!("Stage 2 completed in {}ms", now() - start)); + open_in_place(&mut container, &key, maybe_password) + .map_err(|_| PasteCompleteConstructionError::Decryption)?; let mime_type = tree_magic_mini::from_u8(&container); log!("Mimetype: ", mime_type); @@ -79,6 +58,7 @@ pub fn decrypt( Blob::new_with_u8_array_sequence_and_options(blob_chunks.dyn_ref().unwrap(), &blob_props) .unwrap(), ); + log!(format!("Blob conversion completed in {}ms", now() - start)); if mime_type.starts_with("text/") { @@ -125,8 +105,7 @@ pub fn decrypt( #[derive(Debug)] pub enum PasteCompleteConstructionError { - StageOneFailure, - StageTwoFailure, + Decryption, InvalidEncoding, } @@ -135,11 +114,8 @@ impl std::error::Error for PasteCompleteConstructionError {} impl Display for PasteCompleteConstructionError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - PasteCompleteConstructionError::StageOneFailure => { - write!(f, "Failed to decrypt stage one.") - } - PasteCompleteConstructionError::StageTwoFailure => { - write!(f, "Failed to decrypt stage two.") + PasteCompleteConstructionError::Decryption => { + write!(f, "Failed to decrypt data.") } PasteCompleteConstructionError::InvalidEncoding => write!( f, diff --git a/web/src/main.rs b/web/src/main.rs index bba5b8c..1cbe632 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -8,9 +8,9 @@ use decrypt::DecryptedData; use gloo_console::{error, log}; use http::uri::PathAndQuery; use http::{StatusCode, Uri}; -use js_sys::{JsString, Object, Uint8Array, Array}; -use omegaupload_common::crypto::{Key, Nonce}; -use omegaupload_common::{hash, Expiration, PartialParsedUrl}; +use js_sys::{Array, JsString, Object, Uint8Array}; +use omegaupload_common::crypto::Key; +use omegaupload_common::{Expiration, PartialParsedUrl}; use reqwasm::http::Request; use wasm_bindgen::prelude::{wasm_bindgen, Closure}; use wasm_bindgen::{JsCast, JsValue}; @@ -69,7 +69,7 @@ fn main() { log!(&url); log!(&request_uri.to_string()); log!(&location().pathname().unwrap()); - let (key, nonce, needs_pw) = { + let (key, needs_pw) = { let partial_parsed_url = url .split_once('#') .map(|(_, fragment)| PartialParsedUrl::from(fragment)) @@ -81,14 +81,7 @@ fn main() { render_message("Invalid paste link: Missing decryption key.".into()); return; }; - let nonce = if let Some(nonce) = partial_parsed_url.nonce { - nonce - } else { - error!("Nonce is missing in url; bailing."); - render_message("Invalid paste link: Missing nonce.".into()); - return; - }; - (key, nonce, partial_parsed_url.needs_password) + (key, partial_parsed_url.needs_password) }; let password = if needs_pw { @@ -97,7 +90,7 @@ fn main() { if let Ok(Some(password)) = pw { if !password.is_empty() { - break Some(hash(password)); + break Some(password); } } } @@ -108,7 +101,7 @@ fn main() { if location().pathname().unwrap() == "/" { } else { spawn_local(async move { - if let Err(e) = fetch_resources(request_uri, key, nonce, password).await { + if let Err(e) = fetch_resources(request_uri, key, password.as_deref()).await { log!(e.to_string()); } }); @@ -116,12 +109,7 @@ fn main() { } #[allow(clippy::future_not_send)] -async fn fetch_resources( - request_uri: Uri, - key: Key, - nonce: Nonce, - password: Option, -) -> Result<()> { +async fn fetch_resources(request_uri: Uri, key: Key, password: Option<&str>) -> Result<()> { match Request::get(&request_uri.to_string()).send().await { Ok(resp) if resp.status() == StatusCode::OK => { let expires = Expiration::try_from(resp.headers()).map_or_else( @@ -154,7 +142,7 @@ async fn fetch_resources( return Ok(()); } - let decrypted = decrypt(data, key, nonce, password)?; + let decrypted = decrypt(data, key, password)?; let db_open_req = open_idb()?; // On success callback @@ -197,10 +185,13 @@ async fn fetch_resources( .data(blob) .extra( "entries", - JsValue::from(entries.into_iter() - .filter_map(|x| JsValue::from_serde(x).ok()) - .collect::()) + JsValue::from( + entries + .into_iter() + .filter_map(|x| JsValue::from_serde(x).ok()) + .collect::(), ), + ), }; let put_action = transaction