use confium_composite::{CompositeSignature as RustComposite, VerificationResult};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct CompositeSignature {
inner: RustComposite,
}
#[wasm_bindgen]
impl CompositeSignature {
#[wasm_bindgen(constructor)]
pub fn from_json(json: &str) -> Result<CompositeSignature, JsValue> {
let inner: RustComposite = serde_json::from_str(json)
.map_err(|e| js_err(&format!("invalid composite signature JSON: {e}")))?;
Ok(Self { inner })
}
#[wasm_bindgen(getter)]
pub fn component_count(&self) -> usize {
self.inner.component_count()
}
#[wasm_bindgen(getter)]
pub fn algorithms(&self) -> Vec<String> {
self.inner
.algorithms()
.into_iter()
.map(String::from)
.collect()
}
#[wasm_bindgen]
pub fn verify(&self, message: &[u8]) -> Result<CompositeVerificationResult, JsValue> {
let result = self
.inner
.verify(message, |algorithm, public_key, m, signature| {
if algorithm == confium_composite::ED25519 {
confium_composite::ed25519_verifier(algorithm, public_key, m, signature)
} else if algorithm == "ECDSA-P256" || algorithm == "ECDSA" {
p256_verifier(public_key, m, signature)
} else {
Err(format!("unsupported algorithm: {algorithm}"))
}
})
.map_err(|e| js_err(&e.to_string()))?;
Ok(CompositeVerificationResult { inner: result })
}
}
fn p256_verifier(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<(), String> {
use p256::ecdsa::{Signature, VerifyingKey, signature::Verifier};
let vk = VerifyingKey::from_sec1_bytes(public_key)
.map_err(|e| format!("invalid P-256 public key: {e}"))?;
let sig = Signature::from_der(signature).map_err(|e| format!("invalid DER signature: {e}"))?;
vk.verify(message, &sig).map_err(|e| format!("verify: {e}"))
}
#[wasm_bindgen]
pub struct CompositeVerificationResult {
inner: VerificationResult,
}
#[wasm_bindgen]
impl CompositeVerificationResult {
#[wasm_bindgen(getter)]
pub fn all_verified(&self) -> bool {
self.inner.all_verified
}
#[wasm_bindgen(getter)]
pub fn per_component_json(&self) -> String {
let entries: Vec<String> = self
.inner
.per_component
.iter()
.map(|c| {
let alg = serde_json::to_string(&c.algorithm).unwrap_or_else(|_| "\"\"".into());
let err = match &c.error {
Some(e) => format!(
",\"error\":{}",
serde_json::to_string(e).unwrap_or_else(|_| "null".into())
),
None => String::new(),
};
format!(
"{{\"index\":{},\"algorithm\":{},\"verified\":{}{}}}",
c.index, alg, c.verified, err
)
})
.collect();
format!("[{}]", entries.join(","))
}
}
fn js_err(msg: &str) -> JsValue {
JsValue::from_str(msg)
}