use aes_gcm::{ aead::{Aead, AeadCore, KeyInit, OsRng}, Aes256Gcm, Key, Nonce }; use std::io; static PASSWORD_TEST: &str = "MyPasswordStorage"; // will be added with nonce for storing each time pub struct Encoder { // will be stored with padding 32 bytes passphrase: String } impl Encoder { pub fn from(passphrase: &String) -> Encoder { // TODO: throw error if password longer that 32 bytes let padded_passphrase = Encoder::get_passhrase_with_padding(passphrase); Encoder { passphrase: padded_passphrase } } fn get_passhrase_with_padding(passphrase: &String) -> String { let mut result = passphrase.clone(); while result.len() < 32 { // use '-' for padding, can be anything else result.push('-'); } result } // TODO: error type pub fn encrypt(&self, plain_text: &String) -> io::Result { let key = Key::::from_slice(self.passphrase.as_bytes()); let nonce = Aes256Gcm::generate_nonce(&mut OsRng); let cipher = Aes256Gcm::new(key); // TODO: mar error inted of expect let ciphered_data = cipher.encrypt(&nonce, plain_text.as_bytes()) .map_err(|_| io::Error::new( io::ErrorKind::Other, "Failed to encrypt" ))?; // combining nonce and encrypted data together // for storage purpose let mut encrypted_data: Vec = nonce.to_vec(); encrypted_data.extend_from_slice(&ciphered_data); Ok(hex::encode(encrypted_data)) } // TODO: review error type pub fn decrypt(&self, encrypted_data: String) -> io::Result { let encrypted_data = hex::decode(encrypted_data) .map_err(|_| io::Error::new( io::ErrorKind::Other, "failed to decode hex string into vec" ))?; let key = Key::::from_slice(self.passphrase.as_bytes()); let (nonce_arr, ciphered_data) = encrypted_data.split_at(12); let nonce = Nonce::from_slice(nonce_arr); let cipher = Aes256Gcm::new(key); let plaintext = cipher.decrypt(nonce, ciphered_data) .map_err(|_| io::Error::new( io::ErrorKind::InvalidData, "failed to decrypt data" ))?; let result = String::from_utf8(plaintext) .map_err(|_| io::Error::new( io::ErrorKind::InvalidData, "failed to convert vector of bytes to string" ))?; Ok(result) } pub fn test_encoded_passphrase(&self, passphrase_encrypted: String) -> io::Result { // TODO: better way to check error let decrypted = match self.decrypt(passphrase_encrypted) { Ok(decrypted) => decrypted, Err(_) => return Ok(false) }; Ok(PASSWORD_TEST == decrypted) } pub fn get_encoded_test_passphrase(&self) -> io::Result { self.encrypt(&PASSWORD_TEST.to_string()) } }