use wasm_bindgen::prelude::*;
use js_sys::Uint8Array;
use getrandom::getrandom;
use ed25519_dalek::{PublicKey, SecretKey, Signature, Verifier, ExpandedSecretKey};
#[wasm_bindgen]
pub fn generate_keypair() -> Result<js_sys::Object, JsValue> {
let mut seed = [0u8; 32];
getrandom(&mut seed).map_err(|e| JsValue::from_str(&format!("rng error: {}", e)))?;
let secret = SecretKey::from_bytes(&seed).map_err(|e| JsValue::from_str(&e.to_string()))?;
let public = PublicKey::from(&secret);
let sk_arr = Uint8Array::from(&secret.to_bytes()[..]);
let pk_arr = Uint8Array::from(&public.to_bytes()[..]);
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &JsValue::from_str("secretKey"), &sk_arr.into())?;
js_sys::Reflect::set(&obj, &JsValue::from_str("publicKey"), &pk_arr.into())?;
Ok(obj)
}
#[wasm_bindgen]
pub fn sign(message: &[u8], secret_key: &[u8]) -> Result<Uint8Array, JsValue> {
if secret_key.len() != 32 {
return Err(JsValue::from_str("secret_key must be 32 bytes"));
}
let secret = SecretKey::from_bytes(secret_key).map_err(|e| JsValue::from_str(&e.to_string()))?;
let public = PublicKey::from(&secret);
let expanded = ExpandedSecretKey::from(&secret);
let sig: Signature = expanded.sign(message, &public);
Ok(Uint8Array::from(&sig.to_bytes()[..]))
}
#[wasm_bindgen]
pub fn verify(message: &[u8], signature: &[u8], public_key: &[u8]) -> Result<bool, JsValue> {
if signature.len() != 64 {
return Err(JsValue::from_str("signature must be 64 bytes"));
}
if public_key.len() != 32 {
return Err(JsValue::from_str("public_key must be 32 bytes"));
}
let sig = Signature::from_bytes(signature).map_err(|e| JsValue::from_str(&e.to_string()))?;
let public = PublicKey::from_bytes(public_key).map_err(|e| JsValue::from_str(&e.to_string()))?;
Ok(public.verify(message, &sig).is_ok())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sign_and_verify_roundtrip() {
let mut seed = [0u8; 32];
getrandom(&mut seed).expect("rng");
let secret = SecretKey::from_bytes(&seed).unwrap();
let public = PublicKey::from(&secret);
let message = b"hello wasm";
let expanded = ExpandedSecretKey::from(&secret);
let sig = expanded.sign(message, &public);
assert!(public.verify(message, &sig).is_ok());
}
}