diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs
index 99cb3620..e048b58d 100755
--- a/TShockAPI/Commands.cs
+++ b/TShockAPI/Commands.cs
@@ -919,16 +919,18 @@ namespace TShockAPI
try
{
var user = new User();
-
+ string echoPassword = "";
if (args.Parameters.Count == 1)
{
user.Name = args.Player.Name;
- user.Password = args.Parameters[0];
+ echoPassword = args.Parameters[0];
+ user.CreateBCryptHash(args.Parameters[0]);
}
else if (args.Parameters.Count == 2 && TShock.Config.AllowRegisterAnyUsername)
{
user.Name = args.Parameters[0];
- user.Password = args.Parameters[1];
+ echoPassword = args.Parameters[1];
+ user.CreateBCryptHash(args.Parameters[1]);
}
else
{
@@ -942,7 +944,7 @@ namespace TShockAPI
if (TShock.Users.GetUserByName(user.Name) == null && user.Name != TSServerPlayer.AccountName) // Cheap way of checking for existance of a user
{
args.Player.SendSuccessMessage("Account \"{0}\" has been registered.", user.Name);
- args.Player.SendSuccessMessage("Your password is {0}.", user.Password);
+ args.Player.SendSuccessMessage("Your password is {0}.", echoPassword);
TShock.Users.AddUser(user);
TShock.Log.ConsoleInfo("{0} registered an account: \"{1}\".", args.Player.Name, user.Name);
}
@@ -976,7 +978,7 @@ namespace TShockAPI
var user = new User();
user.Name = args.Parameters[1];
- user.Password = args.Parameters[2];
+ user.CreateBCryptHash(args.Parameters[2]);
user.Group = args.Parameters[3];
try
diff --git a/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs
index 5b920ba1..a9b134d1 100755
--- a/TShockAPI/DB/UserManager.cs
+++ b/TShockAPI/DB/UserManager.cs
@@ -21,9 +21,11 @@ using System.CodeDom.Compiler;
using System.Data;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using MySql.Data.MySqlClient;
using System.Text.RegularExpressions;
using BCrypt.Net;
+using System.Security.Cryptography;
namespace TShockAPI.DB
{
@@ -309,7 +311,7 @@ namespace TShockAPI.DB
{
public int ID { get; set; }
public string Name { get; set; }
- public string Password { get; set; }
+ public string Password { get; internal set; }
public string UUID { get; set; }
public string Group { get; set; }
public string Registered { get; set; }
@@ -354,7 +356,7 @@ namespace TShockAPI.DB
return true;
}
} catch (SaltParseException) {
- if (TShock.Utils.HashPassword(password).ToUpper() == this.Password.ToUpper()) {
+ if (hashPassword(password).ToUpper() == this.Password.ToUpper()) {
// The password is not stored using BCrypt; upgrade it to BCrypt immediately
upgradePasswordToBCrypt(password);
return true;
@@ -406,6 +408,61 @@ namespace TShockAPI.DB
}
}
}
+
+ public void CreateBCryptHash(string password) {
+ try {
+ this.Password = BCrypt.Net.BCrypt.HashPassword(password, TShock.Config.WorkFactor);
+ } catch (ArgumentOutOfRangeException) {
+ TShock.Log.ConsoleError("Invalid BCrypt work factor! Creating new hash using default work factor.");
+ this.Password = BCrypt.Net.BCrypt.HashPassword(password);
+ }
+ }
+
+ ///
+ /// A dictionary of hashing algortihms and an implementation object.
+ ///
+ internal readonly Dictionary> HashTypes = new Dictionary>
+ {
+ {"sha512", () => new SHA512Managed()},
+ {"sha256", () => new SHA256Managed()},
+ {"md5", () => new MD5Cng()},
+ {"sha512-xp", () => SHA512.Create()},
+ {"sha256-xp", () => SHA256.Create()},
+ {"md5-xp", () => MD5.Create()},
+ };
+
+ ///
+ /// Returns a Sha256 string for a given string
+ ///
+ /// bytes to hash
+ /// string sha256
+ internal string hashPassword(byte[] bytes)
+ {
+ if (bytes == null)
+ throw new NullReferenceException("bytes");
+ Func func;
+ if (!HashTypes.TryGetValue(TShock.Config.HashAlgorithm.ToLower(), out func))
+ throw new NotSupportedException("Hashing algorithm {0} is not supported".SFormat(TShock.Config.HashAlgorithm.ToLower()));
+
+ using (var hash = func())
+ {
+ var ret = hash.ComputeHash(bytes);
+ return ret.Aggregate("", (s, b) => s + b.ToString("X2"));
+ }
+ }
+
+ ///
+ /// Returns a hashed password string for a given string
+ ///
+ /// string to hash
+ /// string sha256
+ internal string hashPassword(string password)
+ {
+ if (string.IsNullOrEmpty(password) || password == "non-existant password")
+ return null;
+ return hashPassword(Encoding.UTF8.GetBytes(password));
+ }
+
}
[Serializable]
diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs
index f6033796..bdb82b9c 100644
--- a/TShockAPI/Utils.cs
+++ b/TShockAPI/Utils.cs
@@ -27,6 +27,7 @@ using System.Text;
using System.Text.RegularExpressions;
using Terraria;
using TShockAPI.DB;
+using BCrypt.Net;
namespace TShockAPI
{
@@ -719,14 +720,16 @@ namespace TShockAPI
ply.SendErrorMessage("Use \"my query\" for items with spaces");
}
- ///
- /// Default hashing algorithm.
- ///
- public string HashAlgo = "sha512";
+ ///
+ /// Default hashing algorithm.
+ ///
+ [Obsolete("This is no longer necessary, please use TShock.Config.HashAlgorithm instead.")]
+ public string HashAlgo = "sha512";
- ///
- /// A dictionary of hashing algortihms and an implementation object.
- ///
+ ///
+ /// A dictionary of hashing algortihms and an implementation object.
+ ///
+ [Obsolete("This is no longer necessary, after switching to User.VerifyPassword(password) instead.")]
public readonly Dictionary> HashTypes = new Dictionary>
{
{"sha512", () => new SHA512Managed()},
@@ -742,6 +745,7 @@ namespace TShockAPI
///
/// bytes to hash
/// string sha256
+ [Obsolete("Please use User.VerifyPassword(password) instead. Warning: This will upgrade passwords to BCrypt. Already converted passwords will not hash correctly using this method.")]
public string HashPassword(byte[] bytes)
{
if (bytes == null)
@@ -762,6 +766,7 @@ namespace TShockAPI
///
/// string to hash
/// string sha256
+ [Obsolete("Please use User.VerifyPassword(password) instead. Warning: This will upgrade passwords to BCrypt. Already converted passwords will not hash correctly using this method.")]
public string HashPassword(string password)
{
if (string.IsNullOrEmpty(password) || password == "non-existant password")