use serde::{Deserialize, Serialize}; use uuid::Uuid; use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::infrastructure::time; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RecoveryPhrase { words: Vec, } impl RecoveryPhrase { pub fn new(words: Vec) -> Self { Self { words } } pub fn words(&self) -> &[String] { &self.words } pub fn to_string(&self) -> String { self.words.join(" ") } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PublicKey(pub Vec); #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PrivateKey(pub Vec); impl Zeroize for PrivateKey { fn zeroize(&mut self) { self.0.zeroize(); } } impl Drop for PrivateKey { fn drop(&mut self) { self.zeroize(); } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Did(pub String); impl Did { pub fn new(public_key: &PublicKey) -> Self { // Passport DID format with "p:" prefix let did_str = format!("p:{}", hex::encode(&public_key.0)); Self(did_str) } pub fn as_str(&self) -> &str { &self.0 } } #[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] pub struct Seed { bytes: Vec, } impl Seed { pub fn new(bytes: Vec) -> Self { Self { bytes } } pub fn as_bytes(&self) -> &[u8] { &self.bytes } } #[derive(Debug, Serialize, Deserialize)] pub struct Passport { pub seed: Seed, pub public_key: PublicKey, pub private_key: PrivateKey, pub did: Did, pub univ_id: String, pub user_profiles: Vec, pub date_of_birth: Option, pub default_user_profile_id: Option, // UUIDv7 of the default user profile } impl Passport { pub fn new( seed: Seed, public_key: PublicKey, private_key: PrivateKey, univ_id: String, ) -> Self { let did = Did::new(&public_key); // Create default user profile let default_profile = UserProfile::new( None, UserIdentity { handle: None, display_name: None, first_name: None, last_name: None, email: None, avatar_url: None, bio: None, }, UserPreferences { theme: None, language: None, notifications_enabled: true, auto_sync: true, show_date_of_birth: false, }, ); Self { seed, public_key, private_key, did, univ_id, user_profiles: vec![default_profile.clone()], date_of_birth: None, default_user_profile_id: Some(default_profile.id.clone()), } } pub fn public_key(&self) -> &PublicKey { &self.public_key } pub fn did(&self) -> &Did { &self.did } pub fn univ_id(&self) -> &str { &self.univ_id } pub fn user_profiles(&self) -> &[UserProfile] { &self.user_profiles } pub fn default_user_profile(&self) -> Option<&UserProfile> { if let Some(default_id) = &self.default_user_profile_id { self.user_profile_by_id(default_id) } else { // Fallback to implicit detection for backward compatibility self.user_profiles.iter().find(|p| p.is_default()) } } pub fn user_profile_for_hub(&self, hub_did: &str) -> Option<&UserProfile> { self.user_profiles.iter().find(|p| p.hub_did.as_deref() == Some(hub_did)) } pub fn user_profile_by_id(&self, profile_id: &str) -> Option<&UserProfile> { self.user_profiles.iter().find(|p| p.id == profile_id) } pub fn user_profile_by_id_mut(&mut self, profile_id: &str) -> Option<&mut UserProfile> { self.user_profiles.iter_mut().find(|p| p.id == profile_id) } pub fn add_user_profile(&mut self, profile: UserProfile) -> Result<(), String> { // If this is a default profile (no hub_did), set it as the default if profile.hub_did.is_none() { if self.default_user_profile_id.is_some() { return Err("Default user profile already exists".to_string()); } self.default_user_profile_id = Some(profile.id.clone()); } // Ensure hub_did is unique if let Some(hub_did) = &profile.hub_did { if self.user_profile_for_hub(hub_did).is_some() { return Err(format!("User profile for hub DID {} already exists", hub_did)); } } self.user_profiles.push(profile); Ok(()) } pub fn update_user_profile(&mut self, hub_did: Option<&str>, profile: UserProfile) -> Result<(), String> { let index = self.user_profiles.iter().position(|p| { match (p.hub_did.as_deref(), hub_did) { (None, None) => true, // Default profile (Some(p_hub), Some(hub)) if p_hub == hub => true, // Hub-specific profile _ => false, } }); match index { Some(idx) => { self.user_profiles[idx] = profile; Ok(()) } None => Err("User profile not found".to_string()), } } pub fn remove_user_profile(&mut self, hub_did: Option<&str>) -> Result<(), String> { if hub_did.is_none() { return Err("Cannot delete default user profile".to_string()); } let index = self.user_profiles.iter().position(|p| p.hub_did.as_deref() == hub_did); match index { Some(idx) => { self.user_profiles.remove(idx); Ok(()) } None => Err("User profile not found".to_string()), } } pub fn update_user_profile_by_id(&mut self, profile_id: &str, profile: UserProfile) -> Result<(), String> { let index = self.user_profiles.iter().position(|p| p.id == profile_id); match index { Some(idx) => { self.user_profiles[idx] = profile; Ok(()) } None => Err("User profile not found".to_string()), } } pub fn remove_user_profile_by_id(&mut self, profile_id: &str) -> Result<(), String> { let index = self.user_profiles.iter().position(|p| p.id == profile_id); match index { Some(idx) => { // Check if this is the default profile if self.default_user_profile_id.as_deref() == Some(profile_id) { return Err("Cannot delete default user profile".to_string()); } self.user_profiles.remove(idx); Ok(()) } None => Err("User profile not found".to_string()), } } pub fn set_default_user_profile(&mut self, profile_id: &str) -> Result<(), String> { // Verify the profile exists if self.user_profile_by_id(profile_id).is_none() { return Err("User profile not found".to_string()); } // Verify the profile is a default profile (no hub_did) if let Some(profile) = self.user_profile_by_id(profile_id) { if profile.hub_did.is_some() { return Err("Cannot set hub-specific profile as default".to_string()); } } self.default_user_profile_id = Some(profile_id.to_string()); Ok(()) } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UserIdentity { 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(Debug, Clone, Serialize, Deserialize)] pub struct UserPreferences { pub theme: Option, pub language: Option, pub notifications_enabled: bool, pub auto_sync: bool, pub show_date_of_birth: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DateOfBirth { pub month: u8, pub day: u8, pub year: u16, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UserProfile { pub id: String, // UUIDv7 unique identifier for the profile pub hub_did: Option, // None for default profile pub identity: UserIdentity, pub preferences: UserPreferences, pub created_at: u64, pub updated_at: u64, } impl UserProfile { pub fn new( hub_did: Option, identity: UserIdentity, preferences: UserPreferences, ) -> Self { let now = time::now_seconds().unwrap_or_default(); Self { id: Uuid::now_v7().to_string(), hub_did, identity, preferences, created_at: now, updated_at: now, } } pub fn is_default(&self) -> bool { self.hub_did.is_none() } } #[derive(Debug, Serialize, Deserialize)] pub struct PassportFile { pub enc_seed: Vec, pub kdf: String, pub cipher: String, pub salt: Vec, pub nonce: Vec, pub public_key: Vec, pub did: String, pub univ_id: String, pub created_at: u64, pub version: String, pub enc_user_profiles: Vec, // Encrypted CBOR of Vec #[serde(default)] pub enc_date_of_birth: Vec, // Encrypted CBOR of Option #[serde(default)] pub enc_default_user_profile_id: Vec, // Encrypted CBOR of Option }