Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Derive a 32-byte key from a password deterministically in PHP

Today I learned that "password" tends to mean a memorizable string of an arbitrary number of characters, while "key" means a highly random string of bits (of a specific length based on the encryption algorithm used).

And so today I first heard of the concept of a Key derivation function.

I'm confused about how to derive a 32-byte key from a password of arbitrary length (in PHP).

The following approach works but ignores the instruction of "[The salt] should be generated randomly" (so does Sodium):

$salt = 'this salt remains constant';
$iterations = 10;
$length = 32;
$aesKey = hash_pbkdf2('sha256', $somePasswordOfArbitraryLength, $salt, $iterations, $length, false);

The following approach also works but doesn't quite feel right either:

$hash = password_hash($somePasswordOfArbitraryLength, PASSWORD_BCRYPT, ['cost' => $iterations]);
$aesKey = substr($hash, -$length);//this smells weird

With all of my searching, I'm surprised I haven't found an "official" way in PHP to derive a 32-byte key from a password deterministically.

How should I do it?

P.S. I'm using Laravel in PHP and want to use AES-256-CBC encryption like this:

$encrypter = new \Illuminate\Encryption\Encrypter($aesKey, 'AES-256-CBC');
$encryptedText = $encrypter->encrypt($text);

Laravel's encryption helper (e.g. Crypt::encryptString('Hello world.')) seems unfit for my requirements since I want each user's data to be encrypted separately based on each individual's password.

Whatever key derivation function I use needs produce the same key every time since I'll be using symmetric encryption to then decrypt strings that that user had encrypted with that key.

P.P.S. Thanks to questions 1, 2, and 3 for introducing certain concepts to me.

like image 364
Ryan Avatar asked Aug 10 '18 17:08

Ryan


1 Answers

For hash-pbkdf2 you say:

"The following approach works but ignores the instruction of "[The salt] should be generated randomly"

Well, the fix to that is to do generate the salt randomly, and store it with the ciphertext. See this question for methods on how to generate secure random bytes within PHP. The output can then be used as key to encrypt; of course the key will always be regenerated using the stored salt and memorized password, and doesn't need to be stored. Note that keys consist of raw bytes; it's probably best to retrieve a raw key from hash-pbkdf2 (the last parameter).

Note that the iteration count should be as high as possible. Normally 100,000 or so is considered optimal nowadays, but the higher the more secure. It takes about as much time for an attacker to calculate the resulting key for each password, and as passwords only contain 30 to 60 bits (for good passwords) it really helps against dictionary attacks.

like image 73
Maarten Bodewes Avatar answered Sep 30 '22 14:09

Maarten Bodewes