diff --git a/crates/relicario-wasm/src/lib.rs b/crates/relicario-wasm/src/lib.rs index 51539b9..f3b5a2e 100644 --- a/crates/relicario-wasm/src/lib.rs +++ b/crates/relicario-wasm/src/lib.rs @@ -46,6 +46,75 @@ pub fn lock(handle: &SessionHandle) -> bool { // Subsequent wasm_bindgen fns added in Tasks 19-21. +use serde_wasm_bindgen::to_value; +use relicario_core::{ + decrypt_item, decrypt_manifest, decrypt_settings, + encrypt_item, encrypt_manifest, encrypt_settings, + Item, Manifest, VaultSettings, +}; + +fn need_key(handle: &SessionHandle) -> Result<(), JsError> { + if session::with(handle.0, |_| ()).is_some() { Ok(()) } + else { Err(JsError::new("invalid or locked session handle")) } +} + +#[wasm_bindgen] +pub fn manifest_decrypt(handle: &SessionHandle, encrypted: &[u8]) -> Result { + need_key(handle)?; + let out = session::with(handle.0, |k| decrypt_manifest(encrypted, k)) + .unwrap() + .map_err(|e| JsError::new(&e.to_string()))?; + to_value(&out).map_err(|e| JsError::new(&e.to_string())) +} + +#[wasm_bindgen] +pub fn manifest_encrypt(handle: &SessionHandle, manifest_json: &str) -> Result, JsError> { + need_key(handle)?; + let m: Manifest = serde_json::from_str(manifest_json) + .map_err(|e| JsError::new(&format!("manifest json: {e}")))?; + session::with(handle.0, |k| encrypt_manifest(&m, k)) + .unwrap() + .map_err(|e| JsError::new(&e.to_string())) +} + +#[wasm_bindgen] +pub fn item_decrypt(handle: &SessionHandle, encrypted: &[u8]) -> Result { + need_key(handle)?; + let out = session::with(handle.0, |k| decrypt_item(encrypted, k)) + .unwrap() + .map_err(|e| JsError::new(&e.to_string()))?; + to_value(&out).map_err(|e| JsError::new(&e.to_string())) +} + +#[wasm_bindgen] +pub fn item_encrypt(handle: &SessionHandle, item_json: &str) -> Result, JsError> { + need_key(handle)?; + let item: Item = serde_json::from_str(item_json) + .map_err(|e| JsError::new(&format!("item json: {e}")))?; + session::with(handle.0, |k| encrypt_item(&item, k)) + .unwrap() + .map_err(|e| JsError::new(&e.to_string())) +} + +#[wasm_bindgen] +pub fn settings_decrypt(handle: &SessionHandle, encrypted: &[u8]) -> Result { + need_key(handle)?; + let out = session::with(handle.0, |k| decrypt_settings(encrypted, k)) + .unwrap() + .map_err(|e| JsError::new(&e.to_string()))?; + to_value(&out).map_err(|e| JsError::new(&e.to_string())) +} + +#[wasm_bindgen] +pub fn settings_encrypt(handle: &SessionHandle, settings_json: &str) -> Result, JsError> { + need_key(handle)?; + let s: VaultSettings = serde_json::from_str(settings_json) + .map_err(|e| JsError::new(&format!("settings json: {e}")))?; + session::with(handle.0, |k| encrypt_settings(&s, k)) + .unwrap() + .map_err(|e| JsError::new(&e.to_string())) +} + #[cfg(test)] mod session_tests { use super::*; @@ -70,4 +139,22 @@ mod session_tests { let byte = session::with(h, |k| k[0]); assert_eq!(byte, None); } + + #[test] + fn manifest_round_trip_via_handle() { + use relicario_core::{Manifest, decrypt_manifest}; + session::clear(); + let h = session::insert(Zeroizing::new([0x55u8; 32])); + let handle = SessionHandle(h); + let key = Zeroizing::new([0x55u8; 32]); + let empty = Manifest::new(); + let bytes = manifest_encrypt(&handle, &serde_json::to_string(&empty).unwrap()).unwrap(); + assert!(!bytes.is_empty()); + // Decrypt via core directly (avoids js-sys on native). + let parsed: Manifest = decrypt_manifest(&bytes, &key).unwrap(); + assert_eq!(parsed.items.len(), 0); + // Random nonces mean two encryptions of the same plaintext differ. + let bytes2 = manifest_encrypt(&handle, &serde_json::to_string(&empty).unwrap()).unwrap(); + assert_ne!(bytes, bytes2, "nonces must differ"); + } }