mirror of
https://github.com/AlexMacocian/SystemExtensions.git
synced 2026-07-16 22:39:29 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7eed1ca466 | |||
| b6cc64fd51 | |||
| c8bfaffb42 | |||
| bf163251dd | |||
| c0b5734b71 | |||
| 2944d9887e |
+17
@@ -104,6 +104,23 @@ namespace System.Extensions
|
||||
return serviceManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a <see cref="ILoggerFactory"/> with a <see cref="CVLoggerProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="serviceManager"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceManager RegisterCVLoggerFactory(this IServiceManager serviceManager)
|
||||
{
|
||||
serviceManager.RegisterScoped<ILoggerFactory, LoggerFactory>(sp =>
|
||||
{
|
||||
LoggerFactory loggerFactory = new();
|
||||
loggerFactory.AddProvider(new CVLoggerProvider(sp.GetService<ILogsWriter>()));
|
||||
return loggerFactory;
|
||||
});
|
||||
|
||||
return serviceManager;
|
||||
}
|
||||
|
||||
public static IServiceManager RegisterLoggerFactory(this IServiceManager serviceManager, Func<Slim.IServiceProvider, ILoggerFactory> loggerFactory)
|
||||
{
|
||||
serviceManager.ThrowIfNull(nameof(serviceManager));
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Version>1.1.3</Version>
|
||||
<Version>1.1.6</Version>
|
||||
<Authors>Alexandru Macocian</Authors>
|
||||
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
|
||||
</PropertyGroup>
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CorrelationVector" Version="1.0.42" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
|
||||
<PackageReference Include="Slim" Version="1.5.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageReference Include="Slim" Version="1.6.0" />
|
||||
<PackageReference Include="SystemExtensions.NetStandard" Version="1.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Encryption;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SystemExtensions.NetStandard.Security.Encryption;
|
||||
|
||||
namespace SystemExtensions.NetStandard.Security.Tests
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System.Linq;
|
||||
using System.Rng;
|
||||
using System.Security.Rng;
|
||||
|
||||
namespace SystemExtensions.NetStandard.Security.Tests
|
||||
{
|
||||
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Security.Hashing;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SystemExtensions.NetStandard.Security.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Rfc2898DeriveBytesPasswordHashingServiceTests
|
||||
{
|
||||
private const int TooShortHashLength = 31;
|
||||
private const int DesiredHashLength = 32;
|
||||
private const int Iterations = 10000;
|
||||
private const int TooLittleIterations = 1000;
|
||||
|
||||
private readonly Rfc2898DeriveBytesPasswordHashingService rfc2898DeriveBytesPasswordHashingService = new();
|
||||
|
||||
private byte[] tooShortSaltBytes;
|
||||
private string tooShortSaltString;
|
||||
private byte[] incorrectSaltBytes;
|
||||
private string incorrectSaltString;
|
||||
private byte[] saltBytes;
|
||||
private string saltString;
|
||||
private byte[] passwordBytes;
|
||||
private string passwordString;
|
||||
private byte[] incorrectPasswordBytes;
|
||||
private string incorrectPasswordString;
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
this.tooShortSaltBytes = new byte[16];
|
||||
this.tooShortSaltString = Convert.ToBase64String(this.tooShortSaltBytes);
|
||||
this.saltBytes = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
|
||||
this.incorrectSaltBytes = new byte[32];
|
||||
this.incorrectSaltString = Convert.ToBase64String(this.incorrectSaltBytes);
|
||||
this.saltString = Convert.ToBase64String(this.saltBytes);
|
||||
this.passwordBytes = new byte[] { 5, 1, 2, 34, 35, 123, 4, 23, 1, 235, 32, 234 };
|
||||
this.passwordString = Convert.ToBase64String(this.passwordBytes);
|
||||
this.incorrectPasswordBytes = new byte[] { 14, 123, 23, 4, 2, 1, 23, 25 };
|
||||
this.incorrectPasswordString = Convert.ToBase64String(this.incorrectPasswordBytes);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PasswordNull_HashBytes_Throws_ArgumentNullException()
|
||||
{
|
||||
var action = new Func<Task<byte[]>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(null, this.saltBytes, DesiredHashLength, Iterations));
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void PasswordNull_HashString_Throws_ArgumentNullException()
|
||||
{
|
||||
var action = new Func<Task<string>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(null, this.saltString, DesiredHashLength, Iterations));
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void SaltNull_HashBytes_Throws_ArgumentNullException()
|
||||
{
|
||||
var action = new Func<Task<byte[]>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, null, DesiredHashLength, Iterations));
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void SaltNull_HashString_Throws_ArgumentNullException()
|
||||
{
|
||||
var action = new Func<Task<string>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, null, DesiredHashLength, Iterations));
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void HashLengthTooSmall_HashBytes_Throws_InvalidOperationException()
|
||||
{
|
||||
var action = new Func<Task<byte[]>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, TooShortHashLength, Iterations));
|
||||
|
||||
action.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void HashLengthTooSmall_HashString_Throws_InvalidOperationException()
|
||||
{
|
||||
var action = new Func<Task<string>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, TooShortHashLength, Iterations));
|
||||
|
||||
action.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void TooLittleIterations_HashBytes_Throws_InvalidOperationException()
|
||||
{
|
||||
var action = new Func<Task<byte[]>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, TooLittleIterations));
|
||||
|
||||
action.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void TooLittleIterations_HashString_Throws_InvalidOperationException()
|
||||
{
|
||||
var action = new Func<Task<string>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, TooLittleIterations));
|
||||
|
||||
action.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void TooShortSalt_HashBytes_Throws_InvalidOperationException()
|
||||
{
|
||||
var action = new Func<Task<byte[]>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.tooShortSaltBytes, DesiredHashLength, TooLittleIterations));
|
||||
|
||||
action.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void TooShortSalt_HashString_Throws_InvalidOperationException()
|
||||
{
|
||||
var action = new Func<Task<string>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.tooShortSaltString, DesiredHashLength, TooLittleIterations));
|
||||
|
||||
action.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void PasswordNull_VerifyBytes_Throws_ArgumentNullException()
|
||||
{
|
||||
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(null, this.incorrectPasswordBytes, this.saltBytes, Iterations));
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void PasswordNull_VerifyString_Throws_ArgumentNullException()
|
||||
{
|
||||
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(null, this.incorrectPasswordString, this.saltString, Iterations));
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void HashNull_VerifyBytes_Throws_ArgumentNullException()
|
||||
{
|
||||
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordBytes, null, this.saltBytes, Iterations));
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void HashNull_VerifyString_Throws_ArgumentNullException()
|
||||
{
|
||||
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordString, null, this.saltString, Iterations));
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void SaltNull_VerifyBytes_Throws_ArgumentNullException()
|
||||
{
|
||||
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordBytes, this.incorrectPasswordBytes, null, Iterations));
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void SaltNull_VerifyString_Throws_ArgumentNullException()
|
||||
{
|
||||
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordString, this.incorrectPasswordString, null, Iterations));
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void TooLittleIterations_VerifyBytes_Throws_InvalidOperationException()
|
||||
{
|
||||
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordBytes, this.incorrectPasswordBytes, this.saltBytes, TooLittleIterations));
|
||||
|
||||
action.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public void TooLittleIterations_VerifyString_Throws_InvalidOperationException()
|
||||
{
|
||||
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordString, this.incorrectPasswordString, this.saltString, TooLittleIterations));
|
||||
|
||||
action.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task HashBytes_ReturnsHashedBytes()
|
||||
{
|
||||
var hashedBytes = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, Iterations);
|
||||
|
||||
hashedBytes.Should().NotBeNull();
|
||||
hashedBytes.Should().HaveCount(DesiredHashLength);
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task HashString_ReturnsHashedString()
|
||||
{
|
||||
var hashedString = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, Iterations);
|
||||
|
||||
hashedString.Should().NotBeNull();
|
||||
var hashedBytes = Convert.FromBase64String(hashedString);
|
||||
hashedBytes.Should().HaveCount(DesiredHashLength);
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task VerifyBytes_CorrectPassword_ReturnsTrue()
|
||||
{
|
||||
var hashedBytes = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, Iterations);
|
||||
|
||||
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedBytes, this.passwordBytes, this.saltBytes, Iterations);
|
||||
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task VerifyString_CorrectPassword_ReturnsTrue()
|
||||
{
|
||||
var hashedString = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, Iterations);
|
||||
|
||||
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedString, this.passwordString, this.saltString, Iterations);
|
||||
|
||||
result.Should().BeTrue();
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task VerifyBytes_IncorrectPassword_ReturnsFalse()
|
||||
{
|
||||
var hashedBytes = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, Iterations);
|
||||
|
||||
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedBytes, this.incorrectPasswordBytes, this.saltBytes, Iterations);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task VerifyString_IncorrectPassword_ReturnsFalse()
|
||||
{
|
||||
var hashedString = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, Iterations);
|
||||
|
||||
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedString, this.incorrectPasswordString, this.saltString, Iterations);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task VerifyBytes_IncorrectSalt_ReturnsFalse()
|
||||
{
|
||||
var hashedBytes = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, Iterations);
|
||||
|
||||
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedBytes, this.passwordBytes, this.incorrectSaltBytes, Iterations);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task VerifyString_IncorrectSalt_ReturnsFalse()
|
||||
{
|
||||
var hashedString = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, Iterations);
|
||||
|
||||
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedString, this.passwordString, this.incorrectSaltString, Iterations);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task VerifyBytes_IncorrectIterations_ReturnsFalse()
|
||||
{
|
||||
var hashedBytes = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, Iterations);
|
||||
|
||||
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedBytes, this.passwordBytes, this.saltBytes, Iterations + 1);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task VerifyString_IncorrectIterations_ReturnsFalse()
|
||||
{
|
||||
var hashedString = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, Iterations);
|
||||
|
||||
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedString, this.passwordString, this.saltString, Iterations + 1);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Encryption;
|
||||
using System.Security.Encryption;
|
||||
|
||||
namespace SystemExtensions.NetStandard.Security.Tests
|
||||
{
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Hashing;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SystemExtensions.NetStandard.Security.Hashing;
|
||||
|
||||
namespace SystemExtensions.NetStandard.Security.Tests
|
||||
{
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using SystemExtensions.NetStandard.Security.Utilities;
|
||||
using System.Security.Utilities;
|
||||
|
||||
namespace SystemExtensions.NetStandard.Security.Encryption
|
||||
namespace System.Security.Encryption
|
||||
{
|
||||
public sealed class AesEncrypter : ISymmetricEncrypter
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SystemExtensions.NetStandard.Security.Encryption
|
||||
namespace System.Security.Encryption
|
||||
{
|
||||
public interface ISymmetricEncrypter
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Encryption
|
||||
namespace System.Security.Encryption
|
||||
{
|
||||
public sealed class SecureString
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SystemExtensions.NetStandard.Security.Hashing
|
||||
namespace System.Security.Hashing
|
||||
{
|
||||
public interface IHashingService
|
||||
{
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Security.Hashing
|
||||
{
|
||||
public interface IPasswordHashingService
|
||||
{
|
||||
/// <summary>
|
||||
/// Hash provided raw password and return the hashed value.
|
||||
/// </summary>
|
||||
/// <param name="raw">Base64 encoded password.</param>
|
||||
/// <param name="salt">Base64 encoded salt to be used for hashing.</param>
|
||||
/// <param name="length">Length of the resulting hash.</param>
|
||||
/// <param name="iterations">Number of hashing iterations to be performed.</param>
|
||||
/// <remarks>
|
||||
/// The length of the hash will vary due to the base64 encoding of the resulting hash.
|
||||
/// </remarks>
|
||||
/// <returns>Base64 encoded hashed password.</returns>
|
||||
Task<string> Hash(string raw, string salt, int length, int iterations);
|
||||
/// <summary>
|
||||
/// Hash provided raw password and return the hashed value.
|
||||
/// </summary>
|
||||
/// <param name="raw">Password to be hashed.</param>
|
||||
/// <param name="salt">Salt to be used for hashing.</param>
|
||||
/// <param name="length">Length of the resulting hash.</param>
|
||||
/// <param name="iterations">Number of hashing iterations to be performed.</param>
|
||||
/// <returns>Hashed password.</returns>
|
||||
Task<byte[]> Hash(byte[] raw, byte[] salt, int length, int iterations);
|
||||
/// <summary>
|
||||
/// Verify provided password against a hashed password.
|
||||
/// </summary>
|
||||
/// <param name="hash">Base64 encoding of the previously hashed password.</param>
|
||||
/// <param name="password">Base64 encoding of the password to be verified.</param>
|
||||
/// <param name="salt">Salt used to hash the previous password.</param>
|
||||
/// <param name="iterations">Number of iterations used to hash the previous password.</param>
|
||||
/// <returns>Returns true if password matches the previously hashed password. Otherwise returns false.</returns>
|
||||
Task<bool> VerifyPassword(string hash, string password, string salt, int iterations);
|
||||
/// <summary>
|
||||
/// Verify provided password against a hashed password.
|
||||
/// </summary>
|
||||
/// <param name="hash">Previously hashed password.</param>
|
||||
/// <param name="password">Password to be verified.</param>
|
||||
/// <param name="salt">Salt used to hash the previous password.</param>
|
||||
/// <param name="iterations">Number of iterations used to hash the previous password.</param>
|
||||
/// <returns>Returns true if password matches the previously hashed password. Otherwise returns false.</returns>
|
||||
Task<bool> VerifyPassword(byte[] hash, byte[] password, byte[] salt, int iterations);
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.Security.Hashing
|
||||
{
|
||||
public sealed class Rfc2898DeriveBytesPasswordHashingService : IPasswordHashingService
|
||||
{
|
||||
private const int MinimumIterations = 10000;
|
||||
private const int MinimumHashLength = 32;
|
||||
|
||||
public Rfc2898DeriveBytesPasswordHashingService()
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<string> Hash(string raw, string salt, int length, int iterations)
|
||||
{
|
||||
this.ValidateHashArguments(raw, salt, length, iterations);
|
||||
|
||||
var rawBytes = Convert.FromBase64String(raw);
|
||||
var saltBytes = Convert.FromBase64String(salt);
|
||||
var hashBytes = await this.HashInternal(rawBytes, saltBytes, length, iterations);
|
||||
return Convert.ToBase64String(hashBytes);
|
||||
}
|
||||
public Task<byte[]> Hash(byte[] raw, byte[] salt, int length, int iterations)
|
||||
{
|
||||
this.ValidateHashArguments(raw, salt, length, iterations);
|
||||
|
||||
return this.HashInternal(raw, salt, length, iterations);
|
||||
}
|
||||
public Task<bool> VerifyPassword(string hash, string password, string salt, int iterations)
|
||||
{
|
||||
this.ValidateVerifyArguments(hash, password, salt, iterations);
|
||||
var hashBytes = Convert.FromBase64String(hash);
|
||||
var saltBytes = Convert.FromBase64String(salt);
|
||||
var passwordBytes = Convert.FromBase64String(password);
|
||||
return this.VerifyPasswordInternal(hashBytes, passwordBytes, saltBytes, iterations);
|
||||
}
|
||||
public Task<bool> VerifyPassword(byte[] hash, byte[] password, byte[] salt, int iterations)
|
||||
{
|
||||
this.ValidateVerifyArguments(hash, password, salt, iterations);
|
||||
return this.VerifyPasswordInternal(hash, password, salt, iterations);
|
||||
}
|
||||
|
||||
private void ValidateHashArguments(object raw, object salt, int length, int iterations)
|
||||
{
|
||||
_ = raw ?? throw new ArgumentNullException(nameof(raw));
|
||||
_ = salt ?? throw new ArgumentNullException(nameof(salt));
|
||||
|
||||
if (iterations < MinimumIterations)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to perform secure hash. Iteration count must be over {MinimumIterations}");
|
||||
}
|
||||
|
||||
if (length < MinimumHashLength)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to perform secure hash. Hash length must be over {MinimumHashLength}");
|
||||
}
|
||||
}
|
||||
private void ValidateVerifyArguments(object hash, object password, object salt, int iterations)
|
||||
{
|
||||
_ = hash ?? throw new ArgumentNullException(nameof(hash));
|
||||
_ = salt ?? throw new ArgumentNullException(nameof(salt));
|
||||
_ = password ?? throw new ArgumentNullException(nameof(password));
|
||||
|
||||
if (iterations < MinimumIterations)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to verify hash. Iteration count must be over {MinimumIterations}");
|
||||
}
|
||||
}
|
||||
private Task<byte[]> HashInternal(byte[] raw, byte[] salt, int length, int iterations)
|
||||
{
|
||||
using var pbkdf2 = new Rfc2898DeriveBytes(raw, salt, iterations);
|
||||
var hash = pbkdf2.GetBytes(length);
|
||||
return Task.FromResult(hash);
|
||||
}
|
||||
private Task<bool> VerifyPasswordInternal(byte[] hash, byte[] password, byte[] salt, int iterations)
|
||||
{
|
||||
using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations);
|
||||
var hashToVerify = pbkdf2.GetBytes(hash.Length);
|
||||
var lengthToVerify = Math.Max(hash.Length, hashToVerify.Length);
|
||||
var match = true;
|
||||
if (lengthToVerify <= 0)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
for(var i = 0; i < lengthToVerify; i++)
|
||||
{
|
||||
if (i < hash.Length && i < hashToVerify.Length)
|
||||
{
|
||||
if (hashToVerify[i].Equals(hash[i]) is false)
|
||||
{
|
||||
match = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(match && hash.Length.Equals(hashToVerify.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SystemExtensions.NetStandard.Security.Hashing
|
||||
namespace System.Security.Hashing
|
||||
{
|
||||
public sealed class Sha256HashingService : IHashingService
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace System.Rng
|
||||
namespace System.Security.Rng
|
||||
{
|
||||
public sealed class CryptoRngProvider : ICryptoRngProvider, IDisposable
|
||||
{
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
namespace System.Rng
|
||||
namespace System.Security.Rng
|
||||
{
|
||||
public interface ICryptoRngProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Populate provided array of bytes with cryptographically secure random bytes.
|
||||
/// </summary>
|
||||
/// <param name="data">Array of bytes to be populated.</param>
|
||||
public void GetBytes(byte[] data);
|
||||
/// <summary>
|
||||
/// Return an array of cryptographically secure random bytes.
|
||||
/// </summary>
|
||||
/// <param name="byteCount">Length of the returned array.</param>
|
||||
/// <returns>Array containing cryptographically secure random values.</returns>
|
||||
public byte[] GetBytes(int byteCount);
|
||||
/// <summary>
|
||||
/// Populate provided array of bytes with cryptographically secure random non-zero bytes.
|
||||
/// </summary>
|
||||
/// <param name="data">Array of bytes to be populated.</param>
|
||||
public void GetNonZeroBytes(byte[] data);
|
||||
/// <summary>
|
||||
/// Return an array of cryptographically secure random non-zero bytes.
|
||||
/// </summary>
|
||||
/// <param name="byteCount">Length of the returned array.</param>
|
||||
/// <returns>Array containing cryptographically secure random non-zero values.</returns>
|
||||
public byte[] GetNonZeroBytes(int byteCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||
<RootNamespace>System</RootNamespace>
|
||||
<Version>1.1.1</Version>
|
||||
<Version>1.2.2</Version>
|
||||
<Authors>Alexandru Macocian</Authors>
|
||||
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace SystemExtensions.NetStandard.Security.Utilities
|
||||
namespace System.Security.Utilities
|
||||
{
|
||||
internal sealed class NotClosingCryptoStream : CryptoStream
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user