PBKDF2 (Password-Based Key Derivation Function 2) is defined in RFC 2898 and generates a salted hash. Often this is used to create an encryption key from a defined password, and where it is not possible to reverse the password from the hashed value. It is used in TrueCrypt to generate the key required to read the header information of the encrypted drive, and which stores the encryption keys.
PBKDF2 in Rust |
Outline
In many applications we need to generate an encryption key. For this we could create a random key, but we would need to store it, and where it could be discovered. A typical method is thus to use a Key Derivation Function (KDF) with a salt value in order to generate a hashed value, and then use this hash to generate the encryption. As this method would be open to brute force, we often use a method which will slow the hashing process down, and defeat a hash cracker based on parallel processing. This is normally implemented using a hashing loop, and where we hash over a number of rounds. Typical methods are PBKDF2, bcrypt and scrypt, as these are more robust against default hash crackers. This page implements these methods with a given salt value, along with including HKDF, and which is not recommended for generating encryption keys.
In computing, we often need things to be fast, but sometimes we need to slow things down and create a problem that cannot be scaled onto parallel processors. One of these applications is in the hashing of a password. The fast hash crackers can process SHA-1 and SHA-256 hashes at rates that can be over 1 terahashes per second. That's 1,000 billion passwords tried every second. These crackers often run on Cloud-based systems and use GPUs with over 4,000 cores.
In many applications, we need to generate an encryption key. For this, we could create a random key, but we would need to store it, and where it could be discovered. A typical alternative method is thus to use a Key Derivation Function (KDF) with a salt value in order to generate a hashed value and then use this hash to generate the encryption key. As this method would be open to brute force, we often use a method that will slow the hashing process down, and defeat a hash cracker using parallel processing. This increased difficulity is normally implemented using a hashing loop, and where we hash over a number of rounds. Typical methods are PBKDF2 (Password-Based Key Derivation Function 2), bcrypt and scrypt, as these are more robust against default hash crackers. A sample run here shows some of the speeds:
Method: Hashes per second SHA-1: 588235 SHA-256: 602409 SHA-512: 75216 MD5: 606060 SHA-3 (224-bit): 331674 DES: 396 Bcrypt: 215 APR1: 692 PBKDF2 (SHA1): 4884 PBKDF2 (SHA-256): 9648 LM Hash: 2480 NT Hash: 33112 MS DCC: 53361 LDAP (MD5): 11928 LDAP (SHA1): 48828 MS SQL 2000: 16654 MySQL: 30769 Oracle 10: 201 Postgres (MD5): 23900 Cisco PIX: 16297 Cisco Type 7: 16223 Murmur: 2499999
An outline of the Rust code is:
extern crate crypto; use crypto::pbkdf2::{pbkdf2_check, pbkdf2_simple}; use std::env; fn main() { let mut c=1; let mut inpass = String::from("Hello world!"); let args: Vec<String> = env::args().collect(); if args.len() >1 { inpass = args[1].clone();} if args.len() >2 { c = args[2].trim().parse().unwrap(); } if (c>10000) {return;} let pass: &str = &inpass[..]; let out=pbkdf2_simple(pass,c).unwrap().to_string(); println!("Password: {}",pass); println!("Count: {}",c); println!("\nPBKDF2: {}",out); let rtn=pbkdf2_check(pass, &out).unwrap().to_string(); if rtn=="true" { println!("Password check correct"); } else { println!("Password not correct"); } }
Finally we simply build with:
cargo build
A sample run is:
Password: Qwerty123 Count: 10 PBKDF2: $rpbkdf2$0$AAAACg==$nZognaeZC+9qywLBOgTtKQ==$6x3YtPcJmqmrwphYH3Kmx6BHb4I640pipp9hnxYbens=$ Password check correct