In Progress EncryptionHelper

This commit is contained in:
Jordan Wages 2024-05-09 22:29:37 -05:00
parent 260caf0008
commit b6a1d6a9d0
4 changed files with 238 additions and 17 deletions

23
CapyKit/EncryptedValue.cs Normal file
View file

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CapyKit
{
public class EncryptedValue<T>
{
#region Members
//
#endregion
#region Properties
public T Value { get; set; }
#endregion
}
}

View file

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.ObjectiveC;
using System.Text;
using System.Threading.Tasks;
namespace CapyKit.Helpers
{
public class EncryptionHelper
{
#region Members
private string encryptionKey;
#endregion
#region Constructors
public EncryptionHelper(string encryptionKey)
{
this.encryptionKey = encryptionKey;
}
#endregion
}
public interface IEncryptionAlgorithm
{
#region Properties
string AlgorithmName { get; }
#endregion
#region Methods
EncryptedValue<T> Encrypt<T>(object obj, params object[] args);
T Decrypt<T>(EncryptedValue<T> encryptedValue, params object[] args);
#endregion
}
}

View file

@ -15,8 +15,8 @@ namespace CapyKit.Helpers
{ {
#region Members #region Members
/// <summary> Default size of the generated salt. </summary> /// <summary> Default size to use when generating a new salt. </summary>
private const int saltSize = 32; private const int SALT_SIZE = 32;
/// <summary> A string of all the lower case characters. </summary> /// <summary> A string of all the lower case characters. </summary>
internal const string LOWER_CASE_CHARACTERS = "abcdefghijklmnopqrstuvwxyz"; internal const string LOWER_CASE_CHARACTERS = "abcdefghijklmnopqrstuvwxyz";
@ -35,25 +35,131 @@ namespace CapyKit.Helpers
#region Methods #region Methods
/// <summary> /// <summary>
/// Compares an unencrypted <paramref name="providedPassword"/> with a stored, encrypted /// Compares an unencrypted <paramref name="password"/> with a stored, encrypted <paramref name="existingPassword"/>.
/// <paramref name="existingPassword"/>. /// This method uses the specified password algorithm type <typeparamref name="T"/> to retrieve the hashed version
/// of the <paramref name="password"/> and then compares it with the <paramref name="existingPassword"/>.
/// </summary> /// </summary>
/// <param name="providedPassword"> The provided password, unencrypted. </param> /// <typeparam name="T">The type of the password hashing algorithm.</typeparam>
/// <param name="existingPassword">The existing, encrypted password.</param> /// <param name="existingPassword">The existing, encrypted password.</param>
/// <param name="password">The unencrypted password to be compared.</param>
/// <param name="salt">The salt value used in password hashing.</param>
/// <param name="args">Additional arguments required for constructing the password algorithm instance.</param>
/// <returns> /// <returns>
/// <see langword="true"/> if hash comparison succeeds, <see langword="false"/> if it fails. /// <see langword="true"/> if hash comparison succeeds, <see langword="false"/> if it fails.
/// </returns> /// </returns>
public static bool CompareHashedPassword(string providedPassword, string existingPassword) public static bool CompareHashedPassword<T>(Password existingPassword, string password, byte[] salt, params object[] args)
{ {
throw new NotImplementedException(); var providedPassword = typeof(SecurityHelper)
.GetMethod("GetPassword")
?.MakeGenericMethod(typeof(T))
?.Invoke(null, new object[] { password, salt, args });
return existingPassword.Equals(providedPassword);
} }
/// <summary> Hashes an unencrypted password. </summary> /// <summary>
/// <param name="password"> The password. </param> /// Compares an unencrypted <paramref name="password"/> with a stored, encrypted <paramref name="existingPassword"/>.
/// <returns> The hashed password. </returns> /// This method uses the specified <paramref name="algorithm"/> to retrieve the hashed version
public static string HashPassword(string password) /// of the <paramref name="password"/> and then compares it with the <paramref name="existingPassword"/>.
/// </summary>
/// <param name="existingPassword">The existing, encrypted password.</param>
/// <param name="password">The unencrypted password to be compared.</param>
/// <param name="salt">The salt value used in password hashing.</param>
/// <param name="algorithm">The password hashing algorithm.</param>
/// <param name="args">Additional arguments required for constructing the password algorithm instance.</param>
/// <returns>
/// <see langword="true"/> if hash comparison succeeds, <see langword="false"/> if it fails.
/// </returns>
public static bool CompareHashedPassword(Password existingPassword, string password, byte[] salt, IPasswordAlgorithm algorithm, params object[] args)
{ {
throw new NotImplementedException(); var algorithmType = algorithm.GetType();
var providedPassword = typeof(SecurityHelper)
.GetMethod("GetPassword")
?.MakeGenericMethod(algorithmType)
?.Invoke(null, new object[] { password, salt, args });
return existingPassword.Equals(providedPassword);
}
/// <summary>
/// Retrieves a <see cref="Password"/> object using the specified password and generates a random salt value.
/// Then it uses that salt to call the overloaded <see cref="GetPassword{T}(string, byte[], object[])"/> method with the given password and
/// the generated salt as arguments.
/// </summary>
/// <typeparam name="T"> The type of <see cref="IPasswordAlgorithm"/> implementation to use. </typeparam>
/// <param name="password"> The plaintext password to be hashed. </param>
/// <param name="args">
/// Optional constructor arguments for the <see cref="IPasswordAlgorithm"/> implementation
/// instance.
/// </param>
/// <returns>
/// A new <see cref="Password"/> object with the given password and a randomly generated salt, as well as an
/// instance of <typeparamref name="T"/> created using any optional constructor arguments provided.
/// </returns>
/// <seealso cref="SecurityHelper.SALT_SIZE"/>
public static Password GetPassword<T>(string password, params object[] args)
{
var salt = SecurityHelper.GetRandomBytes(SecurityHelper.SALT_SIZE);
return GetPassword<T>(password, salt, args);
}
/// <summary>
/// Retrieves a <see cref="Password"/> object using the specified password, salt, and optional
/// constructor arguments.
/// </summary>
/// <remarks>
/// This method uses reflection to find a constructor for the specified password algorithm type (<typeparamref name="T"/>).
/// It emits an error event if a suitable constructor is not found or if there is an error invoking the constructor.
/// </remarks>
/// <typeparam name="T">
/// The type of <see cref="IPasswordAlgorithm"/> implementation to use.
/// </typeparam>
/// <param name="password"> The plaintext password to be hashed. </param>
/// <param name="salt">
/// A random value used as an additional input to the one-way function that hashes data, a
/// password or passphrase. This is used to make each output different for the same input
/// thus adding security.
/// </param>
/// <param name="args">
/// Optional constructor arguments for the <see cref="IPasswordAlgorithm"/> implementation
/// instance.
/// </param>
/// <returns>
/// A new <see cref="Password"/> object with the given password and salt, as well as an instance
/// of <typeparamref name="T"/> created using the provided constructor arguments.
/// </returns>
public static Password GetPassword<T>(string password, byte[] salt, params object[] args) where T : IPasswordAlgorithm
{
var allArgs = args.Prepend(salt).Prepend(password).ToArray(); // Prepend in reverse order so that password precedes salt.
var argTypes = allArgs.Select(arg => arg.GetType()).ToArray();
var algorithmConstructor = typeof(T).GetConstructor(argTypes);
if (algorithmConstructor == null)
{
CapyEventReporter.EmitEvent(EventLevel.Error, "Cannot find a constructor for {0} that matches the given arguments: {1}",
args: new[]
{
typeof(T).Name,
string.Join(",", argTypes.Select(arg => arg.Name))
});
return default(Password);
}
var passwordInstance = (T)algorithmConstructor.Invoke(allArgs);
if (passwordInstance == null)
{
CapyEventReporter.EmitEvent(EventLevel.Error, "There was an error invoking the constructor for {0} with the given arguments: {1}",
args: new[]
{
typeof(T).Name,
string.Join(",", allArgs)
});
return default(Password);
}
return new Password(password, salt, passwordInstance, args);
} }
/// <summary> /// <summary>
@ -95,7 +201,7 @@ namespace CapyKit.Helpers
/// </returns> /// </returns>
public static Password Pbkdf2(string password) public static Password Pbkdf2(string password)
{ {
var salt = SecurityHelper.GetRandomBytes(saltSize); var salt = SecurityHelper.GetRandomBytes(SALT_SIZE);
var pwd = new Password(password, salt, Password.Pbkdf2Algorithm); var pwd = new Password(password, salt, Password.Pbkdf2Algorithm);
return pwd; return pwd;
@ -186,6 +292,11 @@ namespace CapyKit.Helpers
return buffer; return buffer;
} }
/// <summary>
/// Static method that returns a valid character composition based on the given ValidCharacterCollection parameters.
/// </summary>
/// <param name="validCharacters">An array of ValidCharacterCollection enumeration values representing the desired character sets.</param>
/// <returns>A string containing all the characters from the specified character sets.</returns>
private static string GetValidCharacterComposition(params ValidCharacterCollection[] validCharacters) private static string GetValidCharacterComposition(params ValidCharacterCollection[] validCharacters)
{ {
var composition = new StringBuilder(); var composition = new StringBuilder();

View file

@ -16,7 +16,7 @@ namespace CapyKit
{ {
#region Members #region Members
private static Lazy<Pbkdf2Algorithm> algorithm = new Lazy<Pbkdf2Algorithm>(() => new Pbkdf2Algorithm()); private static Lazy<Pbkdf2Algorithm> pbkdf2Algorithm = new Lazy<Pbkdf2Algorithm>(() => new Pbkdf2Algorithm());
#endregion #endregion
@ -37,16 +37,22 @@ namespace CapyKit
/// </summary> /// </summary>
public IPasswordAlgorithm Algorithm { get; private set; } public IPasswordAlgorithm Algorithm { get; private set; }
#region Preconfigued Password Algorithms
/// <summary> Gets the preconfigured PBKDF2 algorithm. </summary>
/// <value> The preconfigured PBKDF2 algorithm. </value>
public static Pbkdf2Algorithm Pbkdf2Algorithm public static Pbkdf2Algorithm Pbkdf2Algorithm
{ {
get get
{ {
return algorithm.Value; return pbkdf2Algorithm.Value;
} }
} }
#endregion #endregion
#endregion
/// <summary> Constructor. </summary> /// <summary> Constructor. </summary>
/// <param name="password"> The password to be hashed. </param> /// <param name="password"> The password to be hashed. </param>
/// <param name="salt"> The salt used for encryption. </param> /// <param name="salt"> The salt used for encryption. </param>
@ -64,6 +70,25 @@ namespace CapyKit
#region Methods #region Methods
/// <inheritdoc/>
public override bool Equals(object? obj)
{
if(obj == null) return false; // The object is null, and this object is not.
if(ReferenceEquals(this, obj)) return true; // The object is literally this object.
var objPassword = obj as Password;
if (objPassword == null)
{
return base.Equals(obj); // Objects aren't the same type. We can fall back to the default comparison.
}
return this.Algorithm.AlgorithmName == objPassword.Algorithm.AlgorithmName
&& this.Hash == objPassword.Hash
&& this.Salt == objPassword.Salt;
}
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString()
{ {
@ -71,6 +96,24 @@ namespace CapyKit
} }
#endregion #endregion
#region Operators
/// <inheritdoc/>
public static bool operator ==(Password a, Password b)
{
return ReferenceEquals(a, b) // Literally the same object.
|| (ReferenceEquals(a, null) && ReferenceEquals(b,null)) // Both are null
|| a.Equals(b); // Both are not null but not the same object.
}
/// <inheritdoc/>
public static bool operator !=(Password a, Password b)
{
return !(a == b);
}
#endregion
} }
/// <summary> /// <summary>