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
14use serde::Serialize;
15use crate::verbs::Verbs;
16
17/// Represents a local encrypted identity used in dApps or Web3 environments.
18#[derive(Serialize)]
19pub struct Me {
20    pub username: String,
21    pub file_path: PathBuf,
22    pub data: Option<String>,
23    pub verbs: Verbs,
24}
25
26impl Me {
27    /// Initializes a Me instance without validating the username.
28    pub fn from_username_unchecked(username: &str) -> Self {
29        let home_dir = env::var("HOME").unwrap_or_else(|_| ".".to_string());
30        let file_path = PathBuf::from(format!("{}/.this/me/{}.me", home_dir, username));
31        Me {
32            username: username.to_string(),
33            file_path,
34            data: None,
35            verbs: Verbs::new(),
36        }
37    }
38
39    pub fn delete(username: &str, hash: &str) -> std::io::Result<()> {
40        let me = Me::from_username_unchecked(username);
41    
42        if !me.file_path.exists() {
43            return Err(std::io::Error::new(
44                std::io::ErrorKind::NotFound,
45                format!("❌ Identity '{}' does not exist.", username),
46            ));
47        }
48    
49        let contents = fs::read(&me.file_path)?;
50        let decrypted = me.decrypt(&contents, hash);
51    
52        match decrypted {
53            Ok(_) => {
54                fs::remove_file(&me.file_path)?;
55                println!("🗑️ Identity '{}' deleted.", username);
56                Ok(())
57            }
58            Err(_) => Err(std::io::Error::new(
59                std::io::ErrorKind::PermissionDenied,
60                "❌ Invalid password. Deletion aborted.",
61            )),
62        }
63    }
64
65    /// Initializes a new Me instance with the given username and hash.
66    pub fn new(username: &str, hash: &str) -> std::io::Result<Self> {
67        validate_username(username)?;
68        validate_hash(hash)?;
69        let home_dir = env::var("HOME").unwrap_or_else(|_| ".".to_string());
70        let file_path = PathBuf::from(format!("{}/.this/me/{}.me", home_dir, username));
71        if file_path.exists() {
72            return Err(std::io::Error::new(
73                std::io::ErrorKind::AlreadyExists,
74                format!("❌ Identity '{}' already exists.", username),
75            ));
76        }
77        Ok(Me {
78            username: username.to_string(),
79            file_path,
80            data: None,
81            verbs: Verbs::new(),
82        })
83    }
84
85    /// Encrypts the provided plaintext with the given hash.
86    pub fn encrypt(&self, plaintext: &str, hash: &str) -> std::io::Result<Vec<u8>> {
87        validate_hash(hash)?;
88        let key_hash = Sha256::digest(hash.as_bytes());
89        let key = Key::<Aes256Gcm>::from_slice(&key_hash);
90        let cipher = Aes256Gcm::new(key);
91        let nonce_bytes: [u8; 12] = rand::random();
92        let nonce = Nonce::from_slice(&nonce_bytes);
93        let ciphertext = cipher.encrypt(nonce, plaintext.as_bytes())
94            .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Encryption failed"))?;
95
96        let mut result = nonce_bytes.to_vec();
97        result.extend(ciphertext);
98        Ok(result)
99    }
100
101    /// Decrypts the provided encrypted data using the given hash.
102    pub fn decrypt(&self, data: &[u8], hash: &str) -> Result<String, ()> {
103        if validate_hash(hash).is_err() {
104            return Err(());
105        }
106        if data.len() < 12 {
107            return Err(());
108        }
109
110        let key_hash = Sha256::digest(hash.as_bytes());
111        let key = Key::<Aes256Gcm>::from_slice(&key_hash);
112        let cipher = Aes256Gcm::new(key);
113        let (nonce_bytes, ciphertext) = data.split_at(12);
114        let nonce = Nonce::from_slice(nonce_bytes);
115        let decrypted = cipher.decrypt(nonce, ciphertext).map_err(|_| ())?;
116        String::from_utf8(decrypted).map_err(|_| ())
117    }
118
119    /// Lists all existing identity files.
120    pub fn list() -> std::io::Result<Vec<String>> {
121        validate_setup(false)?;
122        let home_dir = env::var("HOME").unwrap_or_else(|_| ".".to_string());
123        let dir_path = PathBuf::from(format!("{}/.this/me", home_dir));
124
125        if !dir_path.exists() {
126            return Ok(vec![]);
127        }
128
129        let mut identities = vec![];
130        for entry in fs::read_dir(dir_path)? {
131            let entry = entry?;
132            if let Some(name) = entry.file_name().to_str() {
133                if name.ends_with(".me") {
134                    identities.push(name.trim_end_matches(".me").to_string());
135                }
136            }
137        }
138
139        Ok(identities)
140    }
141
142        /// Displays the decrypted contents of the identity file.
143        pub fn display(username: &str, hash: &str) -> std::io::Result<String> {
144            validate_username(username)?;
145            validate_hash(hash)?;
146            let home_dir = env::var("HOME").unwrap_or_else(|_| ".".to_string());
147            let file_path = PathBuf::from(format!("{}/.this/me/{}.me", home_dir, username));
148            if !file_path.exists() {
149                return Err(std::io::Error::new(
150                    std::io::ErrorKind::NotFound,
151                    format!("❌ Identity '{}' does not exist.", username),
152                ));
153            }
154    
155            let contents = fs::read(&file_path)?;
156            let me_temp = Me::from_username_unchecked(username);
157            let decrypted = me_temp.decrypt(&contents, hash)
158                .map_err(|_| std::io::Error::new(std::io::ErrorKind::PermissionDenied, "❌ Invalid password."))?;
159    
160            Ok(decrypted)
161        }
162
163
164    /// Changes the encryption password (hash) of the identity file.
165    pub fn change_hash(&self, old_hash: &str, new_hash: &str) -> std::io::Result<()> {
166        validate_hash(new_hash)?;
167
168        let contents = fs::read(&self.file_path)?;
169        let decrypted = self.decrypt(&contents, old_hash)
170            .map_err(|_| std::io::Error::new(std::io::ErrorKind::PermissionDenied, "❌ Old password incorrect."))?;
171
172        let encrypted = self.encrypt(&decrypted, new_hash)?;
173        let mut file = File::create(&self.file_path)?;
174        file.write_all(&encrypted)?;
175        println!("🔐 Password for '{}' successfully updated.", self.username);
176        Ok(())
177    }
178
179    /// Saves the current identity by encrypting and writing to file.
180    pub fn save(&self, hash: &str) -> std::io::Result<()> {
181        validate_hash(hash)?;
182        let data = serde_json::to_string(&self).map_err(|_| {
183            std::io::Error::new(std::io::ErrorKind::Other, "❌ Failed to serialize identity")
184        })?;
185        let encrypted = self.encrypt(&data, hash)?;
186        fs::create_dir_all(self.file_path.parent().unwrap())?;
187        let mut file = File::create(&self.file_path)?;
188        file.write_all(&encrypted)?;
189        println!("✅ Identity '{}' saved.", self.username);
190        Ok(())
191    }
192
193    pub fn have(&mut self, key: &str, value: &str) -> std::io::Result<()> {
194        self.verbs.have(key, value)
195    }
196
197    pub fn be(&mut self, key: &str, value: &str) -> std::io::Result<()> {
198        self.verbs.be(key, value)
199    }
200
201    pub fn at(&mut self, key: &str, value: &str) -> std::io::Result<()> {
202        self.verbs.at(key, value)
203    }
204
205    pub fn relate(&mut self, key: &str, value: &str) -> std::io::Result<()> {
206        self.verbs.relate(key, value)
207    }
208
209    pub fn react(&mut self, key: &str, value: &str) -> std::io::Result<()> {
210        self.verbs.react(key, value)
211    }
212
213    pub fn say(&mut self, key: &str, value: &str) -> std::io::Result<()> {
214        self.verbs.say(key, value)
215    }
216
217    /// Loads an existing identity by validating and preparing the instance.
218    pub fn load(username: &str, hash: &str) -> std::io::Result<Self> {
219        validate_username(username)?;
220        validate_hash(hash)?;
221        let home_dir = env::var("HOME").unwrap_or_else(|_| ".".to_string());
222        let file_path = PathBuf::from(format!("{}/.this/me/{}.me", home_dir, username));
223        if !file_path.exists() {
224            return Err(std::io::Error::new(
225                std::io::ErrorKind::NotFound,
226                format!("❌ Identity '{}' does not exist.", username),
227            ));
228        }
229        Ok(Me {
230            username: username.to_string(),
231            file_path,
232            data: None,
233            verbs: Verbs::new(),
234        })
235    }
236}