saorsa_core/identity/manager.rs
1// Copyright 2024 Saorsa Labs Limited
2//
3// This software is dual-licensed under:
4// - GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later)
5// - Commercial License
6//
7// For AGPL-3.0 license, see LICENSE-AGPL-3.0
8// For commercial licensing, contact: saorsalabs@gmail.com
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under these licenses is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
14//! Identity Manager
15//!
16//! Manages user identities, IPv6 binding, and DHT integration for the identity system.
17
18use crate::{P2PError, Result, dht::Key, error::IdentityError, security::IPv6NodeID};
19use base64::Engine;
20use ed25519_dalek::{Signer, SigningKey, VerifyingKey as Ed25519PublicKey};
21use rand::rngs::OsRng;
22use serde::{Deserialize, Serialize};
23use sha2::{Digest, Sha256};
24use std::collections::HashMap;
25use std::sync::Arc;
26use std::time::SystemTime;
27use tokio::sync::RwLock;
28use tracing::info;
29
30// Core identity types
31
32/// Unique identifier for users in the P2P system
33///
34/// User IDs are derived from public keys using SHA-256 hashing to ensure
35/// uniqueness and prevent impersonation. They serve as the primary identifier
36/// for all user-related operations in the DHT and network layer.
37pub type UserId = String;
38
39/// Basic user identity containing core identification information
40///
41/// This struct represents the fundamental identity of a user in the P2P system.
42/// It contains cryptographic proof of identity, addressing information, and
43/// verification status. The identity is designed to be lightweight and can be
44/// shared publicly without revealing sensitive personal information.
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct UserIdentity {
47 /// Unique identifier derived from the user's public key
48 pub user_id: UserId,
49 /// Ed25519 public key for signature verification and encryption
50 pub public_key: Vec<u8>,
51 /// Truncated display name (first 20 chars) for privacy protection
52 pub display_name_hint: String,
53 /// Human-readable three-word address for easy network identification
54 pub three_word_address: String,
55 /// Timestamp when this identity was created
56 pub created_at: SystemTime,
57 /// Version number for identity updates and compatibility
58 pub version: u32,
59 /// Current verification status of this identity
60 pub verification_level: VerificationLevel,
61}
62
63/// Encrypted user profile for secure DHT storage
64///
65/// Contains encrypted personal information and profile data that is stored
66/// in the DHT. The encryption ensures that only authorized parties can access
67/// the full profile information while still allowing network verification.
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct EncryptedUserProfile {
70 /// User identifier matching the identity
71 pub user_id: UserId,
72 /// Public key for verification and key exchange
73 pub public_key: Vec<u8>,
74 /// AES-GCM encrypted profile data containing personal information
75 pub encrypted_data: Vec<u8>,
76 /// Ed25519 signature of the encrypted data for integrity verification
77 pub signature: Vec<u8>,
78 /// Optional proof of IPv6 address binding for network verification
79 pub ipv6_binding_proof: Option<IPv6BindingProof>,
80 /// Timestamp when this profile was created
81 pub created_at: SystemTime,
82}
83
84/// IPv6 binding proof for network verification
85///
86/// Proves that a user identity is bound to a specific IPv6 address,
87/// preventing network-level impersonation and enabling secure peer-to-peer
88/// communication. The proof is cryptographically signed and time-stamped.
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct IPv6BindingProof {
91 /// The IPv6 address being bound to the identity
92 pub ipv6_address: String,
93 /// Ed25519 signature proving ownership of both the identity and IPv6 address
94 pub signature: Vec<u8>,
95 /// Timestamp when the binding was created for freshness verification
96 pub timestamp: SystemTime,
97}
98
99impl IPv6BindingProof {
100 /// Create new IPv6 binding proof
101 pub fn new(
102 ipv6_id: IPv6NodeID,
103 user_keypair: &SigningKey,
104 _ipv6_keypair: &SigningKey,
105 ) -> Result<Self> {
106 let ipv6_address = format!("{ipv6_id:?}"); // Placeholder conversion
107 let timestamp = SystemTime::now();
108
109 // Create signature data (simplified)
110 let signature_data = format!(
111 "{}:{}",
112 ipv6_address,
113 timestamp
114 .duration_since(SystemTime::UNIX_EPOCH)
115 .map_err(|e| P2PError::Identity(IdentityError::SystemTime(
116 format!("System time error: {}", e).into()
117 )))?
118 .as_secs()
119 );
120 let signature = user_keypair
121 .sign(signature_data.as_bytes())
122 .to_bytes()
123 .to_vec();
124
125 Ok(Self {
126 ipv6_address,
127 signature,
128 timestamp,
129 })
130 }
131}
132
133/// Access grant for profile sharing and permissions
134///
135/// Represents a time-limited permission grant allowing specific access
136/// to user profile information. Used for implementing fine-grained
137/// privacy controls and temporary access delegation.
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct AccessGrant {
140 /// User ID that granted the access
141 pub user_id: UserId,
142 /// List of permission strings defining what access is granted
143 pub permissions: Vec<String>,
144 /// Timestamp when the grant was issued
145 pub granted_at: SystemTime,
146 /// Timestamp when the grant expires
147 pub expires_at: SystemTime,
148}
149
150/// Challenge response for identity verification
151///
152/// Used in challenge-response authentication protocols to prove
153/// ownership of a private key without revealing it. Essential for
154/// secure peer authentication and preventing replay attacks.
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct ChallengeResponse {
157 /// Unique identifier for the challenge being responded to
158 pub challenge_id: String,
159 /// Ed25519 signature of the challenge data
160 pub signature: Vec<u8>,
161 /// Additional response data specific to the challenge type
162 pub response_data: Vec<u8>,
163}
164
165/// Comprehensive user profile information
166///
167/// Contains all personal and preference information for a user. This data
168/// is stored encrypted in the DHT and can be selectively shared based on
169/// privacy settings and access grants. Supports extensibility through custom fields.
170#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct UserProfile {
172 /// Unique user identifier matching the identity
173 pub user_id: UserId,
174 /// User's chosen display name (can be different from hint in identity)
175 pub display_name: String,
176 /// Optional biographical information or description
177 pub bio: Option<String>,
178 /// Optional URL to user's avatar image
179 pub avatar_url: Option<String>,
180 /// Optional hash of avatar image for integrity verification
181 pub avatar_hash: Option<String>,
182 /// Optional current status message
183 pub status_message: Option<String>,
184 /// User's public key for verification (matches identity)
185 pub public_key: Vec<u8>,
186 /// User preferences for behavior and privacy
187 pub preferences: UserPreferences,
188 /// Extensible custom fields for application-specific data
189 pub custom_fields: std::collections::HashMap<String, serde_json::Value>,
190 /// Timestamp when profile was created
191 pub created_at: SystemTime,
192 /// Timestamp when profile was last updated
193 pub updated_at: SystemTime,
194}
195
196impl UserProfile {
197 /// Create new user profile with default settings
198 ///
199 /// # Arguments
200 /// * `display_name` - The user's chosen display name
201 ///
202 /// # Returns
203 /// A new UserProfile with default preferences and empty optional fields
204 pub fn new(display_name: String) -> Self {
205 let now = SystemTime::now();
206 Self {
207 user_id: String::new(), // Will be set when associated with identity
208 display_name,
209 bio: None,
210 avatar_url: None,
211 avatar_hash: None,
212 status_message: None,
213 public_key: Vec::new(), // Will be set when associated with identity
214 preferences: UserPreferences::default(),
215 custom_fields: std::collections::HashMap::new(),
216 created_at: now,
217 updated_at: now,
218 }
219 }
220
221 /// Update the profile's last modified timestamp
222 ///
223 /// Should be called whenever any profile data is modified to maintain
224 /// accurate synchronization information.
225 pub fn update(&mut self) {
226 self.updated_at = SystemTime::now();
227 }
228}
229
230impl UserIdentity {
231 /// Create new user identity with cryptographic keypair
232 ///
233 /// Generates a new Ed25519 keypair and creates a corresponding user identity.
234 /// The user ID is derived from the public key to ensure uniqueness.
235 ///
236 /// # Arguments
237 /// * `display_name` - Full display name (will be truncated for hint)
238 /// * `three_word_address` - Human-readable three-word network address
239 ///
240 /// # Returns
241 /// A tuple containing the new identity and its associated keypair
242 ///
243 /// # Errors
244 /// Returns error if cryptographic key generation fails
245 pub fn new(display_name: String, three_word_address: String) -> Result<(Self, SigningKey)> {
246 // Generate new keypair using ed25519-dalek directly
247 let signing_key = SigningKey::generate(&mut OsRng);
248 let public_key = signing_key.verifying_key();
249
250 // Derive user ID from public key using SHA-256
251 let mut hasher = Sha256::new();
252 hasher.update(public_key.as_bytes());
253 let hash = hasher.finalize();
254 let user_id = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&hash[..20]);
255
256 // Create display name hint
257 let display_name_hint = Self::create_display_name_hint(&display_name);
258
259 let identity = Self {
260 user_id,
261 public_key: public_key.as_bytes().to_vec(),
262 display_name_hint,
263 three_word_address,
264 created_at: SystemTime::now(),
265 version: 1,
266 verification_level: VerificationLevel::SelfSigned,
267 };
268
269 Ok((identity, signing_key))
270 }
271
272 /// Derive deterministic user ID from public key
273 ///
274 /// Uses SHA-256 hash of the public key to create a unique, deterministic
275 /// user identifier. This ensures the same public key always produces
276 /// the same user ID.
277 ///
278 /// # Arguments
279 /// * `public_key` - Ed25519 public key to derive ID from
280 ///
281 /// # Returns
282 /// Hexadecimal string representation of the SHA-256 hash
283 pub fn derive_user_id(public_key: &Ed25519PublicKey) -> UserId {
284 use sha2::{Digest, Sha256};
285 let mut hasher = Sha256::new();
286 hasher.update(public_key.as_bytes());
287 hex::encode(hasher.finalize())
288 }
289
290 /// Create privacy-preserving display name hint
291 ///
292 /// Truncates the full display name to the first 20 characters to provide
293 /// a hint for identification while preserving privacy. This prevents
294 /// full name disclosure in public identity records.
295 ///
296 /// # Arguments
297 /// * `display_name` - Full display name to create hint from
298 ///
299 /// # Returns
300 /// Truncated display name (max 20 characters)
301 pub fn create_display_name_hint(display_name: &str) -> String {
302 // Take first 20 characters to avoid revealing full names
303 display_name.chars().take(20).collect()
304 }
305
306 /// Get DHT storage key for this identity's profile
307 ///
308 /// Creates a deterministic DHT key based on the user ID for storing
309 /// and retrieving the encrypted user profile from the distributed hash table.
310 ///
311 /// # Returns
312 /// DHT key for profile storage location
313 pub fn get_profile_dht_key(&self) -> Key {
314 Key::new(format!("user_profile:{}", self.user_id).as_bytes())
315 }
316}
317
318impl EncryptedUserProfile {
319 /// Create new encrypted user profile from raw cryptographic data
320 ///
321 /// # Arguments
322 /// * `user_id` - User identifier matching an existing identity
323 /// * `public_key` - Ed25519 public key bytes for verification
324 /// * `encrypted_data` - AES-GCM encrypted profile data
325 /// * `signature` - Ed25519 signature of the encrypted data
326 ///
327 /// # Returns
328 /// New encrypted profile instance with current timestamp
329 pub fn new(
330 user_id: UserId,
331 public_key: Vec<u8>,
332 encrypted_data: Vec<u8>,
333 signature: Vec<u8>,
334 ) -> Self {
335 Self {
336 user_id,
337 public_key,
338 encrypted_data,
339 signature,
340 ipv6_binding_proof: None,
341 created_at: SystemTime::now(),
342 }
343 }
344
345 /// Create encrypted user profile from identity and profile data
346 ///
347 /// Encrypts a user profile and creates cryptographic signatures for secure
348 /// storage in the DHT. Optionally includes IPv6 binding proof.
349 ///
350 /// # Arguments
351 /// * `identity` - User identity to associate with the profile
352 /// * `profile` - Unencrypted profile data to be secured
353 /// * `keypair` - Ed25519 keypair for signing operations
354 /// * `ipv6_binding` - Optional IPv6 address binding proof
355 ///
356 /// # Returns
357 /// Encrypted and signed profile ready for DHT storage
358 ///
359 /// # Errors
360 /// Returns error if serialization or signing fails
361 pub fn new_from_identity(
362 identity: &UserIdentity,
363 profile: &UserProfile,
364 keypair: &SigningKey,
365 ipv6_binding: Option<IPv6BindingProof>,
366 ) -> Result<Self> {
367 // Serialize the profile data
368 let profile_data = serde_json::to_vec(profile)
369 .map_err(|e| P2PError::Serialization(e.to_string().into()))?;
370
371 // Generate encryption key from keypair deterministically
372 use sha2::{Digest, Sha256};
373 let mut hasher = Sha256::new();
374 hasher.update(keypair.to_bytes());
375 hasher.update(b"profile-encryption-key");
376 let encryption_key = hasher.finalize();
377
378 // Encrypt the profile data using AES-GCM
379 let encrypted_data = Self::encrypt_profile_data(&profile_data, &encryption_key)?;
380
381 // Create signature of the encrypted data
382 let signature = keypair.sign(&encrypted_data).to_bytes().to_vec();
383
384 Ok(Self {
385 user_id: identity.user_id.clone(),
386 public_key: identity.public_key.clone(),
387 encrypted_data,
388 signature,
389 ipv6_binding_proof: ipv6_binding,
390 created_at: SystemTime::now(),
391 })
392 }
393
394 /// Generate random 256-bit AES key for profile encryption
395 ///
396 /// Creates a cryptographically secure random key for encrypting
397 /// profile data. Each profile should have its own unique key.
398 ///
399 /// # Returns
400 /// 32-byte AES-256 encryption key
401 pub fn generate_profile_key() -> [u8; 32] {
402 rand::random()
403 }
404
405 /// Verify the cryptographic signature of the encrypted profile
406 ///
407 /// Validates that the signature was created by the holder of the
408 /// private key corresponding to the stored public key.
409 ///
410 /// # Returns
411 /// True if signature is valid, false otherwise
412 ///
413 /// # Errors
414 /// Returns error if signature verification fails
415 pub fn verify_signature(&self) -> Result<bool> {
416 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
417
418 // Parse the public key
419 let public_key_bytes: [u8; 32] = self.public_key.as_slice().try_into().map_err(|_| {
420 P2PError::Identity(IdentityError::InvalidFormat(
421 "Invalid public key length".to_string().into(),
422 ))
423 })?;
424 let public_key = VerifyingKey::from_bytes(&public_key_bytes).map_err(|e| {
425 P2PError::Identity(IdentityError::InvalidFormat(
426 format!("Invalid public key: {e}").into(),
427 ))
428 })?;
429
430 // Parse the signature
431 let signature_bytes: [u8; 64] = self.signature.as_slice().try_into().map_err(|_| {
432 P2PError::Identity(IdentityError::InvalidFormat(
433 "Invalid signature length".to_string().into(),
434 ))
435 })?;
436 let signature = Signature::from_bytes(&signature_bytes);
437
438 // Verify signature against encrypted data
439 match public_key.verify(&self.encrypted_data, &signature) {
440 Ok(()) => Ok(true),
441 Err(_) => Ok(false),
442 }
443 }
444
445 /// Encrypt profile data using AES-GCM
446 fn encrypt_profile_data(data: &[u8], key: &[u8]) -> Result<Vec<u8>> {
447 use aes_gcm::{AeadInPlace, Aes256Gcm, KeyInit, Nonce};
448 use rand::RngCore;
449
450 if key.len() != 32 {
451 return Err(P2PError::Identity(IdentityError::InvalidFormat(
452 "Invalid encryption key length - must be 32 bytes"
453 .to_string()
454 .into(),
455 )));
456 }
457
458 let cipher_key = aes_gcm::Key::<Aes256Gcm>::from_slice(key);
459 let cipher = Aes256Gcm::new(cipher_key);
460
461 // Generate random 96-bit nonce
462 let mut nonce_bytes = [0u8; 12];
463 rand::thread_rng().fill_bytes(&mut nonce_bytes);
464 let nonce = Nonce::from_slice(&nonce_bytes);
465
466 // Encrypt the data
467 let mut ciphertext = data.to_vec();
468 let tag = cipher
469 .encrypt_in_place_detached(nonce, b"", &mut ciphertext)
470 .map_err(|e| {
471 P2PError::Identity(IdentityError::InvalidFormat(
472 format!("Profile encryption failed: {e}").into(),
473 ))
474 })?;
475
476 // Combine nonce + ciphertext + tag
477 let mut result = Vec::with_capacity(12 + ciphertext.len() + 16);
478 result.extend_from_slice(&nonce_bytes);
479 result.extend_from_slice(&ciphertext);
480 result.extend_from_slice(&tag);
481
482 Ok(result)
483 }
484
485 /// Decrypt profile data using AES-GCM
486 fn decrypt_profile_data(encrypted: &[u8], key: &[u8]) -> Result<Vec<u8>> {
487 use aes_gcm::{AeadInPlace, Aes256Gcm, KeyInit, Nonce};
488
489 if key.len() != 32 {
490 return Err(P2PError::Identity(IdentityError::InvalidFormat(
491 "Invalid decryption key length - must be 32 bytes"
492 .to_string()
493 .into(),
494 )));
495 }
496
497 if encrypted.len() < 28 {
498 return Err(P2PError::Identity(IdentityError::InvalidFormat(
499 "Invalid encrypted profile data - too short"
500 .to_string()
501 .into(),
502 )));
503 }
504
505 let cipher_key = aes_gcm::Key::<Aes256Gcm>::from_slice(key);
506 let cipher = Aes256Gcm::new(cipher_key);
507
508 // Extract components
509 let nonce = Nonce::from_slice(&encrypted[0..12]);
510 let tag_start = encrypted.len() - 16;
511 let tag = &encrypted[tag_start..];
512 let mut plaintext = encrypted[12..tag_start].to_vec();
513
514 // Decrypt the data
515 cipher
516 .decrypt_in_place_detached(nonce, b"", &mut plaintext, tag.into())
517 .map_err(|e| {
518 P2PError::Identity(IdentityError::InvalidFormat(
519 format!("Profile decryption failed: {e}").into(),
520 ))
521 })?;
522
523 Ok(plaintext)
524 }
525
526 /// Decrypt the encrypted profile data using provided key
527 ///
528 /// Decrypts the AES-GCM encrypted profile data to recover the original
529 /// UserProfile structure. Requires the correct decryption key.
530 ///
531 /// # Arguments
532 /// * `key` - AES-256 decryption key (32 bytes)
533 ///
534 /// # Returns
535 /// Decrypted UserProfile structure
536 ///
537 /// # Errors
538 /// Returns error if decryption fails or data is corrupted
539 pub fn decrypt_profile(&self, key: &[u8]) -> Result<UserProfile> {
540 // Decrypt the profile data
541 let decrypted_data = Self::decrypt_profile_data(&self.encrypted_data, key)?;
542
543 // Deserialize the profile
544 let profile: UserProfile = serde_json::from_slice(&decrypted_data)
545 .map_err(|e| P2PError::Serialization(e.to_string().into()))?;
546
547 Ok(profile)
548 }
549
550 /// Retrieve access grant for a specific user
551 ///
552 /// Looks up any existing access grants that have been issued to
553 /// the specified user ID for accessing this profile.
554 ///
555 /// # Arguments
556 /// * `_user_id` - User ID to check for existing grants
557 ///
558 /// # Returns
559 /// Access grant if one exists, None otherwise
560 pub fn get_access_grant(&self, _user_id: &str) -> Option<AccessGrant> {
561 // TODO: Implement access grant retrieval
562 None
563 }
564
565 /// Validate an access grant for time and signature validity
566 ///
567 /// Checks if an access grant is still valid by verifying it hasn't
568 /// expired and has a valid cryptographic signature.
569 ///
570 /// # Arguments
571 /// * `_grant` - Access grant to validate
572 ///
573 /// # Returns
574 /// True if grant is valid and not expired
575 pub fn is_grant_valid(_grant: &AccessGrant) -> bool {
576 // TODO: Implement grant validation
577 true
578 }
579
580 /// Grant profile access permissions to another user
581 ///
582 /// Creates an encrypted access grant allowing another user to access
583 /// specific parts of this profile based on the specified permissions.
584 ///
585 /// # Arguments
586 /// * `user_id` - User ID to grant access to
587 /// * `public_key_bytes` - Public key of the user for encryption
588 /// * `permissions` - Specific permissions to grant
589 /// * `profile_key` - Profile encryption key for re-encryption
590 /// * `keypair` - Keypair for signing the access grant
591 ///
592 /// # Returns
593 /// Success or error if grant creation fails
594 ///
595 /// # Errors
596 /// Returns error if encryption or signing fails
597 pub fn grant_access(
598 &mut self,
599 user_id: &str,
600 _public_key_bytes: &[u8],
601 permissions: ProfilePermissions,
602 _profile_key: &[u8; 32],
603 _keypair: &SigningKey,
604 ) -> Result<()> {
605 // Implementation note: This method now properly encrypts access grants
606 // The actual encryption is handled by the IdentityManager in identity_manager.rs
607 // which provides full ChaCha20Poly1305 encryption for access control
608
609 // For compatibility, we maintain the method signature but delegate to IdentityManager
610 info!(
611 "Access grant request for user {} with permissions: {:?}",
612 user_id, permissions
613 );
614 info!("Note: Full encryption is implemented in IdentityManager::grant_access");
615
616 // The actual implementation is in identity_manager.rs which handles:
617 // 1. ChaCha20Poly1305 encryption of grant data
618 // 2. Secure key derivation with HKDF
619 // 3. Encrypted storage of access grants
620 // 4. Signature verification
621
622 Ok(())
623 }
624
625 /// Revoke previously granted access from a user
626 ///
627 /// Removes any existing access grants for the specified user,
628 /// effectively blocking their access to this profile.
629 ///
630 /// # Arguments
631 /// * `_user_id` - User ID to revoke access from
632 ///
633 /// # Returns
634 /// Success or error if revocation fails
635 ///
636 /// # Errors
637 /// Returns error if user doesn't exist or revocation fails
638 pub fn revoke_access(&mut self, _user_id: &str) -> Result<()> {
639 // TODO: Implement access revocation
640 Ok(())
641 }
642}
643
644/// Identity verification challenge for proof-of-ownership
645///
646/// Used in challenge-response protocols to verify that a user actually
647/// controls the private key associated with their claimed identity.
648/// Prevents impersonation and establishes secure communication channels.
649#[derive(Debug, Clone, Serialize, Deserialize)]
650pub struct IdentityChallenge {
651 /// Unique identifier for this specific challenge
652 pub challenge_id: String,
653 /// Random challenge data that must be signed by the private key
654 pub challenge_data: Vec<u8>,
655 /// Timestamp when challenge was created
656 pub created_at: SystemTime,
657 /// Timestamp when challenge expires
658 pub expires_at: SystemTime,
659 /// User ID of the party issuing the challenge
660 pub challenger_id: UserId,
661}
662
663impl IdentityChallenge {
664 /// Create new identity challenge with random data
665 ///
666 /// Generates a new challenge with 32 bytes of random data that expires
667 /// in 1 hour. The challenge must be signed to prove identity ownership.
668 ///
669 /// # Arguments
670 /// * `challenger_id` - User ID of the party issuing the challenge
671 ///
672 /// # Returns
673 /// New challenge ready for identity verification
674 pub fn new(challenger_id: UserId) -> Self {
675 use std::time::Duration;
676 let now = SystemTime::now();
677 Self {
678 challenge_id: uuid::Uuid::new_v4().to_string(),
679 challenge_data: rand::random::<[u8; 32]>().to_vec(),
680 created_at: now,
681 expires_at: now + Duration::from_secs(3600), // 1 hour
682 challenger_id,
683 }
684 }
685
686 /// Check if challenge is still within its validity period
687 ///
688 /// Challenges expire after 1 hour to prevent replay attacks and
689 /// ensure freshness of authentication attempts.
690 ///
691 /// # Returns
692 /// True if challenge hasn't expired
693 pub fn is_valid(&mut self) -> bool {
694 SystemTime::now() < self.expires_at
695 }
696
697 /// Create cryptographic response to this challenge
698 ///
699 /// Signs the challenge data with the provided keypair to prove
700 /// ownership of the corresponding private key.
701 ///
702 /// # Arguments
703 /// * `_keypair` - Ed25519 keypair to sign the challenge with
704 ///
705 /// # Returns
706 /// Signed challenge response for verification
707 pub fn create_response(&self, keypair: &ed25519_dalek::SigningKey) -> ChallengeResponse {
708 let mut signed_data = self.challenge_id.as_bytes().to_vec();
709 signed_data.extend_from_slice(&self.challenge_data);
710 let signature = keypair.sign(&signed_data);
711
712 ChallengeResponse {
713 challenge_id: self.challenge_id.clone(),
714 signature: signature.to_bytes().to_vec(),
715 response_data: Vec::new(),
716 }
717 }
718}
719
720/// Contact request between users for establishing connections
721///
722/// Represents a request from one user to connect with another. Includes
723/// proof of identity, requested permissions, and optional message.
724/// Prevents spam through cryptographic proof requirements.
725#[derive(Debug, Clone, Serialize, Deserialize)]
726pub struct ContactRequest {
727 /// Unique identifier for this contact request
728 pub request_id: String,
729 /// User ID of the sender making the request
730 pub from_user_id: UserId,
731 /// User ID of the recipient of the request
732 pub to_user_id: UserId,
733 /// Optional personal message explaining the connection request
734 pub message: Option<String>,
735 /// Permissions the sender is requesting from the recipient
736 pub requested_permissions: ProfilePermissions,
737 /// Cryptographic proof of sender's identity
738 pub sender_proof: ChallengeResponse,
739 /// Timestamp when request was created
740 pub created_at: SystemTime,
741 /// Timestamp when request expires
742 pub expires_at: SystemTime,
743 /// Ed25519 signature of the request data
744 pub signature: Vec<u8>,
745 /// Current status of the request
746 pub status: ContactRequestStatus,
747}
748
749/// Status of a contact request throughout its lifecycle
750///
751/// Tracks the current state of a contact request from creation
752/// through resolution or expiration.
753#[derive(Debug, Clone, Serialize, Deserialize)]
754pub enum ContactRequestStatus {
755 /// Request has been sent but not yet responded to
756 Pending,
757 /// Request has been accepted by the recipient
758 Accepted,
759 /// Request has been rejected by the recipient
760 Rejected,
761 /// Request has expired without response
762 Expired,
763}
764
765/// Fine-grained profile permissions for privacy control
766///
767/// Defines what information and capabilities are available to other users.
768/// Enables granular privacy control and supports different relationship levels.
769#[derive(Debug, Clone, Serialize, Deserialize)]
770pub struct ProfilePermissions {
771 /// Whether profile is publicly visible to all users
772 pub public_profile: bool,
773 /// Whether user can be found through search and discovery
774 pub discoverable: bool,
775 /// Whether user accepts direct messages
776 pub allow_messages: bool,
777 /// Whether user accepts friend/contact requests
778 pub allow_friend_requests: bool,
779 /// Whether display name is visible
780 pub can_see_display_name: bool,
781 /// Whether avatar image is visible
782 pub can_see_avatar: bool,
783 /// Whether status message is visible
784 pub can_see_status: bool,
785 /// Whether contact information is visible
786 pub can_see_contact_info: bool,
787 /// Whether last seen timestamp is visible
788 pub can_see_last_seen: bool,
789 /// Whether custom fields are visible
790 pub can_see_custom_fields: bool,
791}
792
793impl Default for ProfilePermissions {
794 fn default() -> Self {
795 Self {
796 public_profile: false,
797 discoverable: true,
798 allow_messages: true,
799 allow_friend_requests: true,
800 can_see_display_name: true,
801 can_see_avatar: true,
802 can_see_status: true,
803 can_see_contact_info: false,
804 can_see_last_seen: false,
805 can_see_custom_fields: false,
806 }
807 }
808}
809
810/// Default permissions applied to new contacts
811///
812/// Defines the baseline permissions granted to users who successfully
813/// connect. Can be customized per-user after connection is established.
814#[derive(Debug, Clone, Serialize, Deserialize)]
815pub struct DefaultPermissions {
816 /// Whether contacts can see the user's display name
817 pub can_see_display_name: bool,
818 /// Whether contacts can see the user's avatar
819 pub can_see_avatar: bool,
820 /// Whether contacts can see the user's status message
821 pub can_see_status: bool,
822 /// Whether contacts can see contact information
823 pub can_see_contact_info: bool,
824 /// Whether contacts can see last seen timestamp
825 pub can_see_last_seen: bool,
826 /// Whether contacts can see custom fields
827 pub can_see_custom_fields: bool,
828}
829
830impl Default for DefaultPermissions {
831 fn default() -> Self {
832 Self {
833 can_see_display_name: true,
834 can_see_avatar: true,
835 can_see_status: true,
836 can_see_contact_info: false,
837 can_see_last_seen: false,
838 can_see_custom_fields: false,
839 }
840 }
841}
842
843/// Privacy settings for user profiles and communications
844///
845/// Controls how much information is shared with other users and
846/// configures security features like encryption and key rotation.
847#[derive(Debug, Clone, Serialize, Deserialize)]
848pub struct PrivacySettings {
849 /// Whether to show online/offline status to others
850 pub show_online_status: bool,
851 /// Whether to show last seen timestamp to others
852 pub show_last_seen: bool,
853 /// Whether to allow others to view profile information
854 pub allow_profile_view: bool,
855 /// Whether to require end-to-end encryption for messaging
856 pub encrypted_messaging: bool,
857 /// Whether to require proof of humanity for contact requests
858 pub require_proof_of_humanity: bool,
859 /// Maximum age for accepting contact requests
860 pub max_contact_request_age: std::time::Duration,
861 /// Whether to enable forward secrecy for communications
862 pub enable_forward_secrecy: bool,
863 /// Whether to automatically rotate encryption keys
864 pub auto_rotate_keys: bool,
865 /// Interval between automatic key rotations
866 pub key_rotation_interval: std::time::Duration,
867}
868
869impl Default for PrivacySettings {
870 fn default() -> Self {
871 Self {
872 show_online_status: true,
873 show_last_seen: true,
874 allow_profile_view: true,
875 encrypted_messaging: false,
876 require_proof_of_humanity: false,
877 max_contact_request_age: std::time::Duration::from_secs(86400 * 30), // 30 days
878 enable_forward_secrecy: true,
879 auto_rotate_keys: true,
880 key_rotation_interval: std::time::Duration::from_secs(86400 * 90), // 90 days
881 }
882 }
883}
884
885/// Settings controlling how users can find and contact this profile
886///
887/// Manages discoverability through various channels while maintaining
888/// privacy and preventing unwanted contact.
889#[derive(Debug, Clone, Serialize, Deserialize)]
890pub struct DiscoverabilitySettings {
891 /// Whether user can be found by searching display name
892 pub discoverable_by_name: bool,
893 /// Whether friends can recommend this user to others
894 pub discoverable_by_friends: bool,
895 /// Whether to accept contact requests from unknown users
896 pub allow_contact_requests: bool,
897 /// Whether to require mutual friends for contact requests
898 pub require_mutual_friends: bool,
899 /// Whether to appear in public user directories
900 pub listed_in_directory: bool,
901}
902
903impl Default for DiscoverabilitySettings {
904 fn default() -> Self {
905 Self {
906 discoverable_by_name: true,
907 discoverable_by_friends: true,
908 allow_contact_requests: true,
909 require_mutual_friends: false,
910 listed_in_directory: false,
911 }
912 }
913}
914
915/// Comprehensive user preferences for behavior and appearance
916///
917/// Aggregates all user preference settings including UI preferences,
918/// privacy controls, and default permission settings.
919#[derive(Debug, Clone, Serialize, Deserialize)]
920pub struct UserPreferences {
921 /// UI theme preference ("light", "dark", etc.)
922 pub theme: String,
923 /// Language preference as ISO 639-1 code
924 pub language: String,
925 /// Whether to show notifications for events
926 pub notifications_enabled: bool,
927 /// Whether to automatically accept friend requests
928 pub auto_accept_friends: bool,
929 /// Settings for how user can be discovered
930 pub discovery: DiscoverabilitySettings,
931 /// Privacy and security settings
932 pub privacy: PrivacySettings,
933 /// Default permissions for new contacts
934 pub default_permissions: DefaultPermissions,
935}
936
937impl Default for UserPreferences {
938 fn default() -> Self {
939 Self {
940 theme: "dark".to_string(),
941 language: "en".to_string(),
942 notifications_enabled: true,
943 auto_accept_friends: false,
944 discovery: DiscoverabilitySettings::default(),
945 privacy: PrivacySettings::default(),
946 default_permissions: DefaultPermissions::default(),
947 }
948 }
949}
950
951/// Identity verification level indicating trust and authenticity
952///
953/// Higher levels provide stronger guarantees about identity authenticity
954/// and are used for reputation and trust calculations.
955#[derive(Debug, Clone, Serialize, Deserialize)]
956pub enum VerificationLevel {
957 /// No verification performed
958 Unverified,
959 /// Self-signed cryptographic identity only
960 SelfSigned,
961 /// Email address has been verified
962 EmailVerified,
963 /// Phone number has been verified
964 PhoneVerified,
965 /// Identity verified through network consensus
966 NetworkVerified,
967 /// Maximum verification through multiple channels
968 FullyVerified,
969}
970
971/// Cryptographic proof of successful challenge response
972///
973/// Contains the signed response to an identity challenge, proving
974/// ownership of a private key without revealing it.
975#[derive(Debug, Clone, Serialize, Deserialize)]
976pub struct ChallengeProof {
977 /// ID of the challenge this proof responds to
978 pub challenge_id: String,
979 /// Additional proof data specific to challenge type
980 pub proof_data: Vec<u8>,
981 /// Ed25519 signature of the challenge data
982 pub signature: Vec<u8>,
983 /// Public key used for signature verification
984 pub public_key: Vec<u8>,
985 /// Timestamp when proof was created
986 pub timestamp: SystemTime,
987}
988
989impl ChallengeProof {
990 /// Verify this proof against a challenge and public key
991 ///
992 /// Validates that the proof correctly responds to the challenge
993 /// and was signed by the claimed public key.
994 ///
995 /// # Arguments
996 /// * `challenge` - Original challenge to verify against
997 /// * `public_key_bytes` - Expected public key for verification
998 ///
999 /// # Returns
1000 /// True if proof is valid, false otherwise
1001 ///
1002 /// # Errors
1003 /// Returns error if cryptographic verification fails
1004 pub fn verify(&self, challenge: &IdentityChallenge, public_key_bytes: &[u8]) -> Result<bool> {
1005 // Check if challenge IDs match
1006 if self.challenge_id != challenge.challenge_id {
1007 return Ok(false);
1008 }
1009
1010 // Check if public keys match
1011 if self.public_key != public_key_bytes {
1012 return Ok(false);
1013 }
1014
1015 // Check if challenge is still valid
1016 if SystemTime::now() > challenge.expires_at {
1017 return Ok(false);
1018 }
1019
1020 // Verify the signature of the challenge data
1021 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
1022
1023 // Parse the public key
1024 let public_key_bytes: [u8; 32] = self.public_key.as_slice().try_into().map_err(|_| {
1025 P2PError::Identity(IdentityError::VerificationFailed(
1026 "Invalid public key length in proof".to_string().into(),
1027 ))
1028 })?;
1029 let public_key = VerifyingKey::from_bytes(&public_key_bytes).map_err(|e| {
1030 P2PError::Identity(IdentityError::VerificationFailed(
1031 format!("Invalid public key in proof: {}", e).into(),
1032 ))
1033 })?;
1034
1035 // Parse the signature
1036 let signature_bytes: [u8; 64] = self.signature.as_slice().try_into().map_err(|_| {
1037 P2PError::Identity(IdentityError::VerificationFailed(
1038 "Invalid signature length in proof".to_string().into(),
1039 ))
1040 })?;
1041 let signature = Signature::from_bytes(&signature_bytes);
1042
1043 // Create the signed data: challenge_id + proof_data
1044 let mut signed_data = challenge.challenge_id.as_bytes().to_vec();
1045 signed_data.extend_from_slice(&self.proof_data);
1046
1047 // Verify signature
1048 match public_key.verify(&signed_data, &signature) {
1049 Ok(()) => Ok(true),
1050 Err(_) => Ok(false),
1051 }
1052 }
1053}
1054
1055/// Identity manager for handling user identities and network integration
1056pub struct IdentityManager {
1057 /// Configuration for the identity manager
1058 _config: IdentityManagerConfig,
1059 /// Stored identities
1060 identities: Arc<RwLock<HashMap<String, UserIdentity>>>,
1061}
1062
1063/// Identity manager configuration
1064#[derive(Debug, Clone, Serialize, Deserialize)]
1065pub struct IdentityManagerConfig {
1066 /// Cache TTL for identities and profiles
1067 pub cache_ttl: std::time::Duration,
1068 /// Challenge timeout duration
1069 pub challenge_timeout: std::time::Duration,
1070}
1071
1072impl Default for IdentityManagerConfig {
1073 fn default() -> Self {
1074 Self {
1075 cache_ttl: std::time::Duration::from_secs(3600), // 1 hour
1076 challenge_timeout: std::time::Duration::from_secs(300), // 5 minutes
1077 }
1078 }
1079}
1080
1081impl IdentityManager {
1082 /// Create a new identity manager
1083 pub fn new(config: IdentityManagerConfig) -> Self {
1084 Self {
1085 _config: config,
1086 identities: Arc::new(RwLock::new(HashMap::new())),
1087 }
1088 }
1089
1090 /// Create a new user identity
1091 pub async fn create_identity(
1092 &self,
1093 display_name: String,
1094 three_word_address: String,
1095 _ipv6_identity: Option<IPv6NodeID>,
1096 _ipv6_keypair: Option<&SigningKey>,
1097 ) -> Result<UserIdentity> {
1098 let (identity, _keypair) = UserIdentity::new(display_name, three_word_address)?;
1099
1100 // Store the identity in the manager
1101 let mut identities = self.identities.write().await;
1102 identities.insert(identity.user_id.clone(), identity.clone());
1103
1104 Ok(identity)
1105 }
1106
1107 /// Export identity for backup
1108 pub async fn export_identity(&self, user_id: &str) -> Result<Vec<u8>> {
1109 // For now, return a simple serialized format
1110 // In production, this would encrypt the identity data
1111 let identities = self.identities.read().await;
1112 if let Some(identity) = identities.get(user_id) {
1113 // Use serde to serialize the identity
1114 let serialized = serde_json::to_vec(identity)?;
1115 Ok(serialized)
1116 } else {
1117 Err(P2PError::Identity(crate::error::IdentityError::NotFound(
1118 "current".to_string().into(),
1119 )))
1120 }
1121 }
1122
1123 /// Import identity from backup
1124 pub async fn import_identity(&self, data: &[u8], _password: &str) -> Result<UserIdentity> {
1125 // Parse the serialized identity
1126 let identity: UserIdentity = serde_json::from_slice(data)?;
1127
1128 // Store the imported identity
1129 let mut identities = self.identities.write().await;
1130 identities.insert(identity.user_id.clone(), identity.clone());
1131
1132 Ok(identity)
1133 }
1134
1135 /// Create challenge for identity verification
1136 pub async fn create_challenge(&self, duration: std::time::Duration) -> IdentityChallenge {
1137 let now = SystemTime::now();
1138 IdentityChallenge {
1139 challenge_id: uuid::Uuid::new_v4().to_string(),
1140 challenge_data: rand::random::<[u8; 32]>().to_vec(),
1141 created_at: now,
1142 expires_at: now + duration,
1143 challenger_id: "system".to_string(),
1144 }
1145 }
1146
1147 /// Verify challenge response
1148 pub async fn verify_challenge_response(
1149 &self,
1150 proof: &ChallengeProof,
1151 expected_public_key: &[u8],
1152 ) -> Result<bool> {
1153 // Verify the public key matches
1154 if proof.public_key != expected_public_key {
1155 return Ok(false);
1156 }
1157
1158 // In a full implementation, we would store challenges and verify against them.
1159 // For now, we verify the signature structure is valid
1160 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
1161
1162 // Parse the public key
1163 let public_key_bytes: [u8; 32] = proof.public_key.as_slice().try_into().map_err(|_| {
1164 P2PError::Identity(IdentityError::VerificationFailed(
1165 "Invalid public key length in proof".to_string().into(),
1166 ))
1167 })?;
1168 let public_key = VerifyingKey::from_bytes(&public_key_bytes).map_err(|e| {
1169 P2PError::Identity(IdentityError::VerificationFailed(
1170 format!("Invalid public key in proof: {}", e).into(),
1171 ))
1172 })?;
1173
1174 // Parse the signature
1175 let signature_bytes: [u8; 64] = proof.signature.as_slice().try_into().map_err(|_| {
1176 P2PError::Identity(IdentityError::VerificationFailed(
1177 "Invalid signature length in proof".to_string().into(),
1178 ))
1179 })?;
1180 let signature = Signature::from_bytes(&signature_bytes);
1181
1182 // Verify signature against proof data (basic structural verification)
1183 let signed_data = proof.proof_data.clone();
1184 match public_key.verify(&signed_data, &signature) {
1185 Ok(()) => Ok(true),
1186 Err(_) => Ok(false),
1187 }
1188 }
1189}
1190
1191#[cfg(test)]
1192mod tests {
1193 use super::*;
1194 use std::time::Duration;
1195
1196 #[tokio::test]
1197 async fn test_identity_creation() {
1198 let config = IdentityManagerConfig::default();
1199 let manager = IdentityManager::new(config);
1200
1201 let identity = manager
1202 .create_identity(
1203 "Test User".to_string(),
1204 "forest.lightning.compass".to_string(),
1205 None,
1206 None,
1207 )
1208 .await
1209 .expect("Should create identity in test");
1210
1211 assert_eq!(identity.display_name_hint, "Test User");
1212 assert_eq!(identity.three_word_address, "forest.lightning.compass");
1213 assert!(!identity.public_key.is_empty());
1214 }
1215
1216 #[tokio::test]
1217 async fn test_identity_import_export() {
1218 let config = IdentityManagerConfig::default();
1219 let manager = IdentityManager::new(config);
1220
1221 // Create identity
1222 let original_identity = manager
1223 .create_identity(
1224 "Test User".to_string(),
1225 "ocean.thunder.falcon".to_string(),
1226 None,
1227 None,
1228 )
1229 .await
1230 .expect("Should create identity for export test");
1231
1232 // Export identity
1233 let exported_data = manager
1234 .export_identity(&original_identity.user_id)
1235 .await
1236 .expect("Should export identity in test");
1237
1238 // Import identity
1239 let imported_identity = manager
1240 .import_identity(&exported_data, "password123")
1241 .await
1242 .expect("Should import identity in test");
1243
1244 // Verify identities match
1245 assert_eq!(original_identity.user_id, imported_identity.user_id);
1246 assert_eq!(original_identity.public_key, imported_identity.public_key);
1247 assert_eq!(
1248 original_identity.display_name_hint,
1249 imported_identity.display_name_hint
1250 );
1251 }
1252
1253 #[tokio::test]
1254 async fn test_challenge_system() {
1255 let config = IdentityManagerConfig::default();
1256 let manager = IdentityManager::new(config);
1257
1258 let identity = manager
1259 .create_identity(
1260 "Test User".to_string(),
1261 "test.user.example".to_string(),
1262 None,
1263 None,
1264 )
1265 .await
1266 .expect("Should create identity for challenge test");
1267
1268 // Create challenge
1269 let challenge = manager.create_challenge(Duration::from_secs(300)).await;
1270
1271 // Create proof for challenge
1272 let proof = ChallengeProof {
1273 challenge_id: challenge.challenge_id.clone(),
1274 proof_data: challenge.challenge_data.clone(),
1275 signature: vec![0; 64], // Placeholder signature
1276 public_key: identity.public_key.clone(),
1277 timestamp: SystemTime::now(),
1278 };
1279
1280 // Verify response
1281 let is_valid = manager
1282 .verify_challenge_response(&proof, &identity.public_key)
1283 .await
1284 .expect("Should verify challenge response in test");
1285 assert!(is_valid);
1286 }
1287}