-
Notifications
You must be signed in to change notification settings - Fork 27
Scrypt
scrypt is a password-based key derivation function created by Colin Percival, originally for the Tarsnap online backup service. The algorithm was specifically designed to make it costly to perform large-scale custom hardware attacks by requiring large amounts of memory.
scrypt accepts 4 parameters: the work factor which defines the CPU/memory cost, the resources which fine-tunes sequential memory read size and performance, the parallelisation level and the desired key length.
Name | Default value | Properties | Description |
---|---|---|---|
Work factor (N) | 65536 | hash.scrypt.workfactor |
Defines the CPU/memory cost. Must be a power of 2. |
Resources (r) | 8 | hash.scrypt.resources |
Defines the size of memory blocks. |
Parallelisation (p) | 1 | hash.scrypt.parallelization |
Defines the cost of parallelisation for an attacker. |
Output length | 64 | hash.scrypt.derivedKeyLength |
Defines the desired length of the final derived key |
You can define a singleton custom scrypt function by calling ScryptFunction.getInstance(int, int, int, int)
or ScryptFunction.getInstance(int, int, int)
ScryptFunction scrypt = ScryptFunction.getInstance(16384, 8, 2, 128);
In this case you have created a singleton instance which uses a work factor of 214, with 8 bytes memory blocks, a parallelisation cost of 2 and that produces a 128 bytes derived key.
Alternatively if you have defined the parameters in the psw4j.properties
file
ScryptFunction scrypt = AlgorithmFinder.getScryptInstance();
Additionally you can create a ScryptFunction
singleton instance from the hash, since all the parameters required are stored into it.
String hashed = "$s0$e0801$c2FsdA==$dFcxr0SE8yOWiWntoomu7gBbWQOsVh5kpayhIXl793NO+f1YQi4uIhg7ysup7Ie6DIO3oueI8Dzg2gZGNDPNpg==";
ScryptFunction scrypt = ScryptFunction.getInstanceFromHash(hashed);
Hashing passwords with scrypt can be done quite easily.
Hash hash = Password.hash(plainTextPassword).withScrypt();
hash.getResult(); // $s0$e0801$c2FsdA==$dFcxr0SE8yOWiWntoomu7gBbWQOsVh5kpayhIXl793NO+f1YQi4uIhg7ysup7Ie6DIO3oueI8Dzg2gZGNDPNpg==
This approach takes the parameters from psw4j.properties
file (like AlgorithmFinder.getScryptInstance()
).
However it's possible to use user-defined parameters as we saw previously
ScryptFunction myScrypt = ScryptFunction.getInstance(16384, 8, 2, 128);
Password.hash(plainTextPassword).with(myScrypt);
You have two way to define the cryptographic salt.
Method addSalt(String)
make you define the intended salt.
Hash hash = Password.hash(plainTextPassword).addSalt("a1b2c3d4").withScrypt();
For security reasons, the salt must never be the same.
Method addRandomSalt(int)
adds a random generated salt with a defined length (in bytes)
Hash hash = Password.hash(plainTextPassword).addRandomSalt(42).withScrypt();
Alternatively, you can use addRandomSalt()
(no arguments) to define a random salt of 64 bytes
Hash hash = Password.hash(plainTextPassword).addRandomSalt().withScrypt();
Method addPepper(CharSequence)
make you define the intended pepper.
Hash hash = Password.hash(plainTextPassword).addPepper("AlicePepper").withScrypt();
Alternatively you can define the pepper in the psw4j.properties
file at the property global.pepper
Hash hash = Password.hash(plainTextPassword).addPepper().withScrypt();
The pepper is always prepended.
Ideally the hash is retrieved from the database. Once retrieved you can check the user-provided passwords against the hash from your database. The salt is always encoded within the hash.
String hashFromDB = getHashFromDatabase(user);
boolean verified = Password.check(userProvidedPassword, hashFromDB).withScrypt();
The parameters used are taken from your psw4j.properties
file.
Alternatively you can define your own parameters
String hashFromDB = getHashFromDatabase(user);
ScryptFunction myScrypt = ScryptFunction.getInstance(16384, 8, 2, 128);
boolean verified = Password.check(userProvidedPassword, hashFromDB).with(myScrypt);
If you want to migrate your cryptographic hashes from the original configuration to a more secure one, you can refresh them during the first user login.
String hashFromDB = getHashFromDatabase(user);
ScryptFunction myScrypt = ScryptFunction.getInstance(16384, 8, 2, 128);
HashUpdate update = Password.check(userProvidedPassword, hashFromDB)
.andUpdate()
.addNewRandomSalt().with(myScrypt);
if(update.isVerified())
{
Hash newHash = update.getHash();
storeNewHash(user, newHash.getHash());
}
You can switch to any other hashing function offered by Password4j (for example Argon2)
ScryptFunction oldFunction = AlgorithmFinder.getScryptFunction();
Argon2Function newFunction = AlgorithmFinder.getArgon2Function();
HashUpdate update = Password.check(userProvidedPassword, hashFromDB)
.andUpdate()
.addNewRandomSalt().with(oldFunction, newFunction);