Expand description

PBKDF2 derivation and verification.

Use derive to derive PBKDF2 outputs. Use verify to verify secret against previously-derived outputs.

PBKDF2 is specified in RFC 2898 Section 5.2 with test vectors given in RFC 6070. See also NIST Special Publication 800-132.

Examples

Password Database Example

use ring::{digest, pbkdf2};
use std::{collections::HashMap, num::NonZeroU32};

static PBKDF2_ALG: pbkdf2::Algorithm = pbkdf2::PBKDF2_HMAC_SHA256;
const CREDENTIAL_LEN: usize = digest::SHA256_OUTPUT_LEN;
pub type Credential = [u8; CREDENTIAL_LEN];

enum Error {
    WrongUsernameOrPassword
}

struct PasswordDatabase {
    pbkdf2_iterations: NonZeroU32,
    db_salt_component: [u8; 16],

    // Normally this would be a persistent database.
    storage: HashMap<String, Credential>,
}

impl PasswordDatabase {
    pub fn store_password(&mut self, username: &str, password: &str) {
        let salt = self.salt(username);
        let mut to_store: Credential = [0u8; CREDENTIAL_LEN];
        pbkdf2::derive(PBKDF2_ALG, self.pbkdf2_iterations, &salt,
                       password.as_bytes(), &mut to_store);
        self.storage.insert(String::from(username), to_store);
    }

    pub fn verify_password(&self, username: &str, attempted_password: &str)
                           -> Result<(), Error> {
        match self.storage.get(username) {
           Some(actual_password) => {
               let salt = self.salt(username);
               pbkdf2::verify(PBKDF2_ALG, self.pbkdf2_iterations, &salt,
                              attempted_password.as_bytes(),
                              actual_password)
                    .map_err(|_| Error::WrongUsernameOrPassword)
           },

           None => Err(Error::WrongUsernameOrPassword)
        }
    }

    // The salt should have a user-specific component so that an attacker
    // cannot crack one password for multiple users in the database. It
    // should have a database-unique component so that an attacker cannot
    // crack the same user's password across databases in the unfortunate
    // but common case that the user has used the same password for
    // multiple systems.
    fn salt(&self, username: &str) -> Vec<u8> {
        let mut salt = Vec::with_capacity(self.db_salt_component.len() +
                                          username.as_bytes().len());
        salt.extend(self.db_salt_component.as_ref());
        salt.extend(username.as_bytes());
        salt
    }
}

fn main() {
    // Normally these parameters would be loaded from a configuration file.
    let mut db = PasswordDatabase {
        pbkdf2_iterations: NonZeroU32::new(100_000).unwrap(),
        db_salt_component: [
            // This value was generated from a secure PRNG.
            0xd6, 0x26, 0x98, 0xda, 0xf4, 0xdc, 0x50, 0x52,
            0x24, 0xf2, 0x27, 0xd1, 0xfe, 0x39, 0x01, 0x8a
        ],
        storage: HashMap::new(),
    };

    db.store_password("alice", "@74d7]404j|W}6u");

    // An attempt to log in with the wrong password fails.
    assert!(db.verify_password("alice", "wrong password").is_err());

    // Normally there should be an expoentially-increasing delay between
    // attempts to further protect against online attacks.

    // An attempt to log in with the right password succeeds.
    assert!(db.verify_password("alice", "@74d7]404j|W}6u").is_ok());
}

Structs

A PBKDF2 algorithm.

Statics

PBKDF2 using HMAC-SHA1.
PBKDF2 using HMAC-SHA256.
PBKDF2 using HMAC-SHA384.
PBKDF2 using HMAC-SHA512.

Functions

Fills out with the key derived using PBKDF2 with the given inputs.
Verifies that a previously-derived (e.g., using derive) PBKDF2 value matches the PBKDF2 value derived from the other inputs.