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 fn encrypt(
282 data: &[u8],
283 key: &[u8],
284 iv: &[u8],
285 ) -> Result<Vec<u8>, symmetriccipher::SymmetricCipherError> {
286 let mut encryptor =
289 aes::cbc_encryptor(aes::KeySize::KeySize256, key, iv, blockmodes::PkcsPadding);
290
291 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 loop {
322 let result = encryptor.encrypt(&mut read_buffer, &mut write_buffer, true)?;
323
324 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 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}