clippy
This commit is contained in:
parent
24eff63a5e
commit
b833f97c55
7 changed files with 86 additions and 96 deletions
3
build.sh
3
build.sh
|
@ -12,9 +12,6 @@ sed -i 's#stylesheet" href="/main#stylesheet" href="/static/main#g' dist/index.h
|
|||
# Build server
|
||||
cargo build --release --bin omegaupload-server
|
||||
|
||||
# index.html no longer needed, served statically by the upload server
|
||||
rm dist/index.html
|
||||
|
||||
# Prepare assets for upload to webserver
|
||||
mkdir -p dist/static
|
||||
# Move everything that's not index.html into a `static` subdir
|
||||
|
|
|
@ -134,13 +134,14 @@ fn handle_download(mut url: ParsedUrl) -> Result<()> {
|
|||
|
||||
let mut data = res.bytes()?.as_ref().to_vec();
|
||||
|
||||
let mut password = None;
|
||||
if url.needs_password {
|
||||
let password = if url.needs_password {
|
||||
// Only print prompt on interactive, else it messes with output
|
||||
let maybe_password =
|
||||
prompt_password_stderr("Please enter the password to access this paste: ")?;
|
||||
password = Some(SecretVec::new(maybe_password.into_bytes()));
|
||||
}
|
||||
Some(SecretVec::new(maybe_password.into_bytes()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
open_in_place(&mut data, &url.decryption_key, password)?;
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use std::fmt::Display;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use argon2::Argon2;
|
||||
|
@ -11,33 +10,16 @@ use rand::{thread_rng, Rng};
|
|||
use secrecy::{ExposeSecret, Secret, SecretVec, Zeroize};
|
||||
use typenum::Unsigned;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
ChaCha20Poly1305(chacha20poly1305::aead::Error),
|
||||
Argon2(argon2::Error),
|
||||
}
|
||||
|
||||
impl From<chacha20poly1305::aead::Error> for Error {
|
||||
fn from(err: chacha20poly1305::aead::Error) -> Self {
|
||||
Error::ChaCha20Poly1305(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<argon2::Error> 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"),
|
||||
}
|
||||
}
|
||||
#[error("Invalid password.")]
|
||||
Password,
|
||||
#[error("Invalid secret key.")]
|
||||
SecretKey,
|
||||
#[error("An error occurred while trying to decrypt the blob.")]
|
||||
Encryption,
|
||||
#[error("An error occurred while trying to derive a secret key.")]
|
||||
Kdf,
|
||||
}
|
||||
|
||||
// This struct intentionally prevents implement Clone or Copy
|
||||
|
@ -72,7 +54,7 @@ impl DerefMut for Key {
|
|||
|
||||
impl Zeroize for Key {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize()
|
||||
self.0.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +74,7 @@ impl Zeroize for Key {
|
|||
///
|
||||
/// Where:
|
||||
/// - `C(message, key, nonce)` represents encrypting a provided message with
|
||||
/// XChaCha20Poly1305.
|
||||
/// `XChaCha20Poly1305`.
|
||||
/// - `rng_key` represents a randomly generated key.
|
||||
/// - `kdf(pw, salt)` represents a key derived from Argon2.
|
||||
pub fn seal_in_place(
|
||||
|
@ -101,14 +83,18 @@ pub fn seal_in_place(
|
|||
) -> Result<Secret<Key>, Error> {
|
||||
let (key, nonce) = gen_key_nonce();
|
||||
let cipher = XChaCha20Poly1305::new(key.expose_secret());
|
||||
cipher.encrypt_in_place(&nonce, &[], message)?;
|
||||
cipher
|
||||
.encrypt_in_place(&nonce, &[], message)
|
||||
.map_err(|_| Error::Encryption)?;
|
||||
|
||||
let mut maybe_salt_string = None;
|
||||
if let Some(password) = pw {
|
||||
let (key, salt_string) = kdf(&password)?;
|
||||
let (key, salt_string) = kdf(&password).map_err(|_| Error::Kdf)?;
|
||||
maybe_salt_string = Some(salt_string);
|
||||
let cipher = XChaCha20Poly1305::new(key.expose_secret());
|
||||
cipher.encrypt_in_place(&nonce.increment(), &[], message)?;
|
||||
cipher
|
||||
.encrypt_in_place(&nonce.increment(), &[], message)
|
||||
.map_err(|_| Error::Encryption)?;
|
||||
}
|
||||
|
||||
message.extend_from_slice(nonce.as_slice());
|
||||
|
@ -127,7 +113,9 @@ pub fn open_in_place(
|
|||
let salt_buf = data.split_off(data.len() - Salt::SIZE);
|
||||
let argon = Argon2::default();
|
||||
let mut pw_key = Key::default();
|
||||
argon.hash_password_into(password.expose_secret(), &salt_buf, &mut pw_key)?;
|
||||
argon
|
||||
.hash_password_into(password.expose_secret(), &salt_buf, &mut pw_key)
|
||||
.map_err(|_| Error::Kdf)?;
|
||||
Some(Secret::new(pw_key))
|
||||
} else {
|
||||
None
|
||||
|
@ -139,11 +127,15 @@ pub fn open_in_place(
|
|||
|
||||
if let Some(key) = pw_key {
|
||||
let cipher = XChaCha20Poly1305::new(key.expose_secret());
|
||||
cipher.decrypt_in_place(&nonce.increment(), &[], data)?;
|
||||
cipher
|
||||
.decrypt_in_place(&nonce.increment(), &[], data)
|
||||
.map_err(|_| Error::Password)?;
|
||||
}
|
||||
|
||||
let cipher = XChaCha20Poly1305::new(key.expose_secret());
|
||||
cipher.decrypt_in_place(&nonce, &[], data)?;
|
||||
cipher
|
||||
.decrypt_in_place(&nonce, &[], data)
|
||||
.map_err(|_| Error::SecretKey)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -208,13 +200,13 @@ impl Nonce {
|
|||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
struct Salt([u8; Salt::SIZE]);
|
||||
struct Salt([u8; Self::SIZE]);
|
||||
|
||||
impl Salt {
|
||||
const SIZE: usize = argon2::password_hash::Salt::RECOMMENDED_LENGTH;
|
||||
|
||||
fn random() -> Self {
|
||||
let mut salt = [0u8; Salt::SIZE];
|
||||
let mut salt = [0_u8; Self::SIZE];
|
||||
thread_rng().fill(&mut salt);
|
||||
Self(salt)
|
||||
}
|
||||
|
|
|
@ -41,9 +41,7 @@ impl From<&str> for PartialParsedUrl {
|
|||
// Base64 has an interesting property that the length of an encoded text
|
||||
// is always 4/3rds larger than the original data.
|
||||
if !fragment.contains("key") {
|
||||
let decryption_key = base64::decode(fragment)
|
||||
.ok()
|
||||
.and_then(|k| Key::new_secret(k));
|
||||
let decryption_key = base64::decode(fragment).ok().and_then(Key::new_secret);
|
||||
|
||||
return Self {
|
||||
decryption_key,
|
||||
|
@ -66,7 +64,7 @@ impl From<&str> for PartialParsedUrl {
|
|||
for (key, value) in args {
|
||||
match (key, value) {
|
||||
("key", Some(value)) => {
|
||||
decryption_key = base64::decode(value).ok().and_then(|k| Key::new_secret(k));
|
||||
decryption_key = base64::decode(value).ok().and_then(Key::new_secret);
|
||||
}
|
||||
("pw", _) => {
|
||||
needs_password = true;
|
||||
|
|
|
@ -94,6 +94,8 @@ async fn main() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// See https://link.eddie.sh/5JHlD
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn set_up_expirations(db: &Arc<DB>) {
|
||||
let mut corrupted = 0;
|
||||
let mut expired = 0;
|
||||
|
@ -161,16 +163,13 @@ fn set_up_expirations(db: &Arc<DB>) {
|
|||
|
||||
async fn handle_signals(mut signals: Signals, db: Arc<DB>) {
|
||||
while let Some(signal) = signals.next().await {
|
||||
match signal {
|
||||
SIGUSR1 => {
|
||||
if signal == SIGUSR1 {
|
||||
let meta_cf = db.cf_handle(META_CF_NAME).unwrap();
|
||||
info!(
|
||||
"Active paste count: {}",
|
||||
db.iterator_cf(meta_cf, IteratorMode::Start).count()
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
use std::io::Cursor;
|
||||
use std::sync::Arc;
|
||||
|
||||
use gloo_console::log;
|
||||
use js_sys::{Array, Uint8Array};
|
||||
use omegaupload_common::crypto::{open_in_place, Key};
|
||||
use omegaupload_common::crypto::{open_in_place, Error, Key};
|
||||
use omegaupload_common::secrecy::{Secret, SecretVec};
|
||||
use serde::Serialize;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
@ -36,11 +35,10 @@ fn now() -> f64 {
|
|||
|
||||
pub fn decrypt(
|
||||
mut container: Vec<u8>,
|
||||
key: Secret<Key>,
|
||||
key: &Secret<Key>,
|
||||
maybe_password: Option<SecretVec<u8>>,
|
||||
) -> Result<DecryptedData, PasteCompleteConstructionError> {
|
||||
open_in_place(&mut container, &key, maybe_password)
|
||||
.map_err(|_| PasteCompleteConstructionError::Decryption)?;
|
||||
) -> Result<DecryptedData, Error> {
|
||||
open_in_place(&mut container, key, maybe_password)?;
|
||||
|
||||
let mime_type = tree_magic_mini::from_u8(&container);
|
||||
log!("Mimetype: ", mime_type);
|
||||
|
@ -63,15 +61,22 @@ pub fn decrypt(
|
|||
log!(format!("Blob conversion completed in {}ms", now() - start));
|
||||
|
||||
if mime_type.starts_with("text/") {
|
||||
String::from_utf8(container)
|
||||
.map(Arc::new)
|
||||
.map(DecryptedData::String)
|
||||
.map_err(|_| PasteCompleteConstructionError::InvalidEncoding)
|
||||
} else if mime_type.starts_with("image/") || mime_type == "application/x-riff" {
|
||||
if let Ok(string) = String::from_utf8(container) {
|
||||
Ok(DecryptedData::String(Arc::new(string)))
|
||||
} else {
|
||||
Ok(DecryptedData::Blob(blob))
|
||||
}
|
||||
} else if mime_type.starts_with("image/")
|
||||
// application/x-riff is WebP
|
||||
|| mime_type == "application/x-riff"
|
||||
{
|
||||
Ok(DecryptedData::Image(blob, container.len()))
|
||||
} else if mime_type.starts_with("audio/") {
|
||||
Ok(DecryptedData::Audio(blob))
|
||||
} else if mime_type.starts_with("video/") || mime_type == "application/x-matroska" {
|
||||
} else if mime_type.starts_with("video/")
|
||||
// application/x-matroska is mkv
|
||||
|| mime_type == "application/x-matroska"
|
||||
{
|
||||
Ok(DecryptedData::Video(blob))
|
||||
} else if mime_type == "application/zip" {
|
||||
let mut entries = vec![];
|
||||
|
@ -103,25 +108,3 @@ pub fn decrypt(
|
|||
Ok(DecryptedData::Blob(blob))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PasteCompleteConstructionError {
|
||||
Decryption,
|
||||
InvalidEncoding,
|
||||
}
|
||||
|
||||
impl std::error::Error for PasteCompleteConstructionError {}
|
||||
|
||||
impl Display for PasteCompleteConstructionError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PasteCompleteConstructionError::Decryption => {
|
||||
write!(f, "Failed to decrypt data.")
|
||||
}
|
||||
PasteCompleteConstructionError::InvalidEncoding => write!(
|
||||
f,
|
||||
"Got an file with a text/* mime type, but was unable to parsed as valid UTF-8?"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use gloo_console::{error, log};
|
|||
use http::uri::PathAndQuery;
|
||||
use http::{StatusCode, Uri};
|
||||
use js_sys::{Array, JsString, Object, Uint8Array};
|
||||
use omegaupload_common::crypto::Key;
|
||||
use omegaupload_common::crypto::{Error as CryptoError, Key};
|
||||
use omegaupload_common::secrecy::{Secret, SecretVec};
|
||||
use omegaupload_common::{Expiration, PartialParsedUrl};
|
||||
use reqwasm::http::Request;
|
||||
|
@ -89,10 +89,15 @@ fn main() {
|
|||
loop {
|
||||
let pw = window().prompt_with_message("A password is required to decrypt this paste:");
|
||||
|
||||
if let Ok(Some(password)) = pw {
|
||||
if !password.is_empty() {
|
||||
match pw {
|
||||
Ok(Some(password)) if password.is_empty() => {
|
||||
break Some(SecretVec::new(password.into_bytes()));
|
||||
}
|
||||
Err(_) => {
|
||||
render_message("This paste requires a password.".into());
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -147,7 +152,22 @@ async fn fetch_resources(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let decrypted = decrypt(data, key, password)?;
|
||||
let decrypted = match decrypt(data, &key, password) {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
let msg = match e {
|
||||
CryptoError::Password => "The provided password was incorrect.",
|
||||
CryptoError::SecretKey => "The secret key in the URL was incorrect.",
|
||||
ref e => {
|
||||
log!(format!("Bad kdf or corrupted blob: {}", e));
|
||||
"An internal error occurred."
|
||||
}
|
||||
};
|
||||
|
||||
render_message(JsString::from(msg));
|
||||
bail!(e);
|
||||
}
|
||||
};
|
||||
let db_open_req = open_idb()?;
|
||||
|
||||
// On success callback
|
||||
|
@ -192,7 +212,7 @@ async fn fetch_resources(
|
|||
"entries",
|
||||
JsValue::from(
|
||||
entries
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter_map(|x| JsValue::from_serde(x).ok())
|
||||
.collect::<Array>(),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue