diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index 7d78fc6f..326a748e 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -2,7 +2,15 @@ import "./index.css"; import "@szhsin/react-menu/dist/index.css"; import "./fonts/inter.css"; -import { compress, expand_filter, flat_merge, get_diff, pow, default as wasmInit } from "@snort/system-wasm"; +import { + compress, + expand_filter, + flat_merge, + get_diff, + pow, + schnorr_verify, + default as wasmInit, +} from "@snort/system-wasm"; import WasmPath from "@snort/system-wasm/pkg/system_wasm_bg.wasm"; import { StrictMode } from "react"; @@ -76,6 +84,9 @@ const WasmQueryOptimizer = { compress: (all: Array) => { return compress(all) as Array; }, + schnorrVerify: (id, sig, pubkey) => { + return schnorr_verify(id, sig, pubkey); + }, } as QueryOptimizer; export class WasmPowWorker implements PowMiner { diff --git a/packages/system-wasm/Cargo.lock b/packages/system-wasm/Cargo.lock index cc9ae096..227365c2 100644 --- a/packages/system-wasm/Cargo.lock +++ b/packages/system-wasm/Cargo.lock @@ -644,6 +644,24 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "secp256k1" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" +dependencies = [ + "cc", +] + [[package]] name = "serde" version = "1.0.188" @@ -736,6 +754,7 @@ dependencies = [ "hex", "itertools 0.11.0", "rand", + "secp256k1", "serde", "serde-wasm-bindgen", "serde_json", diff --git a/packages/system-wasm/Cargo.toml b/packages/system-wasm/Cargo.toml index 14e657d1..e5142eb9 100644 --- a/packages/system-wasm/Cargo.toml +++ b/packages/system-wasm/Cargo.toml @@ -12,6 +12,7 @@ argon2 = "0.5.2" console_error_panic_hook = "0.1.7" hex = { version = "0.4.3", features = [], default-features = false } itertools = "0.11.0" +secp256k1 = "0.28.0" serde = { version = "1.0.188", features = ["derive"], default-features = false } serde-wasm-bindgen = "0.5.0" sha256 = { version = "1.4.0", features = [], default-features = false } diff --git a/packages/system-wasm/pkg/system_wasm.d.ts b/packages/system-wasm/pkg/system_wasm.d.ts index 1ebca4b4..5ef3cc31 100644 --- a/packages/system-wasm/pkg/system_wasm.d.ts +++ b/packages/system-wasm/pkg/system_wasm.d.ts @@ -39,6 +39,13 @@ export function pow(val: any, target: any): any; * @returns {any} */ export function argon2(password: any, salt: any): any; +/** + * @param {any} hash + * @param {any} sig + * @param {any} pub_key + * @returns {boolean} + */ +export function schnorr_verify(hash: any, sig: any, pub_key: any): boolean; export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; @@ -51,6 +58,11 @@ export interface InitOutput { readonly compress: (a: number, b: number) => void; readonly pow: (a: number, b: number, c: number) => void; readonly argon2: (a: number, b: number, c: number) => void; + readonly schnorr_verify: (a: number, b: number, c: number, d: number) => void; + readonly rustsecp256k1_v0_9_1_context_create: (a: number) => number; + readonly rustsecp256k1_v0_9_1_context_destroy: (a: number) => void; + readonly rustsecp256k1_v0_9_1_default_illegal_callback_fn: (a: number, b: number) => void; + readonly rustsecp256k1_v0_9_1_default_error_callback_fn: (a: number, b: number) => void; readonly __wbindgen_malloc: (a: number, b: number) => number; readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; readonly __wbindgen_add_to_stack_pointer: (a: number) => number; diff --git a/packages/system-wasm/pkg/system_wasm.js b/packages/system-wasm/pkg/system_wasm.js index 7f7ae5af..a09f08b4 100644 --- a/packages/system-wasm/pkg/system_wasm.js +++ b/packages/system-wasm/pkg/system_wasm.js @@ -361,6 +361,28 @@ export function argon2(password, salt) { } } +/** + * @param {any} hash + * @param {any} sig + * @param {any} pub_key + * @returns {boolean} + */ +export function schnorr_verify(hash, sig, pub_key) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.schnorr_verify(retptr, addHeapObject(hash), addHeapObject(sig), addHeapObject(pub_key)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return r0 !== 0; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + function handleError(f, args) { try { return f.apply(this, args); diff --git a/packages/system-wasm/pkg/system_wasm_bg.wasm b/packages/system-wasm/pkg/system_wasm_bg.wasm index ce33b120..1e6f801b 100644 Binary files a/packages/system-wasm/pkg/system_wasm_bg.wasm and b/packages/system-wasm/pkg/system_wasm_bg.wasm differ diff --git a/packages/system-wasm/pkg/system_wasm_bg.wasm.d.ts b/packages/system-wasm/pkg/system_wasm_bg.wasm.d.ts index b83a0ee8..344d1aec 100644 --- a/packages/system-wasm/pkg/system_wasm_bg.wasm.d.ts +++ b/packages/system-wasm/pkg/system_wasm_bg.wasm.d.ts @@ -8,6 +8,11 @@ export function flat_merge(a: number, b: number): void; export function compress(a: number, b: number): void; export function pow(a: number, b: number, c: number): void; export function argon2(a: number, b: number, c: number): void; +export function schnorr_verify(a: number, b: number, c: number, d: number): void; +export function rustsecp256k1_v0_9_1_context_create(a: number): number; +export function rustsecp256k1_v0_9_1_context_destroy(a: number): void; +export function rustsecp256k1_v0_9_1_default_illegal_callback_fn(a: number, b: number): void; +export function rustsecp256k1_v0_9_1_default_error_callback_fn(a: number, b: number): void; export function __wbindgen_malloc(a: number, b: number): number; export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number; export function __wbindgen_add_to_stack_pointer(a: number): number; diff --git a/packages/system-wasm/src/lib.rs b/packages/system-wasm/src/lib.rs index 1343ddbb..7dc2baaf 100644 --- a/packages/system-wasm/src/lib.rs +++ b/packages/system-wasm/src/lib.rs @@ -1,6 +1,7 @@ extern crate console_error_panic_hook; use argon2::{Argon2}; +use secp256k1::{Message, Secp256k1, XOnlyPublicKey}; use serde::{Deserialize, Serialize}; use crate::filter::{FlatReqFilter, ReqFilter}; use wasm_bindgen::prelude::*; @@ -98,6 +99,20 @@ pub fn argon2(password: JsValue, salt: JsValue) -> Result { Ok(serde_wasm_bindgen::to_value(&hex::encode(key))?) } +#[wasm_bindgen] +pub fn schnorr_verify(hash: JsValue, sig: JsValue, pub_key: JsValue) -> Result { + console_error_panic_hook::set_once(); + let msg_hex: String = serde_wasm_bindgen::from_value(hash)?; + let sig_hex: String = serde_wasm_bindgen::from_value(sig)?; + let pub_key_hex: String = serde_wasm_bindgen::from_value(pub_key)?; + + let secp = Secp256k1::new(); + let msg = Message::from_digest_slice(&hex::decode(msg_hex).unwrap()).unwrap(); + let key = XOnlyPublicKey::from_slice(&hex::decode(pub_key_hex).unwrap()).unwrap(); + let sig = secp256k1::schnorr::Signature::from_slice(&hex::decode(sig_hex).unwrap()).unwrap(); + Ok(secp.verify_schnorr(&sig, &msg, &key).is_ok()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/packages/system/src/nostr-system.ts b/packages/system/src/nostr-system.ts index 6d994825..fabd1368 100644 --- a/packages/system/src/nostr-system.ts +++ b/packages/system/src/nostr-system.ts @@ -199,9 +199,12 @@ export class NostrSystem extends EventEmitter implements Syst this.#log("Rejecting invalid event %O", ev); return; } - if (this.checkSigs && !EventExt.verify(ev)) { - this.#log("Invalid sig %O", ev); - return; + if (this.checkSigs) { + const id = EventExt.createId(ev); + if (!this.#queryOptimizer.schnorrVerify(id, ev.sig, ev.pubkey)) { + this.#log("Invalid sig %O", ev); + return; + } } for (const [, v] of this.Queries) { diff --git a/packages/system/src/query-optimizer/index.ts b/packages/system/src/query-optimizer/index.ts index 21d8b44a..12220665 100644 --- a/packages/system/src/query-optimizer/index.ts +++ b/packages/system/src/query-optimizer/index.ts @@ -1,3 +1,4 @@ +import { schnorr } from "@noble/curves/secp256k1"; import { ReqFilter } from "../nostr"; import { expandFilter } from "./request-expander"; import { flatMerge, mergeSimilar } from "./request-merger"; @@ -24,6 +25,7 @@ export interface QueryOptimizer { getDiff(prev: Array, next: Array): Array; flatMerge(all: Array): Array; compress(all: Array): Array; + schnorrVerify(hash: string, sig: string, pubkey: string): boolean; } export const DefaultQueryOptimizer = { @@ -43,4 +45,7 @@ export const DefaultQueryOptimizer = { compress: (all: Array) => { return mergeSimilar(all); }, + schnorrVerify: (hash, sig, pubkey) => { + return schnorr.verify(sig, hash, pubkey); + }, } as QueryOptimizer;