this_me/
me.rs

1use crate::utils::setup::validate_setup;
2use crate::utils::validate_input::validate_username;
3use crate::utils::validate_input::validate_hash;
4use std::fs::File;
5use std::io::Write;
6use std::path::PathBuf;
7use std::env;
8use std::fs;
9
10use aes_gcm::{Aes256Gcm, Key, Nonce}; // AES-GCM cipher
11use aes_gcm::aead::{Aead, KeyInit};
12use sha2::{Sha256, Digest};
13use serde_json;  // added import
14
15/// Represents a local encrypted identity used in dApps or Web3 environments.
16pub struct Me {
17    pub username: String,
18    pub file_path: PathBuf,
19    pub data: Option<String>,
20}
21
22impl Me {
23    /// Initializes a Me instance without validating the username.
24    pub fn from_username_unchecked(username: &str) -> Self {
25        let home_dir = env::var("HOME").unwrap_or_else(|_| ".".to_string());
26        let file_path = PathBuf::from(format!("{}/.this/me/{}.me", home_dir, username));
27        Me {
28            username: username.to_string(),
29            file_path,
30            data: None,
31        }
32    }
33
34    pub fn delete(username: &str, hash: &str) -> std::io::Result<()> {
35        let me = Me::from_username_unchecked(username);
36    
37        if !me.file_path.exists() {
38            return Err(std::io::Error::new(
39                std::io::ErrorKind::NotFound,
40                format!("❌ Identity '{}' does not exist.", username),
41            ));
42        }
43    
44        let contents = fs::read(&me.file_path)?;
45        let decrypted = me.decrypt(&contents, hash);
46    
47        match decrypted {
48            Ok(_) => {
49                fs::remove_file(&me.file_path)?;
50                println!("🗑️ Identity '{}' deleted.", username);
51                Ok(())
52            }
53            Err(_) => Err(std::io::Error::new(
54                std::io::ErrorKind::PermissionDenied,
55                "❌ Invalid password. Deletion aborted.",
56            )),
57        }
58    }
59
60    /// Initializes a new Me instance with the given username.
61    pub fn new(username: &str) -> std::io::Result<Self> {
62        match validate_username(username) {
63            Ok(_) => {
64                let home_dir = env::var("HOME").unwrap_or_else(|_| ".".to_string());
65                let file_path = PathBuf::from(format!("{}/.this/me/{}.me", home_dir, username));
66                Ok(Me {
67                    username: username.to_string(),
68                    file_path,
69                    data: None,
70                })
71            }
72            Err(e) => {
73                eprintln!("❌ Error validating username: {}", e);
74                Err(std::io::Error::new(
75                    std::io::ErrorKind::InvalidInput,
76                    format!("❌ Error validating username: {}", e),
77                ))
78            }
79        }
80    }
81
82    /// Creates and saves a new encrypted identity file.
83    pub fn create(username: &str, hash: &str) -> std::io::Result<Self> {
84        let me = Me::new(username)?;
85        validate_setup(false)?;
86        
87        validate_hash(hash)?;
88        if me.file_path.exists() {
89            return Err(std::io::Error::new(
90                std::io::ErrorKind::AlreadyExists,
91                format!("❌ Identity '{}' already exists.", username),
92            ));
93        }
94
95        let data = format!(r#"{{"identity":{{"username":"{}"}}}}"#, username);
96        let encrypted = me.encrypt(&data, hash)?;
97        fs::create_dir_all(me.file_path.parent().unwrap())?;
98        let mut file = File::create(&me.file_path)?;
99        file.write_all(&encrypted)?;
100        Ok(me)
101    }
102
103
104    /// Encrypts the provided plaintext with the given hash.
105    pub fn encrypt(&self, plaintext: &str, hash: &str) -> std::io::Result<Vec<u8>> {
106        validate_hash(hash)?;
107        let key_hash = Sha256::digest(hash.as_bytes());
108        let key = Key::<Aes256Gcm>::from_slice(&key_hash);
109        let cipher = Aes256Gcm::new(key);
110        let nonce_bytes: [u8; 12] = rand::random();
111        let nonce = Nonce::from_slice(&nonce_bytes);
112        let ciphertext = cipher.encrypt(nonce, plaintext.as_bytes())
113            .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Encryption failed"))?;
114
115        let mut result = nonce_bytes.to_vec();
116        result.extend(ciphertext);
117        Ok(result)
118    }
119
120    /// Decrypts the provided encrypted data using the given hash.
121    pub fn decrypt(&self, data: &[u8], hash: &str) -> Result<String, ()> {
122        if validate_hash(hash).is_err() {
123            return Err(());
124        }
125        if data.len() < 12 {
126            return Err(());
127        }
128
129        let key_hash = Sha256::digest(hash.as_bytes());
130        let key = Key::<Aes256Gcm>::from_slice(&key_hash);
131        let cipher = Aes256Gcm::new(key);
132        let (nonce_bytes, ciphertext) = data.split_at(12);
133        let nonce = Nonce::from_slice(nonce_bytes);
134        let decrypted = cipher.decrypt(nonce, ciphertext).map_err(|_| ())?;
135        String::from_utf8(decrypted).map_err(|_| ())
136    }
137
138    /// Lists all existing identity files.
139    pub fn list() -> std::io::Result<Vec<String>> {
140        validate_setup(false)?;
141        let home_dir = env::var("HOME").unwrap_or_else(|_| ".".to_string());
142        let dir_path = PathBuf::from(format!("{}/.this/me", home_dir));
143
144        if !dir_path.exists() {
145            return Ok(vec![]);
146        }
147
148        let mut identities = vec![];
149        for entry in fs::read_dir(dir_path)? {
150            let entry = entry?;
151            if let Some(name) = entry.file_name().to_str() {
152                if name.ends_with(".me") {
153                    identities.push(name.trim_end_matches(".me").to_string());
154                }
155            }
156        }
157
158        Ok(identities)
159    }
160
161    /// Displays the decrypted content of the identity file.
162    pub fn show(&self, password: &str) -> std::io::Result<()> {
163        validate_setup(false)?;
164        let contents = fs::read(&self.file_path)?;
165        let json = self.decrypt(&contents, password).map_err(|_| {
166            std::io::Error::new(std::io::ErrorKind::InvalidData, "❌ Failed to decrypt. Wrong password?")
167        })?;
168
169        match serde_json::from_str::<serde_json::Value>(&json) {
170            Ok(parsed) => {
171                println!("{}", serde_json::to_string_pretty(&parsed)?);
172            }
173            Err(_) => {
174                println!("{}", json);
175            }
176        }
177
178        Ok(())
179    }
180
181    /// Changes the encryption password (hash) of the identity file.
182    pub fn change_hash(&self, old_hash: &str, new_hash: &str) -> std::io::Result<()> {
183        validate_hash(new_hash)?;
184
185        let contents = fs::read(&self.file_path)?;
186        let decrypted = self.decrypt(&contents, old_hash)
187            .map_err(|_| std::io::Error::new(std::io::ErrorKind::PermissionDenied, "❌ Old password incorrect."))?;
188
189        let encrypted = self.encrypt(&decrypted, new_hash)?;
190        let mut file = File::create(&self.file_path)?;
191        file.write_all(&encrypted)?;
192        println!("🔐 Password for '{}' successfully updated.", self.username);
193        Ok(())
194    }
195}