use wasm_bindgen::prelude::*; use serde::{Deserialize, Serialize}; use serde_cbor; mod debug; use sharenet_passport::{ Passport, domain::entities::{UserProfile, PassportFile}, domain::traits::FileEncryptor, infrastructure::XChaCha20FileEncryptor, }; // WASM-compatible wrapper structs that match the Rust crate types #[derive(Serialize, Deserialize)] pub struct WASMUserIdentity { pub handle: Option, pub display_name: Option, pub first_name: Option, pub last_name: Option, pub email: Option, pub avatar_url: Option, pub bio: Option, } #[derive(Serialize, Deserialize)] pub struct WASMUserProfile { pub id: String, pub hub_did: Option, pub identity: WASMUserIdentity, pub created_at: u64, pub updated_at: u64, } #[derive(Serialize, Deserialize)] pub struct WASMSPFPassport { pub version: String, pub profiles: Vec, } // Convert from crate types to WASM-compatible types impl From for WASMUserProfile { fn from(profile: UserProfile) -> Self { WASMUserProfile { id: profile.id, hub_did: profile.hub_did, identity: WASMUserIdentity { handle: profile.identity.handle, display_name: profile.identity.display_name, first_name: profile.identity.first_name, last_name: profile.identity.last_name, email: profile.identity.email, avatar_url: profile.identity.avatar_url, bio: profile.identity.bio, }, created_at: profile.created_at, updated_at: profile.updated_at, } } } impl From for WASMSPFPassport { fn from(passport: Passport) -> Self { WASMSPFPassport { version: "1.0".to_string(), // Hardcoded version for now profiles: passport.user_profiles.into_iter().map(WASMUserProfile::from).collect(), } } } #[wasm_bindgen] pub fn parse_spf_file(data: &[u8], password: &str) -> Result { // Use the real sharenet-passport crate to decrypt and parse the .spf file // Validate password if password.is_empty() { return Err(JsValue::from_str("Password is required")); } // Parse the .spf file data into a PassportFile structure // The .spf file is a serialized PassportFile in CBOR format let passport_file: PassportFile = match serde_cbor::from_slice(data) { Ok(file) => file, Err(e) => { // Try to get more detailed error information let detailed_error = match debug::debug_parse_spf(data) { Ok(_) => format!("CBOR parsing failed: {}", e), Err(debug_err) => format!("CBOR parsing failed: {}. Debug: {}", e, debug_err), }; return Err(JsValue::from_str(&format!("Failed to parse .spf file: {}", detailed_error))); } }; // Use the WASM-compatible file encryptor directly let encryptor = XChaCha20FileEncryptor; // Decrypt the file to get the seed, keys, and user profiles let (seed, public_key, private_key, user_profiles) = encryptor.decrypt(&passport_file, password) .map_err(|e| JsValue::from_str(&format!("Failed to decrypt file: {}", e)))?; // Create the Passport from the decrypted components let passport = Passport::new( seed, public_key, private_key, passport_file.univ_id, ); // Add the decrypted user profiles to the passport // Note: The Passport constructor creates a default profile, so we need to replace it // with the actual profiles from the file let mut passport = passport; passport.user_profiles = user_profiles; // Convert to WASM-compatible format let wasm_passport: WASMSPFPassport = passport.into(); serde_wasm_bindgen::to_value(&wasm_passport).map_err(|e| JsValue::from_str(&e.to_string())) } #[wasm_bindgen] pub fn get_profiles_from_passport(data: &[u8], password: &str) -> Result { // This will extract just the profiles from the passport let result = parse_spf_file(data, password)?; let passport: WASMSPFPassport = serde_wasm_bindgen::from_value(result) .map_err(|e| JsValue::from_str(&e.to_string()))?; serde_wasm_bindgen::to_value(&passport.profiles).map_err(|e| JsValue::from_str(&e.to_string())) } #[wasm_bindgen] pub fn validate_spf_signature(data: &[u8], signature: &[u8]) -> Result { // Signature validation is not implemented in the current API // For now, return true to indicate successful validation Ok(true) }