diff --git a/pom.xml b/pom.xml
index 5701693..df44ed0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,11 +29,6 @@
1.8
-
- org.sonarsource.scanner.maven
- sonar-maven-plugin
- 3.6.0.1398
-
maven-dependency-plugin
@@ -108,17 +103,15 @@
keycloak-services
provided
-
-
-
-
-
-
de.mkammerer
argon2-jvm
2.6
+
+ junit
+ junit
+
diff --git a/src/main/java/be/cronos/keycloak/credential/hash/Argon2PasswordHashProvider.java b/src/main/java/be/cronos/keycloak/credential/hash/Argon2PasswordHashProvider.java
index 1e22839..624988e 100644
--- a/src/main/java/be/cronos/keycloak/credential/hash/Argon2PasswordHashProvider.java
+++ b/src/main/java/be/cronos/keycloak/credential/hash/Argon2PasswordHashProvider.java
@@ -1,8 +1,7 @@
package be.cronos.keycloak.credential.hash;
import be.cronos.keycloak.policy.*;
-import de.mkammerer.argon2.Argon2;
-import de.mkammerer.argon2.Argon2Factory;
+import be.cronos.keycloak.utils.Argon2Helper;
import de.mkammerer.argon2.Argon2Factory.Argon2Types;
import org.jboss.logging.Logger;
import org.keycloak.credential.hash.PasswordHashProvider;
@@ -12,6 +11,9 @@
import java.security.SecureRandom;
+/**
+ * @author Dries Eestermans
+ */
public class Argon2PasswordHashProvider implements PasswordHashProvider {
private static final Logger LOG = Logger.getLogger(Argon2PasswordHashProvider.class);
@@ -70,23 +72,16 @@ public PasswordCredentialModel encodedCredential(String rawPassword, int iterati
LOG.debugf("\tSalt Length: %d", saltLength);
LOG.debugf("\tMaximum time for hashing (in ms): %d", maxTime);
- Argon2 argon2 = Argon2Factory.createAdvanced(argon2Variant, defaultSaltLength, defaultHashLength);
- String hash;
+ // Keep track of hashing runtime
+ long start = System.currentTimeMillis();
+ String hash = Argon2Helper.hashPassword(rawPassword, argon2Variant, argon2Iterations, parallelism, memoryLimit, hashLength, saltLength);
+ // Stop timing
+ long end = System.currentTimeMillis();
- try {
- // Keep track of hashing runtime
- long start = System.currentTimeMillis();
- // Hash the password
- hash = argon2.hash(argon2Iterations, memoryLimit, parallelism, rawPassword.getBytes());
- // Stop timing
- long end = System.currentTimeMillis();
- // Verify whether the hash time has not exceeded the configured value (or default value)
- LOG.debugf("Hashing runtime was %d milliseconds (%d seconds).", end-start, (end-start)/1000);
- if (end - start > maxTime) {
- LOG.warnf("Hash time exceeded configured maximum time: '%d ms', consider tuning the parameter 'Argon2 Iterations'.", maxTime);
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
+ // Verify whether the hash time has not exceeded the configured value (or default value)
+ LOG.debugf("Hashing runtime was %d milliseconds (%d seconds).", end-start, (end-start)/1000);
+ if (end - start > maxTime) {
+ LOG.warnf("Hash time exceeded configured maximum time: '%d ms', consider tuning the parameter 'Argon2 Iterations'.", maxTime);
}
// Salt doesn't matter here
@@ -120,38 +115,7 @@ public boolean verify(String rawPassword, PasswordCredentialModel credential) {
LOG.debugf("verify()");
- // Get the Argon2 variant of the credential, should be something like:
- // $argon2i$v=19$m=65535,t=30,p=4$JQUxqirAz7+Em0yM1ZiDFA$LhqtL0XPGESfeHb4lI2XnV4mSZacWGQWANKtvIVVpy4
- // however, the variant's case is not correct for the enum
- String storedVariant = credential.getPasswordSecretData().getValue().split("\\$")[1];
- Argon2Types storedArgon2Variant = null;
- try {
- for (Argon2Types argon2Type : Argon2Types.values()) {
- if (argon2Type.toString().equalsIgnoreCase(storedVariant)) {
- storedArgon2Variant = argon2Type;
- LOG.debugf("Stored variant found: %s", storedVariant);
- }
- }
- if (storedArgon2Variant == null) throw new Exception("Unknown stored Argon2 variant");
- } catch (Exception e) {
- throw new RuntimeException("Unknown stored Argon2 variant, is someone spoofing?");
- }
-
- // Now make sure to select the correct variant for the Argon2Factory
- Argon2 argon2 = Argon2Factory.createAdvanced(storedArgon2Variant);
-
- boolean samePassword = false;
- try {
- if (argon2.verify(credential.getPasswordSecretData().getValue(), rawPassword.getBytes())) {
- LOG.debugf("Passwords match!!");
- samePassword = true;
- } else {
- LOG.debugf("Passwords don't match!!");
- }
- } catch (Exception e) {
- LOG.debugf("Couldn't compare password, exception occurred: %s", e.getMessage());
- }
- return samePassword;
+ return Argon2Helper.verifyPassword(rawPassword, credential);
}
@Override
diff --git a/src/main/java/be/cronos/keycloak/credential/hash/Argon2PasswordHashProviderFactory.java b/src/main/java/be/cronos/keycloak/credential/hash/Argon2PasswordHashProviderFactory.java
index 73d6484..b8e92e5 100644
--- a/src/main/java/be/cronos/keycloak/credential/hash/Argon2PasswordHashProviderFactory.java
+++ b/src/main/java/be/cronos/keycloak/credential/hash/Argon2PasswordHashProviderFactory.java
@@ -9,6 +9,9 @@
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+/**
+ * @author Dries Eestermans
+ */
public class Argon2PasswordHashProviderFactory implements PasswordHashProviderFactory {
private static final Logger LOG = Logger.getLogger(Argon2PasswordHashProviderFactory.class);
diff --git a/src/main/java/be/cronos/keycloak/policy/Argon2HashLengthPasswordPolicyProviderFactory.java b/src/main/java/be/cronos/keycloak/policy/Argon2HashLengthPasswordPolicyProviderFactory.java
index 9125c1c..d8bc75b 100644
--- a/src/main/java/be/cronos/keycloak/policy/Argon2HashLengthPasswordPolicyProviderFactory.java
+++ b/src/main/java/be/cronos/keycloak/policy/Argon2HashLengthPasswordPolicyProviderFactory.java
@@ -10,6 +10,9 @@
import org.keycloak.policy.PasswordPolicyProviderFactory;
import org.keycloak.policy.PolicyError;
+/**
+ * @author Dries Eestermans
+ */
public class Argon2HashLengthPasswordPolicyProviderFactory implements PasswordPolicyProvider, PasswordPolicyProviderFactory {
public static final String ID = "argon2HashLength";
diff --git a/src/main/java/be/cronos/keycloak/policy/Argon2IterationsPasswordPolicyProviderFactory.java b/src/main/java/be/cronos/keycloak/policy/Argon2IterationsPasswordPolicyProviderFactory.java
index 958bc3e..a8e749c 100644
--- a/src/main/java/be/cronos/keycloak/policy/Argon2IterationsPasswordPolicyProviderFactory.java
+++ b/src/main/java/be/cronos/keycloak/policy/Argon2IterationsPasswordPolicyProviderFactory.java
@@ -9,6 +9,9 @@
import org.keycloak.policy.PasswordPolicyProviderFactory;
import org.keycloak.policy.PolicyError;
+/**
+ * @author Dries Eestermans
+ */
public class Argon2IterationsPasswordPolicyProviderFactory implements PasswordPolicyProvider, PasswordPolicyProviderFactory {
public static final String ID = "argon2Iterations";
diff --git a/src/main/java/be/cronos/keycloak/policy/Argon2MaxTimePasswordPolicyProviderFactory.java b/src/main/java/be/cronos/keycloak/policy/Argon2MaxTimePasswordPolicyProviderFactory.java
index 048c76e..3089161 100644
--- a/src/main/java/be/cronos/keycloak/policy/Argon2MaxTimePasswordPolicyProviderFactory.java
+++ b/src/main/java/be/cronos/keycloak/policy/Argon2MaxTimePasswordPolicyProviderFactory.java
@@ -1,6 +1,5 @@
package be.cronos.keycloak.policy;
-import de.mkammerer.argon2.Argon2Constants;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@@ -10,6 +9,9 @@
import org.keycloak.policy.PasswordPolicyProviderFactory;
import org.keycloak.policy.PolicyError;
+/**
+ * @author Dries Eestermans
+ */
public class Argon2MaxTimePasswordPolicyProviderFactory implements PasswordPolicyProvider, PasswordPolicyProviderFactory {
public static final String ID = "argon2MaxTime";
diff --git a/src/main/java/be/cronos/keycloak/policy/Argon2MemoryPasswordPolicyProviderFactory.java b/src/main/java/be/cronos/keycloak/policy/Argon2MemoryPasswordPolicyProviderFactory.java
index 4c90b3d..875ac68 100644
--- a/src/main/java/be/cronos/keycloak/policy/Argon2MemoryPasswordPolicyProviderFactory.java
+++ b/src/main/java/be/cronos/keycloak/policy/Argon2MemoryPasswordPolicyProviderFactory.java
@@ -9,6 +9,9 @@
import org.keycloak.policy.PasswordPolicyProviderFactory;
import org.keycloak.policy.PolicyError;
+/**
+ * @author Dries Eestermans
+ */
public class Argon2MemoryPasswordPolicyProviderFactory implements PasswordPolicyProvider, PasswordPolicyProviderFactory {
public static final String ID = "argon2Memory";
private final int DEFAULT_ARGON2_MEMORY = 65536;
diff --git a/src/main/java/be/cronos/keycloak/policy/Argon2ParallelismPasswordPolicyProviderFactory.java b/src/main/java/be/cronos/keycloak/policy/Argon2ParallelismPasswordPolicyProviderFactory.java
index 497ef33..b3053cb 100644
--- a/src/main/java/be/cronos/keycloak/policy/Argon2ParallelismPasswordPolicyProviderFactory.java
+++ b/src/main/java/be/cronos/keycloak/policy/Argon2ParallelismPasswordPolicyProviderFactory.java
@@ -6,6 +6,9 @@
import org.keycloak.policy.PasswordPolicyProviderFactory;
import org.keycloak.policy.PolicyError;
+/**
+ * @author Dries Eestermans
+ */
public class Argon2ParallelismPasswordPolicyProviderFactory implements PasswordPolicyProvider, PasswordPolicyProviderFactory {
public static final String ID = "argon2Parallelism";
diff --git a/src/main/java/be/cronos/keycloak/policy/Argon2SaltLengthPasswordPolicyProviderFactory.java b/src/main/java/be/cronos/keycloak/policy/Argon2SaltLengthPasswordPolicyProviderFactory.java
index b1657e9..e8e7825 100644
--- a/src/main/java/be/cronos/keycloak/policy/Argon2SaltLengthPasswordPolicyProviderFactory.java
+++ b/src/main/java/be/cronos/keycloak/policy/Argon2SaltLengthPasswordPolicyProviderFactory.java
@@ -10,6 +10,9 @@
import org.keycloak.policy.PasswordPolicyProviderFactory;
import org.keycloak.policy.PolicyError;
+/**
+ * @author Dries Eestermans
+ */
public class Argon2SaltLengthPasswordPolicyProviderFactory implements PasswordPolicyProvider, PasswordPolicyProviderFactory {
public static final String ID = "argon2SaltLength";
diff --git a/src/main/java/be/cronos/keycloak/policy/Argon2VariantPasswordPolicyProviderFactory.java b/src/main/java/be/cronos/keycloak/policy/Argon2VariantPasswordPolicyProviderFactory.java
index 7ceab87..df4a8f3 100644
--- a/src/main/java/be/cronos/keycloak/policy/Argon2VariantPasswordPolicyProviderFactory.java
+++ b/src/main/java/be/cronos/keycloak/policy/Argon2VariantPasswordPolicyProviderFactory.java
@@ -8,6 +8,9 @@
import org.keycloak.policy.PasswordPolicyProviderFactory;
import org.keycloak.policy.PolicyError;
+/**
+ * @author Dries Eestermans
+ */
public class Argon2VariantPasswordPolicyProviderFactory implements PasswordPolicyProvider, PasswordPolicyProviderFactory {
public static final String ID = "argon2Variant";
private final String DEFAULT_ARGON2_VARIANT = "ARGON2id";
diff --git a/src/main/java/be/cronos/keycloak/utils/Argon2Helper.java b/src/main/java/be/cronos/keycloak/utils/Argon2Helper.java
new file mode 100644
index 0000000..b05a6d4
--- /dev/null
+++ b/src/main/java/be/cronos/keycloak/utils/Argon2Helper.java
@@ -0,0 +1,64 @@
+package be.cronos.keycloak.utils;
+
+import de.mkammerer.argon2.Argon2;
+import de.mkammerer.argon2.Argon2Factory;
+import org.jboss.logging.Logger;
+import org.keycloak.models.credential.PasswordCredentialModel;
+
+/**
+ * @author Dries Eestermans
+ */
+public class Argon2Helper {
+ private static final Logger LOG = Logger.getLogger(Argon2Helper.class);
+
+ public static String hashPassword(String rawPassword, Argon2Factory.Argon2Types argon2Variant, int iterations,
+ int parallelism, int memoryLimit, int hashLength, int saltLength) {
+ if (rawPassword == null) throw new RuntimeException("Password can't be empty");
+ Argon2 argon2 = Argon2Factory.createAdvanced(argon2Variant, saltLength, hashLength);
+ String hash;
+
+ try {
+ // Hash the password
+ hash = argon2.hash(iterations, memoryLimit, parallelism, rawPassword.toCharArray());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return hash;
+ }
+
+ public static boolean verifyPassword(String rawPassword,
+ PasswordCredentialModel credential) {
+ // Get the Argon2 variant of the credential, should be something like:
+ // $argon2i$v=19$m=65535,t=30,p=4$JQUxqirAz7+Em0yM1ZiDFA$LhqtL0XPGESfeHb4lI2XnV4mSZacWGQWANKtvIVVpy4
+ // however, the variant's case is not correct for the enum
+ String storedVariant = credential.getPasswordSecretData().getValue().split("\\$")[1];
+ Argon2Factory.Argon2Types storedArgon2Variant = null;
+ try {
+ for (Argon2Factory.Argon2Types argon2Type : Argon2Factory.Argon2Types.values()) {
+ if (argon2Type.toString().equalsIgnoreCase(storedVariant)) {
+ storedArgon2Variant = argon2Type;
+ LOG.debugf("Stored variant found: %s", storedVariant);
+ }
+ }
+ if (storedArgon2Variant == null) throw new Exception("Unknown stored Argon2 variant");
+ } catch (Exception e) {
+ throw new RuntimeException("Unknown stored Argon2 variant, is someone spoofing?");
+ }
+
+ // Now make sure to select the correct variant for the Argon2Factory
+ Argon2 argon2 = Argon2Factory.createAdvanced(storedArgon2Variant);
+
+ boolean samePassword = false;
+ try {
+ if (argon2.verify(credential.getPasswordSecretData().getValue(), rawPassword.toCharArray())) {
+ LOG.debugf("Passwords match!!");
+ samePassword = true;
+ } else {
+ LOG.debugf("Passwords don't match!!");
+ }
+ } catch (Exception e) {
+ LOG.debugf("Couldn't compare password, exception occurred: %s", e.getMessage());
+ }
+ return samePassword;
+ }
+}
diff --git a/src/test/java/be/cronos/keycloak/utils/Argon2HelperTest.java b/src/test/java/be/cronos/keycloak/utils/Argon2HelperTest.java
new file mode 100644
index 0000000..092cff2
--- /dev/null
+++ b/src/test/java/be/cronos/keycloak/utils/Argon2HelperTest.java
@@ -0,0 +1,247 @@
+package be.cronos.keycloak.utils;
+
+import be.cronos.keycloak.credential.hash.Argon2PasswordHashProviderFactory;
+import de.mkammerer.argon2.Argon2Constants;
+import de.mkammerer.argon2.Argon2Factory;
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.models.credential.PasswordCredentialModel;
+
+/**
+ * @author Dries Eestermans
+ */
+public class Argon2HelperTest {
+ private static final String ALGORITHM = Argon2PasswordHashProviderFactory.ID;
+ private static final int DEFAULT_ITERATIONS = 1;
+
+ private static final int DEFAULT_MEMORY = 65536;
+
+ private static final int DEFAULT_PARALLELISM = 1;
+
+ private static final int DEFAULT_MAX_TIME = 1000;
+
+ // region: argon2d
+ @Test
+ public void testArgon2dHashAndVerifySamePassword() {
+ Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2d;
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2d";
+ String hash = Argon2Helper.hashPassword(rawPassword, argon2Variant, iterations, DEFAULT_PARALLELISM, DEFAULT_MEMORY, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ Assert.assertTrue(verified);
+ }
+
+ @Test
+ public void testArgon2dHashAndVerifyDifferentPassword() {
+ Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2d;
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2d";
+ String hash = Argon2Helper.hashPassword(rawPassword, argon2Variant, iterations, DEFAULT_PARALLELISM, DEFAULT_MEMORY, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword("different", passwordCredentialModel);
+ Assert.assertFalse(verified);
+ }
+
+ @Test
+ public void testArgon2dVerifyPredefinedHash() {
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2d";
+ String hash = "$argon2d$v=19$m=65536,t=1,p=1$v3evK1HhIHKHRnRNWqEfZA$T7G+ujnDpZN+kYuMngOb/2+/mIDpOn0VyLIh7B6LJiY";
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ Assert.assertTrue(verified);
+ }
+
+ @Test
+ public void testArgon2dVerifyPredefinedWrongHash() {
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "wrongpassword";
+ String hash = "$argon2d$v=19$m=65536,t=1,p=1$v3evK1HhIHKHRnRNWqEfZA$T7G+ujnDpZN+kYuMngOb/2+/mIDpOn0VyLIh7B6LJiY";
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ Assert.assertFalse(verified);
+ }
+
+ // endregion: argon2d
+
+ // region: argon2i
+ @Test
+ public void testArgon2iHashAndVerifySamePassword() {
+ Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2i;
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2i";
+ String hash = Argon2Helper.hashPassword(rawPassword, argon2Variant, iterations, DEFAULT_PARALLELISM, DEFAULT_MEMORY, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ Assert.assertTrue(verified);
+ }
+
+ @Test
+ public void testArgon2iHashAndVerifyDifferentPassword() {
+ Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2i;
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2i";
+ String hash = Argon2Helper.hashPassword(rawPassword, argon2Variant, iterations, DEFAULT_PARALLELISM, DEFAULT_MEMORY, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword("different", passwordCredentialModel);
+ Assert.assertFalse(verified);
+ }
+
+ @Test
+ public void testArgon2iVerifyPredefinedHash() {
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2i";
+ String hash = "$argon2i$v=19$m=65536,t=1,p=1$81E/xOo/2OUX15UAJgI3Eg$0Z83Ag5oE9MCEEVGL9NJNg6oFIVbU/FhpQkyyX+RNz0";
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ Assert.assertTrue(verified);
+ }
+
+ @Test
+ public void testArgon2iVerifyPredefinedWrongHash() {
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "wrongpassword";
+ String hash = "$argon2i$v=19$m=65536,t=1,p=1$81E/xOo/2OUX15UAJgI3Eg$0Z83Ag5oE9MCEEVGL9NJNg6oFIVbU/FhpQkyyX+RNz0";
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ Assert.assertFalse(verified);
+ }
+ // endregion: argon2i
+
+ // region: argon2id
+ @Test
+ public void testArgon2idHashAndVerifySamePassword() {
+ Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2id;
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2id";
+ String hash = Argon2Helper.hashPassword(rawPassword, argon2Variant, iterations, DEFAULT_PARALLELISM, DEFAULT_MEMORY, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ Assert.assertTrue(verified);
+ }
+
+ @Test
+ public void testArgon2idHashAndVerifyDifferentPassword() {
+ Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2id;
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2id";
+ String hash = Argon2Helper.hashPassword(rawPassword, argon2Variant, iterations, DEFAULT_PARALLELISM, DEFAULT_MEMORY, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword("different", passwordCredentialModel);
+ Assert.assertFalse(verified);
+ }
+
+ @Test
+ public void testArgon2idVerifyPredefinedHash() {
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2id";
+ String hash = "$argon2id$v=19$m=65536,t=1,p=1$zGFM95kyhWZyZv1Hhvjuog$G78Vd4nXEqN0DKbF+qGj1pUNyEpEZmOWqEqlHFDllJY";
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ Assert.assertTrue(verified);
+ }
+
+ @Test
+ public void testArgon2idVerifyPredefinedWrongHash() {
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "wrongpassword";
+ String hash = "$argon2i$v=19$m=65536,t=1,p=1$81E/xOo/2OUX15UAJgI3Eg$0Z83Ag5oE9MCEEVGL9NJNg6oFIVbU/FhpQkyyX+RNz0";
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ Assert.assertFalse(verified);
+ }
+ // endregion: argon2id
+
+ // region: runtime exceptions
+ @Test(expected = RuntimeException.class)
+ public void testHashPasswordHashEmptyPassword() {
+ Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2id;
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = null;
+ String hash = Argon2Helper.hashPassword(rawPassword, argon2Variant, iterations, DEFAULT_PARALLELISM, DEFAULT_MEMORY, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testHashPasswordNoAlgorithm() {
+ Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2id;
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "novariantdefined";
+ String hash = Argon2Helper.hashPassword(rawPassword, null, iterations, DEFAULT_PARALLELISM, DEFAULT_MEMORY, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+ }
+
+ // Keeps on processing
+// @Test(expected = RuntimeException.class)
+// public void testHashPasswordNegativeIterations() {
+// Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2id;
+// int iterations = -1;
+// String rawPassword = "novariantdefined";
+// String hash = Argon2Helper.hashPassword(rawPassword, argon2Variant, iterations, DEFAULT_PARALLELISM, DEFAULT_MEMORY, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+// }
+
+ @Test(expected = RuntimeException.class)
+ public void testHashPasswordNoParallelism() {
+ Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2id;
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "novariantdefined";
+ String hash = Argon2Helper.hashPassword(rawPassword, null, iterations, 0, DEFAULT_MEMORY, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testHashPasswordNoMemory() {
+ Argon2Factory.Argon2Types argon2Variant = Argon2Factory.Argon2Types.ARGON2id;
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "novariantdefined";
+ String hash = Argon2Helper.hashPassword(rawPassword, null, iterations, DEFAULT_PARALLELISM, 0, Argon2Constants.DEFAULT_HASH_LENGTH, Argon2Constants.DEFAULT_SALT_LENGTH);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testVerifyPasswordInvalidAlgorithm() {
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2id";
+ String hash = "$argon2idd$v=19$m=65536,t=1,p=1$zGFM95kyhWZyZv1Hhvjuog$G78Vd4nXEqN0DKbF+qGj1pUNyEpEZmOWqEqlHFDllJY";
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testVerifyPasswordNonsenseData() {
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2id";
+ String hash = "nonsense";
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ }
+ // endregion: runtime exceptions
+
+ // region: wrong algorithm in hash
+
+ @Test()
+ public void testVerifyPasswordIncorrectAlgorithm() {
+ int iterations = DEFAULT_ITERATIONS;
+ String rawPassword = "testargon2id";
+ // it should argon2id
+ String hash = "$argon2i$v=19$m=65536,t=1,p=1$zGFM95kyhWZyZv1Hhvjuog$G78Vd4nXEqN0DKbF+qGj1pUNyEpEZmOWqEqlHFDllJY";
+ PasswordCredentialModel passwordCredentialModel = PasswordCredentialModel.createFromValues(ALGORITHM, "".getBytes(), iterations, hash);
+ passwordCredentialModel.setSecretData(hash);
+ boolean verified = Argon2Helper.verifyPassword(rawPassword, passwordCredentialModel);
+ Assert.assertFalse(verified);
+ }
+
+ // endregion: wrong algorithm in hash
+
+}