use wasm_bindgen::prelude::*;
use crate::event::{compute_event_id as compute_id_inner, UnsignedEvent};
use crate::keys;
use crate::nip44;
use crate::nip98;
#[wasm_bindgen]
pub fn nip44_encrypt(
sender_sk: &[u8],
recipient_pk: &[u8],
plaintext: &str,
) -> Result<String, JsValue> {
let sk: [u8; 32] = sender_sk
.try_into()
.map_err(|_| JsValue::from_str("sender_sk must be 32 bytes"))?;
let pk: [u8; 32] = recipient_pk
.try_into()
.map_err(|_| JsValue::from_str("recipient_pk must be 32 bytes"))?;
nip44::encrypt(&sk, &pk, plaintext).map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn nip44_decrypt(
recipient_sk: &[u8],
sender_pk: &[u8],
ciphertext: &str,
) -> Result<String, JsValue> {
let sk: [u8; 32] = recipient_sk
.try_into()
.map_err(|_| JsValue::from_str("recipient_sk must be 32 bytes"))?;
let pk: [u8; 32] = sender_pk
.try_into()
.map_err(|_| JsValue::from_str("sender_pk must be 32 bytes"))?;
nip44::decrypt(&sk, &pk, ciphertext).map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn derive_keypair_from_prf(prf_output: &[u8]) -> Result<JsValue, JsValue> {
let prf: [u8; 32] = prf_output
.try_into()
.map_err(|_| JsValue::from_str("prf_output must be 32 bytes"))?;
let kp = keys::derive_from_prf(&prf).map_err(|e| JsValue::from_str(&e.to_string()))?;
let obj = js_sys::Object::new();
let sk_array = js_sys::Uint8Array::from(kp.secret.as_bytes().as_slice());
js_sys::Reflect::set(&obj, &"secretKey".into(), &sk_array)?;
js_sys::Reflect::set(&obj, &"publicKey".into(), &kp.public.to_hex().into())?;
Ok(obj.into())
}
#[wasm_bindgen]
pub fn create_nip98_token(
secret_key: &[u8],
url: &str,
method: &str,
body: Option<Vec<u8>>,
created_at: Option<u32>,
) -> Result<String, JsValue> {
let sk: [u8; 32] = secret_key
.try_into()
.map_err(|_| JsValue::from_str("secret_key must be 32 bytes"))?;
let ts = created_at
.map(|t| t as u64)
.unwrap_or_else(|| (js_sys::Date::now() / 1000.0) as u64);
nip98::create_token_at(&sk, url, method, body.as_deref(), ts)
.map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn verify_nip98_token(
auth_header: &str,
url: &str,
method: &str,
body: Option<Vec<u8>>,
) -> Result<JsValue, JsValue> {
let token = nip98::verify_token(auth_header, url, method, body.as_deref())
.map_err(|e| JsValue::from_str(&e.to_string()))?;
nip98_token_to_js(&token)
}
#[wasm_bindgen]
pub fn verify_nip98_token_at(
auth_header: &str,
url: &str,
method: &str,
body: Option<Vec<u8>>,
now: u32,
) -> Result<JsValue, JsValue> {
let token = nip98::verify_token_at(auth_header, url, method, body.as_deref(), now as u64)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
nip98_token_to_js(&token)
}
fn nip98_token_to_js(token: &nip98::Nip98Token) -> Result<JsValue, JsValue> {
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"pubkey".into(), &token.pubkey.clone().into())?;
js_sys::Reflect::set(&obj, &"url".into(), &token.url.clone().into())?;
js_sys::Reflect::set(&obj, &"method".into(), &token.method.clone().into())?;
js_sys::Reflect::set(
&obj,
&"payloadHash".into(),
&match &token.payload_hash {
Some(h) => JsValue::from_str(h),
None => JsValue::NULL,
},
)?;
js_sys::Reflect::set(
&obj,
&"createdAt".into(),
&JsValue::from_f64(token.created_at as f64),
)?;
Ok(obj.into())
}
#[wasm_bindgen]
pub fn compute_event_id(
pubkey: &str,
created_at: u32,
kind: u32,
tags_json: &str,
content: &str,
) -> Result<String, JsValue> {
let tags: Vec<Vec<String>> =
serde_json::from_str(tags_json).map_err(|e| JsValue::from_str(&e.to_string()))?;
let unsigned = UnsignedEvent {
pubkey: pubkey.to_string(),
created_at: created_at as u64,
kind: kind as u64,
tags,
content: content.to_string(),
};
let id_bytes = compute_id_inner(&unsigned);
Ok(hex::encode(id_bytes))
}
#[wasm_bindgen]
pub fn schnorr_sign(secret_key: &[u8], message: &[u8]) -> Result<Vec<u8>, JsValue> {
let sk_bytes: [u8; 32] = secret_key
.try_into()
.map_err(|_| JsValue::from_str("secret_key must be 32 bytes"))?;
let msg: [u8; 32] = message
.try_into()
.map_err(|_| JsValue::from_str("message must be 32 bytes"))?;
let sk =
keys::SecretKey::from_bytes(sk_bytes).map_err(|e| JsValue::from_str(&e.to_string()))?;
let sig = sk
.sign(&msg)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
Ok(sig.as_bytes().to_vec())
}
#[wasm_bindgen]
pub fn schnorr_verify(
public_key: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<bool, JsValue> {
let pk_bytes: [u8; 32] = public_key
.try_into()
.map_err(|_| JsValue::from_str("public_key must be 32 bytes"))?;
let msg: [u8; 32] = message
.try_into()
.map_err(|_| JsValue::from_str("message must be 32 bytes"))?;
let sig_bytes: [u8; 64] = signature
.try_into()
.map_err(|_| JsValue::from_str("signature must be 64 bytes"))?;
let pk =
keys::PublicKey::from_bytes(pk_bytes).map_err(|e| JsValue::from_str(&e.to_string()))?;
let sig = keys::Signature::from_bytes(sig_bytes);
match pk.verify(&msg, &sig) {
Ok(()) => Ok(true),
Err(_) => Ok(false),
}
}
#[wasm_bindgen]
pub fn generate_keypair() -> Result<JsValue, JsValue> {
let kp = keys::generate_keypair().map_err(|e| JsValue::from_str(&e.to_string()))?;
let obj = js_sys::Object::new();
let sk_array = js_sys::Uint8Array::from(kp.secret.as_bytes().as_slice());
js_sys::Reflect::set(&obj, &"secretKey".into(), &sk_array)?;
js_sys::Reflect::set(&obj, &"publicKey".into(), &kp.public.to_hex().into())?;
Ok(obj.into())
}