feat(wasm): manifest / item / settings encrypt+decrypt via SessionHandle
Adds six #[wasm_bindgen] functions (manifest_encrypt/decrypt, item_encrypt/decrypt, settings_encrypt/decrypt) plus a native round-trip test that verifies encrypt→core_decrypt and nonce uniqueness without calling js-sys (serde_wasm_bindgen::from_value is wasm32-only; documented in test comment).
This commit is contained in:
@@ -46,6 +46,75 @@ pub fn lock(handle: &SessionHandle) -> bool {
|
|||||||
|
|
||||||
// Subsequent wasm_bindgen fns added in Tasks 19-21.
|
// 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<JsValue, JsError> {
|
||||||
|
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<Vec<u8>, 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<JsValue, JsError> {
|
||||||
|
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<Vec<u8>, 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<JsValue, JsError> {
|
||||||
|
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<Vec<u8>, 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)]
|
#[cfg(test)]
|
||||||
mod session_tests {
|
mod session_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -70,4 +139,22 @@ mod session_tests {
|
|||||||
let byte = session::with(h, |k| k[0]);
|
let byte = session::with(h, |k| k[0]);
|
||||||
assert_eq!(byte, None);
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user