omegaupload/web/src/decrypt.rs

120 lines
3.9 KiB
Rust
Raw Normal View History

2021-10-24 02:25:42 -07:00
use std::fmt::{Display, Formatter};
use std::sync::Arc;
2021-10-23 10:10:55 -07:00
use gloo_console::log;
use js_sys::{Array, Uint8Array};
2021-10-24 02:25:42 -07:00
use omegaupload_common::crypto::{open_in_place, Key, Nonce};
2021-10-23 10:10:55 -07:00
use wasm_bindgen::JsCast;
use web_sys::Blob;
2021-10-24 11:40:19 -07:00
#[derive(Clone)]
pub enum DecryptedData {
String(Arc<String>),
Blob(Arc<Blob>),
2021-10-25 02:42:20 -07:00
Image(Arc<Blob>, (usize, usize), usize),
2021-10-24 11:40:19 -07:00
Audio(Arc<Blob>),
Video(Arc<Blob>),
}
2021-10-23 10:10:55 -07:00
2021-10-24 20:54:49 -07:00
fn now() -> f64 {
web_sys::window()
.expect("should have a Window")
.performance()
.expect("should have a Performance")
.now()
}
2021-10-24 02:25:42 -07:00
pub fn decrypt(
2021-10-23 10:10:55 -07:00
mut container: Vec<u8>,
key: Key,
nonce: Nonce,
maybe_password: Option<Key>,
) -> Result<DecryptedData, PasteCompleteConstructionError> {
let container = &mut container;
2021-10-24 20:54:49 -07:00
log!("Stage 1 decryption started.");
let start = now();
2021-10-25 02:42:20 -07:00
2021-10-23 10:10:55 -07:00
if let Some(password) = maybe_password {
2021-10-25 02:42:20 -07:00
crate::render_message("Decrypting Stage 1...".into());
open_in_place(container, &nonce.increment(), &password).map_err(|_| {
crate::render_message("Unable to decrypt paste with the provided password.".into());
PasteCompleteConstructionError::StageOneFailure
})?;
2021-10-23 10:10:55 -07:00
}
2021-10-24 20:54:49 -07:00
log!(format!("Stage 1 completed in {}ms", now() - start));
2021-10-23 10:10:55 -07:00
2021-10-24 20:54:49 -07:00
log!("Stage 2 decryption started.");
let start = now();
2021-10-25 02:42:20 -07:00
crate::render_message("Decrypting Stage 2...".into());
open_in_place(container, &nonce, &key).map_err(|_| {
crate::render_message(
"Unable to decrypt paste with the provided encryption key and nonce.".into(),
);
PasteCompleteConstructionError::StageTwoFailure
})?;
2021-10-24 20:54:49 -07:00
log!(format!("Stage 2 completed in {}ms", now() - start));
2021-10-23 10:10:55 -07:00
2021-10-24 12:14:55 -07:00
if let Ok(decrypted) = std::str::from_utf8(container) {
2021-10-23 10:10:55 -07:00
Ok(DecryptedData::String(Arc::new(decrypted.to_owned())))
} else {
2021-10-24 20:54:49 -07:00
log!("Blob conversion started.");
let start = now();
2021-10-23 10:10:55 -07:00
let blob_chunks = Array::new_with_length(container.chunks(65536).len().try_into().unwrap());
for (i, chunk) in container.chunks(65536).enumerate() {
let array = Uint8Array::new_with_length(chunk.len().try_into().unwrap());
2021-10-24 12:14:55 -07:00
array.copy_from(chunk);
2021-10-23 10:10:55 -07:00
blob_chunks.set(i.try_into().unwrap(), array.dyn_into().unwrap());
}
let blob =
Arc::new(Blob::new_with_u8_array_sequence(blob_chunks.dyn_ref().unwrap()).unwrap());
2021-10-24 20:54:49 -07:00
log!(format!("Blob conversion completed in {}ms", now() - start));
2021-10-23 10:10:55 -07:00
2021-10-24 20:54:49 -07:00
log!("Image introspection started");
let start = now();
2021-10-25 02:42:20 -07:00
let dimensions = imagesize::blob_size(&container).ok();
2021-10-24 20:54:49 -07:00
log!(format!(
"Image introspection completed in {}ms",
now() - start
));
2021-10-25 02:42:20 -07:00
if let Some(dimensions) = dimensions {
Ok(DecryptedData::Image(
blob,
(dimensions.width, dimensions.height),
container.len(),
))
2021-10-23 10:10:55 -07:00
} else {
2021-10-24 12:14:55 -07:00
let mime_type = tree_magic_mini::from_u8(container);
2021-10-24 11:40:19 -07:00
log!(mime_type);
if mime_type.starts_with("audio/") {
Ok(DecryptedData::Audio(blob))
} else if mime_type.starts_with("video/") || mime_type.ends_with("x-matroska") {
Ok(DecryptedData::Video(blob))
} else {
Ok(DecryptedData::Blob(blob))
}
2021-10-23 10:10:55 -07:00
}
}
}
2021-10-24 02:25:42 -07:00
#[derive(Debug)]
pub enum PasteCompleteConstructionError {
StageOneFailure,
StageTwoFailure,
}
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.")
}
}
}
}