From 9a0e65c78755488657ba55b83f69a587298df690 Mon Sep 17 00:00:00 2001 From: Claas Date: Sun, 7 Dec 2025 01:47:43 +0100 Subject: [PATCH] Port decoding invite package --- Cargo.lock | 87 +++++++++++++++-------- app/src/routes/{join.jsx => join.tsx} | 91 ++++++++++++------------- app/src/service-worker/serviceWorker.ts | 14 +++- core/Cargo.toml | 1 + core/src/lib.rs | 12 ++-- core/src/v2/provider.rs | 11 +-- core/src/v2/serializable.rs | 40 ++++++++++- 7 files changed, 163 insertions(+), 93 deletions(-) rename app/src/routes/{join.jsx => join.tsx} (56%) diff --git a/Cargo.lock b/Cargo.lock index 2c5a5c0..a3edfe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1605,6 +1605,19 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "group" version = "0.13.0" @@ -2316,9 +2329,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -2553,6 +2566,7 @@ dependencies = [ "thiserror 2.0.9", "time", "tls_codec", + "tsify", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -4431,6 +4445,32 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tsify" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec5505497c87f1c050b4392d3f11b49a04537fcb9dc0da57bc0af168a6331f2" +dependencies = [ + "gloo-utils", + "serde", + "serde-wasm-bindgen", + "serde_json", + "tsify-macros", + "wasm-bindgen", +] + +[[package]] +name = "tsify-macros" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc2c44dc9fe4baf55b88e032621b7a11b215a1f0a7de8d0aa04367207d915bc" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.92", +] + [[package]] name = "tugboat" version = "0.1.0" @@ -4652,34 +4692,22 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.92", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -4690,9 +4718,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4700,28 +4728,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn 2.0.92", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/app/src/routes/join.jsx b/app/src/routes/join.tsx similarity index 56% rename from app/src/routes/join.jsx rename to app/src/routes/join.tsx index 01d611b..379c5ad 100644 --- a/app/src/routes/join.jsx +++ b/app/src/routes/join.tsx @@ -1,17 +1,15 @@ import { Navigate, useNavigate, useParams } from "@solidjs/router"; -import { createResource, Match, Switch } from "solid-js"; -import { useAppContext, messagesUrl } from "../components/AppContext"; +import { createResource, JSX, Match, Switch } from "solid-js"; +import { useAppContext } from "../components/AppContext"; import { sendMessage } from "../sendMessage"; +import { setupCrackle } from "../useCrackle"; -//@ts-expect-error TS6192 Can not handle new JSDoc syntax (yet?) -// https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#the-jsdoc-@import-tag -/**@import { JSX, Accessor } from "solid-js" */ -//@ts-expect-error TS6192 See above -/** @import { Group } from "../components/AppContext" */ +async function decodeKeyPackage(encodedInvite: string) { + const handle = await setupCrackle; + const result = await handle.decodeKeyPackage(encodedInvite); + return result; +} -/** - * @returns {JSX.Element} - */ export default function Join() { const parameters = useParams(); @@ -21,15 +19,16 @@ export default function Join() { const [app, setApp] = useAppContext(); - const [keyPackage] = createResource(parameters.package, app.client.decode_key_package); + const [keyPackage] = createResource(parameters.package, decodeKeyPackage); const navigate = useNavigate(); - /** @param {Parameters>[0]} event */ - async function handleDecision(event) { + async function handleDecision( + event: Parameters>[0] + ) { event.preventDefault(); - const name = /** @type {HTMLInputElement} */ (event.currentTarget.username) - .value; + const name = + /** @type {HTMLInputElement} */ event.currentTarget.username.value; sendMessage({ type: "inviteFromPackage", @@ -51,36 +50,36 @@ export default function Join() { return; } - const groupId = app.client.create_group(); - /** @type {Group} */ - const group = { - id: groupId, - friend: keys.friend, - messages: [], - }; + // const groupId = app.client.create_group(); + // /** @type {Group} */ + // const group = { + // id: groupId, + // friend: keys.friend, + // messages: [], + // }; // Add group to store https://docs.solidjs.com/concepts/stores#appending-new-values - setApp("groups", app.groups.length, group); - - // Need to extract id before key package is consumed or it will error - const url = new URL(group.friend.id, messagesUrl); - - const welcomePackage = app.client.invite(groupId, keys); - // Send welcome to peer - const request = new Request(url, { - method: "post", - headers: { - //https://www.rfc-editor.org/rfc/rfc9420.html#name-the-message-mls-media-type - "Content-Type": "message/mls", - }, - body: welcomePackage, - }); - - //TODO error handling - await fetch(request); - - // Navigate to the chat - navigate(`/chat/${groupId}`); + // setApp("groups", app.groups.length, group); + + // // Need to extract id before key package is consumed or it will error + // const url = new URL(group.friend.id, messagesUrl); + + // const welcomePackage = app.client.invite(groupId, keys); + // // Send welcome to peer + // const request = new Request(url, { + // method: "post", + // headers: { + // //https://www.rfc-editor.org/rfc/rfc9420.html#name-the-message-mls-media-type + // "Content-Type": "message/mls", + // }, + // body: welcomePackage, + // }); + + // //TODO error handling + // await fetch(request); + + // // Navigate to the chat + // navigate(`/chat/${groupId}`); } return ( @@ -96,10 +95,8 @@ export default function Join() { { /** @type {(item: Accessor>) => JSX.Element} */ - ( - (friendName) => ( -

{friendName()} has invited you to chat with them

- ) + (friendName) => ( +

{friendName()} has invited you to chat with them

) }
diff --git a/app/src/service-worker/serviceWorker.ts b/app/src/service-worker/serviceWorker.ts index 5dfb1e0..282f42e 100644 --- a/app/src/service-worker/serviceWorker.ts +++ b/app/src/service-worker/serviceWorker.ts @@ -1,6 +1,10 @@ import { precacheAndRoute } from "workbox-precaching"; import { openDB } from "idb"; -import init, { create_client, create_invite } from "meal-core"; +import init, { + create_client, + create_invite, + decode_key_package, +} from "meal-core"; import { expose } from "../crackle"; import { Schema } from "./schema"; @@ -156,6 +160,14 @@ const handler = { const inviteUrl = new URL(`/join/${invite_payload}`, location.origin); return inviteUrl.href; }, + + async decodeKeyPackage(encodedInvite: string) { + const client = await getClient; + + const result = decode_key_package(client, encodedInvite); + + return result; + }, }; export type Handler = typeof handler; diff --git a/core/Cargo.toml b/core/Cargo.toml index 83de937..2f46372 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -20,6 +20,7 @@ serde-wasm-bindgen = "0.6.5" thiserror = { workspace = true } time = { version = "0.3.41", features = ["formatting", "parsing", "serde"] } tls_codec = { workspace = true } +tsify = { version = "0.5.6", features = ["js"] } wasm-bindgen = "0.2.92" wasm-bindgen-futures = "0.4.42" web-sys = { version = "0.3.70", features = ["Storage", "Window"] } diff --git a/core/src/lib.rs b/core/src/lib.rs index 1b08ea7..23c5f75 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -13,6 +13,7 @@ use provider::Provider; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use tls_codec::Serialize as _; +use tsify::Tsify; use wasm_bindgen::prelude::*; pub(crate) const CIPHERSUITE: Ciphersuite = @@ -37,15 +38,16 @@ pub struct Client { provider: Provider, } -#[wasm_bindgen(getter_with_clone)] +#[derive(Tsify, serde::Serialize)] +#[tsify(into_wasm_abi)] pub struct DecodedPackage { /// The friend that sent the key package pub friend: Friend, key_package: KeyPackage, } -#[derive(serde::Serialize, Clone)] -#[wasm_bindgen(getter_with_clone)] +#[derive(Tsify, serde::Serialize)] +#[tsify(into_wasm_abi)] pub struct Friend { pub id: String, pub name: Option, @@ -109,9 +111,9 @@ enum ApplicationMessage { name: Option, }, } -#[wasm_bindgen] +// #[wasm_bindgen] impl Client { - #[wasm_bindgen(constructor)] + // #[wasm_bindgen(constructor)] pub fn new(id: Option, name: Option) -> Self { console_error_panic_hook::set_once(); diff --git a/core/src/v2/provider.rs b/core/src/v2/provider.rs index 132f158..227cf5a 100644 --- a/core/src/v2/provider.rs +++ b/core/src/v2/provider.rs @@ -44,22 +44,13 @@ impl Serialize for Storage { } } -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Default)] pub(super) struct Provider { #[serde(default, skip)] crypto: RustCrypto, storage: Storage, } -impl Provider { - pub(super) fn new() -> Self { - Self { - crypto: RustCrypto::default(), - storage: Storage::default(), - } - } -} - impl OpenMlsProvider for Provider { type CryptoProvider = RustCrypto; diff --git a/core/src/v2/serializable.rs b/core/src/v2/serializable.rs index 56b15c5..ee6ddd8 100644 --- a/core/src/v2/serializable.rs +++ b/core/src/v2/serializable.rs @@ -14,7 +14,9 @@ use openmls_basic_credential::SignatureKeyPair; use serde::{Deserialize, Serialize}; use wasm_bindgen::{JsError, prelude::wasm_bindgen}; -use crate::{CIPHERSUITE, ID_LENGTH, encode_application_id, v2::provider::Provider}; +use crate::{ + CIPHERSUITE, DecodedPackage, Friend, ID_LENGTH, encode_application_id, v2::provider::Provider, +}; #[derive(Serialize, Deserialize)] struct User { @@ -40,7 +42,7 @@ struct Client { pub fn create_client(id: Option, name: Option) -> Result, JsError> { console_error_panic_hook::set_once(); - let provider = Provider::new(); + let provider = Provider::default(); let client_id = id.unwrap_or_else(|| nanoid!(ID_LENGTH)); //TODO Basic credentials only for tests and demo @@ -109,3 +111,37 @@ pub fn create_invite(client: &[u8], user_name: Option) -> Result Result { + let data = BASE64_URL_SAFE_NO_PAD.decode(encoded_invite)?; + // let package = KeyPackageIn::tls_deserialize_exact_bytes(&data).unwrap(); + let package: KeyPackageIn = postcard::from_bytes(&data)?; + + let client: Client = postcard::from_bytes(client)?; + + let validated = package.validate(client.provider.crypto(), ProtocolVersion::Mls10)?; + let id = validated + .extensions() + .application_id() + .map(|id| str::from_utf8(id.as_slice())) + .transpose()? + .ok_or_else(|| { + JsError::new("Invite did not contain an id to contact the other client with") + })?; + + let (id, friend_name) = if id.len() > ID_LENGTH { + let (id, friend_name) = id.split_at(ID_LENGTH); + (id, Some(friend_name)) + } else { + (id, None) + }; + + Ok(DecodedPackage { + friend: Friend { + id: id.to_owned(), + name: friend_name.map(str::to_owned), + }, + key_package: validated, + }) +} -- 2.51.2