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}"
// Argon2 library for password hashing
compile 'de.mkammerer:argon2-jvm-nolibs:2.6'
compile 'de.mkammerer:argon2-jvm:2.6'
// IDEA stuff
implementation 'org.jetbrains:annotations:15.0'

View File

@ -1,5 +1,7 @@
package org.samo_lego.simpleauth;
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
import net.fabricmc.fabric.api.registry.CommandRegistry;
@ -29,9 +31,9 @@ public class SimpleAuth implements DedicatedServerModInitializer {
// Creating data directory (database is stored there)
File file = new File("./mods/SimpleAuth");
if (!file.exists() && !file.mkdir()) {
if (!file.exists() && !file.mkdir())
LOGGER.error("Error creating directory");
}
// Registering the commands
CommandRegistry.INSTANCE.register(false, dispatcher -> {
@ -49,7 +51,7 @@ public class SimpleAuth implements DedicatedServerModInitializer {
// Connection to database
SimpleAuthDatabase.main();
}
}
public static HashSet<ServerPlayerEntity> authenticatedUsers = new HashSet<>();
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.exceptions.CommandSyntaxException;
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
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;
public class LoginCommand {
private static LiteralText PleaseLogin = new LiteralText("§4Type /login <password> to login.");
private static TranslatableText EnterPassword = new TranslatableText("command.simpleauth.password");
private static LiteralText pleaseLogin = new LiteralText("§4Type /login <password> to login.");
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) {
// 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 -> {
ctx.getSource().getPlayer().sendMessage(EnterPassword);
ctx.getSource().getPlayer().sendMessage(enterPassword);
return 1;
}));
}
// Method called for checking the password
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
ServerPlayerEntity player = source.getPlayer();
// Comparing hashed password with one from the file
if(true/*BCrypt.checkpw(pass, savedHashed)*/){ //From database
Text text = new LiteralText(source.getName() + ", you have entered login command");
source.getMinecraftServer().getPlayerManager().broadcastChatMessage(text, false);
SimpleAuth.authenticatedUsers.add(player);
System.out.println(SimpleAuth.authenticatedUsers);
if(SimpleAuth.isAuthenticated(player)) {
player.sendMessage(alreadyAuthenticated);
}
else {
// Create instance
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
}

View File

@ -1,14 +1,16 @@
package org.samo_lego.simpleauth.commands;
import com.google.common.io.Files;
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.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.TranslatableText;
import org.samo_lego.simpleauth.SimpleAuth;
import org.samo_lego.simpleauth.database.SimpleAuthDatabase;
import java.util.Objects;
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
import static com.mojang.brigadier.arguments.StringArgumentType.word;
import static net.minecraft.server.command.CommandManager.argument;
@ -16,8 +18,9 @@ import static net.minecraft.server.command.CommandManager.literal;
public class RegisterCommand {
private static TranslatableText PleaseRegister = new TranslatableText("§4Type /register <password> <password> to login.");
private static TranslatableText EnterPassword = new TranslatableText("command.simpleauth.passwordTwice");
private static TranslatableText pleaseRegister = new TranslatableText("§4Type /register <password> <password> to login.");
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) {
@ -28,25 +31,42 @@ public class RegisterCommand {
.executes( ctx -> register(ctx.getSource(), getString(ctx, "password"), getString(ctx, "passwordAgain")))
))
.executes(ctx -> {
ctx.getSource().getPlayer().sendMessage(EnterPassword);
ctx.getSource().getPlayer().sendMessage(enterPassword);
return 1;
}));
}
// Registering our "register" command (ik, sounds a bit confusing)
private static int register(ServerCommandSource source, String pass1, String pass2) {
if(pass1.equals(pass2)){
// Hashing the password
SimpleAuthDatabase.insert(Objects.requireNonNull(source.getEntity()).getUuidAsString(), source.getName(), pass1);
source.getMinecraftServer().getPlayerManager().broadcastChatMessage(
new LiteralText(source.getName() + ", you have registered successfully!"),
false
);
// Method called for hashing the password & writing to DB
private static int register(ServerCommandSource source, String pass1, String pass2) throws CommandSyntaxException {
ServerPlayerEntity player = source.getPlayer();
if(SimpleAuth.isAuthenticated(player)) {
player.sendMessage(alreadyAuthenticated);
}
else if(pass1.equals(pass2)) { // Hashing the password with the Argon2 power
// 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
source.getMinecraftServer().getPlayerManager().broadcastChatMessage(
new LiteralText(source.getName() + ", passwords must match!"),
false
player.sendMessage(
new LiteralText(source.getName() + ", passwords must match!")
);
return 1;
}

View File

@ -56,6 +56,7 @@ public class SimpleAuthDatabase {
" `Username` VARCHAR(16) NOT NULL,\n" +
" `Password` VARCHAR(64) NOT NULL,\n" +
" UNIQUE (`UUID`)\n" +
" UNIQUE (`Username`)\n" +
");";
Statement stmt = conn.createStatement();
stmt.execute(sql);
@ -70,7 +71,7 @@ public class SimpleAuthDatabase {
String sql = "INSERT INTO users(uuid, username, password) VALUES(?,?,?)";
try (Connection conn = connect();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, uuid);
pstmt.setString(2, username);
pstmt.setString(3, password);
@ -79,7 +80,23 @@ public class SimpleAuthDatabase {
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) {
try {
if (conn != null) {

View File

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