rustotpony/
lib.rs

1extern crate age;
2extern crate base32;
3extern crate crypto;
4extern crate dirs;
5extern crate rand;
6extern crate serde_json;
7extern crate sha2;
8extern crate totp_lite;
9
10#[macro_use]
11extern crate serde_derive;
12
13use crypto::buffer::{BufferResult, ReadBuffer, WriteBuffer};
14use crypto::{aes, blockmodes, buffer, symmetriccipher};
15use sha2::{Digest, Sha256};
16
17use totp_lite::{totp_custom, Sha1, DEFAULT_STEP};
18
19use rand::prelude::*;
20
21use std::collections::HashMap;
22use std::fs::{create_dir_all, File, OpenOptions};
23use std::io::ErrorKind;
24use std::io::{Read, Write};
25use std::path::{Path, PathBuf};
26use std::time::SystemTime;
27
28const DATABASE_VERSION: u8 = 1;
29
30pub struct RusTOTPony<DB: Database> {
31    database: DB,
32    applications: HashMap<String, GenApp>,
33}
34
35impl<DB: Database> RusTOTPony<DB> {
36    pub fn new(db: DB) -> RusTOTPony<DB> {
37        RusTOTPony {
38            applications: db.get_applications(),
39            database: db,
40        }
41    }
42
43    pub fn create_application(
44        &mut self,
45        name: &str,
46        username: &str,
47        secret: &str,
48    ) -> Result<(), String> {
49        if let Some(secret_bytes) = GenApp::base32_to_bytes(secret) {
50            let new_app = GenApp::new(name, username, secret, secret_bytes);
51            if self.applications.contains_key(name) {
52                Err(format!("Application with name '{}' already exists!", name))
53            } else {
54                self.applications.insert(String::from(name), new_app);
55                Ok(())
56            }
57        } else {
58            Err(String::from("Couldn't decode secret key"))
59        }
60    }
61
62    pub fn delete_application(&mut self, name: &str) -> Result<(), String> {
63        if self.applications.remove(name).is_some() {
64            Ok(())
65        } else {
66            Err(format!(
67                "Application with the name '{}' doesn't exist",
68                name
69            ))
70        }
71    }
72
73    pub fn rename_application(&mut self, name: &str, newname: &str) -> Result<(), String> {
74        if let Some(app) = self.applications.get_mut(name) {
75            app.name = String::from(newname);
76            Ok(())
77        } else {
78            Err(format!("Application '{}' wasn't found", name))
79        }
80    }
81
82    pub fn get_applications(&self) -> Result<&HashMap<String, GenApp>, String> {
83        if self.applications.is_empty() {
84            Err(String::from("There are no applications"))
85        } else {
86            Ok(&self.applications)
87        }
88    }
89
90    pub fn get_application(&self, name: &str) -> Result<&GenApp, String> {
91        if let Some(app) = self.applications.get(name) {
92            Ok(app)
93        } else {
94            Err(format!("Application '{}' wasn't found", name))
95        }
96    }
97
98    pub fn delete_all_applications(&mut self) {
99        self.applications = HashMap::new();
100    }
101
102    pub fn flush(&self) {
103        self.database.save_applications(&self.applications);
104    }
105}
106
107pub trait Database {
108    fn get_applications(&self) -> HashMap<String, GenApp>;
109    fn save_applications(&self, applications: &HashMap<String, GenApp>);
110}
111
112macro_rules! impl_database_trait {
113    ($type:ty) => {
114        impl Database for $type {
115            fn get_applications(&self) -> HashMap<String, GenApp> {
116                let db_content = self.read_database_file();
117                db_content.content.applications
118            }
119
120            fn save_applications(&self, applications: &HashMap<String, GenApp>) {
121                let mut db_content = Self::get_empty_schema();
122                db_content.content.applications = applications.clone();
123                self.save_database_file(db_content);
124            }
125        }
126    };
127}
128
129impl_database_trait!(JsonDatabase);
130impl_database_trait!(AgeJsonDatabase);
131
132#[derive(Serialize, Deserialize)]
133pub struct JsonDatabaseSchema {
134    version: u8,
135    content: DatabaseContentSchema,
136}
137
138#[derive(Serialize, Deserialize)]
139struct DatabaseContentSchema {
140    applications: HashMap<String, GenApp>,
141}
142
143pub struct JsonDatabase {
144    file_path: PathBuf,
145    secret: String,
146}
147
148pub struct AgeJsonDatabase {
149    file_path: PathBuf,
150    secret: String,
151}
152
153const IV_SIZE: usize = 16;
154const KEY_SIZE: usize = 32;
155pub trait JsonDatabaseTrait {
156    fn get_file_path(&self) -> &PathBuf;
157    fn get_secret(&self) -> String;
158
159    fn new(path: PathBuf, secret: String) -> Self;
160
161    fn encrypt_data(data: &str, key: &str) -> Vec<u8>;
162
163    fn decrypt_data(data: &[u8], key: &str) -> String;
164
165    fn read_database_file(&self) -> JsonDatabaseSchema {
166        let data = match std::fs::read(self.get_file_path()) {
167            Ok(d) => d,
168            Err(ref err) if err.kind() == ErrorKind::NotFound => return Self::get_empty_schema(),
169            Err(err) => panic!("There was a problem opening file: {:?}", err),
170        };
171        let decrypted_data = Self::decrypt_data(&data, self.get_secret().as_str());
172        serde_json::from_str(decrypted_data.as_str())
173            .expect("Couldn't parse JSON from database file")
174    }
175
176    fn create_iv() -> Vec<u8> {
177        let mut iv = vec![0; IV_SIZE];
178        let mut rng = rand::thread_rng();
179        rng.fill_bytes(&mut iv);
180        iv
181    }
182
183    fn save_database_file(&self, content: JsonDatabaseSchema) {
184        let mut file = match self.open_database_file_for_write() {
185            Ok(f) => f,
186            Err(ref err) if err.kind() == ErrorKind::NotFound => self
187                .create_database_file()
188                .expect("Couldn't create database file"),
189            Err(err) => panic!("Couldn't open database file: {:?}", err),
190        };
191        let data = serde_json::to_string(&content).expect("Couldn't serialize data to JSON");
192        let encrypted_data = Self::encrypt_data(&data, self.get_secret().as_str());
193        file.write_all(&encrypted_data)
194            .expect("Couldn't write data to database file");
195    }
196
197    fn create_database_file(&self) -> Result<File, std::io::Error> {
198        let dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
199        if let Some(parent_dir) = Path::new(&self.get_file_path()).parent() {
200            let dir = dir.join(parent_dir);
201            create_dir_all(dir)?;
202        }
203        self.open_database_file_for_write()
204    }
205
206    fn open_database_file_for_write(&self) -> Result<File, std::io::Error> {
207        OpenOptions::new()
208            .write(true)
209            .truncate(true)
210            .create(true)
211            .open(self.get_file_path())
212    }
213
214    fn get_empty_schema() -> JsonDatabaseSchema {
215        JsonDatabaseSchema {
216            version: DATABASE_VERSION,
217            content: DatabaseContentSchema {
218                applications: HashMap::new(),
219            },
220        }
221    }
222}
223
224macro_rules! impl_json_database_trait {
225    ($type:ty) => {
226        impl JsonDatabaseTrait for $type {
227            fn new(path: PathBuf, secret: String) -> Self {
228                Self {
229                    file_path: path,
230                    secret,
231                }
232            }
233
234            fn get_file_path(&self) -> &PathBuf {
235                &self.file_path
236            }
237
238            fn get_secret(&self) -> String {
239                self.secret.clone()
240            }
241
242            fn encrypt_data(data: &str, key: &str) -> Vec<u8> {
243                <$type>::encrypt_data(data, key)
244            }
245
246            fn decrypt_data(data: &[u8], key: &str) -> String {
247                <$type>::decrypt_data(data, key)
248            }
249        }
250    };
251}
252
253impl_json_database_trait!(JsonDatabase);
254impl_json_database_trait!(AgeJsonDatabase);
255
256impl JsonDatabase {
257    fn form_secret_key(input: &str) -> [u8; KEY_SIZE] {
258        let mut hasher = Sha256::new();
259        hasher.update(input);
260        hasher.finalize().into()
261    }
262
263    fn encrypt_data(data: &str, key: &str) -> Vec<u8> {
264        let key = Self::form_secret_key(key);
265        let iv = Self::create_iv();
266        let encrypted_data =
267            Self::encrypt(data.as_bytes(), &key, &iv).expect("Couldn't encrypt data");
268        [&iv, &encrypted_data[..]].concat()
269    }
270
271    fn decrypt_data(data: &[u8], key: &str) -> String {
272        let key = Self::form_secret_key(key);
273        let iv = &data[..IV_SIZE];
274        String::from_utf8(Self::decrypt(&data[IV_SIZE..], &key, iv).expect("Couldn't decrypt data"))
275            .ok()
276            .unwrap()
277    }
278
279    // Encrypt a buffer with the given key and iv using
280    // AES-256/CBC/Pkcs encryption.
281    fn encrypt(
282        data: &[u8],
283        key: &[u8],
284        iv: &[u8],
285    ) -> Result<Vec<u8>, symmetriccipher::SymmetricCipherError> {
286        // Create an encryptor instance of the best performing
287        // type available for the platform.
288        let mut encryptor =
289            aes::cbc_encryptor(aes::KeySize::KeySize256, key, iv, blockmodes::PkcsPadding);
290
291        // Each encryption operation encrypts some data from
292        // an input buffer into an output buffer. Those buffers
293        // must be instances of RefReaderBuffer and RefWriteBuffer
294        // (respectively) which keep track of how much data has been
295        // read from or written to them.
296        let mut final_result = Vec::<u8>::new();
297        let mut read_buffer = buffer::RefReadBuffer::new(data);
298        let mut buffer = [0; 4096];
299        let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);
300
301        // Each encryption operation will "make progress". "Making progress"
302        // is a bit loosely defined, but basically, at the end of each operation
303        // either BufferUnderflow or BufferOverflow will be returned (unless
304        // there was an error). If the return value is BufferUnderflow, it means
305        // that the operation ended while wanting more input data. If the return
306        // value is BufferOverflow, it means that the operation ended because it
307        // needed more space to output data. As long as the next call to the encryption
308        // operation provides the space that was requested (either more input data
309        // or more output space), the operation is guaranteed to get closer to
310        // completing the full operation - ie: "make progress".
311        //
312        // Here, we pass the data to encrypt to the enryptor along with a fixed-size
313        // output buffer. The 'true' flag indicates that the end of the data that
314        // is to be encrypted is included in the input buffer (which is true, since
315        // the input data includes all the data to encrypt). After each call, we copy
316        // any output data to our result Vec. If we get a BufferOverflow, we keep
317        // going in the loop since it means that there is more work to do. We can
318        // complete as soon as we get a BufferUnderflow since the encryptor is telling
319        // us that it stopped processing data due to not having any more data in the
320        // input buffer.
321        loop {
322            let result = encryptor.encrypt(&mut read_buffer, &mut write_buffer, true)?;
323
324            // "write_buffer.take_read_buffer().take_remaining()" means:
325            // from the writable buffer, create a new readable buffer which
326            // contains all data that has been written, and then access all
327            // of that data as a slice.
328            final_result.extend(
329                write_buffer
330                    .take_read_buffer()
331                    .take_remaining()
332                    .iter()
333                    .copied(),
334            );
335
336            match result {
337                BufferResult::BufferUnderflow => break,
338                BufferResult::BufferOverflow => {}
339            }
340        }
341
342        Ok(final_result)
343    }
344
345    // Decrypts a buffer with the given key and iv using
346    // AES-256/CBC/Pkcs encryption.
347    fn decrypt(
348        encrypted_data: &[u8],
349        key: &[u8],
350        iv: &[u8],
351    ) -> Result<Vec<u8>, symmetriccipher::SymmetricCipherError> {
352        let mut decryptor =
353            aes::cbc_decryptor(aes::KeySize::KeySize256, key, iv, blockmodes::PkcsPadding);
354
355        let mut final_result = Vec::<u8>::new();
356        let mut read_buffer = buffer::RefReadBuffer::new(encrypted_data);
357        let mut buffer = [0; 4096];
358        let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);
359
360        loop {
361            let result = decryptor.decrypt(&mut read_buffer, &mut write_buffer, true)?;
362            final_result.extend(
363                write_buffer
364                    .take_read_buffer()
365                    .take_remaining()
366                    .iter()
367                    .copied(),
368            );
369            match result {
370                BufferResult::BufferUnderflow => break,
371                BufferResult::BufferOverflow => {}
372            }
373        }
374
375        Ok(final_result)
376    }
377}
378
379impl AgeJsonDatabase {
380    fn encrypt_data(data: &str, key: &str) -> Vec<u8> {
381        let encryptor =
382            age::Encryptor::with_user_passphrase(age::secrecy::Secret::new(key.to_owned()));
383
384        let mut encrypted = vec![];
385        let mut writer = encryptor.wrap_output(&mut encrypted).unwrap();
386        writer.write_all(data.as_bytes()).unwrap();
387        writer.finish().unwrap();
388
389        encrypted
390    }
391
392    fn decrypt_data(data: &[u8], key: &str) -> String {
393        let decryptor = match age::Decryptor::new(data).unwrap() {
394            age::Decryptor::Passphrase(d) => d,
395            _ => unreachable!(),
396        };
397
398        let mut decrypted = vec![];
399        let mut reader = decryptor
400            .decrypt(&age::secrecy::Secret::new(key.to_owned()), None)
401            .unwrap();
402        reader.read_to_end(&mut decrypted).unwrap();
403
404        String::from_utf8(decrypted).unwrap()
405    }
406}
407
408#[derive(Serialize, Deserialize, Debug, Clone)]
409pub struct GenApp {
410    name: String,
411    secret: String,
412    username: String,
413    secret_bytes: Vec<u8>,
414}
415
416impl GenApp {
417    fn new(name: &str, username: &str, secret: &str, secret_bytes: Vec<u8>) -> Self {
418        GenApp {
419            name: String::from(name),
420            secret: String::from(secret),
421            username: String::from(username),
422            secret_bytes,
423        }
424    }
425
426    pub fn get_name(&self) -> &str {
427        self.name.as_str()
428    }
429
430    pub fn get_secret(&self) -> &str {
431        self.secret.as_str()
432    }
433
434    pub fn get_username(&self) -> &str {
435        self.username.as_str()
436    }
437
438    pub fn get_code(&self) -> String {
439        Self::totp(&self.secret_bytes)
440    }
441
442    fn base32_to_bytes(secret: &str) -> Option<Vec<u8>> {
443        base32::decode(base32::Alphabet::Rfc4648 { padding: false }, secret)
444    }
445
446    fn totp(secret_bytes: &[u8]) -> String {
447        let seconds: u64 = SystemTime::now()
448            .duration_since(SystemTime::UNIX_EPOCH)
449            .unwrap()
450            .as_secs();
451        totp_custom::<Sha1>(DEFAULT_STEP, 6, secret_bytes, seconds)
452    }
453}