diff --git a/src/de/steamwar/sql/SteamwarUser.java b/src/de/steamwar/sql/SteamwarUser.java index 211c4da..1b9eb14 100644 --- a/src/de/steamwar/sql/SteamwarUser.java +++ b/src/de/steamwar/sql/SteamwarUser.java @@ -21,11 +21,18 @@ package de.steamwar.sql; import de.steamwar.sql.internal.*; import lombok.Getter; +import lombok.SneakyThrows; +import javax.crypto.Mac; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import java.security.MessageDigest; +import java.security.SecureRandom; import java.sql.Timestamp; import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.logging.Level; import java.util.stream.Collectors; public class SteamwarUser { @@ -50,6 +57,7 @@ public class SteamwarUser { private static final SelectStatement getAll = new SelectStatement<>(table, "SELECT * FROM UserData"); private static final Statement updateName = table.update(Table.PRIMARY, "UserName"); + private static final Statement updatePassword = table.update(Table.PRIMARY, "Password"); private static final Statement updateLocale = table.update(Table.PRIMARY, "Locale", "ManualLocale"); private static final Statement updateTeam = table.update(Table.PRIMARY, "Team"); private static final Statement updateLeader = table.update(Table.PRIMARY, "Leader"); @@ -112,7 +120,9 @@ public class SteamwarUser { @Getter @Field private String userName; - @Getter + @Field(nullable = true) + private String password; + @Getter @Field(def = "0") private int team; @Getter @@ -130,10 +140,11 @@ public class SteamwarUser { private Set permissions = null; private UserPerm.Prefix prefix = null; - public SteamwarUser(int id, UUID uuid, String userName, int team, boolean leader, Locale locale, boolean manualLocale, Long discordId) { + public SteamwarUser(int id, UUID uuid, String userName, String password, int team, boolean leader, Locale locale, boolean manualLocale, Long discordId) { this.id = id; this.uuid = uuid; this.userName = userName; + this.password = password; this.team = team; this.leader = leader; this.locale = locale; @@ -233,6 +244,47 @@ public class SteamwarUser { updateDiscord.update(discordId, id); } + @SneakyThrows + public void setPassword(String password) { + SecureRandom random = new SecureRandom(); + byte[] salt = new byte[16]; + random.nextBytes(salt); + String saltString = Base64.getEncoder().encodeToString(salt); + + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128); + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); + byte[] hash = factory.generateSecret(spec).getEncoded(); + + String hashString = Base64.getEncoder().encodeToString(hash); + + this.password = hashString + ":" + saltString; + updatePassword.update(this.password, id); + } + + @SneakyThrows + public boolean verifyPassword(String password) { + if (this.password == null) { + return false; + } + + String[] parts = this.password.split(":"); + if (parts.length != 2) { + SQLConfig.impl.getLogger().log(Level.SEVERE ,"Invalid password hash for user {0} ({1})", new Object[]{userName, id}); + return false; + } + + String hashString = parts[0]; + byte[] realHash = Base64.getDecoder().decode(hashString); + String saltString = parts[1]; + byte[] salt = Base64.getDecoder().decode(saltString); + + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128); + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); + byte[] hash = factory.generateSecret(spec).getEncoded(); + + return Arrays.equals(realHash, hash); + } + private void initPunishments() { if(punishments != null) return; diff --git a/src/de/steamwar/sql/Token.java b/src/de/steamwar/sql/Token.java index db34967..007a16e 100644 --- a/src/de/steamwar/sql/Token.java +++ b/src/de/steamwar/sql/Token.java @@ -25,9 +25,13 @@ import de.steamwar.sql.internal.Statement; import de.steamwar.sql.internal.Table; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.SneakyThrows; import lombok.ToString; +import java.security.MessageDigest; +import java.security.SecureRandom; import java.sql.Timestamp; +import java.util.Base64; import java.util.List; @AllArgsConstructor @@ -41,6 +45,29 @@ public class Token { private static final SelectStatement getHash = table.selectFields("hash"); private static final Statement delete = table.delete(Table.PRIMARY); + @SneakyThrows + private static String getHash(String code) { + return Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-512").digest(code.getBytes())); + } + + @SneakyThrows + public static String createToken(String name, SteamwarUser owner) { + SecureRandom random = new SecureRandom(); + byte[] bytes = new byte[20]; + random.nextBytes(bytes); + + String code = Base64.getEncoder().encodeToString(bytes); + + String hash = getHash(code); + create(name, owner, hash); + return code; + } + + public static Token getTokenByCode(String code) { + String hash = getHash(code); + return get(hash); + } + public static Token create(String name, SteamwarUser owner, String hash) { int id = insert.insertGetKey(name, owner, hash); return get(id);