Skip to content

Commit

Permalink
download and decrypt files
Browse files Browse the repository at this point in the history
I hate how much time supabase wasted, making me figure out that array is stringified before uploading: https://github.com/supabase/supabase-js/issues/780
  • Loading branch information
ranile committed May 27, 2023
1 parent 86a9654 commit c054d70
Show file tree
Hide file tree
Showing 13 changed files with 381 additions and 168 deletions.
11 changes: 11 additions & 0 deletions crates/moe/build.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#! /usr/bin/fish

set -u CARGO_TARGET_DIR

cargo build --bin worker --target wasm32-unknown-unknown
wasm-bindgen --target no-modules --out-dir ./dist/worker target/wasm32-unknown-unknown/debug/worker.wasm

cargo build --bin decrypt_attachment_worker --target wasm32-unknown-unknown
wasm-bindgen --target no-modules --out-dir ./dist/decrypt_attachment_worker target/wasm32-unknown-unknown/debug/decrypt_attachment_worker.wasm

wasm-pack build --target bundler --dev;
9 changes: 9 additions & 0 deletions crates/moe/src/bin/decrypt_attachment_worker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use moe::worker::DecryptAttachmentsWorker;

use gloo::worker::Registrable;

fn main() {
console_error_panic_hook::set_once();

DecryptAttachmentsWorker::registrar().register();
}
1 change: 0 additions & 1 deletion crates/moe/src/json_web_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::serde::{Base64, UrlSafe};
/// To create an instance of this type, first create a `JsonWebKeyInit` and convert it via
/// `JsonWebKey::from` / `.into()`.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct JsonWebKey {
/// Key type.
///
Expand Down
30 changes: 27 additions & 3 deletions crates/moe/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod attachments;
mod json_web_key;
mod serde;
pub mod attachments;
pub mod json_web_key;
pub mod serde;
pub mod worker;

use vodozemac::megolm::{GroupSession, GroupSessionPickle, InboundGroupSession, MegolmMessage, SessionConfig, SessionKey};
Expand Down Expand Up @@ -78,10 +78,13 @@ pub fn encrypt_file(file: web_sys::File) {

#[cfg(test)]
mod tests {
use std::io::Read;
use rand::RngCore;
use crate::{InboundSession, OutboundSession};

wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
use wasm_bindgen_test::wasm_bindgen_test as test;
use crate::attachments::{AttachmentDecryptor, AttachmentEncryptor};

#[test]
fn message_encryption_roundtrip() {
Expand All @@ -98,6 +101,27 @@ mod tests {
let mut inbound = InboundSession::new(&session_key).unwrap();
let plaintext = inbound.decrypt(&ciphertext).unwrap();
assert_eq!(plaintext, msg);
}

#[test]
fn attachments_roundtrip() {
let input = {
let mut input = [0; 1024];
rand::thread_rng().fill_bytes(&mut input);
input
};
let mut bytes = input.to_vec();
let mut bytes = &bytes[..];
let mut encryptor = AttachmentEncryptor::new(&mut bytes);
let mut encrypted = Vec::new();
encryptor.read_to_end(&mut encrypted).unwrap();
let info = encryptor.finish();

let mut encrypted = &encrypted[..];
let mut decryptor = AttachmentDecryptor::new(&mut encrypted, info).unwrap();
let mut decrypted_data = Vec::new();
decryptor.read_to_end(&mut decrypted_data).unwrap();

assert_eq!(input, *decrypted_data);
}
}
110 changes: 110 additions & 0 deletions crates/moe/src/worker/decrypt_attachment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use std::io::Read;
use gloo::console::{console_dbg, log};
use gloo::worker::{HandlerId, Worker, WorkerBridge, WorkerScope};
use serde::{Deserialize, Serialize};
use wasm_bindgen::{JsValue, UnwrapThrowExt};
use wasm_bindgen::prelude::wasm_bindgen;
use gloo::worker::Spawnable;
use gloo::utils::format::JsValueSerdeExt;
use crate::attachments::{AttachmentDecryptor, DecryptorError, MediaEncryptionInfo};

#[derive(Debug, Serialize, Deserialize)]
pub struct WorkerInput {
bytes: Vec<u8>,
info: MediaEncryptionInfo,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct WorkerOutput {
bytes: Vec<u8>,
}

pub struct DecryptAttachmentsWorker {}

pub enum WorkerMessage {}

impl Worker for DecryptAttachmentsWorker {
type Message = WorkerMessage;

type Input = WorkerInput;

type Output = WorkerOutput;

fn create(_scope: &WorkerScope<Self>) -> Self {
Self {}
}

fn update(&mut self, _scope: &WorkerScope<Self>, msg: Self::Message) {
match msg {}
}

fn received(&mut self, scope: &WorkerScope<Self>, input: Self::Input, who: HandlerId) {
let WorkerInput { bytes, info } = input;
let mut bytes = &bytes[..];
log!("LOG FROM WORKER -- dec", format!("{:#?}", info), format!("{:?}", bytes));
let mut decryptor = match AttachmentDecryptor::new(&mut bytes, info) {
Ok(d) => d,
Err(e) => {
log!("Error creating decryptor", e.to_string());
return;
}
};
let mut decrypted_data = Vec::new();
match decryptor.read_to_end(&mut decrypted_data) {
Ok(_) => {}
Err(e) => {
log!("Error decrypting attachment", e.to_string());
return;
}
}
scope.respond(who, WorkerOutput {
bytes: decrypted_data,
});
}
}

#[wasm_bindgen]
pub struct JsDecryptAttachmentsWorker {
worker: WorkerBridge<DecryptAttachmentsWorker>,
}

#[wasm_bindgen]
impl JsDecryptAttachmentsWorker {
#[wasm_bindgen(js_name = "decryptAttachment")]
pub async fn decrypt_attachment(
&self,
blob: web_sys::Blob,
info: JsValue,
) {
log!("Decrypting attachment");
let file = gloo::file::Blob::from(blob);
let bytes = gloo::file::futures::read_as_bytes(&file).await.unwrap_throw();
let info: MediaEncryptionInfo = info.into_serde().unwrap_throw();
let input = WorkerInput {
bytes,
info,
};
self.worker.send(input);
}
}

#[wasm_bindgen(js_name = "newDecryptAttachmentsWorker")]
pub fn new(cb: js_sys::Function) -> JsDecryptAttachmentsWorker {

console_error_panic_hook::set_once();

let bridge = DecryptAttachmentsWorker::spawner()
.callback(move |m| {
let bytes = m.bytes;
let m = js_sys::Uint8Array::from(bytes.as_slice());
let ret = cb.call1(&JsValue::NULL, &m);
if let Err(e) = ret {
log!("Error calling callback", e);
}
})
.spawn("/crates/moe/dist/decrypt_attachment_worker/decrypt_attachment_worker.js");

JsDecryptAttachmentsWorker {
worker: bridge,
}
}
162 changes: 162 additions & 0 deletions crates/moe/src/worker/encrypt_messages.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use std::io::Read;
use gloo::console::log;
use gloo::worker::{HandlerId, Worker, WorkerBridge, WorkerScope};
use gloo::utils::format::JsValueSerdeExt;
use js_sys::{JsString, Uint8Array};
use serde::{Deserialize, Serialize};
use wasm_bindgen::{JsValue, UnwrapThrowExt};
use wasm_bindgen::prelude::wasm_bindgen;
use crate::worker::EncryptedFile;
use gloo::worker::Spawnable;


#[derive(Debug, Serialize, Deserialize)]
pub struct FileToEncrypt {
bytes: Vec<u8>,
name: String,
type_: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct WorkerInput {
ciphertext: String,
files: Vec<FileToEncrypt>,
room_id: String,
uid: String,
}

#[wasm_bindgen]
#[derive(Debug, Serialize, Deserialize)]
pub struct WorkerOutput {
uid: String,
files: Vec<EncryptedFile>,
room_id: String,
ciphertext: String,
}

#[wasm_bindgen]
impl WorkerOutput {
#[wasm_bindgen(getter)]
pub fn uid(&self) -> JsString {
JsString::from(self.uid.as_str())
}

#[wasm_bindgen(getter)]
pub fn files(&self) -> js_sys::Array {
js_sys::Array::from_iter(self.files.iter().map(|it| {
JsValue::from_serde(it).unwrap_throw()
}))
}

#[wasm_bindgen(getter)]
pub fn room_id(&self) -> JsString {
JsString::from(self.room_id.as_str())
}
#[wasm_bindgen(getter)]
pub fn ciphertext(&self) -> JsString {
JsString::from(self.ciphertext.as_str())
}
}

pub struct EncryptedMessagesWorker {}

pub enum WorkerMessage {}

impl Worker for EncryptedMessagesWorker {
type Message = WorkerMessage;

type Input = WorkerInput;

type Output = WorkerOutput;

fn create(_scope: &WorkerScope<Self>) -> Self {
Self {}
}

fn update(&mut self, _scope: &WorkerScope<Self>, msg: Self::Message) {
match msg {}
}

fn received(&mut self, scope: &WorkerScope<Self>, input: Self::Input, who: HandlerId) {
let WorkerInput {
ciphertext,
files,
room_id,
uid,
} = input;

let files = files.into_iter().map(|FileToEncrypt { bytes, name, type_ }| {
let mut bytes = &bytes[..];
let mut encryptor = crate::attachments::AttachmentEncryptor::new(&mut bytes);
let mut encrypted = Vec::new();
encryptor.read_to_end(&mut encrypted).unwrap();
let key = encryptor.finish();
log!("LOG FROM WORKER -- enc", format!("{:#?}", key), format!("{:?}", encrypted));
EncryptedFile { bytes: encrypted, key, name, type_ }
});
scope.respond(who, WorkerOutput {
ciphertext,
files: files.collect(),
room_id,
uid,
})
}
}

#[wasm_bindgen]
pub struct JsWorker {
worker: WorkerBridge<EncryptedMessagesWorker>,
}

#[wasm_bindgen]
impl JsWorker {
#[wasm_bindgen(js_name = "newMessage")]
pub async fn new_message(
&self,
outbound_session: &mut crate::OutboundSession,
room_id: String,
uid: String,
content: &str,
files: Vec<web_sys::File>,
) {
let mut new_files = Vec::with_capacity(files.len());
for file in files {
let name = file.name();
let type_ = file.type_();
let bytes = {
let file = gloo::file::File::from(file);
gloo::file::futures::read_as_bytes(&file).await.unwrap_throw()
};
new_files.push(FileToEncrypt { bytes, name, type_ });
}

let ciphertext = outbound_session.encrypt(&content);

let input = WorkerInput {
ciphertext,
files: new_files,
room_id,
uid,
};
self.worker.send(input);
}
}

#[wasm_bindgen(js_name = "initAttachmentsWorker")]
pub fn worker_init(cb: js_sys::Function) -> JsWorker {
console_error_panic_hook::set_once();

let bridge = EncryptedMessagesWorker::spawner()
.callback(move |m| {
let m = JsValue::from(m);
let ret = cb.call1(&JsValue::NULL, &m);
if let Err(e) = ret {
log!("Error calling callback", e);
}
})
.spawn("/crates/moe/dist/worker/worker.js");

JsWorker {
worker: bridge,
}
}
Loading

0 comments on commit c054d70

Please sign in to comment.