2021-10-24 02:25:42 -07:00
|
|
|
use std::fmt::{Display, Formatter};
|
2021-10-26 18:19:14 -07:00
|
|
|
use std::io::Cursor;
|
2021-10-24 02:25:42 -07:00
|
|
|
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-26 18:19:14 -07:00
|
|
|
use serde::Serialize;
|
2021-10-27 01:49:06 -07:00
|
|
|
use wasm_bindgen::JsCast;
|
|
|
|
use web_sys::{Blob, BlobPropertyBag};
|
2021-10-26 18:19:14 -07:00
|
|
|
|
|
|
|
#[derive(Clone, Serialize)]
|
|
|
|
pub struct ArchiveMeta {
|
|
|
|
name: String,
|
|
|
|
file_size: usize,
|
|
|
|
}
|
2021-10-23 10:10:55 -07:00
|
|
|
|
2021-10-24 11:40:19 -07:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub enum DecryptedData {
|
|
|
|
String(Arc<String>),
|
|
|
|
Blob(Arc<Blob>),
|
2021-10-25 17:31:30 -07:00
|
|
|
Image(Arc<Blob>, usize),
|
2021-10-24 11:40:19 -07:00
|
|
|
Audio(Arc<Blob>),
|
|
|
|
Video(Arc<Blob>),
|
2021-10-26 18:19:14 -07:00
|
|
|
Archive(Arc<Blob>, Vec<ArchiveMeta>),
|
2021-10-24 11:40:19 -07:00
|
|
|
}
|
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> {
|
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());
|
2021-10-27 02:23:28 -07:00
|
|
|
open_in_place(&mut container, &nonce.increment(), &password).map_err(|_| {
|
2021-10-25 02:42:20 -07:00
|
|
|
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());
|
2021-10-27 02:23:28 -07:00
|
|
|
open_in_place(&mut container, &nonce, &key).map_err(|_| {
|
2021-10-25 02:42:20 -07:00
|
|
|
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-27 02:23:28 -07:00
|
|
|
let mime_type = tree_magic_mini::from_u8(&container);
|
|
|
|
log!("Mimetype: ", mime_type);
|
2021-10-27 01:49:06 -07:00
|
|
|
|
2021-10-27 02:23:28 -07:00
|
|
|
log!("Blob conversion started.");
|
|
|
|
let start = now();
|
|
|
|
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());
|
|
|
|
array.copy_from(chunk);
|
|
|
|
blob_chunks.set(i.try_into().unwrap(), array.dyn_into().unwrap());
|
|
|
|
}
|
|
|
|
let mut blob_props = BlobPropertyBag::new();
|
|
|
|
blob_props.type_(mime_type);
|
|
|
|
let blob = Arc::new(
|
|
|
|
Blob::new_with_u8_array_sequence_and_options(blob_chunks.dyn_ref().unwrap(), &blob_props)
|
2021-10-27 01:49:06 -07:00
|
|
|
.unwrap(),
|
2021-10-27 02:23:28 -07:00
|
|
|
);
|
|
|
|
log!(format!("Blob conversion completed in {}ms", now() - start));
|
2021-10-23 10:10:55 -07:00
|
|
|
|
2021-10-27 02:23:28 -07:00
|
|
|
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" {
|
|
|
|
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" {
|
|
|
|
Ok(DecryptedData::Video(blob))
|
|
|
|
} else if mime_type == "application/zip" {
|
|
|
|
let mut entries = vec![];
|
|
|
|
let cursor = Cursor::new(container);
|
|
|
|
if let Ok(mut zip) = zip::ZipArchive::new(cursor) {
|
|
|
|
for i in 0..zip.len() {
|
|
|
|
match zip.by_index(i) {
|
|
|
|
Ok(file) => entries.push(ArchiveMeta {
|
|
|
|
name: file.name().to_string(),
|
|
|
|
file_size: file.size() as usize,
|
|
|
|
}),
|
|
|
|
Err(err) => match err {
|
|
|
|
zip::result::ZipError::UnsupportedArchive(s) => {
|
|
|
|
log!("Unsupported: ", s.to_string());
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
log!(format!("Error: {}", err));
|
|
|
|
}
|
|
|
|
},
|
2021-10-26 18:19:14 -07:00
|
|
|
}
|
|
|
|
}
|
2021-10-23 10:10:55 -07:00
|
|
|
}
|
2021-10-27 17:45:36 -07:00
|
|
|
|
|
|
|
entries.sort_by(|a, b| a.name.cmp(&b.name));
|
2021-10-27 02:23:28 -07:00
|
|
|
Ok(DecryptedData::Archive(blob, entries))
|
|
|
|
} else if mime_type == "application/gzip" {
|
2021-10-27 17:45:36 -07:00
|
|
|
Ok(DecryptedData::Archive(blob, vec![]))
|
2021-10-27 02:23:28 -07:00
|
|
|
} 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,
|
2021-10-27 02:23:28 -07:00
|
|
|
InvalidEncoding,
|
2021-10-24 02:25:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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.")
|
|
|
|
}
|
2021-10-27 02:23:28 -07:00
|
|
|
PasteCompleteConstructionError::InvalidEncoding => write!(
|
|
|
|
f,
|
|
|
|
"Got an file with a text/* mime type, but was unable to parsed as valid UTF-8?"
|
|
|
|
),
|
2021-10-24 02:25:42 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|