using Scrypt;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace Manager.Framework.Helpers
{
///
/// Password utils
///
public static class PasswordUtils
{
private const int DefaultIterationCount = 16384;
private const int DefaultBlockSize = 8;
private const int DefaultThreadCount = 1;
private static readonly Random s_random = new Random((int)(DateTimeOffset.Now.Ticks % 100000));
#region Secure password management
///
/// Generate a random key of the specified length (source: https://stackoverflow.com/a/31959204/249000)
///
/// Length of the resulting key
/// Allowed characters for the resulting key
///
public static string GetUniqueKey(int length = 32, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
using (var crypto = new RNGCryptoServiceProvider())
{
var data = new byte[length];
// If chars.Length isn't a power of 2 then there is a bias if
// we simply use the modulus operator. The first characters of
// chars will be more probable than the last ones.
// buffer used if we encounter an unusable random byte. We will
// regenerate it in this buffer
byte[] smallBuffer = null;
// Maximum random number that can be used without introducing a
// bias
int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length);
crypto.GetBytes(data);
var result = new char[length];
for (int i = 0; i < length; i++)
{
byte v = data[i];
while (v > maxRandom)
{
if (smallBuffer == null)
{
smallBuffer = new byte[1];
}
crypto.GetBytes(smallBuffer);
v = smallBuffer[0];
}
result[i] = chars[v % chars.Length];
}
return new string(result);
}
}
///
/// Encode the specified password with the specified pepper with the scrypt algorithm
///
/// Password to encode
/// Pepper to use
/// scrypt algorithm iteration count
/// scrypt algorithm block size
/// scrypt algorithm thread count
/// Encoded password
public static string Encode(string password, string pepper = "", int iterationCount = DefaultIterationCount, int blockSize = DefaultBlockSize, int threadCount = DefaultThreadCount)
{
var encoder = new ScryptEncoder(iterationCount, blockSize, threadCount);
return encoder.Encode(pepper + password);
}
///
/// Compare the specified encoded password with the specified password with the specified pepper with the scrypt algorithm
///
/// Hashed password
/// Password to compare
/// Pepper to use
/// scrypt algorithm iteration count
/// scrypt algorithm block size
/// scrypt algorithm thread count
///
public static bool Compare(string hashedPassword, string password, string pepper = "", int iterationCount = DefaultIterationCount, int blockSize = DefaultBlockSize, int threadCount = DefaultThreadCount)
{
var encoder = new ScryptEncoder(iterationCount, blockSize, threadCount);
return encoder.Compare(pepper + password, hashedPassword);
}
///
/// Generate a random password
///
/// Pepper to use
/// scrypt algorithm iteration count
/// scrypt algorithm block size
/// scrypt algorithm thread count
/// Generated password
public static string Generate(string pepper = "", int iterationCount = DefaultIterationCount, int blockSize = DefaultBlockSize, int threadCount = DefaultThreadCount)
{
return Encode(GetUniqueKey(), pepper, iterationCount, blockSize, threadCount);
}
#endregion
///
/// Get a random string of the specified length
///
/// Number of characters
/// Random string of the specified length
public static string GetRandomString(int length)
{
string pass = "";
for (int i = 0; i < length; i++)
{
int r = s_random.Next(10 + 26 + 26);
char c;
if (r < 10) c = (char)(r + 48);
else if (r < 10 + 26) c = (char)((r - 10) + 65);
else c = (char)((r - 10 - 26) + 97);
pass += c;
}
return pass;
}
///
/// Get a random number with the specified maximum number of digits
///
/// Maximum number of digits
/// Random number with the specified maximum number of digits
public static int GetRandomNumber(int maxDigits)
{
return s_random.Next((int)Math.Pow(10, maxDigits));
}
///
/// Calculate the MD5 sum of the specified byte array and remove potential dashes
///
/// Source array
/// Source array MD5 sum
public static string MD5Sum(byte[] source)
{
return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(source)).Replace("-", "").ToLower();
}
///
/// Calculate the MD5 sum of the specified string and remove potential dashes
///
/// Source string
/// Source string MD5 sum
public static string MD5Sum(string source)
{
return MD5Sum(Encoding.ASCII.GetBytes(source));
}
}
}