~cyborg/crypt

0d2c6fadfa130f8b4e426593c26ee4d9fff42c29 — christian-cleberg 4 months ago ba26786
implement cryptography
4 files changed, 162 insertions(+), 52 deletions(-)

M Cargo.lock
M Cargo.toml
M README.md
M src/main.rs
M Cargo.lock => Cargo.lock +113 -0
@@ 31,6 31,18 @@ dependencies = [
]

[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"

[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"

[[package]]
name = "bitflags"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 55,6 67,18 @@ dependencies = [
]

[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"

[[package]]
name = "cc"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 104,6 128,7 @@ version = "0.1.1"
dependencies = [
 "clap",
 "cli-table",
 "fernet",
 "rand",
 "rusqlite",
 "uuid",


@@ 144,6 169,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"

[[package]]
name = "fernet"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93804560e638370a8be6d59ce71ed803e55e230abdbf42598e666b41adda9b1f"
dependencies = [
 "base64",
 "byteorder",
 "getrandom",
 "openssl",
 "zeroize",
]

[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
 "foreign-types-shared",
]

[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"

[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 222,6 275,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"

[[package]]
name = "openssl"
version = "0.10.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a"
dependencies = [
 "bitflags 1.3.2",
 "cfg-if",
 "foreign-types",
 "libc",
 "once_cell",
 "openssl-sys",
]

[[package]]
name = "openssl-sys"
version = "0.9.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1996d2d305e561b70d1ee0c53f1542833f4e1ac6ce9a6708b6ff2738ca67dc82"
dependencies = [
 "autocfg",
 "cc",
 "libc",
 "pkg-config",
 "vcpkg",
]

[[package]]
name = "pkg-config"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 348,6 428,18 @@ dependencies = [
]

[[package]]
name = "synstructure"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "unicode-xid",
]

[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 440,3 532,24 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

[[package]]
name = "zeroize"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "377db0846015f7ae377174787dd452e1c5f5a9050bc6f954911d01f116daa0cd"
dependencies = [
 "zeroize_derive",
]

[[package]]
name = "zeroize_derive"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "synstructure",
]

M Cargo.toml => Cargo.toml +2 -1
@@ 18,4 18,5 @@ clap = "2.27"
uuid = { version = "0.8", features = ["v4"] }
rand = "0.8.4"
rusqlite = "0.25.3"
cli-table = "0.4"
\ No newline at end of file
cli-table = "0.4"
fernet = "0.1"
\ No newline at end of file

M README.md => README.md +6 -13
@@ 84,16 84,6 @@ crypt ARGUMENT [VALUES]
      <td>--purge</td>
      <td>Purge all accounts and delete the vault</td>
    </tr>
    <tr>
      <td>-ex</td>
      <td>--encrypt</td>
      <td>Encrypt the vault database</td>
    </tr>
    <tr>
      <td>-dx</td>
      <td>--decrypt</td>
      <td>Decrypt the vault database</td>
    </tr>
  </tbody>
</table>



@@ 111,11 101,14 @@ Any and all contributions are welcome. Feel free to fork the project, add featur
- [x] Create an empty database or file, if not created yet
- [x] Save new accounts to database or file
- [x] Pretty-print all saved accounts
- [ ] Allow user encryption of database or file
- [ ] Allow user-created keys to automatically encrypt/decrypt the database or file
- [ ] Allow editing of a saved account
- [ ] Allow deletion of a saved account
- [ ] Allow purging the database
- [x] Allow user encryption of database or file
- [x] Allow user-created keys to automatically encrypt/decrypt the database or file
- [ ] Create test suite
- [ ] Publish to crates.io when the package is in a minimally-usable state
- [ ] Restructure and format code according to best practices
- [ ] Restructure and format code according to best practices (dead code, unused imports, etc.)

## Development


M src/main.rs => src/main.rs +41 -38
@@ 1,11 1,13 @@
use clap::{Arg, App, SubCommand};
use cli_table::{format::Justify, print_stdout, Cell, Style, Table};
use rand::{thread_rng, Rng, distributions::{Alphanumeric, Uniform, Standard}, seq::IteratorRandom};
use fernet::Fernet;
use rand::{thread_rng, Rng, distributions::{Alphanumeric, Uniform, Standard}, seq::IteratorRandom, RngCore, CryptoRng};
use rusqlite::{Connection, Result};
use std::{fs::File, io::{self, Read, BufRead, BufReader}, str};
use std::{fs, io::{self, Read, BufRead, BufReader}, str};
use uuid::Uuid;

pub const SQLITE_DB: &str = "vault.sqlite";
pub const KEY_FILE: &str = "vault.key";
pub const UPPERCASE: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
pub const LOWERCASE: &str = "abcdefghijklmnopqrstuvwxyz";
pub const NUMBERS: &str = "0123456789";


@@ 21,12 23,6 @@ struct Account {
    url: String,
}

impl Account {
    fn update_password(&mut self, new_password: String) {
        self.password = new_password;
    }
}

// Read user input as a string
fn read_string() -> String {
    let mut input = String::new();


@@ 131,12 127,18 @@ fn read_db() -> Result<()> {
        })
    })?;

    // Loop through saved accounts and collec them in a vec
    // Loop through saved accounts and collect them in a vec
    let mut tmp_table = vec![];
    for account in accounts {
        let tmp_account = account.unwrap();
        tmp_table.push(
            vec![tmp_account.uuid.cell(), tmp_account.title.cell(), tmp_account.username.cell(), tmp_account.password.cell(), tmp_account.url.cell()]
            vec![
                decrypt(tmp_account.uuid).cell(),
                decrypt(tmp_account.title).cell(),
                decrypt(tmp_account.username).cell(),
                decrypt(tmp_account.password).cell(),
                decrypt(tmp_account.url).cell(),
            ]
        );
    }



@@ 205,11 207,11 @@ fn new() {

    // Generate an Account struct
    let account = Account {
        uuid: uuid.to_string(),
        title,
        username,
        password,
        url,
        uuid: encrypt(uuid.to_string()),
        title: encrypt(title),
        username: encrypt(username),
        password: encrypt(password),
        url: encrypt(url),
    };

    // Create the database, if necessary, and insert data


@@ 224,29 226,44 @@ fn list() -> Result<()> {
    Ok(())
}

// Edit a saved account
// TODO: Edit a saved account
fn edit() {
    println!();
}

// Delete a saved account
// TODO: Delete a saved account
fn delete() {
    println!();
}

// Delete all saved accounts and delete the vault file
// TODO: Delete all saved accounts and delete the vault file
fn purge() {
    println!();
}

// Encrypt the vault file
fn encrypt() {
    println!();
// Encrypt plaintext using a generated key file
fn encrypt(plaintext: String) -> String {
    let key_exists: bool = std::path::Path::new(KEY_FILE).exists();
    let mut key = String::from("");
    if key_exists {
        key = fs::read_to_string(KEY_FILE).expect("Unable to read saved key file.");
    } else {
        key = fernet::Fernet::generate_key();
        fs::write(KEY_FILE, &key).expect("Unable to save key to file.");
        println!("Key file has been written to: {}. DO NOT DELETE OR MODIFY THIS FILE.", KEY_FILE);
    }
    let fernet = fernet::Fernet::new(&key).unwrap();
    let ciphertext = fernet.encrypt(plaintext.as_ref());
    ciphertext
}

// Decrypt the vault file
fn decrypt() {
    println!();
// Decrypt ciphertext using a saved key file
fn decrypt(ciphertext: String) -> String {
    let key = fs::read_to_string(KEY_FILE).expect("Unable to read saved key file.");
    let fernet = fernet::Fernet::new(&key).unwrap();
    let decrypted_plaintext = fernet.decrypt(&ciphertext).expect("Error decrypting data - the key file may have been modified or deleted.");
    let plaintext = String::from_utf8(decrypted_plaintext).unwrap();
    plaintext
}

// Interpret user commands


@@ 283,16 300,6 @@ fn main() {
            .long("purge")
            .help("Purge all saved accounts")
            .takes_value(false))
        .arg(Arg::with_name("encrypt")
            .short("ex")
            .long("encrypt")
            .help("Encrypt the vault")
            .takes_value(false))
        .arg(Arg::with_name("decrypt")
            .short("dx")
            .long("decrypt")
            .help("Decrypt the vault")
            .takes_value(false))
        .get_matches();

    if matches.is_present("new") {


@@ 305,9 312,5 @@ fn main() {
        delete();
    } else if matches.is_present("purge") {
        purge();
    } else if matches.is_present("encrypt") {
        encrypt();
    } else if matches.is_present("decrypt") {
        decrypt();
    }
}