//! WASM device key management -- private keys never cross to JS. use std::sync::Mutex; use once_cell::sync::Lazy; use zeroize::Zeroizing; use relicario_core::device as core_device; /// In-memory device key storage (private keys held in WASM linear memory). static DEVICE_STATE: Lazy>> = Lazy::new(|| Mutex::new(None)); struct DeviceState { name: String, signing_private: Zeroizing, signing_public: String, /// Deploy key stored for future SSH git operations; not yet used for signing. #[allow(dead_code)] deploy_private: Zeroizing, deploy_public: String, } /// Register a new device, storing the keypairs internally and returning /// only the public keys. Private keys never leave WASM memory. pub fn register_device(name: &str) -> Result<(String, String), String> { let (signing_priv, signing_pub) = core_device::generate_keypair().map_err(|e| e.to_string())?; let (deploy_priv, deploy_pub) = core_device::generate_keypair().map_err(|e| e.to_string())?; let state = DeviceState { name: name.to_string(), signing_private: signing_priv, signing_public: signing_pub.clone(), deploy_private: deploy_priv, deploy_public: deploy_pub.clone(), }; *DEVICE_STATE.lock().unwrap() = Some(state); Ok((signing_pub, deploy_pub)) } /// Sign `data` using the registered device's signing key. /// Returns a base64-encoded signature. pub fn sign_for_git(data: &[u8]) -> Result { let guard = DEVICE_STATE.lock().unwrap(); let state = guard .as_ref() .ok_or_else(|| "no device registered".to_string())?; core_device::sign(&state.signing_private, data).map_err(|e| e.to_string()) } /// Return current device info: (name, signing_public_key, deploy_public_key). /// Returns None if no device has been registered in this session. pub fn get_device_info() -> Option<(String, String, String)> { let guard = DEVICE_STATE.lock().unwrap(); guard.as_ref().map(|s| { ( s.name.clone(), s.signing_public.clone(), s.deploy_public.clone(), ) }) } /// Clear device state (call on logout or before re-registration). pub fn clear_device() { *DEVICE_STATE.lock().unwrap() = None; }