Implemented password hashing

With the help of Argon2 lib
This commit is contained in:
samo_lego 2019-11-09 17:01:59 +01:00
parent bac5c879fa
commit 3e06344197
6 changed files with 97 additions and 37 deletions

View File

@ -23,7 +23,7 @@ dependencies {
modCompile "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modCompile "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// Argon2 library for password hashing // Argon2 library for password hashing
compile 'de.mkammerer:argon2-jvm-nolibs:2.6' compile 'de.mkammerer:argon2-jvm:2.6'
// IDEA stuff // IDEA stuff
implementation 'org.jetbrains:annotations:15.0' implementation 'org.jetbrains:annotations:15.0'

View File

@ -1,5 +1,7 @@
package org.samo_lego.simpleauth; package org.samo_lego.simpleauth;
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
import net.fabricmc.api.DedicatedServerModInitializer; import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.fabric.api.event.player.AttackBlockCallback; import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
import net.fabricmc.fabric.api.registry.CommandRegistry; import net.fabricmc.fabric.api.registry.CommandRegistry;
@ -29,9 +31,9 @@ public class SimpleAuth implements DedicatedServerModInitializer {
// Creating data directory (database is stored there) // Creating data directory (database is stored there)
File file = new File("./mods/SimpleAuth"); File file = new File("./mods/SimpleAuth");
if (!file.exists() && !file.mkdir()) { if (!file.exists() && !file.mkdir())
LOGGER.error("Error creating directory"); LOGGER.error("Error creating directory");
}
// Registering the commands // Registering the commands
CommandRegistry.INSTANCE.register(false, dispatcher -> { CommandRegistry.INSTANCE.register(false, dispatcher -> {
@ -49,7 +51,7 @@ public class SimpleAuth implements DedicatedServerModInitializer {
// Connection to database // Connection to database
SimpleAuthDatabase.main(); SimpleAuthDatabase.main();
} }
public static HashSet<ServerPlayerEntity> authenticatedUsers = new HashSet<>(); public static HashSet<ServerPlayerEntity> authenticatedUsers = new HashSet<>();
public static boolean isAuthenticated(ServerPlayerEntity player) { return authenticatedUsers.contains(player); } public static boolean isAuthenticated(ServerPlayerEntity player) { return authenticatedUsers.contains(player); }

View File

@ -2,6 +2,8 @@ package org.samo_lego.simpleauth.commands;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
@ -15,8 +17,11 @@ import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal; import static net.minecraft.server.command.CommandManager.literal;
public class LoginCommand { public class LoginCommand {
private static LiteralText PleaseLogin = new LiteralText("§4Type /login <password> to login."); private static LiteralText pleaseLogin = new LiteralText("§4Type /login <password> to login.");
private static TranslatableText EnterPassword = new TranslatableText("command.simpleauth.password"); private static TranslatableText enterPassword = new TranslatableText("command.simpleauth.password");
private static TranslatableText wrongPassword = new TranslatableText("command.simpleauth.wrongPassword");
private static TranslatableText alreadyAuthenticated = new TranslatableText("command.simpleauth.alreadyAuthenticated");
private static Text text = new LiteralText("You have entered login command");
public static void registerCommand(CommandDispatcher<ServerCommandSource> dispatcher) { public static void registerCommand(CommandDispatcher<ServerCommandSource> dispatcher) {
// Registering the "/login" command // Registering the "/login" command
@ -25,23 +30,40 @@ public class LoginCommand {
.executes(ctx -> login(ctx.getSource(), getString(ctx, "password")) // Tries to authenticate user .executes(ctx -> login(ctx.getSource(), getString(ctx, "password")) // Tries to authenticate user
)) ))
.executes(ctx -> { .executes(ctx -> {
ctx.getSource().getPlayer().sendMessage(EnterPassword); ctx.getSource().getPlayer().sendMessage(enterPassword);
return 1; return 1;
})); }));
} }
// Method called for checking the password // Method called for checking the password
private static int login(ServerCommandSource source, String pass) throws CommandSyntaxException { private static int login(ServerCommandSource source, String pass) throws CommandSyntaxException {
String savedHashed = "judf"; // Hashed password provided upon registration
// Getting the player who send the command // Getting the player who send the command
ServerPlayerEntity player = source.getPlayer(); ServerPlayerEntity player = source.getPlayer();
// Comparing hashed password with one from the file if(SimpleAuth.isAuthenticated(player)) {
if(true/*BCrypt.checkpw(pass, savedHashed)*/){ //From database player.sendMessage(alreadyAuthenticated);
Text text = new LiteralText(source.getName() + ", you have entered login command"); }
source.getMinecraftServer().getPlayerManager().broadcastChatMessage(text, false); else {
SimpleAuth.authenticatedUsers.add(player); // Create instance
System.out.println(SimpleAuth.authenticatedUsers); Argon2 argon2 = Argon2Factory.create();
// Read password from user
char[] password = pass.toCharArray();
try {
// Hashed password from DB
String hashed = argon2.hash(10, 65536, 1, password);
// Verify password
if (argon2.verify(hashed, password)) {
SimpleAuth.authenticatedUsers.add(player);
player.sendMessage(text);
} else {
player.sendMessage(wrongPassword);
}
} finally {
// Wipe confidential data
argon2.wipeArray(password);
}
} }
return 1; // Success return 1; // Success
} }

View File

@ -1,14 +1,16 @@
package org.samo_lego.simpleauth.commands; package org.samo_lego.simpleauth.commands;
import com.google.common.io.Files;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
import net.minecraft.text.TranslatableText; import net.minecraft.text.TranslatableText;
import org.samo_lego.simpleauth.SimpleAuth;
import org.samo_lego.simpleauth.database.SimpleAuthDatabase; import org.samo_lego.simpleauth.database.SimpleAuthDatabase;
import java.util.Objects; import java.util.Objects;
import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.getString;
import static com.mojang.brigadier.arguments.StringArgumentType.word; import static com.mojang.brigadier.arguments.StringArgumentType.word;
import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.argument;
@ -16,8 +18,9 @@ import static net.minecraft.server.command.CommandManager.literal;
public class RegisterCommand { public class RegisterCommand {
private static TranslatableText PleaseRegister = new TranslatableText("§4Type /register <password> <password> to login."); private static TranslatableText pleaseRegister = new TranslatableText("§4Type /register <password> <password> to login.");
private static TranslatableText EnterPassword = new TranslatableText("command.simpleauth.passwordTwice"); private static TranslatableText enterPassword = new TranslatableText("command.simpleauth.passwordTwice");
private static TranslatableText alreadyAuthenticated = new TranslatableText("command.simpleauth.alreadyAuthenticated");
public static void registerCommand(CommandDispatcher<ServerCommandSource> dispatcher) { public static void registerCommand(CommandDispatcher<ServerCommandSource> dispatcher) {
@ -28,25 +31,42 @@ public class RegisterCommand {
.executes( ctx -> register(ctx.getSource(), getString(ctx, "password"), getString(ctx, "passwordAgain"))) .executes( ctx -> register(ctx.getSource(), getString(ctx, "password"), getString(ctx, "passwordAgain")))
)) ))
.executes(ctx -> { .executes(ctx -> {
ctx.getSource().getPlayer().sendMessage(EnterPassword); ctx.getSource().getPlayer().sendMessage(enterPassword);
return 1; return 1;
})); }));
} }
// Registering our "register" command (ik, sounds a bit confusing) // Method called for hashing the password & writing to DB
private static int register(ServerCommandSource source, String pass1, String pass2) { private static int register(ServerCommandSource source, String pass1, String pass2) throws CommandSyntaxException {
if(pass1.equals(pass2)){ ServerPlayerEntity player = source.getPlayer();
// Hashing the password if(SimpleAuth.isAuthenticated(player)) {
SimpleAuthDatabase.insert(Objects.requireNonNull(source.getEntity()).getUuidAsString(), source.getName(), pass1); player.sendMessage(alreadyAuthenticated);
source.getMinecraftServer().getPlayerManager().broadcastChatMessage( }
new LiteralText(source.getName() + ", you have registered successfully!"), else if(pass1.equals(pass2)) { // Hashing the password with the Argon2 power
false // Create instance
); Argon2 argon2 = Argon2Factory.create();
// Read password from user
char[] password = pass1.toCharArray();
try {
// Hash password
String hash = argon2.hash(10, 65536, 1, password);
// Writing into database
SimpleAuthDatabase.insert(Objects.requireNonNull(source.getEntity()).getUuidAsString(), source.getName(), hash);
SimpleAuth.authenticatedUsers.add(player);
// Letting the player know it was successful
player.sendMessage(
new LiteralText(source.getName() + ", you have registered successfully!")
);
} finally {
// Wipe confidential data
argon2.wipeArray(password);
}
} }
else else
source.getMinecraftServer().getPlayerManager().broadcastChatMessage( player.sendMessage(
new LiteralText(source.getName() + ", passwords must match!"), new LiteralText(source.getName() + ", passwords must match!")
false
); );
return 1; return 1;
} }

View File

@ -56,6 +56,7 @@ public class SimpleAuthDatabase {
" `Username` VARCHAR(16) NOT NULL,\n" + " `Username` VARCHAR(16) NOT NULL,\n" +
" `Password` VARCHAR(64) NOT NULL,\n" + " `Password` VARCHAR(64) NOT NULL,\n" +
" UNIQUE (`UUID`)\n" + " UNIQUE (`UUID`)\n" +
" UNIQUE (`Username`)\n" +
");"; ");";
Statement stmt = conn.createStatement(); Statement stmt = conn.createStatement();
stmt.execute(sql); stmt.execute(sql);
@ -70,7 +71,7 @@ public class SimpleAuthDatabase {
String sql = "INSERT INTO users(uuid, username, password) VALUES(?,?,?)"; String sql = "INSERT INTO users(uuid, username, password) VALUES(?,?,?)";
try (Connection conn = connect(); try (Connection conn = connect();
PreparedStatement pstmt = conn.prepareStatement(sql)) { PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, uuid); pstmt.setString(1, uuid);
pstmt.setString(2, username); pstmt.setString(2, username);
pstmt.setString(3, password); pstmt.setString(3, password);
@ -79,7 +80,23 @@ public class SimpleAuthDatabase {
LOGGER.error(e.getMessage()); LOGGER.error(e.getMessage());
} }
} }
public void selectAll(){
String sql = "SELECT id, name, capacity FROM users";
try (Connection conn = this.connect();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)){
// loop through the result set
while (rs.next()) {
System.out.println(rs.getInt("id") + "\t" +
rs.getString("name") + "\t" +
rs.getDouble("capacity"));
}
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
private static void disconnect(Connection conn) { private static void disconnect(Connection conn) {
try { try {
if (conn != null) { if (conn != null) {

View File

@ -3,14 +3,13 @@
"id": "simpleauth", "id": "simpleauth",
"version": "${version}", "version": "${version}",
"name": "Simple Authentication mod", "name": "Simple Authentication Mod",
"description": "A mod that tries to keep authentication for players simple via commands.", "description": "A mod that tries to keep authentication for players simple via commands.",
"authors": [ "authors": [
"samo_lego" "samo_lego"
], ],
"contact": { "contact": {
"homepage": "https://fabricmc.net/", "sources": "https://github.com/samolego/SimpleAuth"
"sources": "https://github.com/FabricMC/fabric-example-mod"
}, },
"license": "MIT", "license": "MIT",