CLI utility for creating and storing passwords

Hello there! I made a pash crate and would like if you made a feedback of it and advice a better codestyle/crates.

What i've already implemented:

  • initialize necessary files (passwords.toml and config.toml)
  • generate password (optional: store it under specified category (you can create default one using config.toml))
  • basic docs (actually very bad)

I would very appreciate any help/contributing!

1 Like

At first glance, it looks like you’re storing the generated passwords without any sort of encryption. That means any malware that makes it onto your computer has immediate access to any account secured with those passwords, which is less than ideal. I suggest you read up on best practices for securing passwords and incorporate them into this project.

(I unfortunately can’t point you in the right direction here, as I haven’t done security programming recently enough to trust my own judgement.)

6 Likes

RIght now, it is alpha version (v 0.2.0). Thanks for pointing it out! I am going to use a passphrase for interacting with passwords.toml file content soon.

If suggest adding a way to see the entropy of the generated passwords. That would give users some idea how secure the generated passwords are. It might also be worth estimating the time needed to crack the password (under some assumptions) for users who aren't familiar with logarithms.

In case it's of interest, you could check out my memorable-word list crate, which can handle correct horse battery staple style password generation. It has a nice word list that I culled from a few linguistic studies that should include easy to remember words.

2 Likes

zxcvbn-rs is a Rust port of a password strength estimator from Dropbox that may be helpful for estimating password strength.

For password encryption you will want to encrypt them using a primary password that unlocks all the other stored passwords.

To check this password, you can use password hashing functions at GitHub - RustCrypto/password-hashes: Password hashing functions / KDFs to verify that the password is correct, though I'd recommend argon2. You can use the same hash function both for verifying the password and for key derivation, but you must choose different associated data/salt between verification and key derivation to ensure that an attacker cannot derive the key from the stored hash.

For the actual encryption, any of the AEADs at GitHub - RustCrypto/AEADs: Authenticated Encryption with Associated Data Algorithms: high-level encryption ciphers will be OK, though I'd recommend ChaCha20Poly1305. If you encrypt each password separately, make sure to include the password's metadata (e.g. its name and the associated username/email) as associated data so that everything is authenticated together. You must make sure to generate a unique random nonce for each encryption (including choosing a new nonce every time a password is changed/re-encrypted).

zeroize is also a useful crate to ensure cryptographic secrets are wiped from memory once they're no longer needed.

I have experience building cryptographic systems, so feel free to follow up with any other questions that you may have on how to make this secure.

2 Likes

Checked out zeroize and zxcvbn-rs! Will use them in project, thanks much. For file (de/en)cryption planned to use age, but might use some of AEAD's, thanks much!

I'd strongly suggest not using a password strength estimator, since you're creating the passwords and can easily compute the strength of the passwords based on the number of equally likely passwords that your algorithm will generate. It you change the algorithm to be more complicated, where the probability of a character depends on the previous characters, then you'd need to compute the Shannon entropy explicitly (rather than just taking a log of the number of possible passwords). But I'm any case you can and should compute the entropy exactly.

So, I'm stuck at this part. I want to encrypt password using primary one, but can't find proper way to do it. Can you give me a simple example? Though i found magic_crypt crate.

Below is some pseudocode for the key derivation from the password. I made an effort to use the same method names as from the crates I recommended, but you'll need to add type conversions/wrappers if you use this:

// Using default for demonstration purposes
// Exact parameters matter with the key derivation part: you want to ensure they stay the same between versions
// So that you derive the same key every time
let argon2 = Argon2::default();

let stored_password_hash = read_stored_hash();
let password_entered = get_entered_password();
if !argon2.verify_password(password_entered, &stored_password_hash).is_ok() {
    signal_incorrect_password();
    return;
}
// Password is correct now
// https://docs.rs/argon2/0.2.0/argon2/struct.Argon2.html#method.hash_password_into
// salt can be the same as previously stored but ad *must* be something different from the default
// This provides "domain separation" and prevents accidents that may occur otherwise
let mut key_val: [u8; 32] = [0; 32];
argon2.hash_password_into(alg, password_entered, salt, ad, &mut key_val).unwrap();

// Now actually decrypt a password when needed
let encrypted_password = get_encrypted_password();
// Nonce should be different for every encryption
let nonce = read_stored_nonce();
let cipher = ChaCha20Poly1305::new(key_val);
let plaintext_password_option = cipher.decrypt(nonce, encrypted_password);
if let Some(plaintext_password) == plaintext_password_option {
    // Do what you need to with the decrypted password
} else {
    // Oh no-an attacker tampered with the encrypted password
    fail_very_loudly();
}

For encrypting new passwords, you can use the cipher.encrypt method, making sure to generate a new nonce every time you do a new encryption.

I would not recommend magic_crypt because it uses CBC mode, which is not an authenticated encryption mode. CBC can provide confidentiality, but unlike AEAD ciphers, CBC mode provides no protections against an attacker changing the contents of the encrypted file.

On further thought, it would be better to encrypt each password separately instead of encrypting the entire file, so that tampering with one password will be detected while allowing the other untampered-with passwords to be read and decrypted correctly.

Feel free to follow up if you have additional questions.

2 Likes