use wasm_bindgen::prelude::*;
use crate::hybrid::SecurityLevel;
use crate::suite::Suite;
use crate::{
b64, box_seal, hash, hkdf, hybrid, kdf, keys, mac, recovery, seal, secretbox, sign, vrf,
vrf_p256,
};
#[wasm_bindgen(js_name = "deriveSessionKey")]
pub fn derive_session_key(password: &str, salt_b64: &str) -> Result<String, JsValue> {
kdf::derive_session_key(password, salt_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "encryptSecretboxString")]
pub fn encrypt_secretbox_string(plaintext: &str, key_b64: &str) -> Result<String, JsValue> {
secretbox::encrypt_secretbox_string(plaintext, key_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "decryptSecretboxToString")]
pub fn decrypt_secretbox_to_string(ciphertext_b64: &str, key_b64: &str) -> Result<String, JsValue> {
secretbox::decrypt_secretbox_to_string(ciphertext_b64, key_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "encryptSecretbox")]
pub fn encrypt_secretbox(plaintext_b64: &str, key_b64: &str) -> Result<String, JsValue> {
let pt = b64::decode(plaintext_b64).map_err(to_js)?;
secretbox::encrypt_secretbox(&pt, key_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "decryptSecretbox")]
pub fn decrypt_secretbox(ciphertext_b64: &str, key_b64: &str) -> Result<String, JsValue> {
let pt = secretbox::decrypt_secretbox(ciphertext_b64, key_b64).map_err(to_js)?;
Ok(b64::encode(&pt))
}
#[wasm_bindgen(js_name = "boxSeal")]
pub fn box_seal_wasm(plaintext_b64: &str, public_key_b64: &str) -> Result<String, JsValue> {
let pt = b64::decode(plaintext_b64).map_err(to_js)?;
box_seal::box_seal(&pt, public_key_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "boxSealOpen")]
pub fn box_seal_open(
ciphertext_b64: &str,
public_key_b64: &str,
private_key_b64: &str,
) -> Result<String, JsValue> {
box_seal::box_seal_open(ciphertext_b64, public_key_b64, private_key_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "sealForUser")]
pub fn seal_for_user(
plaintext_b64: &str,
public_key_b64: &str,
pq_public_key_b64: Option<String>,
) -> Result<String, JsValue> {
let pt = b64::decode(plaintext_b64).map_err(to_js)?;
seal::seal_for_user(&pt, public_key_b64, pq_public_key_b64.as_deref()).map_err(to_js)
}
#[wasm_bindgen(js_name = "unsealFromUser")]
pub fn unseal_from_user(
ciphertext_b64: &str,
public_key_b64: &str,
private_key_b64: &str,
pq_secret_key_b64: Option<String>,
) -> Result<String, JsValue> {
seal::unseal_from_user(
ciphertext_b64,
public_key_b64,
private_key_b64,
pq_secret_key_b64.as_deref(),
)
.map_err(to_js)
}
#[wasm_bindgen(js_name = "sealForUserWithLevel")]
pub fn seal_for_user_with_level(
plaintext_b64: &str,
public_key_b64: &str,
pq_public_key_b64: Option<String>,
level: &str,
) -> Result<String, JsValue> {
let pt = b64::decode(plaintext_b64).map_err(to_js)?;
let sec_level = parse_security_level(level)?;
seal::seal_for_user_with_level(&pt, public_key_b64, pq_public_key_b64.as_deref(), sec_level)
.map_err(to_js)
}
#[wasm_bindgen(js_name = "generateHybridKeyPair512")]
pub fn generate_hybrid_keypair_512() -> JsValue {
let kp = hybrid::generate_hybrid_keypair_512();
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"publicKey".into(), &kp.public_key.into()).unwrap();
js_sys::Reflect::set(&obj, &"secretKey".into(), &kp.secret_key.into()).unwrap();
obj.into()
}
#[wasm_bindgen(js_name = "generateHybridKeyPair")]
pub fn generate_hybrid_keypair() -> JsValue {
let kp = hybrid::generate_hybrid_keypair();
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"publicKey".into(), &kp.public_key.into()).unwrap();
js_sys::Reflect::set(&obj, &"secretKey".into(), &kp.secret_key.into()).unwrap();
obj.into()
}
#[wasm_bindgen(js_name = "generateHybridKeyPair1024")]
pub fn generate_hybrid_keypair_1024() -> JsValue {
let kp = hybrid::generate_hybrid_keypair_1024();
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"publicKey".into(), &kp.public_key.into()).unwrap();
js_sys::Reflect::set(&obj, &"secretKey".into(), &kp.secret_key.into()).unwrap();
obj.into()
}
#[wasm_bindgen(js_name = "isHybridCiphertext")]
pub fn is_hybrid_ciphertext(ciphertext_b64: &str) -> bool {
hybrid::is_hybrid_ciphertext(ciphertext_b64)
}
#[wasm_bindgen(js_name = "generateHybridKeyPairSuite")]
pub fn generate_hybrid_keypair_suite(suite: &str, level: &str) -> Result<JsValue, JsValue> {
let suite = parse_suite(suite)?;
let lvl = parse_security_level(level)?;
let kp = hybrid::generate_hybrid_keypair_suite(suite, lvl).map_err(to_js)?;
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"publicKey".into(), &kp.public_key.into()).unwrap();
js_sys::Reflect::set(&obj, &"secretKey".into(), &kp.secret_key.into()).unwrap();
Ok(obj.into())
}
#[wasm_bindgen(js_name = "hybridSealSuite")]
pub fn hybrid_seal_suite(
plaintext_b64: &str,
combined_pk_b64: &str,
suite: &str,
level: &str,
) -> Result<String, JsValue> {
let pt = b64::decode(plaintext_b64).map_err(to_js)?;
let suite = parse_suite(suite)?;
let lvl = parse_security_level(level)?;
hybrid::hybrid_seal_suite(&pt, combined_pk_b64, suite, lvl).map_err(to_js)
}
#[wasm_bindgen(js_name = "hybridSealSuiteWithContext")]
pub fn hybrid_seal_suite_with_context(
plaintext_b64: &str,
combined_pk_b64: &str,
suite: &str,
level: &str,
context_label: &str,
) -> Result<String, JsValue> {
let pt = b64::decode(plaintext_b64).map_err(to_js)?;
let suite = parse_suite(suite)?;
let lvl = parse_security_level(level)?;
hybrid::hybrid_seal_suite_with_context(&pt, combined_pk_b64, suite, lvl, context_label)
.map_err(to_js)
}
#[wasm_bindgen(js_name = "hybridOpenWithContext")]
pub fn hybrid_open_with_context(
ciphertext_b64: &str,
seed_b64: &str,
context_label: &str,
) -> Result<String, JsValue> {
let pt =
hybrid::hybrid_open_with_context(ciphertext_b64, seed_b64, context_label).map_err(to_js)?;
Ok(b64::encode(&pt))
}
#[wasm_bindgen(js_name = "sealForUserWithSuite")]
pub fn seal_for_user_with_suite(
plaintext_b64: &str,
public_key_b64: &str,
pq_public_key_b64: Option<String>,
suite: &str,
level: &str,
) -> Result<String, JsValue> {
let pt = b64::decode(plaintext_b64).map_err(to_js)?;
let suite = parse_suite(suite)?;
let lvl = parse_security_level(level)?;
seal::seal_for_user_with_suite(
&pt,
public_key_b64,
pq_public_key_b64.as_deref(),
suite,
lvl,
)
.map_err(to_js)
}
#[wasm_bindgen(js_name = "generateKey")]
pub fn generate_key() -> String {
keys::generate_key()
}
#[wasm_bindgen(js_name = "generateKeyPair")]
pub fn generate_keypair() -> JsValue {
let kp = keys::generate_keypair();
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"publicKey".into(), &kp.public_key.into()).unwrap();
js_sys::Reflect::set(&obj, &"privateKey".into(), &kp.private_key.into()).unwrap();
obj.into()
}
#[wasm_bindgen(js_name = "generateSalt")]
pub fn generate_salt() -> String {
keys::generate_salt()
}
#[wasm_bindgen(js_name = "encryptPrivateKey")]
pub fn encrypt_private_key(
private_key_b64: &str,
session_key_b64: &str,
) -> Result<String, JsValue> {
keys::encrypt_private_key(private_key_b64, session_key_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "decryptPrivateKey")]
pub fn decrypt_private_key(ciphertext_b64: &str, session_key_b64: &str) -> Result<String, JsValue> {
keys::decrypt_private_key(ciphertext_b64, session_key_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "generateRecoveryKey")]
pub fn generate_recovery_key() -> Result<JsValue, JsValue> {
let rk = recovery::generate_recovery_key().map_err(to_js)?;
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"recoveryKey".into(), &rk.recovery_key.into()).unwrap();
js_sys::Reflect::set(
&obj,
&"recoverySecretBase64".into(),
&rk.recovery_secret_b64.into(),
)
.unwrap();
Ok(obj.into())
}
#[wasm_bindgen(js_name = "recoveryKeyToSecret")]
pub fn recovery_key_to_secret(recovery_key: &str) -> Result<String, JsValue> {
recovery::recovery_key_to_secret(recovery_key).map_err(to_js)
}
#[wasm_bindgen(js_name = "encryptPrivateKeyForRecovery")]
pub fn encrypt_private_key_for_recovery(
private_key_b64: &str,
recovery_secret_b64: &str,
) -> Result<String, JsValue> {
recovery::encrypt_private_key_for_recovery(private_key_b64, recovery_secret_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "decryptPrivateKeyWithRecovery")]
pub fn decrypt_private_key_with_recovery(
ciphertext_b64: &str,
recovery_secret_b64: &str,
) -> Result<String, JsValue> {
recovery::decrypt_private_key_with_recovery(ciphertext_b64, recovery_secret_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "parseSaltFromKeyHash")]
pub fn parse_salt_from_key_hash(key_hash: &str) -> Result<String, JsValue> {
b64::parse_salt_from_key_hash(key_hash)
.map(|s| s.to_string())
.map_err(to_js)
}
#[wasm_bindgen(js_name = "sha3_512")]
pub fn sha3_512(data_b64: &str) -> Result<String, JsValue> {
let data = b64::decode(data_b64).map_err(to_js)?;
Ok(b64::encode(&hash::sha3_512(&data)))
}
#[wasm_bindgen(js_name = "sha3_512WithContext")]
pub fn sha3_512_with_context(context: &str, data_b64: &str) -> Result<String, JsValue> {
let data = b64::decode(data_b64).map_err(to_js)?;
Ok(b64::encode(&hash::sha3_512_with_context(context, &data)))
}
#[wasm_bindgen(js_name = "hkdfSha512")]
pub fn hkdf_sha512(
salt_b64: &str,
ikm_b64: &str,
info: &str,
length: usize,
) -> Result<String, JsValue> {
let salt = b64::decode(salt_b64).map_err(to_js)?;
let ikm = b64::decode(ikm_b64).map_err(to_js)?;
let okm = hkdf::hkdf_sha512(&salt, &ikm, info.as_bytes(), length).map_err(to_js)?;
Ok(b64::encode(&okm))
}
#[wasm_bindgen(js_name = "sha3_256")]
pub fn sha3_256(data_b64: &str) -> Result<String, JsValue> {
let data = b64::decode(data_b64).map_err(to_js)?;
Ok(b64::encode(&hash::sha3_256(&data)))
}
#[wasm_bindgen(js_name = "sha256")]
pub fn sha256(data_b64: &str) -> Result<String, JsValue> {
let data = b64::decode(data_b64).map_err(to_js)?;
Ok(b64::encode(&hash::sha256(&data)))
}
#[wasm_bindgen(js_name = "sha512")]
pub fn sha512(data_b64: &str) -> Result<String, JsValue> {
let data = b64::decode(data_b64).map_err(to_js)?;
Ok(b64::encode(&hash::sha512(&data)))
}
#[wasm_bindgen(js_name = "generateSigningKeyPair")]
pub fn generate_signing_keypair(level: &str) -> Result<JsValue, JsValue> {
let lvl = parse_signature_level(level)?;
let kp = sign::generate_signing_keypair_with_level(lvl);
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"publicKey".into(), &kp.public_key.clone().into()).unwrap();
js_sys::Reflect::set(&obj, &"secretKey".into(), &kp.secret_key.clone().into()).unwrap();
Ok(obj.into())
}
#[wasm_bindgen(js_name = "generateSigningKeyPairSuite")]
pub fn generate_signing_keypair_suite(suite: &str, level: &str) -> Result<JsValue, JsValue> {
let suite = parse_suite(suite)?;
let lvl = parse_signature_level(level)?;
let kp = sign::generate_signing_keypair_suite(suite, lvl).map_err(to_js)?;
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"publicKey".into(), &kp.public_key.clone().into()).unwrap();
js_sys::Reflect::set(&obj, &"secretKey".into(), &kp.secret_key.clone().into()).unwrap();
Ok(obj.into())
}
#[wasm_bindgen(js_name = "deriveSigningPublicKey")]
pub fn derive_signing_public_key(secret_key_b64: &str) -> Result<String, JsValue> {
sign::derive_public_key(secret_key_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "sign")]
pub fn sign_message(
message_b64: &str,
context: &str,
secret_key_b64: &str,
) -> Result<String, JsValue> {
let msg = b64::decode(message_b64).map_err(to_js)?;
sign::sign(&msg, context, secret_key_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "verify")]
pub fn verify(
message_b64: &str,
context: &str,
signature_b64: &str,
public_key_b64: &str,
) -> Result<bool, JsValue> {
let msg = b64::decode(message_b64).map_err(to_js)?;
sign::verify(&msg, context, signature_b64, public_key_b64).map_err(to_js)
}
#[wasm_bindgen(js_name = "hmacSha256")]
pub fn hmac_sha256(key_b64: &str, msg_b64: &str) -> Result<String, JsValue> {
let key = b64::decode(key_b64).map_err(to_js)?;
let msg = b64::decode(msg_b64).map_err(to_js)?;
Ok(b64::encode(&mac::hmac_sha256(&key, &msg)))
}
#[wasm_bindgen(js_name = "ecvrfEd25519GenerateKeyPair")]
pub fn ecvrf_ed25519_generate_keypair() -> JsValue {
let (sk, pk) = vrf::ecvrf_generate_keypair();
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"secretKey".into(), &b64::encode(&sk).into()).unwrap();
js_sys::Reflect::set(&obj, &"publicKey".into(), &b64::encode(&pk).into()).unwrap();
obj.into()
}
#[wasm_bindgen(js_name = "ecvrfEd25519PublicKey")]
pub fn ecvrf_ed25519_public_key(secret_key_b64: &str) -> Result<String, JsValue> {
let sk = b64::decode(secret_key_b64).map_err(to_js)?;
Ok(b64::encode(&vrf::ecvrf_public_key(&sk).map_err(to_js)?))
}
#[wasm_bindgen(js_name = "ecvrfEd25519Prove")]
pub fn ecvrf_ed25519_prove(secret_key_b64: &str, alpha_b64: &str) -> Result<String, JsValue> {
let sk = b64::decode(secret_key_b64).map_err(to_js)?;
let alpha = b64::decode(alpha_b64).map_err(to_js)?;
Ok(b64::encode(&vrf::ecvrf_prove(&sk, &alpha).map_err(to_js)?))
}
#[wasm_bindgen(js_name = "ecvrfEd25519Verify")]
pub fn ecvrf_ed25519_verify(
public_key_b64: &str,
alpha_b64: &str,
proof_b64: &str,
) -> Result<Option<String>, JsValue> {
let pk = b64::decode(public_key_b64).map_err(to_js)?;
let alpha = b64::decode(alpha_b64).map_err(to_js)?;
let proof = b64::decode(proof_b64).map_err(to_js)?;
Ok(vrf::ecvrf_verify(&pk, &alpha, &proof)
.map_err(to_js)?
.map(|beta| b64::encode(&beta)))
}
#[wasm_bindgen(js_name = "ecvrfEd25519ProofToHash")]
pub fn ecvrf_ed25519_proof_to_hash(proof_b64: &str) -> Result<String, JsValue> {
let proof = b64::decode(proof_b64).map_err(to_js)?;
Ok(b64::encode(
&vrf::ecvrf_proof_to_hash(&proof).map_err(to_js)?,
))
}
#[wasm_bindgen(js_name = "ecvrfP256GenerateKeyPair")]
pub fn ecvrf_p256_generate_keypair() -> JsValue {
let (sk, pk) = vrf_p256::ecvrf_p256_generate_keypair();
let obj = js_sys::Object::new();
js_sys::Reflect::set(&obj, &"secretKey".into(), &b64::encode(&sk).into()).unwrap();
js_sys::Reflect::set(&obj, &"publicKey".into(), &b64::encode(&pk).into()).unwrap();
obj.into()
}
#[wasm_bindgen(js_name = "ecvrfP256PublicKey")]
pub fn ecvrf_p256_public_key(secret_key_b64: &str) -> Result<String, JsValue> {
let sk = b64::decode(secret_key_b64).map_err(to_js)?;
Ok(b64::encode(
&vrf_p256::ecvrf_p256_public_key(&sk).map_err(to_js)?,
))
}
#[wasm_bindgen(js_name = "ecvrfP256Prove")]
pub fn ecvrf_p256_prove(secret_key_b64: &str, alpha_b64: &str) -> Result<String, JsValue> {
let sk = b64::decode(secret_key_b64).map_err(to_js)?;
let alpha = b64::decode(alpha_b64).map_err(to_js)?;
Ok(b64::encode(
&vrf_p256::ecvrf_p256_prove(&sk, &alpha).map_err(to_js)?,
))
}
#[wasm_bindgen(js_name = "ecvrfP256Verify")]
pub fn ecvrf_p256_verify(
public_key_b64: &str,
alpha_b64: &str,
proof_b64: &str,
) -> Result<Option<String>, JsValue> {
let pk = b64::decode(public_key_b64).map_err(to_js)?;
let alpha = b64::decode(alpha_b64).map_err(to_js)?;
let proof = b64::decode(proof_b64).map_err(to_js)?;
Ok(vrf_p256::ecvrf_p256_verify(&pk, &alpha, &proof)
.map_err(to_js)?
.map(|beta| b64::encode(&beta)))
}
#[wasm_bindgen(js_name = "ecvrfP256ProofToHash")]
pub fn ecvrf_p256_proof_to_hash(proof_b64: &str) -> Result<String, JsValue> {
let proof = b64::decode(proof_b64).map_err(to_js)?;
Ok(b64::encode(
&vrf_p256::ecvrf_p256_proof_to_hash(&proof).map_err(to_js)?,
))
}
fn parse_security_level(level: &str) -> Result<SecurityLevel, JsValue> {
match level.to_ascii_lowercase().as_str() {
"cat1" => Ok(SecurityLevel::Cat1),
"" | "cat3" => Ok(SecurityLevel::Cat3),
"cat5" => Ok(SecurityLevel::Cat5),
other => Err(JsValue::from_str(&format!(
"invalid security level \"{other}\": expected \"cat1\", \"cat3\", or \"cat5\""
))),
}
}
fn to_js(e: crate::CryptoError) -> JsValue {
JsValue::from_str(&e.to_string())
}
fn parse_suite(suite: &str) -> Result<Suite, JsValue> {
let normalized: String = suite
.chars()
.filter(|c| *c != '_' && *c != '-' && *c != ' ')
.collect::<String>()
.to_ascii_lowercase();
match normalized.as_str() {
"" | "hybrid" => Ok(Suite::Hybrid),
"hybridmatched" | "matched" => Ok(Suite::HybridMatched),
"purecnsa2" | "pure" | "cnsa2" => Ok(Suite::PureCnsa2),
other => Err(JsValue::from_str(&format!(
"invalid suite \"{other}\": expected \"hybrid\", \"hybridMatched\", or \"pureCnsa2\""
))),
}
}
fn parse_signature_level(level: &str) -> Result<sign::SignatureLevel, JsValue> {
match level.to_ascii_lowercase().as_str() {
"cat2" => Ok(sign::SignatureLevel::Cat2),
"" | "cat3" => Ok(sign::SignatureLevel::Cat3),
"cat5" => Ok(sign::SignatureLevel::Cat5),
other => Err(JsValue::from_str(&format!(
"invalid signature level \"{other}\": expected \"cat2\", \"cat3\", or \"cat5\""
))),
}
}