diff --git a/.travis.yml b/.travis.yml index ba8dd103..301dfd72 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,17 @@ -sudo: false +sudo: required addons: apt: packages: - oracle-java8-installer + - git language: java jdk: oraclejdk8 +before_script: + - "sudo git clone https://www.github.com/P-H-C/phc-winner-argon2.git argon2-src" + - "cd argon2-src && sudo make && sudo make install && cd .." + script: mvn clean verify -B notifications: diff --git a/circle.yml b/circle.yml index c9742f65..169726b8 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,9 @@ machine: java: version: oraclejdk8 +dependencies: + pre: + - "sudo apt-get update; sudo apt-get install -y git; sudo git clone https://www.github.com/P-H-C/phc-winner-argon2.git argon2-src; cd argon2-src; sudo make; sudo make install" general: artifacts: - "target/AuthMe-*.jar" diff --git a/docs/hash_algorithms.md b/docs/hash_algorithms.md index 45fa6594..00480c80 100644 --- a/docs/hash_algorithms.md +++ b/docs/hash_algorithms.md @@ -7,6 +7,7 @@ AuthMe supports the following hash algorithms for storing your passwords safely. Algorithm | Recommendation | Hash length | ASCII | | Salt type | Length | Separate? --------- | -------------- | ----------- | ----- | --- | --------- | ------ | --------- +ARGON2 | Recommended | 96 | | | Text | 16 | BCRYPT | Recommended | 60 | | | Text | | BCRYPT2Y | Recommended | 60 | | | Text | 22 | CRAZYCRYPT1 | Do not use | 128 | | | Username | | diff --git a/pom.xml b/pom.xml index e44421cc..7d7dc7a3 100644 --- a/pom.xml +++ b/pom.xml @@ -192,6 +192,10 @@ de.rtner fr.xephi.authme.libs.de.rtner + + de.mkammerer + fr.xephi.authme.libs.de.mkammerer + javax.inject fr.xephi.authme.libs.javax.inject @@ -388,6 +392,13 @@ true + + + de.mkammerer + argon2-jvm-nolibs + 2.2 + + org.spigotmc diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 728b469b..532c8aeb 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -23,6 +23,8 @@ import fr.xephi.authme.listener.PlayerListener18; import fr.xephi.authme.listener.PlayerListener19; import fr.xephi.authme.listener.PlayerListener19Spigot; import fr.xephi.authme.listener.ServerListener; +import fr.xephi.authme.security.HashAlgorithm; +import fr.xephi.authme.security.crypts.Argon2; import fr.xephi.authme.security.crypts.Sha256; import fr.xephi.authme.service.BackupService; import fr.xephi.authme.service.BukkitService; @@ -267,6 +269,13 @@ public class AuthMe extends JavaPlugin { && settings.getProperty(EmailSettings.SMTP_PORT) != 25) { ConsoleLogger.warning("Note: You have set Email.useTls to false but this only affects mail over port 25"); } + // Check if argon2 library is present and can be loaded + if (settings.getProperty(SecuritySettings.PASSWORD_HASH).equals(HashAlgorithm.ARGON2) + && !Argon2.isLibraryLoaded()) { + ConsoleLogger.warning("WARNING!!! You use Argon2 Hash Algorithm method but we can't find the Argon2 " + + "library on your system! See https://github.com/AuthMe/AuthMeReloaded/wiki/Argon2-as-Password-Hash"); + stopOrUnload(); + } } /** diff --git a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java index 9c9ff919..fb47f780 100644 --- a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java +++ b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java @@ -7,6 +7,7 @@ import fr.xephi.authme.security.crypts.EncryptionMethod; */ public enum HashAlgorithm { + ARGON2(fr.xephi.authme.security.crypts.Argon2.class), BCRYPT(fr.xephi.authme.security.crypts.BCrypt.class), BCRYPT2Y(fr.xephi.authme.security.crypts.BCrypt2y.class), CRAZYCRYPT1(fr.xephi.authme.security.crypts.CrazyCrypt1.class), diff --git a/src/main/java/fr/xephi/authme/security/crypts/Argon2.java b/src/main/java/fr/xephi/authme/security/crypts/Argon2.java new file mode 100644 index 00000000..27b89027 --- /dev/null +++ b/src/main/java/fr/xephi/authme/security/crypts/Argon2.java @@ -0,0 +1,48 @@ +package fr.xephi.authme.security.crypts; + +import de.mkammerer.argon2.Argon2Constants; +import de.mkammerer.argon2.Argon2Factory; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.security.crypts.description.HasSalt; +import fr.xephi.authme.security.crypts.description.Recommendation; +import fr.xephi.authme.security.crypts.description.SaltType; +import fr.xephi.authme.security.crypts.description.Usage; + +@Recommendation(Usage.RECOMMENDED) +@HasSalt(value = SaltType.TEXT, length = Argon2Constants.DEFAULT_SALT_LENGTH) +// Note: Argon2 is actually a salted algorithm but salt generation is handled internally +// and isn't exposed to the outside, so we treat it as an unsalted implementation +public class Argon2 extends UnsaltedMethod { + + private de.mkammerer.argon2.Argon2 argon2; + + public Argon2() { + argon2 = Argon2Factory.create(); + } + + /** + * Checks if the argon2 library is available in java.library.path. + * + * @return true if the library is present, false otherwise + */ + public static boolean isLibraryLoaded() { + try { + System.loadLibrary("argon2"); + return true; + } catch (UnsatisfiedLinkError e) { + ConsoleLogger.logException( + "Cannot find argon2 library: https://github.com/AuthMe/AuthMeReloaded/wiki/Argon2-as-Password-Hash", e); + } + return false; + } + + @Override + public String computeHash(String password) { + return argon2.hash(2, 65536, 1, password); + } + + @Override + public boolean comparePassword(String password, HashedPassword hashedPassword, String name) { + return argon2.verify(hashedPassword.getHash(), password); + } +} diff --git a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java index 0a32c558..73799516 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java @@ -55,8 +55,9 @@ public final class SecuritySettings implements SettingsHolder { @Comment({ "Possible values: SHA256, BCRYPT, BCRYPT2Y, PBKDF2, SALTEDSHA512,", "MYBB, IPB3, PHPBB, PHPFUSION, SMF, XENFORO, XAUTH, JOOMLA, WBB3, WBB4, MD5VB,", - "PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM (for developers only). See full list at", - "https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/hash_algorithms.md" + "PBKDF2DJANGO, WORDPRESS, ROYALAUTH, ARGON2, CUSTOM (for developers only). See full list at", + "https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/hash_algorithms.md", + "If you use ARGON2, check that you have the argon2 c library on your system" }) public static final Property PASSWORD_HASH = newProperty(HashAlgorithm.class, "settings.security.passwordHash", HashAlgorithm.SHA256); diff --git a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java index 5b835194..fc9fd0d6 100644 --- a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java @@ -2,6 +2,7 @@ package fr.xephi.authme.security; import ch.jalu.injector.Injector; import ch.jalu.injector.InjectorBuilder; +import fr.xephi.authme.security.crypts.Argon2; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.description.Recommendation; @@ -61,6 +62,10 @@ public class HashAlgorithmIntegrationTest { // given / when / then for (HashAlgorithm algorithm : HashAlgorithm.values()) { if (!HashAlgorithm.CUSTOM.equals(algorithm) && !HashAlgorithm.PLAINTEXT.equals(algorithm)) { + if (HashAlgorithm.ARGON2.equals(algorithm) && !Argon2.isLibraryLoaded()) { + System.out.println("[WARNING] Cannot find argon2 library, skipping integration test"); + continue; + } EncryptionMethod method = injector.createIfHasDependencies(algorithm.getClazz()); if (method == null) { fail("Could not create '" + algorithm.getClazz() + "' - forgot to provide some class?"); diff --git a/src/test/java/fr/xephi/authme/security/crypts/Argon2Test.java b/src/test/java/fr/xephi/authme/security/crypts/Argon2Test.java new file mode 100644 index 00000000..46b315d5 --- /dev/null +++ b/src/test/java/fr/xephi/authme/security/crypts/Argon2Test.java @@ -0,0 +1,29 @@ +package fr.xephi.authme.security.crypts; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assume.assumeThat; + +/** + * Test for {@link Argon2}. + */ +public class Argon2Test extends AbstractEncryptionMethodTest { + + private static final boolean IS_LIBRARY_LOADED = Argon2.isLibraryLoaded(); + + public Argon2Test() { + super(new Argon2(), + "$argon2i$v=19$m=65536,t=2,p=1$dOP8NiXsPTcMgzI4Z8Rbew$ShdowtoTEWTL5UTFz1UgQOigb9JOlm4ZxWPA6WbIeUw", // password + "$argon2i$v=19$m=65536,t=2,p=1$amZHbPfgc5peKd/4w1AI1g$Q2PUiOVw47TACijP57U0xf7QfiZ00HV4eFzMDA6yKRE", // PassWord1 + "$argon2i$v=19$m=65536,t=2,p=1$58v7dWNn9/bpD00QLzSebw$7cMC7p0qceE3Mgf2yQp4X7c+UkO9oyJwQ7S6XTBubNs", // &^%te$t?Pw@_ + "$argon2i$v=19$m=65536,t=2,p=1$93OSU71DgBOzpmhti7+6rQ$sSSI6QQQdoG9DlGwLjYz576kTek89nwr9CyNpy6bsL0"); // âË_3(íù* + + assumeThat("Argon2 library is not loaded - skipping test", + IS_LIBRARY_LOADED, equalTo(true)); + } + + @Override + protected boolean testHashEqualityForSameSalt() { + // Argon2 has a salt but it is handled internally + return false; + } +}