rfc:hash_pbkdf2

Request for Comments: Adding hash_pbkdf2 Function

This RFC proposes adding a hash_pbkdf2 function to the hash package

Introduction

The purpose of this RFC is to add the PBKDF2 algorithm to the available hashing functions as a C implementation.

Why do we need PBKDF2?

PBKDF2 is defined in RFC2898 as a method for implementing password based cryptographic needs. These needs can include password storage, password derivation into a key (for encryption) or secure signatures. Additionally, it's NIST Recommended for password storage.

Adding a core implementation of the PBKDF2 algorithm will enable PHP projects to utilize a fast implementation of the algorithm, putting them on a more level ground for attackers. Since the C implementation is more efficient, more rounds can be computed for the same computational cost compared to a PHP land implementation. This enables higher iteration counts to be used, providing more security with less impact to the overall performance of the application.

Projects and Software That Currently Use PBKDF2

  • WPA and WPA2 for key derivation from password
  • OpenDocument encryption (OpenOffice.org)
  • WinZip AES encryption
  • 1Password
  • LastPass
  • Apple iOS
  • Blackberry Backup Encryption
  • Django Python Framework

$algo

The way hash_pbkdf2 is written, any currently supported hash_algos() algorithm can be used as the base for the algorithm. This means that it's up to the developer to choose the appropriate algorithm to use when using the function. Here are a few of the popular algorithms and some recomendations around them. It should be noted that any cryptographic hash algorithm that's supported can be used successfully with PBKDF2 (CRC32 is *not* cryptographic, therefore it should not be used).

  1. SHA512 - This is currently one of the strongest algorithms available in PHP. It makes a good primitive for *hash_pbkdf2*
  2. SHA256 - This is also plenty strong enough for use as the basis for PBKDF2.

A note on other popular algorithms: SHA1 and MD5 - Both are actually strong enough for effective use in PBKDF2. The reason is that the known attack vectors against the algorithm require knowledge of the input string being hashed. Therefore, an iterated algorithm such as PBKDF2 will be immune to the known attack vectors. That means it's OK to use for this task. With that said, the recommended approach is to use SHA512 or SHA256 instead, as the base algorithms are stronger. But it's not necessarily *bad* to use SHA1 or MD5.

$salt

The salt parameter should be a random string containing at least 64 bits of entropy. That means when generated from a function like *mcrypt_create_iv*, at least 8 bytes long. But for salts that consist of only *a-zA-Z0-9* (or are base_64 encoded), the minimum length should be at least 11 characters. It should be generated random for each password that's hashed, and stored along side the generated key.

$iterations

The iterations parameter provides the ability to *tune* the algorithm for different servers and needs. For most web uses, a minimum value of *1000* is recommended. However, as hardware varies greatly, testing should be done to find an iteration count that yields a function runtime of between 0.1 and 0.5 seconds (depending again on application). On higher end servers, this can be as much as 20,000 to 50,000 iterations (also depending on the hash algo used).

It's better to use the highest iteration count possible, as it will only increase the resistance to brute forcing.

$length

The length parameter indicates the length of the returned key. The default value for length is the length of the hash algo's output. However, this can be increased or decreased as necessary. For example, if you're using PBKDF2 to generate a password-based key for use in an encryption routine such as RIJNDAEL 256, which expects a 256 bit key, you would want to pass the length parameter as 256/8 (to get the byte length), and set *$raw_output* to *true*.

$raw_output

This parameter behaves just like the other *hash_* functions. If set to *true*, the function will return a binary string (chr 0-255). If set to *false*, the function will hex encode the result prior to returning it.

Example

Let's say you wanted to encrypt a file using a password. The password shouldn't be applied directly to the encryption function, but should be derived first.

encryption.php
<?php
$password = "foo";
$data = "testing this out";
$salt = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
$key = hash_pbkdf2("sha512", $password, $salt, 5000, 16, true);
// $key will be full-byte 0-255 data
 
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM);
 
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
?>

Or for storing passwords (BCrypt is recommended, but there are use-cases for PBKDF2, such as when NIST compliance is mandated):

password.php
<?php
$password = "foo";
$salt = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
$hash = hash_pbkdf2("sha512", $password, $salt, 5000, 32);
 
// $hash will be a hex encoded string
?>

Proposal and Patch

The proposal is to add a hash_pbkdf2() function to the hash extension in core. The proposed function has a signature:

string hash_pbkdf2(string algo, string password, string salt, int iterations [, int length = 0, bool raw_output = false])

The patch is available as a pull request to trunk.

This RFC intends to add this functionality to master (5.5) only.

Vote

Vote begins on 2012/07/02 and ends on 2012/07/09. This vote is to include the new function in master only (5.5).

rfc/hash_pbkdf2
Real name Yes? No?
dragoonis (dragoonis)  
hradtke (hradtke)  
ircmaxell (ircmaxell)  
kriscraig (kriscraig)  
lynch (lynch)  
nikic (nikic)  
rasmus (rasmus)  
shm (shm)  
stas (stas)  
Final result: 9 0
This poll has been closed.

More about PBKDF2

Changelog

  • 0.1 - Initial Version
  • 0.2 - Proposed
  • 0.3 - Added Parameter Information
  • 0.4 - Reworded to target master only, removing 5.4 section
  • 1.0 - Moving to Accepted state
rfc/hash_pbkdf2.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1