Skip to content
David Bertoldi edited this page Oct 7, 2022 · 3 revisions

Argon2 is a key derivation function that was selected as the winner of the Password Hashing Competition in July 2015.

📑 Define Argon2 parameters

Argon2 accepts 6 parameters: the memory required for computation, the number of iterations , the number of threads, the desired key length, the type (i, d, or id) and the version of the algorithm.

Name Default value Properties Description
Memory 15 hash.argon2.memory Defines amount of memory (in kibibytes) to use.
Iterations 2 hash.argon2.iterations Defines number of iterations to perform.
Parallelisation 1 hash.argon2.parallelism Defines the degree of parallelism (number of threads).
Output length 32 hash.argon2.length Defines the desired length of the final derived key.
Type Argon2id hash.argon2.type Defines the desired type of the algorithm.
Version 19 hash.argon2.version The current version is 0x13 (19 decimal).

The suggested version of the algorithm is 19, the latest one. The other versions should be used only for backward compatibility reasons and we recommend to update your hashes as soon as possible.

Types

Type Description
Argon2d It maximizes resistance to GPU cracking attacks. It accesses the memory array in a password dependent order, which reduces the possibility of time–memory trade-off (TMTO) attacks, but introduces possible side-channel attacks.
Argon2i It is optimized to resist side-channel attacks. It accesses the memory array in a password independent order.
Argon2id It is a hybrid version. It follows the Argon2i approach for the first half pass over memory and the Argon2d approach for subsequent passes. The Internet draft recommends using Argon2id except when there are reasons to prefer one of the other two modes.

You can define a singleton custom scrypt function by calling Argon2Function.getInstance(int, int, int, int, int, int) or Argon2Function.getInstance(int, int, int, int, int)

Argon2Function argon2 = Argon2Function.getInstance(1024, 3, 2, 32, Argon2.d, 19);

In this case you have created a singleton instance which requires 1024 kibibytes of memory, performs 3 iterations, spawns 2 threads, produces a derived key of 32 bytes, uses Argon2d with the latest version (19).

Alternatively if you have defined the parameters in the psw4j.properties file

Argon2Function argon2 = AlgorithmFinder.getArgon2Instance();

Additionally you can create a Argon2Function singleton instance from the hash, since all the parameters required are stored into it.

String hashed = "$argon2id$v=19$m=1024,t=3,p=12$MzMzMzMzMzM$DXUE5N4lm4plldg9nGMq+tYsbGhko8HWpPaADujpgFQ";

Argon2Function argon2 = Argon2Function.getInstanceFromHash(hashed);

#️⃣ How to hash passwords

Hashing passwords with Argon2 can be done quite easily.

Hash hash = Password.hash(plainTextPassword).withArgon2();

hash.getResult(); // $argon2id$v=19$m=1024,t=3,p=12$MTExMTExMTE$0PUE8wVEaK0qdjms3b4pTZOs0+00S/+9j28WZ3gMUno

This approach takes the parameters from psw4j.properties file (like AlgorithmFinder.getArgon2Instance()). However it's possible to use user-defined parameters as we saw previously

Argon2Function myArgon2 = Argon2Function.getInstance(1024, 3, 2, 32, Argon2.d, 19);

Password.hash(plainTextPassword).with(myArgon2);

Add salt

You have two way to define the cryptographic salt.

Explicit

Method addSalt(String) make you define the intended salt.

Hash hash = Password.hash(plainTextPassword).addSalt("a1b2c3d4").withArgon2();

For security reasons, the salt must never be the same.

Randomly

Method addRandomSalt(int) adds a random generated salt with a defined length (in bytes)

Hash hash = Password.hash(plainTextPassword).addRandomSalt(42).withArgon2();

Alternatively, you can use addRandomSalt() (no arguments) to define a random salt of 64 bytes

Hash hash = Password.hash(plainTextPassword).addRandomSalt().withArgon2();

Add pepper

Method addPepper(CharSequence) make you define the intended pepper.

Hash hash = Password.hash(plainTextPassword).addPepper("AlicePepper").withArgon2();

Alternatively you can define the pepper in the psw4j.properties file at the property global.pepper

Hash hash = Password.hash(plainTextPassword).addPepper().withArgon2();

The pepper is used as Argon2's secret parameter (it's not appended/prepended to the plain text password).

✔️ How to check the hash

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).withArgon2();

The parameters used are taken from your psw4j.properties file. Alternatively you can define your own parameters

String hashFromDB = getHashFromDatabase(user);

Argon2Function myArgon2 = Argon2Function.getInstance(1024, 3, 2, 32, Argon2.d, 19);

boolean verified = Password.check(userProvidedPassword, hashFromDB).with(myArgon2);

🔄 How to update the hash

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);

Argon2Function myArgon2 = Argon2Function.getInstance(1024, 3, 2, 32, Argon2.d, 19);

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 scrypt)

Argon2Function oldFunction = AlgorithmFinder.getArgon2Function();
ScryptFunction newFunction = AlgorithmFinder.getScryptFunction();


HashUpdate update = Password.check(userProvidedPassword, hashFromDB)
                            .andUpdate()
                            .addNewRandomSalt().with(oldFunction, newFunction);