* Fields other than {@link FoundResultStatus} are available depending, among other factors, on the status:
*
*
{@link FoundResultStatus#SUCCESS} entails that mapping the input to a command was successful. Therefore,
diff --git a/src/main/java/fr/xephi/authme/command/PlayerCommand.java b/src/main/java/fr/xephi/authme/command/PlayerCommand.java
index a607803b..2d7aca0b 100644
--- a/src/main/java/fr/xephi/authme/command/PlayerCommand.java
+++ b/src/main/java/fr/xephi/authme/command/PlayerCommand.java
@@ -1,10 +1,10 @@
package fr.xephi.authme.command;
+import java.util.List;
+
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
-import java.util.List;
-
/**
* Common base type for player-only commands, handling the verification that the command sender is indeed a player.
*/
@@ -35,7 +35,7 @@ public abstract class PlayerCommand implements ExecutableCommand {
/**
* Return an alternative command (textual representation) that is not restricted to players only.
- * Example: "authme register <playerName> <password>"
+ * Example: {@code "authme register "}
*
* @return Alternative command not only for players, or null if not applicable
*/
diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java
index 50c83353..fb2b8879 100644
--- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java
+++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java
@@ -1,5 +1,11 @@
package fr.xephi.authme.datasource;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
@@ -7,16 +13,11 @@ import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalListeners;
import com.google.common.cache.RemovalNotification;
+
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.security.crypts.HashedPassword;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
/**
*/
public class CacheDataSource implements DataSource {
@@ -336,7 +337,7 @@ public class CacheDataSource implements DataSource {
/**
* Method purgeBanned.
*
- * @param banned List
+ * @param banned List of String
*
* @see fr.xephi.authme.datasource.DataSource#purgeBanned(List)
*/
diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java
index babba2cc..2f466def 100644
--- a/src/main/java/fr/xephi/authme/datasource/DataSource.java
+++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java
@@ -79,7 +79,7 @@ public interface DataSource {
*
* @param until long
*
- * @return List
+ * @return List of String
*/
List autoPurgeDatabase(long until);
@@ -115,7 +115,7 @@ public interface DataSource {
*
* @param auth PlayerAuth
*
- * @return List
+ * @return List of String
*/
List getAllAuthsByName(PlayerAuth auth);
@@ -124,7 +124,7 @@ public interface DataSource {
*
* @param ip String
*
- * @return List * @throws Exception
+ * @return List of String * @throws Exception
*/
List getAllAuthsByIp(String ip);
@@ -133,7 +133,7 @@ public interface DataSource {
*
* @param email String
*
- * @return List * @throws Exception
+ * @return List of String * @throws Exception
*/
List getAllAuthsByEmail(String email);
@@ -153,7 +153,7 @@ public interface DataSource {
/**
* Method purgeBanned.
*
- * @param banned List
+ * @param banned List of String
*/
void purgeBanned(List banned);
@@ -207,14 +207,14 @@ public interface DataSource {
/**
* Method getAllAuths.
*
- * @return List
+ * @return List of PlayerAuth
*/
List getAllAuths();
/**
* Method getLoggedPlayers.
*
- * @return List
+ * @return List of PlayerAuth
*/
List getLoggedPlayers();
diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java
index b84a84c9..4d7b4b38 100644
--- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java
+++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java
@@ -406,7 +406,7 @@ public class FlatFile implements DataSource {
*
* @param until long
*
- * @return List * @see fr.xephi.authme.datasource.DataSource#autoPurgeDatabase(long)
+ * @return List of String * @see fr.xephi.authme.datasource.DataSource#autoPurgeDatabase(long)
*/
@Override
public List autoPurgeDatabase(long until) {
@@ -622,7 +622,7 @@ public class FlatFile implements DataSource {
*
* @param auth PlayerAuth
*
- * @return List * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByName(PlayerAuth)
+ * @return List of String * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByName(PlayerAuth)
*/
@Override
public List getAllAuthsByName(PlayerAuth auth) {
@@ -659,7 +659,7 @@ public class FlatFile implements DataSource {
*
* @param ip String
*
- * @return List * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByIp(String)
+ * @return List of String * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByIp(String)
*/
@Override
public List getAllAuthsByIp(String ip) {
@@ -696,7 +696,7 @@ public class FlatFile implements DataSource {
*
* @param email String
*
- * @return List * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByEmail(String)
+ * @return List of String * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByEmail(String)
*/
@Override
public List getAllAuthsByEmail(String email) {
@@ -731,7 +731,7 @@ public class FlatFile implements DataSource {
/**
* Method purgeBanned.
*
- * @param banned List
+ * @param banned List of String
*
* @see fr.xephi.authme.datasource.DataSource#purgeBanned(List)
*/
@@ -876,7 +876,7 @@ public class FlatFile implements DataSource {
/**
* Method getAllAuths.
*
- * @return List * @see fr.xephi.authme.datasource.DataSource#getAllAuths()
+ * @return List of PlayerAuth * @see fr.xephi.authme.datasource.DataSource#getAllAuths()
*/
@Override
public List getAllAuths() {
@@ -928,7 +928,7 @@ public class FlatFile implements DataSource {
/**
* Method getLoggedPlayers.
*
- * @return List * @see fr.xephi.authme.datasource.DataSource#getLoggedPlayers()
+ * @return List of PlayerAuth * @see fr.xephi.authme.datasource.DataSource#getLoggedPlayers()
*/
@Override
public List getLoggedPlayers() {
diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java
index f3870db5..d4d46979 100644
--- a/src/main/java/fr/xephi/authme/datasource/SQLite.java
+++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java
@@ -1,11 +1,5 @@
package fr.xephi.authme.datasource;
-import fr.xephi.authme.ConsoleLogger;
-import fr.xephi.authme.cache.auth.PlayerAuth;
-import fr.xephi.authme.security.crypts.HashedPassword;
-import fr.xephi.authme.settings.Settings;
-import fr.xephi.authme.util.StringUtils;
-
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
@@ -15,6 +9,12 @@ import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
+import fr.xephi.authme.ConsoleLogger;
+import fr.xephi.authme.cache.auth.PlayerAuth;
+import fr.xephi.authme.security.crypts.HashedPassword;
+import fr.xephi.authme.settings.Settings;
+import fr.xephi.authme.util.StringUtils;
+
/**
*/
public class SQLite implements DataSource {
@@ -40,7 +40,8 @@ public class SQLite implements DataSource {
/**
* Constructor for SQLite.
*
- * @throws ClassNotFoundException * @throws SQLException
+ * @throws ClassNotFoundException Exception
+ * @throws SQLException Exception
*/
public SQLite() throws ClassNotFoundException, SQLException {
this.database = Settings.getMySQLDatabase;
@@ -72,7 +73,8 @@ public class SQLite implements DataSource {
/**
* Method connect.
*
- * @throws ClassNotFoundException * @throws SQLException
+ * @throws ClassNotFoundException
+ * @throws SQLException
*/
private synchronized void connect() throws ClassNotFoundException, SQLException {
Class.forName("org.sqlite.JDBC");
@@ -152,7 +154,8 @@ public class SQLite implements DataSource {
*
* @param user String
*
- * @return boolean * @see fr.xephi.authme.datasource.DataSource#isAuthAvailable(String)
+ * @return boolean
+ * @see fr.xephi.authme.datasource.DataSource#isAuthAvailable(String)
*/
@Override
public synchronized boolean isAuthAvailable(String user) {
@@ -200,7 +203,8 @@ public class SQLite implements DataSource {
*
* @param user String
*
- * @return PlayerAuth * @see fr.xephi.authme.datasource.DataSource#getAuth(String)
+ * @return PlayerAuth
+ * @see fr.xephi.authme.datasource.DataSource#getAuth(String)
*/
@Override
public synchronized PlayerAuth getAuth(String user) {
@@ -229,7 +233,8 @@ public class SQLite implements DataSource {
*
* @param auth PlayerAuth
*
- * @return boolean * @see fr.xephi.authme.datasource.DataSource#saveAuth(PlayerAuth)
+ * @return boolean
+ * @see fr.xephi.authme.datasource.DataSource#saveAuth(PlayerAuth)
*/
@Override
public synchronized boolean saveAuth(PlayerAuth auth) {
@@ -275,7 +280,8 @@ public class SQLite implements DataSource {
*
* @param auth PlayerAuth
*
- * @return boolean * @see fr.xephi.authme.datasource.DataSource#updatePassword(PlayerAuth)
+ * @return boolean
+ * @see fr.xephi.authme.datasource.DataSource#updatePassword(PlayerAuth)
*/
@Override
public synchronized boolean updatePassword(PlayerAuth auth) {
@@ -314,7 +320,8 @@ public class SQLite implements DataSource {
*
* @param auth PlayerAuth
*
- * @return boolean * @see fr.xephi.authme.datasource.DataSource#updateSession(PlayerAuth)
+ * @return boolean
+ * @see fr.xephi.authme.datasource.DataSource#updateSession(PlayerAuth)
*/
@Override
public boolean updateSession(PlayerAuth auth) {
@@ -340,7 +347,8 @@ public class SQLite implements DataSource {
*
* @param until long
*
- * @return int * @see fr.xephi.authme.datasource.DataSource#purgeDatabase(long)
+ * @return int
+ * @see fr.xephi.authme.datasource.DataSource#purgeDatabase(long)
*/
@Override
public int purgeDatabase(long until) {
@@ -363,7 +371,8 @@ public class SQLite implements DataSource {
*
* @param until long
*
- * @return List * @see fr.xephi.authme.datasource.DataSource#autoPurgeDatabase(long)
+ * @return List of String
+ * @see fr.xephi.authme.datasource.DataSource#autoPurgeDatabase(long)
*/
@Override
public List autoPurgeDatabase(long until) {
@@ -392,7 +401,8 @@ public class SQLite implements DataSource {
*
* @param user String
*
- * @return boolean * @see fr.xephi.authme.datasource.DataSource#removeAuth(String)
+ * @return boolean
+ * @see fr.xephi.authme.datasource.DataSource#removeAuth(String)
*/
@Override
public synchronized boolean removeAuth(String user) {
@@ -415,7 +425,8 @@ public class SQLite implements DataSource {
*
* @param auth PlayerAuth
*
- * @return boolean * @see fr.xephi.authme.datasource.DataSource#updateQuitLoc(PlayerAuth)
+ * @return boolean
+ * @see fr.xephi.authme.datasource.DataSource#updateQuitLoc(PlayerAuth)
*/
@Override
public boolean updateQuitLoc(PlayerAuth auth) {
@@ -442,7 +453,8 @@ public class SQLite implements DataSource {
*
* @param ip String
*
- * @return int * @see fr.xephi.authme.datasource.DataSource#getIps(String)
+ * @return int
+ * @see fr.xephi.authme.datasource.DataSource#getIps(String)
*/
@Override
public int getIps(String ip) {
@@ -472,7 +484,8 @@ public class SQLite implements DataSource {
*
* @param auth PlayerAuth
*
- * @return boolean * @see fr.xephi.authme.datasource.DataSource#updateEmail(PlayerAuth)
+ * @return boolean
+ * @see fr.xephi.authme.datasource.DataSource#updateEmail(PlayerAuth)
*/
@Override
public boolean updateEmail(PlayerAuth auth) {
@@ -549,7 +562,8 @@ public class SQLite implements DataSource {
*
* @param auth PlayerAuth
*
- * @return List * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByName(PlayerAuth)
+ * @return List of String
+ * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByName(PlayerAuth)
*/
@Override
public List getAllAuthsByName(PlayerAuth auth) {
@@ -580,7 +594,8 @@ public class SQLite implements DataSource {
*
* @param ip String
*
- * @return List * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByIp(String)
+ * @return List of String
+ * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByIp(String)
*/
@Override
public List getAllAuthsByIp(String ip) {
@@ -611,7 +626,8 @@ public class SQLite implements DataSource {
*
* @param email String
*
- * @return List * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByEmail(String)
+ * @return List of String
+ * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByEmail(String)
*/
@Override
public List getAllAuthsByEmail(String email) {
@@ -656,7 +672,8 @@ public class SQLite implements DataSource {
/**
* Method getType.
*
- * @return DataSourceType * @see fr.xephi.authme.datasource.DataSource#getType()
+ * @return DataSourceType
+ * @see fr.xephi.authme.datasource.DataSource#getType()
*/
@Override
public DataSourceType getType() {
@@ -758,7 +775,8 @@ public class SQLite implements DataSource {
/**
* Method getAccountsRegistered.
*
- * @return int * @see fr.xephi.authme.datasource.DataSource#getAccountsRegistered()
+ * @return int
+ * @see fr.xephi.authme.datasource.DataSource#getAccountsRegistered()
*/
@Override
public int getAccountsRegistered() {
@@ -798,7 +816,7 @@ public class SQLite implements DataSource {
/**
* Method getAllAuths.
*
- * @return List
+ * @return List of PlayerAuth
*/
@Override
public List getAllAuths() {
@@ -824,7 +842,7 @@ public class SQLite implements DataSource {
/**
* Method getLoggedPlayers.
*
- * @return List
+ * @return List of PlayerAuth
*/
@Override
public List getLoggedPlayers() {
diff --git a/src/main/java/fr/xephi/authme/permission/DefaultPermission.java b/src/main/java/fr/xephi/authme/permission/DefaultPermission.java
index f09de526..24e64c74 100644
--- a/src/main/java/fr/xephi/authme/permission/DefaultPermission.java
+++ b/src/main/java/fr/xephi/authme/permission/DefaultPermission.java
@@ -25,7 +25,11 @@ public enum DefaultPermission {
this.title = title;
}
- /** Return the textual representation. */
+ /**
+ * Return the textual representation.
+ *
+ * @return String
+ */
public String getTitle() {
return title;
}
diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java
index 09b23525..ef58b569 100644
--- a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java
+++ b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java
@@ -1,10 +1,11 @@
package fr.xephi.authme.permission;
-import de.bananaco.bpermissions.api.ApiLayer;
-import de.bananaco.bpermissions.api.CalculableType;
-import fr.xephi.authme.command.CommandDescription;
-import fr.xephi.authme.util.CollectionUtils;
-import net.milkbowl.vault.permission.Permission;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
import org.anjocaido.groupmanager.GroupManager;
import org.anjocaido.groupmanager.permissions.AnjoPermissionsHandler;
import org.bukkit.Bukkit;
@@ -17,24 +18,25 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.tyrannyofheaven.bukkit.zPermissions.ZPermissionsService;
+
+import de.bananaco.bpermissions.api.ApiLayer;
+import de.bananaco.bpermissions.api.CalculableType;
+import fr.xephi.authme.command.CommandDescription;
+import fr.xephi.authme.util.CollectionUtils;
+import net.milkbowl.vault.permission.Permission;
import ru.tehkode.permissions.PermissionManager;
import ru.tehkode.permissions.PermissionUser;
import ru.tehkode.permissions.bukkit.PermissionsEx;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Logger;
-
/**
+ *
* PermissionsManager.
- *
+ *
* A permissions manager, to manage and use various permissions systems.
* This manager supports dynamic plugin hooking and various other features.
- *
+ *
* Written by Tim Visée.
- *
+ *
* @author Tim Visée, http://timvisee.com
* @version 0.2.1
*/
diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java b/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java
index eb8c717a..06e6ee70 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java
@@ -13,6 +13,9 @@
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package fr.xephi.authme.security.crypts;
+import java.io.UnsupportedEncodingException;
+import java.security.SecureRandom;
+
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
@@ -21,9 +24,6 @@ import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
-import java.io.UnsupportedEncodingException;
-import java.security.SecureRandom;
-
/**
*
* BCrypt implements OpenBSD-style Blowfish password hashing using the scheme
@@ -64,7 +64,6 @@ import java.security.SecureRandom;
* is twice as much work. The default log_rounds is 10, and the valid range is 4
* to 31.
*
- *
* @author Damien Miller
* @version 0.2
*/
diff --git a/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java b/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java
index 269c1365..cd8af924 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java
@@ -10,8 +10,6 @@ import fr.xephi.authme.security.crypts.description.HasSalt;
import java.nio.charset.Charset;
import java.security.MessageDigest;
-@Recommendation(Usage.DO_NOT_USE)
-@HasSalt(SaltType.USERNAME)
public class CRAZYCRYPT1 extends UsernameSaltMethod {
private static final char[] CRYPTCHARS =
diff --git a/src/main/java/fr/xephi/authme/security/crypts/IPB3.java b/src/main/java/fr/xephi/authme/security/crypts/IPB3.java
index 85789b88..4abfe5d4 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/IPB3.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/IPB3.java
@@ -8,7 +8,7 @@ import fr.xephi.authme.security.crypts.description.Usage;
import static fr.xephi.authme.security.HashUtils.md5;
-@Recommendation(Usage.DO_NOT_USE)
+@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 5)
public class IPB3 extends SeparateSaltMethod {
diff --git a/src/main/java/fr/xephi/authme/security/crypts/MYBB.java b/src/main/java/fr/xephi/authme/security/crypts/MYBB.java
index 97418640..555f3213 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/MYBB.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/MYBB.java
@@ -1,9 +1,15 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.RandomString;
+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;
import static fr.xephi.authme.security.HashUtils.md5;
+@Recommendation(Usage.ACCEPTABLE)
+@HasSalt(value = SaltType.TEXT, length = 8)
public class MYBB extends SeparateSaltMethod {
@Override
diff --git a/src/main/java/fr/xephi/authme/security/crypts/WBB3.java b/src/main/java/fr/xephi/authme/security/crypts/WBB3.java
index 6cc12300..28e97585 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/WBB3.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/WBB3.java
@@ -1,9 +1,15 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.RandomString;
+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;
import static fr.xephi.authme.security.HashUtils.sha1;
+@Recommendation(Usage.ACCEPTABLE)
+@HasSalt(value = SaltType.TEXT, length = 40)
public class WBB3 extends SeparateSaltMethod {
@Override
diff --git a/src/main/java/fr/xephi/authme/security/crypts/WHIRLPOOL.java b/src/main/java/fr/xephi/authme/security/crypts/WHIRLPOOL.java
index 0169d192..72d8e8fb 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/WHIRLPOOL.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/WHIRLPOOL.java
@@ -241,8 +241,9 @@ public class WHIRLPOOL extends UnsaltedMethod {
*
* @param source plaintext data to hash.
* @param sourceBits how many bits of plaintext to process.
- *
- * This method maintains the invariant: bufferBits < 512
+ *
+ * This method maintains the invariant: bufferBits < 512
+ *
*/
public void NESSIEadd(byte[] source, long sourceBits) {
/*
@@ -322,10 +323,12 @@ public class WHIRLPOOL extends UnsaltedMethod {
}
/**
+ *
* Get the hash value from the hashing state.
- *
- * This method uses the invariant: bufferBits < 512
- *
+ *
+ *
+ * This method uses the invariant: bufferBits < 512
+ *
* @param digest byte[]
*/
public void NESSIEfinalize(byte[] digest) {
@@ -367,7 +370,7 @@ public class WHIRLPOOL extends UnsaltedMethod {
* Delivers string input data to the hashing algorithm.
*
* @param source plaintext data to hash (ASCII text string).
- * This method maintains the invariant: bufferBits < 512
+ * This method maintains the invariant: bufferBits < 512
*/
public void NESSIEadd(String source) {
if (source.length() > 0) {
diff --git a/src/main/java/fr/xephi/authme/security/crypts/description/AsciiRestricted.java b/src/main/java/fr/xephi/authme/security/crypts/description/AsciiRestricted.java
index f515717e..bf179fff 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/description/AsciiRestricted.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/description/AsciiRestricted.java
@@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Denotes an encryption algorithm that is restricted to the ASCII charset.
+ * Denotes a hashing algorithm that is restricted to the ASCII charset.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
diff --git a/src/main/java/fr/xephi/authme/security/crypts/description/HasSalt.java b/src/main/java/fr/xephi/authme/security/crypts/description/HasSalt.java
index 1baf1e19..0723a4dd 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/description/HasSalt.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/description/HasSalt.java
@@ -13,10 +13,18 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
public @interface HasSalt {
- /** The type of the salt. */
+ /**
+ * The type of the salt.
+ *
+ * @return The salt type
+ */
SaltType value();
- /** For text salts, the length of the salt. */
+ /**
+ * For text salts, the length of the salt.
+ *
+ * @return The length of the salt the algorithm uses, or 0 if not defined or not applicable.
+ */
int length() default 0;
}
diff --git a/src/main/java/fr/xephi/authme/security/crypts/description/Recommendation.java b/src/main/java/fr/xephi/authme/security/crypts/description/Recommendation.java
index f37c3eac..4b832a68 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/description/Recommendation.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/description/Recommendation.java
@@ -6,13 +6,19 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Annotation to mark a hash algorithm with the usage recommendation, see {@link Usage}.
+ * Annotation to mark a hash algorithm with the usage recommendation.
+ *
+ * @see Usage
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Recommendation {
- /** The recommendation for using the hash algorithm. */
+ /**
+ * The recommendation for using the hash algorithm.
+ *
+ * @return The recommended usage
+ */
Usage value();
}
diff --git a/src/main/java/fr/xephi/authme/security/crypts/description/SaltType.java b/src/main/java/fr/xephi/authme/security/crypts/description/SaltType.java
index 7d6b225c..40b923fa 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/description/SaltType.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/description/SaltType.java
@@ -8,7 +8,7 @@ public enum SaltType {
/** Random, newly generated text. */
TEXT,
- /** Salt is based on the username, including variations and repetitions. */
+ /** Salt is based on the username, including variations and repetitions thereof. */
USERNAME,
/** No salt. */
diff --git a/src/main/java/fr/xephi/authme/security/crypts/description/Usage.java b/src/main/java/fr/xephi/authme/security/crypts/description/Usage.java
index ecf37a98..c11c335a 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/description/Usage.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/description/Usage.java
@@ -2,6 +2,12 @@ package fr.xephi.authme.security.crypts.description;
/**
* Usage recommendation that can be provided for a hash algorithm.
+ *
+ * Use the following rules of thumb:
+ *
+ *
Hashes using MD5 may be {@link #ACCEPTABLE} but never {@link #RECOMMENDED}.
+ *
Hashes using no salt or one based on the username should be {@link #DO_NOT_USE}.
+ *
*/
public enum Usage {
diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/MacBasedPRF.java b/src/main/java/fr/xephi/authme/security/pbkdf2/MacBasedPRF.java
index 5b0268c1..88ff11bf 100644
--- a/src/main/java/fr/xephi/authme/security/pbkdf2/MacBasedPRF.java
+++ b/src/main/java/fr/xephi/authme/security/pbkdf2/MacBasedPRF.java
@@ -1,15 +1,16 @@
package fr.xephi.authme.security.pbkdf2;
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
/**
- * Default PRF implementation based on standard javax.crypt.Mac mechanisms.
*
- *
+ * Default PRF implementation based on standard javax.crypt.Mac mechanisms.
+ *
*
* A free Java implementation of Password Based Key Derivation Function 2 as
* defined by RFC 2898. Copyright (c) 2007 Matthias Gärtner
diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2.java b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2.java
index d1dab302..59bc96df 100644
--- a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2.java
+++ b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2.java
@@ -75,7 +75,7 @@ public interface PBKDF2 {
/**
* Allow setting of configured parameters.
*
- * @param parameters
+ * @param parameters PBKDF2Parameters
*/
void setParameters(PBKDF2Parameters parameters);
diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Engine.java b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Engine.java
index c78f786a..d32b8752 100644
--- a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Engine.java
+++ b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Engine.java
@@ -8,20 +8,17 @@ import java.security.SecureRandom;
/**
*
dkLen intended length in octets of the derived key, a positive integer,
* at most (2^32 - 1) * hLen
*
- *
- *
* Output:
*
*
DK derived key, a dkLen-octet string
*
*
- *
- *
* A free Java implementation of Password Based Key Derivation Function 2 as
* defined by RFC 2898. Copyright (c) 2007 Matthias Gärtner
*
@@ -115,13 +108,14 @@ public class PBKDF2Engine implements PBKDF2 {
* ISO-8559-1 encoding. Output result as
* "Salt:iteration-count:PBKDF2" with binary data in hexadecimal
* encoding.
- *
+ *
* Example: Password "password" (without the quotes) leads to
* 48290A0B96C426C3:1000:973899B1D4AFEB3ED371060D0797E0EE0142BD04
- *
+ *
* @param args Supply the password as argument.
*
- * @throws IOException * @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
+ * @throws IOException an ioexception occured
+ * @throws NoSuchAlgorithmException a NoSuchAlgorithmException occured
*/
public static void main(String[] args)
throws IOException, NoSuchAlgorithmException {
@@ -266,8 +260,8 @@ public class PBKDF2Engine implements PBKDF2 {
/**
* Integer division with ceiling function.
*
- * @param a
- * @param b
+ * @param a Integer
+ * @param b Integer
*
* @return ceil(a/b) * @see RFC 2898 5.2 Step
* 2.
@@ -288,7 +282,7 @@ public class PBKDF2Engine implements PBKDF2 {
* @param prf Pseudo Random Function
* @param S Salt as array of bytes
* @param c Iteration count
- * @param blockIndex
+ * @param blockIndex Integer
*
* @see RFC 2898 5.2 Step
* 3.
@@ -314,8 +308,8 @@ public class PBKDF2Engine implements PBKDF2 {
* Block-Xor. Xor source bytes into destination byte buffer. Destination
* buffer must be same length or less than source buffer.
*
- * @param dest
- * @param src
+ * @param dest byte array
+ * @param src byte array
*/
protected void xor(byte[] dest, byte[] src) {
for (int i = 0; i < dest.length; i++) {
@@ -326,9 +320,9 @@ public class PBKDF2Engine implements PBKDF2 {
/**
* Four-octet encoding of the integer i, most significant octet first.
*
- * @param dest
- * @param offset
- * @param i
+ * @param dest byte array
+ * @param offset Integer
+ * @param i Integer
*
* @see RFC 2898 5.2 Step
* 3.
diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Parameters.java b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Parameters.java
index b7b158a0..04abaa9f 100644
--- a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Parameters.java
+++ b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Parameters.java
@@ -5,8 +5,6 @@ package fr.xephi.authme.security.pbkdf2;
* Parameter data holder for PBKDF2 configuration.
*
*
- *
- *
* A free Java implementation of Password Based Key Derivation Function 2 as
* defined by RFC 2898. Copyright (c) 2007 Matthias Gärtner
*
diff --git a/src/main/java/fr/xephi/authme/settings/OtherAccounts.java b/src/main/java/fr/xephi/authme/settings/OtherAccounts.java
index 063e6af6..6e2796a3 100644
--- a/src/main/java/fr/xephi/authme/settings/OtherAccounts.java
+++ b/src/main/java/fr/xephi/authme/settings/OtherAccounts.java
@@ -1,13 +1,13 @@
package fr.xephi.authme.settings;
-import org.bukkit.Bukkit;
-import org.bukkit.entity.Player;
-
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
/**
* @author Xephi59
* @version $Revision: 1.0 $
@@ -88,7 +88,7 @@ public class OtherAccounts extends CustomConfiguration {
*
* @param uuid UUID
*
- * @return List
+ * @return StringList
*/
public List getAllPlayersByUUID(UUID uuid) {
return this.getStringList(uuid.toString());
diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java
index 6080d45e..cfb28f75 100644
--- a/src/main/java/fr/xephi/authme/settings/Settings.java
+++ b/src/main/java/fr/xephi/authme/settings/Settings.java
@@ -1,32 +1,27 @@
package fr.xephi.authme.settings;
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import fr.xephi.authme.AuthMe;
+import fr.xephi.authme.ConsoleLogger;
+import fr.xephi.authme.datasource.DataSource;
+import fr.xephi.authme.datasource.DataSource.DataSourceType;
+import fr.xephi.authme.security.HashAlgorithm;
+import fr.xephi.authme.util.Wrapper;
+import org.bukkit.configuration.file.YamlConfiguration;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
-import org.bukkit.configuration.file.YamlConfiguration;
-
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-
-import fr.xephi.authme.AuthMe;
-import fr.xephi.authme.ConsoleLogger;
-import fr.xephi.authme.datasource.DataSource;
-import fr.xephi.authme.datasource.DataSource.DataSourceType;
-import fr.xephi.authme.security.HashAlgorithm;
-import fr.xephi.authme.util.Wrapper;
-
/**
*/
public final class Settings {
@@ -122,7 +117,7 @@ public final class Settings {
/**
* Method reload.
*
- * @throws Exception
+ * @throws Exception if something went wrong
*/
public static void reload() throws Exception {
plugin.getLogger().info("Loading Configuration File...");
@@ -141,7 +136,6 @@ public final class Settings {
messageFile = new File(PLUGIN_FOLDER, "messages" + File.separator + "messages_" + messagesLanguage + ".yml");
}
-
public static void loadVariables() {
helpHeader = configFile.getString("settings.helpHeader", "AuthMeReloaded");
messagesLanguage = checkLang(configFile.getString("settings.messagesLanguage", "en").toLowerCase());
@@ -311,35 +305,22 @@ public final class Settings {
}
private static String loadEmailText() {
- if (!EMAIL_FILE.exists())
- saveDefaultEmailText();
- StringBuilder str = new StringBuilder();
- try {
- BufferedReader in = new BufferedReader(new FileReader(EMAIL_FILE));
- String s;
- while ((s = in.readLine()) != null)
- str.append(s);
- in.close();
- } catch (IOException ignored) {
+ if (!EMAIL_FILE.exists()) {
+ plugin.saveResource("email.html", false);
}
- return str.toString();
- }
-
- private static void saveDefaultEmailText() {
- InputStream file = plugin.getResource("email.html");
- StringBuilder str = new StringBuilder();
try {
- BufferedReader in = new BufferedReader(new InputStreamReader(file, Charset.forName("utf-8")));
- String s;
- while ((s = in.readLine()) != null)
- str.append(s);
- in.close();
- Files.touch(EMAIL_FILE);
- Files.write(str.toString(), EMAIL_FILE, Charsets.UTF_8);
- } catch (Exception ignored) {
+ return Files.toString(EMAIL_FILE, Charsets.UTF_8);
+ } catch (IOException e) {
+ ConsoleLogger.showError(e.getMessage());
+ ConsoleLogger.writeStackTrace(e);
+ return "";
}
}
+ /**
+ * @param key the key to set
+ * @param value the value to set
+ */
public static void setValue(String key, Object value) {
instance.set(key, value);
save();
@@ -380,8 +361,9 @@ public final class Settings {
* return false if ip and name doesn't match with player that join the
* server, so player has a restricted access
*
- * @param name String
- * @param ip String
+ * @param name String
+ * @param ip String
+ * @param domain String
*
* @return boolean
*/
@@ -396,14 +378,12 @@ public final class Settings {
String testIp = args[1];
if (testName.equalsIgnoreCase(name)) {
nameFound = true;
- if (ip != null)
- {
+ if (ip != null) {
if (testIp.equalsIgnoreCase(ip)) {
trueOnce = true;
}
}
- if (domain != null)
- {
+ if (domain != null) {
if (testIp.equalsIgnoreCase(domain)) {
trueOnce = true;
}
@@ -737,10 +717,9 @@ public final class Settings {
changes = true;
}
- if (!contains("settings.preventOtherCase"))
- {
- set("settings.preventOtherCase", false);
- changes = true;
+ if (!contains("settings.preventOtherCase")) {
+ set("settings.preventOtherCase", false);
+ changes = true;
}
if (contains("Email.mailText")) {
@@ -749,15 +728,14 @@ public final class Settings {
}
if (!contains("Security.stop.kickPlayersBeforeStopping")) {
- set("Security.stop.kickPlayersBeforeStopping", true);
- changes = true;
+ set("Security.stop.kickPlayersBeforeStopping", true);
+ changes = true;
}
if (!contains("Email.emailOauth2Token"))
- set("Email.emailOauth2Token", "");
+ set("Email.emailOauth2Token", "");
- if (!contains("Hook.sendPlayerTo"))
- {
+ if (!contains("Hook.sendPlayerTo")) {
set("Hooks.sendPlayerTo", "");
changes = true;
}
@@ -768,11 +746,21 @@ public final class Settings {
}
}
+ /**
+ * @param path
+ *
+ * @return
+ */
private static boolean contains(String path) {
return configFile.contains(path);
}
// public because it's used in AuthMe at one place
+
+ /**
+ * @param path String
+ * @param value String
+ */
public void set(String path, Object value) {
configFile.set(path, value);
}
diff --git a/src/main/java/fr/xephi/authme/util/CollectionUtils.java b/src/main/java/fr/xephi/authme/util/CollectionUtils.java
index 101ad968..5c3eaa7f 100644
--- a/src/main/java/fr/xephi/authme/util/CollectionUtils.java
+++ b/src/main/java/fr/xephi/authme/util/CollectionUtils.java
@@ -15,6 +15,8 @@ public final class CollectionUtils {
/**
* Get a range from a list based on start and count parameters in a safe way.
*
+ * @param element
+ * @param list The List
* @param start The start index
* @param count The number of elements to add
*
@@ -34,6 +36,8 @@ public final class CollectionUtils {
/**
* Get all elements from a list starting from the given index.
*
+ * @param element
+ * @param list The List
* @param start The start index
*
* @return The sublist of all elements from index {@code start} and on; empty list
@@ -46,6 +50,11 @@ public final class CollectionUtils {
return getRange(list, start, list.size() - start);
}
+ /**
+ * @param element
+ * @param coll Collection
+ * @return boolean Boolean
+ */
public static boolean isEmpty(Collection coll) {
return coll == null || coll.isEmpty();
}
diff --git a/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java b/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java
index 9acc3c25..ef78c5fd 100644
--- a/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java
+++ b/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java
@@ -142,30 +142,44 @@ public abstract class AbstractEncryptionMethodTest {
return method.comparePassword(password, hashes.get(password), USERNAME);
}
- // @org.junit.Test public void a() { AbstractEncryptionMethodTest.generateTest(); }
- // TODO #364: Remove this method
+ /**
+ * Generates a test class for a given encryption method. Simply create a test class and run the following code,
+ * replacing {@code XXX} with the actual class:
+ *
+ * The output is the entire test class.
+ *
+ * @param method The method to create a test class for
+ */
static void generateTest(EncryptionMethod method) {
String className = method.getClass().getSimpleName();
+ // Create javadoc and "public class extends" and the constructor call "super(new Class(),"
System.out.println("/**\n * Test for {@link " + className + "}.\n */");
System.out.println("public class " + className + "Test extends AbstractEncryptionMethodTest {");
System.out.println("\n\tpublic " + className + "Test() {");
System.out.println("\t\tsuper(new " + className + "(),");
+ // Iterate through the GIVEN_PASSWORDS and generate a hash so we can always check it later
String delim = ", ";
for (String password : GIVEN_PASSWORDS) {
if (password.equals(GIVEN_PASSWORDS[GIVEN_PASSWORDS.length - 1])) {
delim = "); ";
}
+ // Encr. method uses separate salt, so we need to call the constructor that takes HashedPassword instances
if (method.hasSeparateSalt()) {
HashedPassword hashedPassword = method.computeHash(password, USERNAME);
System.out.println(String.format("\t\tnew HashedPassword(\"%s\", \"%s\")%s// %s",
hashedPassword.getHash(), hashedPassword.getSalt(), delim, password));
} else {
+ // Encryption method doesn't have separate salt, so simply pass the generated hash to the constructor
System.out.println("\t\t\"" + method.computeHash(password, USERNAME).getHash()
+ "\"" + delim + "// " + password);
}
}
+
+ // Close the constructor and class declarations
System.out.println("\t}");
System.out.println("\n}");
}
diff --git a/src/tools/docs/hash_algorithms.md b/src/tools/docs/hash_algorithms.md
new file mode 100644
index 00000000..60a011b4
--- /dev/null
+++ b/src/tools/docs/hash_algorithms.md
@@ -0,0 +1,78 @@
+
+
+
+## Hash Algorithms
+AuthMe supports the following hash algorithms for storing your passwords safely.
+
+
+Algorithm | Recommendation | Hash length | ASCII | | Salt type | Length | Separate?
+--------- | -------------- | ----------- | ----- | --- | --------- | ------ | ---------
+BCRYPT | Recommended | 60 | | | Text | |
+BCRYPT2Y | Recommended | 60 | | | Text | 22 |
+CRAZYCRYPT1 | Do not use | 128 | | | Username | |
+DOUBLEMD5 | Do not use | 32 | | | None | |
+IPB3 | Acceptable | 32 | | | Text | 5 | Y
+JOOMLA | Recommended | 65 | | | Text | 32 |
+MD5 | Do not use | 32 | | | None | |
+MD5VB | Acceptable | 56 | | | Text | 16 |
+MYBB | Acceptable | 32 | | | Text | 8 | Y
+PBKDF2 | Does not work | 330 | | | Text | 12 |
+PBKDF2DJANGO | Acceptable | 77 | Y | | Text | 12 |
+PHPBB | Acceptable | 34 | | | Text | 16 |
+PHPFUSION | Do not use | 64 | Y | | | | Y
+ROYALAUTH | Do not use | 128 | | | None | |
+SALTED2MD5 | Acceptable | 32 | | | Text | | Y
+SALTEDSHA512 | Recommended | 128 | | | | | Y
+SHA1 | Do not use | 40 | | | None | |
+SHA256 | Recommended | 86 | | | Text | 16 |
+SHA512 | Do not use | 128 | | | None | |
+SMF | Do not use | 40 | | | Username | |
+WBB3 | Acceptable | 40 | | | Text | 40 | Y
+WBB4 | Does not work | 60 | | | Text | 8 |
+WHIRLPOOL | Do not use | 128 | | | None | |
+WORDPRESS | Do not use | 34 | | | Text | 9 |
+XAUTH | Recommended | 140 | | | Text | 12 |
+CUSTOM | | | | | | | |
+
+
+
+### Columns
+#### Algorithm
+The algorithm is the hashing algorithm used to store passwords with. Default is SHA256 and is recommended.
+You can change the hashing algorithm in the config.yml: under `security`, locate `passwordHash`.
+
+#### Recommendation
+The recommendation lists our usage recommendation in terms of how secure it is (not how _well_ the algorithm works!).
+- Recommended: The hash algorithm appears to be cryptographically secure and is one we recommend.
+- Acceptable: There are safer algorithms that can be chosen but using the algorithm is generally OK.
+- Do not use: Hash algorithm isn't sufficiently secure. Use only if required to hook into another system.
+- Does not work: The algorithm does not work properly; do not use.
+
+#### Hash Length
+The length of the hashes the algorithm produces. Note that the hash length is not (primarily) indicative of
+whether an algorithm is secure or not.
+
+#### ASCII
+If denoted with a **y**, means that the algorithm is restricted to ASCII characters only, i.e. it will simply ignore
+"special characters" such as `ÿ` or `Â`. Note that we do not recommend the use of "special characters" in passwords.
+
+#### Salt Columns
+Before hashing, a _salt_ may be appended to the password to make the hash more secure. The following columns describe
+the salt the algorithm uses.
+
+
+##### Salt Type
+We do not recommend the usage
+of any algorithm that doesn't use a randomly generated text as salt. This "salt type" column indicates what type of
+salt the algorithm uses:
+- Text: randomly generated text (see also the following column, "Length")
+- Username: the salt is constructed from the username (bad)
+- None: the algorithm uses no salt (bad)
+
+##### Length
+If applicable (salt type is "Text"), indicates the length of the generated salt. The longer the better.
+If this column is empty when the salt type is "Text", it typically means the salt length can be defined in config.yml.
+
+##### Separate
+If denoted with a **y**, it means that the salt is stored in a separate column in the database. This is neither good
+or bad.
diff --git a/src/tools/hashmethods/EncryptionMethodInfoGatherer.java b/src/tools/hashmethods/EncryptionMethodInfoGatherer.java
new file mode 100644
index 00000000..07c588ea
--- /dev/null
+++ b/src/tools/hashmethods/EncryptionMethodInfoGatherer.java
@@ -0,0 +1,135 @@
+package hashmethods;
+
+import fr.xephi.authme.security.HashAlgorithm;
+import fr.xephi.authme.security.crypts.EncryptionMethod;
+import fr.xephi.authme.security.crypts.HexSaltedMethod;
+import fr.xephi.authme.security.crypts.description.AsciiRestricted;
+import fr.xephi.authme.security.crypts.description.HasSalt;
+import fr.xephi.authme.security.crypts.description.Recommendation;
+
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+
+/**
+ * Gathers information on {@link fr.xephi.authme.security.crypts.EncryptionMethod} implementations based on
+ * the annotations in {@link fr.xephi.authme.security.crypts.description}.
+ */
+public class EncryptionMethodInfoGatherer {
+
+ @SuppressWarnings("unchecked")
+ private final static Set> RELEVANT_ANNOTATIONS =
+ newHashSet(HasSalt.class, Recommendation.class, AsciiRestricted.class);
+
+ private Map descriptions;
+
+ public EncryptionMethodInfoGatherer() {
+ descriptions = new LinkedHashMap<>();
+ constructDescriptions();
+ }
+
+ public Map getDescriptions() {
+ return descriptions;
+ }
+
+ private void constructDescriptions() {
+ for (HashAlgorithm algorithm : HashAlgorithm.values()) {
+ Class extends EncryptionMethod> methodClazz = algorithm.getClazz();
+ if (!HashAlgorithm.CUSTOM.equals(algorithm) && !methodClazz.isAnnotationPresent(Deprecated.class)) {
+ MethodDescription description = createDescription(methodClazz);
+ descriptions.put(algorithm, description);
+ }
+ }
+ }
+
+ private static MethodDescription createDescription(Class extends EncryptionMethod> clazz) {
+ EncryptionMethod method = instantiateMethod(clazz);
+ MethodDescription description = new MethodDescription(clazz);
+ description.setHashLength(method.computeHash("test", "user").getHash().length());
+ description.setHasSeparateSalt(method.hasSeparateSalt());
+
+ Map, Annotation> annotationMap = gatherAnnotations(clazz);
+ if (annotationMap.containsKey(HasSalt.class)) {
+ setSaltInformation(description, returnTyped(annotationMap, HasSalt.class), method);
+ }
+ if (annotationMap.containsKey(Recommendation.class)) {
+ description.setUsage(returnTyped(annotationMap, Recommendation.class).value());
+ }
+ if (annotationMap.containsKey(AsciiRestricted.class)) {
+ description.setAsciiRestricted(true);
+ }
+ return description;
+ }
+
+ private static Map, Annotation> gatherAnnotations(Class> methodClass) {
+ // Note ljacqu 20151231: The map could be Map, Annotation> and it has the constraint
+ // that for a key Class, the value is of type T. We write a simple "Class>" for brevity.
+ Map, Annotation> collection = new HashMap<>();
+ Class> currentMethodClass = methodClass;
+ while (currentMethodClass != null) {
+ getRelevantAnnotations(currentMethodClass, collection);
+ currentMethodClass = getSuperClass(currentMethodClass);
+ }
+ return collection;
+ }
+
+ // Parameters could be Class extends EncryptionMethod>; Map, Annotation>
+ // but the constraint doesn't have any technical relevance, so just clutters the code
+ private static void getRelevantAnnotations(Class> methodClass, Map, Annotation> collection) {
+ for (Annotation annotation : methodClass.getAnnotations()) {
+ if (RELEVANT_ANNOTATIONS.contains(annotation.annotationType())
+ && !collection.containsKey(annotation.annotationType())) {
+ collection.put(annotation.annotationType(), annotation);
+ }
+ }
+ }
+
+ /**
+ * Returns the super class of the given encryption method if it is also of EncryptionMethod type.
+ * (Anything beyond EncryptionMethod is not of interest.)
+ */
+ private static Class> getSuperClass(Class> methodClass) {
+ Class> zuper = methodClass.getSuperclass();
+ if (EncryptionMethod.class.isAssignableFrom(zuper)) {
+ return zuper;
+ }
+ return null;
+ }
+
+ /**
+ * Set the salt information for the given encryption method and the found {@link HasSalt} annotation.
+ * Also gets the salt length from {@link HexSaltedMethod#getSaltLength()} for such instances.
+ *
+ * @param description The description to update
+ * @param hasSalt The associated HasSalt annotation
+ * @param method The encryption method
+ */
+ private static void setSaltInformation(MethodDescription description, HasSalt hasSalt, EncryptionMethod method) {
+ description.setSaltType(hasSalt.value());
+ if (hasSalt.length() != 0) {
+ description.setSaltLength(hasSalt.length());
+ } else if (method instanceof HexSaltedMethod) {
+ int saltLength = ((HexSaltedMethod) method).getSaltLength();
+ description.setSaltLength(saltLength);
+ }
+ }
+
+ private static EncryptionMethod instantiateMethod(Class extends EncryptionMethod> clazz) {
+ try {
+ return clazz.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new RuntimeException("Could not instantiate " + clazz, e);
+ }
+ }
+
+ // Convenience method for retrieving an annotation in a typed fashion.
+ // We know implicitly that the key of the map always corresponds to the type of the value
+ private static T returnTyped(Map, Annotation> map, Class key) {
+ return key.cast(map.get(key));
+ }
+
+}
diff --git a/src/tools/hashmethods/HashAlgorithmsDescriptionTask.java b/src/tools/hashmethods/HashAlgorithmsDescriptionTask.java
new file mode 100644
index 00000000..5213d6b7
--- /dev/null
+++ b/src/tools/hashmethods/HashAlgorithmsDescriptionTask.java
@@ -0,0 +1,100 @@
+package hashmethods;
+
+import fr.xephi.authme.security.HashAlgorithm;
+import fr.xephi.authme.settings.Settings;
+import fr.xephi.authme.util.WrapperMock;
+import utils.ANewMap;
+import utils.FileUtils;
+import utils.TagReplacer;
+import utils.ToolTask;
+import utils.ToolsConstants;
+
+import java.util.Map;
+import java.util.Scanner;
+
+/**
+ * Task for generating the markdown page describing the AuthMe hash algorithms.
+ *
+ * @see {@link fr.xephi.authme.security.HashAlgorithm}
+ */
+public class HashAlgorithmsDescriptionTask implements ToolTask {
+
+ private static final String CUR_FOLDER = ToolsConstants.TOOLS_SOURCE_ROOT + "hashmethods/";
+ private static final String OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "hash_algorithms.md";
+
+ @Override
+ public void execute(Scanner scanner) {
+ // Unfortunately, we need the Wrapper to be around to work with Settings, and certain encryption methods
+ // directly read from the Settings file
+ WrapperMock.createInstance();
+ Settings.bCryptLog2Rounds = 8;
+ Settings.saltLength = 8;
+
+ // Gather info and construct a row for each method
+ EncryptionMethodInfoGatherer infoGatherer = new EncryptionMethodInfoGatherer();
+ Map descriptions = infoGatherer.getDescriptions();
+ final String methodRows = constructMethodRows(descriptions);
+
+ // Write to the docs file
+ Map tags = ANewMap.with("method_rows", methodRows).build();
+ FileUtils.generateFileFromTemplate(CUR_FOLDER + "hash_algorithms.tpl.md", OUTPUT_FILE, tags);
+ }
+
+ private static String constructMethodRows(Map descriptions) {
+ final String rowTemplate = FileUtils.readFromFile(CUR_FOLDER + "hash_algorithms_row.tpl.md");
+ StringBuilder result = new StringBuilder();
+ for (Map.Entry entry : descriptions.entrySet()) {
+ MethodDescription description = entry.getValue();
+ Map tags = ANewMap
+ .with("name", asString(entry.getKey()))
+ .and("recommendation", asString(description.getUsage()))
+ .and("hash_length", asString(description.getHashLength()))
+ .and("ascii_restricted", asString(description.isAsciiRestricted()))
+ .and("salt_type", asString(description.getSaltType()))
+ .and("salt_length", asString(description.getSaltLength()))
+ .and("separate_salt", asString(description.hasSeparateSalt()))
+ .build();
+ result.append(TagReplacer.applyReplacements(rowTemplate, tags));
+ }
+ return result.toString();
+ }
+
+ @Override
+ public String getTaskName() {
+ return "describeHashAlgos";
+ }
+
+ // ----
+ // String representations
+ // ----
+ private static String asString(boolean value) {
+ return value ? "Y" : "";
+ }
+
+ private static String asString(int value) {
+ return String.valueOf(value);
+ }
+
+ private static String asString(Integer value) {
+ if (value == null) {
+ return "";
+ }
+ return String.valueOf(value);
+ }
+
+ private static String asString(HashAlgorithm value) {
+ return value.toString();
+ }
+
+ private static > String asString(E value) {
+ if (value == null) {
+ return "";
+ }
+ // Get the enum name and replace something like "DO_NOT_USE" to "Do not use"
+ String enumName = value.toString().replace("_", " ");
+ return enumName.length() > 2
+ ? enumName.substring(0, 1) + enumName.substring(1).toLowerCase()
+ : enumName;
+ }
+
+}
diff --git a/src/tools/hashmethods/MethodDescription.java b/src/tools/hashmethods/MethodDescription.java
new file mode 100644
index 00000000..45aaf448
--- /dev/null
+++ b/src/tools/hashmethods/MethodDescription.java
@@ -0,0 +1,85 @@
+package hashmethods;
+
+import fr.xephi.authme.security.crypts.EncryptionMethod;
+import fr.xephi.authme.security.crypts.description.SaltType;
+import fr.xephi.authme.security.crypts.description.Usage;
+
+/**
+ * Description of a {@link EncryptionMethod}.
+ */
+public class MethodDescription {
+
+ /** The implementation class the description belongs to. */
+ private final Class extends EncryptionMethod> method;
+ /** The type of the salt that is used. */
+ private SaltType saltType;
+ /** The length of the salt for SaltType.TEXT salts. */
+ private Integer saltLength;
+ /** The usage recommendation. */
+ private Usage usage;
+ /** Whether or not the encryption method is restricted to ASCII characters for proper functioning. */
+ private boolean asciiRestricted;
+ /** Whether or not the encryption method requires its salt stored separately. */
+ private boolean hasSeparateSalt;
+ /** The length of the hash output, based on a test hash (i.e. assumes same length for all hashes.) */
+ private int hashLength;
+
+ public MethodDescription(Class extends EncryptionMethod> method) {
+ this.method = method;
+ }
+
+
+ // Trivial getters and setters
+ public Class extends EncryptionMethod> getMethod() {
+ return method;
+ }
+
+ public SaltType getSaltType() {
+ return saltType;
+ }
+
+ public void setSaltType(SaltType saltType) {
+ this.saltType = saltType;
+ }
+
+ public Integer getSaltLength() {
+ return saltLength;
+ }
+
+ public void setSaltLength(int saltLength) {
+ this.saltLength = saltLength;
+ }
+
+ public Usage getUsage() {
+ return usage;
+ }
+
+ public void setUsage(Usage usage) {
+ this.usage = usage;
+ }
+
+ public boolean isAsciiRestricted() {
+ return asciiRestricted;
+ }
+
+ public void setAsciiRestricted(boolean asciiRestricted) {
+ this.asciiRestricted = asciiRestricted;
+ }
+
+ public boolean hasSeparateSalt() {
+ return hasSeparateSalt;
+ }
+
+ public void setHasSeparateSalt(boolean hasSeparateSalt) {
+ this.hasSeparateSalt = hasSeparateSalt;
+ }
+
+ public int getHashLength() {
+ return hashLength;
+ }
+
+ public void setHashLength(int hashLength) {
+ this.hashLength = hashLength;
+ }
+
+}
diff --git a/src/tools/hashmethods/hash_algorithms.tpl.md b/src/tools/hashmethods/hash_algorithms.tpl.md
new file mode 100644
index 00000000..b466eeeb
--- /dev/null
+++ b/src/tools/hashmethods/hash_algorithms.tpl.md
@@ -0,0 +1,53 @@
+
+
+
+## Hash Algorithms
+AuthMe supports the following hash algorithms for storing your passwords safely.
+
+
+Algorithm | Recommendation | Hash length | ASCII | | Salt type | Length | Separate?
+--------- | -------------- | ----------- | ----- | --- | --------- | ------ | ---------
+{method_rows}CUSTOM | | | | | | | |
+
+
+
+### Columns
+#### Algorithm
+The algorithm is the hashing algorithm used to store passwords with. Default is SHA256 and is recommended.
+You can change the hashing algorithm in the config.yml: under `security`, locate `passwordHash`.
+
+#### Recommendation
+The recommendation lists our usage recommendation in terms of how secure it is (not how _well_ the algorithm works!).
+- Recommended: The hash algorithm appears to be cryptographically secure and is one we recommend.
+- Acceptable: There are safer algorithms that can be chosen but using the algorithm is generally OK.
+- Do not use: Hash algorithm isn't sufficiently secure. Use only if required to hook into another system.
+- Does not work: The algorithm does not work properly; do not use.
+
+#### Hash Length
+The length of the hashes the algorithm produces. Note that the hash length is not (primarily) indicative of
+whether an algorithm is secure or not.
+
+#### ASCII
+If denoted with a **y**, means that the algorithm is restricted to ASCII characters only, i.e. it will simply ignore
+"special characters" such as `ÿ` or `Â`. Note that we do not recommend the use of "special characters" in passwords.
+
+#### Salt Columns
+Before hashing, a _salt_ may be appended to the password to make the hash more secure. The following columns describe
+the salt the algorithm uses.
+
+
+##### Salt Type
+We do not recommend the usage
+of any algorithm that doesn't use a randomly generated text as salt. This "salt type" column indicates what type of
+salt the algorithm uses:
+- Text: randomly generated text (see also the following column, "Length")
+- Username: the salt is constructed from the username (bad)
+- None: the algorithm uses no salt (bad)
+
+##### Length
+If applicable (salt type is "Text"), indicates the length of the generated salt. The longer the better.
+If this column is empty when the salt type is "Text", it typically means the salt length can be defined in config.yml.
+
+##### Separate
+If denoted with a **y**, it means that the salt is stored in a separate column in the database. This is neither good
+or bad.
diff --git a/src/tools/hashmethods/hash_algorithms_row.tpl.md b/src/tools/hashmethods/hash_algorithms_row.tpl.md
new file mode 100644
index 00000000..411d1127
--- /dev/null
+++ b/src/tools/hashmethods/hash_algorithms_row.tpl.md
@@ -0,0 +1 @@
+{name} | {recommendation} | {hash_length} | {ascii_restricted} | | {salt_type} | {salt_length} | {separate_salt}