made newlines into linux newlines

This commit is contained in:
Phallacy 2019-02-20 01:17:30 -08:00
parent a0d31a49a0
commit 098de6b050
3 changed files with 589 additions and 591 deletions

View File

@ -1,149 +1,149 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
namespace Emby.Server.Implementations.Cryptography namespace Emby.Server.Implementations.Cryptography
{ {
public class CryptographyProvider : ICryptoProvider public class CryptographyProvider : ICryptoProvider
{ {
private HashSet<string> SupportedHashMethods; private HashSet<string> SupportedHashMethods;
public string DefaultHashMethod => "SHA256"; public string DefaultHashMethod => "SHA256";
private RandomNumberGenerator rng; private RandomNumberGenerator rng;
private int defaultiterations = 1000; private int defaultiterations = 1000;
public CryptographyProvider() public CryptographyProvider()
{
//Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1
//there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one
SupportedHashMethods = new HashSet<string>()
{
"MD5"
,"System.Security.Cryptography.MD5"
,"SHA"
,"SHA1"
,"System.Security.Cryptography.SHA1"
,"SHA256"
,"SHA-256"
,"System.Security.Cryptography.SHA256"
,"SHA384"
,"SHA-384"
,"System.Security.Cryptography.SHA384"
,"SHA512"
,"SHA-512"
,"System.Security.Cryptography.SHA512"
};
rng = RandomNumberGenerator.Create();
}
public Guid GetMD5(string str)
{
return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
}
public byte[] ComputeSHA1(byte[] bytes)
{
using (var provider = SHA1.Create())
{
return provider.ComputeHash(bytes);
}
}
public byte[] ComputeMD5(Stream str)
{
using (var provider = MD5.Create())
{
return provider.ComputeHash(str);
}
}
public byte[] ComputeMD5(byte[] bytes)
{
using (var provider = MD5.Create())
{
return provider.ComputeHash(bytes);
}
}
public IEnumerable<string> GetSupportedHashMethods()
{
return SupportedHashMethods;
}
private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations)
{ {
//downgrading for now as we need this library to be dotnetstandard compliant //Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1
using (var r = new Rfc2898DeriveBytes(bytes, salt, iterations)) //there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one
{ SupportedHashMethods = new HashSet<string>()
return r.GetBytes(32); {
} "MD5"
} ,"System.Security.Cryptography.MD5"
,"SHA"
public byte[] ComputeHash(string HashMethod, byte[] bytes) ,"SHA1"
{ ,"System.Security.Cryptography.SHA1"
return ComputeHash(HashMethod, bytes, new byte[0]); ,"SHA256"
} ,"SHA-256"
,"System.Security.Cryptography.SHA256"
public byte[] ComputeHashWithDefaultMethod(byte[] bytes) ,"SHA384"
{ ,"SHA-384"
return ComputeHash(DefaultHashMethod, bytes); ,"System.Security.Cryptography.SHA384"
} ,"SHA512"
,"SHA-512"
public byte[] ComputeHash(string HashMethod, byte[] bytes, byte[] salt) ,"System.Security.Cryptography.SHA512"
{ };
if (SupportedHashMethods.Contains(HashMethod)) rng = RandomNumberGenerator.Create();
{ }
if (salt.Length == 0)
{ public Guid GetMD5(string str)
using (var h = HashAlgorithm.Create(HashMethod)) {
{ return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
return h.ComputeHash(bytes); }
}
} public byte[] ComputeSHA1(byte[] bytes)
else {
{ using (var provider = SHA1.Create())
return PBKDF2(HashMethod, bytes, salt, defaultiterations); {
} return provider.ComputeHash(bytes);
} }
else }
{
throw new CryptographicException($"Requested hash method is not supported: {HashMethod}"); public byte[] ComputeMD5(Stream str)
} {
} using (var provider = MD5.Create())
{
public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt) return provider.ComputeHash(str);
{ }
return PBKDF2(DefaultHashMethod, bytes, salt, defaultiterations); }
}
public byte[] ComputeMD5(byte[] bytes)
public byte[] ComputeHash(PasswordHash hash) {
{ using (var provider = MD5.Create())
int iterations = defaultiterations; {
if (!hash.Parameters.ContainsKey("iterations")) return provider.ComputeHash(bytes);
{ }
hash.Parameters.Add("iterations", defaultiterations.ToString(CultureInfo.InvariantCulture)); }
}
else public IEnumerable<string> GetSupportedHashMethods()
{ {
try return SupportedHashMethods;
{ }
iterations = int.Parse(hash.Parameters["iterations"]);
} private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations)
catch (Exception e) {
{ //downgrading for now as we need this library to be dotnetstandard compliant
throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}", e); using (var r = new Rfc2898DeriveBytes(bytes, salt, iterations))
} {
} return r.GetBytes(32);
return PBKDF2(hash.Id, hash.HashBytes, hash.SaltBytes, iterations); }
} }
public byte[] GenerateSalt() public byte[] ComputeHash(string HashMethod, byte[] bytes)
{ {
byte[] salt = new byte[64]; return ComputeHash(HashMethod, bytes, new byte[0]);
rng.GetBytes(salt); }
return salt;
} public byte[] ComputeHashWithDefaultMethod(byte[] bytes)
} {
} return ComputeHash(DefaultHashMethod, bytes);
}
public byte[] ComputeHash(string HashMethod, byte[] bytes, byte[] salt)
{
if (SupportedHashMethods.Contains(HashMethod))
{
if (salt.Length == 0)
{
using (var h = HashAlgorithm.Create(HashMethod))
{
return h.ComputeHash(bytes);
}
}
else
{
return PBKDF2(HashMethod, bytes, salt, defaultiterations);
}
}
else
{
throw new CryptographicException($"Requested hash method is not supported: {HashMethod}");
}
}
public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
{
return PBKDF2(DefaultHashMethod, bytes, salt, defaultiterations);
}
public byte[] ComputeHash(PasswordHash hash)
{
int iterations = defaultiterations;
if (!hash.Parameters.ContainsKey("iterations"))
{
hash.Parameters.Add("iterations", defaultiterations.ToString(CultureInfo.InvariantCulture));
}
else
{
try
{
iterations = int.Parse(hash.Parameters["iterations"]);
}
catch (Exception e)
{
throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}", e);
}
}
return PBKDF2(hash.Id, hash.HashBytes, hash.SaltBytes, iterations);
}
public byte[] GenerateSalt()
{
byte[] salt = new byte[64];
rng.GetBytes(salt);
return salt;
}
}
}

View File

@ -1,264 +1,264 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using SQLitePCL.pretty; using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data namespace Emby.Server.Implementations.Data
{ {
/// <summary> /// <summary>
/// Class SQLiteUserRepository /// Class SQLiteUserRepository
/// </summary> /// </summary>
public class SqliteUserRepository : BaseSqliteRepository, IUserRepository public class SqliteUserRepository : BaseSqliteRepository, IUserRepository
{ {
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
public SqliteUserRepository( public SqliteUserRepository(
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
IServerApplicationPaths appPaths, IServerApplicationPaths appPaths,
IJsonSerializer jsonSerializer) IJsonSerializer jsonSerializer)
: base(loggerFactory.CreateLogger(nameof(SqliteUserRepository))) : base(loggerFactory.CreateLogger(nameof(SqliteUserRepository)))
{ {
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
DbFilePath = Path.Combine(appPaths.DataPath, "users.db"); DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
} }
/// <summary> /// <summary>
/// Gets the name of the repository /// Gets the name of the repository
/// </summary> /// </summary>
/// <value>The name.</value> /// <value>The name.</value>
public string Name => "SQLite"; public string Name => "SQLite";
/// <summary> /// <summary>
/// Opens the connection to the database /// Opens the connection to the database
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
public void Initialize() public void Initialize()
{ {
using (var connection = CreateConnection()) using (var connection = CreateConnection())
{ {
RunDefaultInitialization(connection); RunDefaultInitialization(connection);
var localUsersTableExists = TableExists(connection, "LocalUsersv2"); var localUsersTableExists = TableExists(connection, "LocalUsersv2");
connection.RunQueries(new[] { connection.RunQueries(new[] {
"create table if not exists LocalUsersv2 (Id INTEGER PRIMARY KEY, guid GUID NOT NULL, data BLOB NOT NULL)", "create table if not exists LocalUsersv2 (Id INTEGER PRIMARY KEY, guid GUID NOT NULL, data BLOB NOT NULL)",
"drop index if exists idx_users" "drop index if exists idx_users"
}); });
if (!localUsersTableExists && TableExists(connection, "Users")) if (!localUsersTableExists && TableExists(connection, "Users"))
{ {
TryMigrateToLocalUsersTable(connection); TryMigrateToLocalUsersTable(connection);
} }
RemoveEmptyPasswordHashes(); RemoveEmptyPasswordHashes();
} }
} }
private void TryMigrateToLocalUsersTable(ManagedConnection connection) private void TryMigrateToLocalUsersTable(ManagedConnection connection)
{ {
try try
{ {
connection.RunQueries(new[] connection.RunQueries(new[]
{ {
"INSERT INTO LocalUsersv2 (guid, data) SELECT guid,data from users" "INSERT INTO LocalUsersv2 (guid, data) SELECT guid,data from users"
}); });
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "Error migrating users database"); Logger.LogError(ex, "Error migrating users database");
} }
} }
private void RemoveEmptyPasswordHashes() private void RemoveEmptyPasswordHashes()
{ {
foreach (var user in RetrieveAllUsers()) foreach (var user in RetrieveAllUsers())
{ {
// If the user password is the sha1 hash of the empty string, remove it // If the user password is the sha1 hash of the empty string, remove it
if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal) if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)
|| !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)) || !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal))
{ {
continue; continue;
} }
user.Password = null; user.Password = null;
var serialized = _jsonSerializer.SerializeToBytes(user); var serialized = _jsonSerializer.SerializeToBytes(user);
using (WriteLock.Write()) using (WriteLock.Write())
using (var connection = CreateConnection()) using (var connection = CreateConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(db =>
{ {
using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId")) using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
{ {
statement.TryBind("@InternalId", user.InternalId); statement.TryBind("@InternalId", user.InternalId);
statement.TryBind("@data", serialized); statement.TryBind("@data", serialized);
statement.MoveNext(); statement.MoveNext();
} }
}, TransactionMode); }, TransactionMode);
} }
} }
} }
/// <summary> /// <summary>
/// Save a user in the repo /// Save a user in the repo
/// </summary> /// </summary>
public void CreateUser(User user) public void CreateUser(User user)
{ {
if (user == null) if (user == null)
{ {
throw new ArgumentNullException(nameof(user)); throw new ArgumentNullException(nameof(user));
} }
var serialized = _jsonSerializer.SerializeToBytes(user); var serialized = _jsonSerializer.SerializeToBytes(user);
using (WriteLock.Write()) using (WriteLock.Write())
{ {
using (var connection = CreateConnection()) using (var connection = CreateConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(db =>
{ {
using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)")) using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)"))
{ {
statement.TryBind("@guid", user.Id.ToGuidBlob()); statement.TryBind("@guid", user.Id.ToGuidBlob());
statement.TryBind("@data", serialized); statement.TryBind("@data", serialized);
statement.MoveNext(); statement.MoveNext();
} }
var createdUser = GetUser(user.Id, false); var createdUser = GetUser(user.Id, false);
if (createdUser == null) if (createdUser == null)
{ {
throw new ApplicationException("created user should never be null"); throw new ApplicationException("created user should never be null");
} }
user.InternalId = createdUser.InternalId; user.InternalId = createdUser.InternalId;
}, TransactionMode); }, TransactionMode);
} }
} }
} }
public void UpdateUser(User user) public void UpdateUser(User user)
{ {
if (user == null) if (user == null)
{ {
throw new ArgumentNullException(nameof(user)); throw new ArgumentNullException(nameof(user));
} }
var serialized = _jsonSerializer.SerializeToBytes(user); var serialized = _jsonSerializer.SerializeToBytes(user);
using (WriteLock.Write()) using (WriteLock.Write())
{ {
using (var connection = CreateConnection()) using (var connection = CreateConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(db =>
{ {
using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId")) using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
{ {
statement.TryBind("@InternalId", user.InternalId); statement.TryBind("@InternalId", user.InternalId);
statement.TryBind("@data", serialized); statement.TryBind("@data", serialized);
statement.MoveNext(); statement.MoveNext();
} }
}, TransactionMode); }, TransactionMode);
} }
} }
} }
private User GetUser(Guid guid, bool openLock) private User GetUser(Guid guid, bool openLock)
{ {
using (openLock ? WriteLock.Read() : null) using (openLock ? WriteLock.Read() : null)
{ {
using (var connection = CreateConnection(true)) using (var connection = CreateConnection(true))
{ {
using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid")) using (var statement = connection.PrepareStatement("select id,guid,data from LocalUsersv2 where guid=@guid"))
{ {
statement.TryBind("@guid", guid); statement.TryBind("@guid", guid);
foreach (var row in statement.ExecuteQuery()) foreach (var row in statement.ExecuteQuery())
{ {
return GetUser(row); return GetUser(row);
} }
} }
} }
} }
return null; return null;
} }
private User GetUser(IReadOnlyList<IResultSetValue> row) private User GetUser(IReadOnlyList<IResultSetValue> row)
{ {
var id = row[0].ToInt64(); var id = row[0].ToInt64();
var guid = row[1].ReadGuidFromBlob(); var guid = row[1].ReadGuidFromBlob();
using (var stream = new MemoryStream(row[2].ToBlob())) using (var stream = new MemoryStream(row[2].ToBlob()))
{ {
stream.Position = 0; stream.Position = 0;
var user = _jsonSerializer.DeserializeFromStream<User>(stream); var user = _jsonSerializer.DeserializeFromStream<User>(stream);
user.InternalId = id; user.InternalId = id;
user.Id = guid; user.Id = guid;
return user; return user;
} }
} }
/// <summary> /// <summary>
/// Retrieve all users from the database /// Retrieve all users from the database
/// </summary> /// </summary>
/// <returns>IEnumerable{User}.</returns> /// <returns>IEnumerable{User}.</returns>
public List<User> RetrieveAllUsers() public List<User> RetrieveAllUsers()
{ {
var list = new List<User>(); var list = new List<User>();
using (WriteLock.Read()) using (WriteLock.Read())
{ {
using (var connection = CreateConnection(true)) using (var connection = CreateConnection(true))
{ {
foreach (var row in connection.Query("select id,guid,data from LocalUsersv2")) foreach (var row in connection.Query("select id,guid,data from LocalUsersv2"))
{ {
list.Add(GetUser(row)); list.Add(GetUser(row));
} }
} }
} }
return list; return list;
} }
/// <summary> /// <summary>
/// Deletes the user. /// Deletes the user.
/// </summary> /// </summary>
/// <param name="user">The user.</param> /// <param name="user">The user.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">user</exception> /// <exception cref="ArgumentNullException">user</exception>
public void DeleteUser(User user) public void DeleteUser(User user)
{ {
if (user == null) if (user == null)
{ {
throw new ArgumentNullException(nameof(user)); throw new ArgumentNullException(nameof(user));
} }
using (WriteLock.Write()) using (WriteLock.Write())
{ {
using (var connection = CreateConnection()) using (var connection = CreateConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(db =>
{ {
using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id")) using (var statement = db.PrepareStatement("delete from LocalUsersv2 where Id=@id"))
{ {
statement.TryBind("@id", user.InternalId); statement.TryBind("@id", user.InternalId);
statement.MoveNext(); statement.MoveNext();
} }
}, TransactionMode); }, TransactionMode);
} }
} }
} }
} }
} }

View File

@ -1,42 +1,42 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
{ {
private readonly ICryptoProvider _cryptographyProvider; private readonly ICryptoProvider _cryptographyProvider;
public DefaultAuthenticationProvider(ICryptoProvider crypto) public DefaultAuthenticationProvider(ICryptoProvider crypto)
{ {
_cryptographyProvider = crypto; _cryptographyProvider = crypto;
} }
public string Name => "Default"; public string Name => "Default";
public bool IsEnabled => true; public bool IsEnabled => true;
//This is dumb and an artifact of the backwards way auth providers were designed. //This is dumb and an artifact of the backwards way auth providers were designed.
//This version of authenticate was never meant to be called, but needs to be here for interface compat //This version of authenticate was never meant to be called, but needs to be here for interface compat
//Only the providers that don't provide local user support use this //Only the providers that don't provide local user support use this
public Task<ProviderAuthenticationResult> Authenticate(string username, string password) public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
//This is the verson that we need to use for local users. Because reasons. //This is the verson that we need to use for local users. Because reasons.
public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser) public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
{ {
bool success = false; bool success = false;
if (resolvedUser == null) if (resolvedUser == null)
{ {
throw new Exception("Invalid username or password"); throw new Exception("Invalid username or password");
} }
//As long as jellyfin supports passwordless users, we need this little block here to accomodate //As long as jellyfin supports passwordless users, we need this little block here to accomodate
@ -47,166 +47,164 @@ namespace Emby.Server.Implementations.Library
Username = username Username = username
}); });
} }
ConvertPasswordFormat(resolvedUser); ConvertPasswordFormat(resolvedUser);
byte[] passwordbytes = Encoding.UTF8.GetBytes(password); byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
PasswordHash readyHash = new PasswordHash(resolvedUser.Password); PasswordHash readyHash = new PasswordHash(resolvedUser.Password);
byte[] CalculatedHash; byte[] CalculatedHash;
string CalculatedHashString; string CalculatedHashString;
if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id)) if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id))
{ {
if (string.IsNullOrEmpty(readyHash.Salt)) if (string.IsNullOrEmpty(readyHash.Salt))
{ {
CalculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes); CalculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes);
CalculatedHashString = BitConverter.ToString(CalculatedHash).Replace("-", string.Empty); CalculatedHashString = BitConverter.ToString(CalculatedHash).Replace("-", string.Empty);
}
else
{
CalculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes, readyHash.SaltBytes);
CalculatedHashString = BitConverter.ToString(CalculatedHash).Replace("-", string.Empty);
} }
else
if (CalculatedHashString == readyHash.Hash) {
{ CalculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes, readyHash.SaltBytes);
success = true; CalculatedHashString = BitConverter.ToString(CalculatedHash).Replace("-", string.Empty);
//throw new Exception("Invalid username or password"); }
}
} if (CalculatedHashString == readyHash.Hash)
else {
{ success = true;
throw new Exception(String.Format($"Requested crypto method not available in provider: {readyHash.Id}")); //throw new Exception("Invalid username or password");
} }
}
//var success = string.Equals(GetPasswordHash(resolvedUser), GetHashedString(resolvedUser, password), StringComparison.OrdinalIgnoreCase); else
{
if (!success) throw new Exception(String.Format($"Requested crypto method not available in provider: {readyHash.Id}"));
{ }
throw new Exception("Invalid username or password");
} //var success = string.Equals(GetPasswordHash(resolvedUser), GetHashedString(resolvedUser, password), StringComparison.OrdinalIgnoreCase);
return Task.FromResult(new ProviderAuthenticationResult if (!success)
{ {
Username = username throw new Exception("Invalid username or password");
}); }
}
return Task.FromResult(new ProviderAuthenticationResult
//This allows us to move passwords forward to the newformat without breaking. They are still insecure, unsalted, and dumb before a password change {
//but at least they are in the new format. Username = username
private void ConvertPasswordFormat(User user) });
{ }
if (!string.IsNullOrEmpty(user.Password))
//This allows us to move passwords forward to the newformat without breaking. They are still insecure, unsalted, and dumb before a password change
//but at least they are in the new format.
private void ConvertPasswordFormat(User user)
{
if (!string.IsNullOrEmpty(user.Password))
{ {
return; return;
} }
if (!user.Password.Contains("$")) if (!user.Password.Contains("$"))
{ {
string hash = user.Password; string hash = user.Password;
user.Password = String.Format("$SHA1${0}", hash); user.Password = String.Format("$SHA1${0}", hash);
} }
if (user.EasyPassword != null && !user.EasyPassword.Contains("$")) if (user.EasyPassword != null && !user.EasyPassword.Contains("$"))
{ {
string hash = user.EasyPassword; string hash = user.EasyPassword;
user.EasyPassword = string.Format("$SHA1${0}", hash); user.EasyPassword = string.Format("$SHA1${0}", hash);
} }
} }
public Task<bool> HasPassword(User user) public Task<bool> HasPassword(User user)
{ {
var hasConfiguredPassword = !IsPasswordEmpty(user, GetPasswordHash(user)); var hasConfiguredPassword = !IsPasswordEmpty(user, GetPasswordHash(user));
return Task.FromResult(hasConfiguredPassword); return Task.FromResult(hasConfiguredPassword);
} }
private bool IsPasswordEmpty(User user, string password) private bool IsPasswordEmpty(User user, string password)
{ {
if (string.IsNullOrEmpty(user.Password)) if (string.IsNullOrEmpty(user.Password))
{ {
return string.IsNullOrEmpty(password); return string.IsNullOrEmpty(password);
} }
return false; return false;
} }
public Task ChangePassword(User user, string newPassword) public Task ChangePassword(User user, string newPassword)
{ {
ConvertPasswordFormat(user); ConvertPasswordFormat(user);
//This is needed to support changing a no password user to a password user //This is needed to support changing a no password user to a password user
if (string.IsNullOrEmpty(user.Password)) if (string.IsNullOrEmpty(user.Password))
{ {
PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider); PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider);
newPasswordHash.SaltBytes = _cryptographyProvider.GenerateSalt(); newPasswordHash.SaltBytes = _cryptographyProvider.GenerateSalt();
newPasswordHash.Salt = PasswordHash.ConvertToByteString(newPasswordHash.SaltBytes); newPasswordHash.Salt = PasswordHash.ConvertToByteString(newPasswordHash.SaltBytes);
newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod; newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod;
newPasswordHash.Hash = GetHashedStringChangeAuth(newPassword, newPasswordHash); newPasswordHash.Hash = GetHashedStringChangeAuth(newPassword, newPasswordHash);
user.Password = newPasswordHash.ToString(); user.Password = newPasswordHash.ToString();
return Task.CompletedTask; return Task.CompletedTask;
} }
PasswordHash passwordHash = new PasswordHash(user.Password); PasswordHash passwordHash = new PasswordHash(user.Password);
if (passwordHash.Id == "SHA1" && string.IsNullOrEmpty(passwordHash.Salt)) if (passwordHash.Id == "SHA1" && string.IsNullOrEmpty(passwordHash.Salt))
{ {
passwordHash.SaltBytes = _cryptographyProvider.GenerateSalt(); passwordHash.SaltBytes = _cryptographyProvider.GenerateSalt();
passwordHash.Salt = PasswordHash.ConvertToByteString(passwordHash.SaltBytes); passwordHash.Salt = PasswordHash.ConvertToByteString(passwordHash.SaltBytes);
passwordHash.Id = _cryptographyProvider.DefaultHashMethod; passwordHash.Id = _cryptographyProvider.DefaultHashMethod;
passwordHash.Hash = GetHashedStringChangeAuth(newPassword, passwordHash); passwordHash.Hash = GetHashedStringChangeAuth(newPassword, passwordHash);
}
else if (newPassword != null)
{
passwordHash.Hash = GetHashedString(user, newPassword);
}
if (string.IsNullOrWhiteSpace(passwordHash.Hash))
{
throw new ArgumentNullException(nameof(passwordHash.Hash));
}
user.Password = passwordHash.ToString();
return Task.CompletedTask;
}
public string GetPasswordHash(User user)
{
return user.Password;
}
public string GetHashedStringChangeAuth(string newPassword, PasswordHash passwordHash)
{
passwordHash.HashBytes = Encoding.UTF8.GetBytes(newPassword);
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash));
}
/// <summary>
/// Gets the hashed string.
/// </summary>
public string GetHashedString(User user, string str)
{
PasswordHash passwordHash;
if (String.IsNullOrEmpty(user.Password))
{
passwordHash = new PasswordHash(_cryptographyProvider);
}
else
{
ConvertPasswordFormat(user);
passwordHash = new PasswordHash(user.Password);
} }
else if (newPassword != null)
if (passwordHash.SaltBytes != null) {
{ passwordHash.Hash = GetHashedString(user, newPassword);
//the password is modern format with PBKDF and we should take advantage of that }
passwordHash.HashBytes = Encoding.UTF8.GetBytes(str);
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash)); if (string.IsNullOrWhiteSpace(passwordHash.Hash))
} {
else throw new ArgumentNullException(nameof(passwordHash.Hash));
{ }
//the password has no salt and should be called with the older method for safety
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str))); user.Password = passwordHash.ToString();
}
return Task.CompletedTask;
}
}
} public string GetPasswordHash(User user)
} {
return user.Password;
}
public string GetHashedStringChangeAuth(string newPassword, PasswordHash passwordHash)
{
passwordHash.HashBytes = Encoding.UTF8.GetBytes(newPassword);
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash));
}
/// <summary>
/// Gets the hashed string.
/// </summary>
public string GetHashedString(User user, string str)
{
PasswordHash passwordHash;
if (String.IsNullOrEmpty(user.Password))
{
passwordHash = new PasswordHash(_cryptographyProvider);
}
else
{
ConvertPasswordFormat(user);
passwordHash = new PasswordHash(user.Password);
}
if (passwordHash.SaltBytes != null)
{
//the password is modern format with PBKDF and we should take advantage of that
passwordHash.HashBytes = Encoding.UTF8.GetBytes(str);
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash));
}
else
{
//the password has no salt and should be called with the older method for safety
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str)));
}
}
}
}