Which algorithm to use to derive the key? So that it is as far as possible from the password and that it always has 256 BIT?
The "best" is Argon2i which is available in PHP 7.2 (or via PECL). But as you may not have LibSodium, you have other options.
You have hash_pbkdf2
available in PHP 5.5, it uses PBKDF2. Briefly, PBKDF2 does a loop of HMAC, using a salt and user input, this does the key derivation. It has a customizable length, there is a maximum based on the algorithm used, except for cheating, as well as the HKDF (which is hash_hkdf
).
The existence of a number of iterations delays the discovery of the password, since dictionary (and related) attacks will slow down as the difficulty is greater and also "farther" from the original entry. By default, PBKDF2 uses SHA-1, but this is not recommended, use newer, more reliable hashing algorithms (SHA-3, SHA-2, or BLAKE).
string hash_pbkdf2 ( string $algo , string $password , string $salt , int $iterations [, int $length = 0 [, bool $raw_output = false ]] )
Example:
$sal = random_bytes(16); // 128-Bits de salt
$iteracoes = 250000; // 250 000 iterações
$tamanho = 256;
if($sal !== false){
$hash = hash_pbkdf2(
"sha384", // SHA-2 de 384 bits
$senha, // Sua senha
$sal,
$iteracoes,
$tamanho,
true);
}
However, the best option is in LibSodium, which is sodium_crypto_pwhash
, it uses the algorithm that was the winner of PHC . , Argon 2i. Its use is similar to PBKDF2, with the difference that we have more options than just choosing the amount of "iterations".
string sodium_crypto_pwhash(int $output_length, string $password, string $salt, int $opslimit, int $memlimit)
Example:
$sal = random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES);
$tamanho = 256;
$limiteOperacoes = 32; // 8 vezes maior que o padrão
$limiteMemoria = 1<<27; // 4 vezes maior que o padrão
if($sal !== false){
$hash = sodium_crypto_pwhash(
$tamanho,
$senha, // Sua senha
$sal,
$limiteOperacoes,
$limiteMemoria
);
}
The value of opslimit
and memlimit
can be obtained by SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE
and SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
, they are secure bases, the greater this supposedly the better. The first value will require more CPU power, more cycles. The second one will require more memory.
However, using weak passwords (alias 123456789) will not have a saving path.
You can also use SCrypt, but I have never used this in PHP and there are no plans to be natively supported in PHP.
Since passwords are sensitive, they should not stay in memory for a long time, so after the derivation use:
sodium_memzero($senha);
And if the user redefines his password, what happens to the key and the already encrypted data?
Simple. Simply "decrypt" the old password and then encrypt it with the result of deriving the new password.
If the user does not know the current password, which is currently used, it already was. It's better that he loses access to data than anyone else has. ;)
And finally, is this feasible? or would I rather generate a "Master" Key that only he would have to store and then report every time I log into a new device? Just like what 1Password does.
I do not know how 1Password works, but I very much doubt that it uses only a "Master Key". Possibly it has a password (chosen by the user) and a key, together they can be used to derive a new key or each one (the password and the key) are used for a different purpose, or the password is used to decrypt itself key. What I believe (this is totally a kick) is that everyone has a purpose, their password (and their derivation) is for encryption / decryption, while the master key is for authentication, to prove that you are who you say you are.
These are different purposes, but this is just a kick. Maybe 1Password has some public information on how it works, but I did not find it.