As far as I know, PBKDF2 is intended to be used to create a key of a required size from a variable-length password for use in symmetric encryption in the following manner (pseudocode):
//Encrypt
var Password = GetSomeUserInput();
var DataToEncrypt = GetSomeUserInput();
var Salt = CreateACryptographicallyStrongRandomSalt(sufficient length);
var DF = new Pbkdf2(Password, Salt, Iterations := sufficient iterations);
var Key = DF.MunchBytes(256); //AES256 key size is 256
var IV = DF.MunchBytes(128); //AES256 block size is 128
var EncryptedData = EncryptWithAes256(DataToEncrypt, Key, IV);
StoreData(Salt, EncryptedData);
//Decrypt
var Password = GetSomeUserInput();
var Salt, EncryptedData = RestoreData();
var DF = new Pbkdf2(Password, Salt, Iterations := sufficient iterations);
var Key = DF.MunchBytes(256);
var IV = DF.MunchBytes(128);
var DecryptedData = DecryptWithAes256(EncryptedData, Key, IV);
Is PBKDF2 also suitable for password verification like this? (pseudocode):
//Registration
var UserName = GetSomeUserInput();
var Password = GetSomeUserInput();
var Salt = CreateACryptographicallyStrongRandomSalt(sufficient length);
var DF = new Pbkdf2(Password, Salt, Iterations := sufficient iterations);
var Hash = DF.MunchBytes(sufficient length);
StoreData(UserName, Salt, Hash);
//Login
var UserName = GetSomeUserInput();
var Password = GetSomeUserInput();
var Salt, StoredHash = RestoreByUserName(UserName)
var DF = new Pbkdf2(Password, Salt, Iterations := sufficient iterations);
var RecomputedHash = DF.MunchBytes(sufficient length);
if (ConstantTimeEquals(StoredHash, RecomputedHash))
{
//OK
}
Please find a good source for your answer, as opposed to just writing "Yes it is."
If yes, what is the/a sufficient length for the stored hash and how is that length determined?
Ideally, the following should not make a difference for the answer, but if you need more context:
It's a run-of-the-mill browser->json->jetty->jdbc->mariadb website where I want users to be able to register an account and then log in using that account. Jetty is Java (hence the tag), but the same would apply to e.g. .NET and other languages/frameworks as well.
Yes, NIST uses PBKDF2 as an example of a suitable one-way hash for use in password authentication systems. Secure key derivation functions in general are recommended for this purpose.
However, potential improvements to PBKDF2 have been recognized, prompting development of a series of algorithms like bcrypt, Scrypt, and Argon2. These are designed to be resistant to hardware optimizations. So, while PBKDF2 may be adequate in many scenarios, additional security may be available.
In practice, I feel like the biggest risk is the use of bad passwords, followed by using too small a work factor. For the first, reject passwords using something like the Have I Been Pwned database. For the second, think big: 10s of thousands or even 100k iterations for PBKDF2. You could also consider designing your authentication database to allow gradual migration to a new algorithm (transparently as users log in) in case you want to change hash algorithms in the future.