diff --git a/AMWD.Common.Tests/Utilities/CryptographyHelperTests.cs b/AMWD.Common.Tests/Utilities/CryptographyHelperTests.cs
new file mode 100644
index 0000000..347cdb3
--- /dev/null
+++ b/AMWD.Common.Tests/Utilities/CryptographyHelperTests.cs
@@ -0,0 +1,598 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text.RegularExpressions;
+using AMWD.Common.Tests.Utils;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace AMWD.Common.Tests.Utilities
+{
+ [TestClass]
+ public class CryptographyHelperTests
+ {
+ private string keyFile;
+ private CryptographyHelper cryptoHelper;
+
+ [TestInitialize]
+ public void Initialize()
+ {
+ keyFile = Path.GetTempFileName();
+ cryptoHelper = new CryptographyHelper(keyFile);
+ }
+
+ [TestCleanup]
+ public void Cleanup()
+ {
+ File.Delete(keyFile);
+ }
+
+ #region Static
+
+ #region Encryption
+
+ #region AES
+
+ [TestMethod]
+ public void ShouldEncryptAesWithoutSalt() // required to test the encryption itself
+ {
+ // arrange
+ using var disposable = CryptographyHelperSaltMock.Create(0);
+
+ byte[] bytes = new byte[] { 0xaf, 0xfe };
+ string str = "ABC";
+ string password1 = "P@ssw0rd!";
+ string password2 = "P@ssw0rd";
+
+ byte[] expectedBytes = new byte[] { 0x7c, 0x7b, 0x77, 0x56, 0x91, 0x1a, 0xd9, 0xc0, 0x72, 0x70, 0x36, 0x88, 0x9f, 0xb4, 0xb5, 0xbc };
+
+ // act
+ byte[] cipherBytes1 = CryptographyHelper.AesEncrypt(bytes, password1);
+ string cipherStr1 = CryptographyHelper.AesEncrypt(str, password1);
+ byte[] cipherBytes2 = CryptographyHelper.AesEncrypt(bytes, password2);
+ string cipherStr2 = CryptographyHelper.AesEncrypt(str, password2);
+
+ // assert
+ CollectionAssert.AreEqual(expectedBytes, cipherBytes1);
+ Assert.AreEqual("ueLuhFNpCuYmx8v3hczHtg==", cipherStr1);
+
+ CollectionAssert.AreNotEqual(expectedBytes, cipherBytes2);
+ Assert.AreNotEqual("ueLuhFNpCuYmx8v3hczHtg==", cipherStr2);
+ }
+
+ [TestMethod]
+ public void ShouldDecryptAesWithoutSalt() // required to test the decryption itself
+ {
+ // arrange
+ using var disposable = CryptographyHelperSaltMock.Create(0);
+
+ string cipherStr = "ueLuhFNpCuYmx8v3hczHtg==";
+ byte[] cipherBytes = new byte[] { 0x7c, 0x7b, 0x77, 0x56, 0x91, 0x1a, 0xd9, 0xc0, 0x72, 0x70, 0x36, 0x88, 0x9f, 0xb4, 0xb5, 0xbc };
+
+ string password1 = "P@ssw0rd!";
+ string password2 = "P@ssw0rd";
+
+ byte[] expectedBytes = new byte[] { 0xaf, 0xfe };
+
+ // act
+ byte[] plainBytes1 = CryptographyHelper.AesDecrypt(cipherBytes, password1);
+ string plainStr1 = CryptographyHelper.AesDecrypt(cipherStr, password1);
+
+ try
+ {
+ CryptographyHelper.AesDecrypt(cipherBytes, password2);
+ Assert.Fail();
+ }
+ catch (CryptographicException)
+ { }
+
+ try
+ {
+ CryptographyHelper.AesDecrypt(cipherStr, password2);
+ Assert.Fail();
+ }
+ catch (CryptographicException)
+ { }
+
+ // assert
+ CollectionAssert.AreEqual(expectedBytes, plainBytes1);
+ Assert.AreEqual("ABC", plainStr1);
+ }
+
+ [TestMethod]
+ public void ShouldEncryptDecryptAesBytes()
+ {
+ // arrange
+ byte[] plain = new byte[] { 0xaf, 0xfe };
+ string password = "P@ssw0rd!";
+
+ // act
+ byte[] cipher1 = CryptographyHelper.AesEncrypt(plain, password);
+ byte[] cipher2 = CryptographyHelper.AesEncrypt(plain, password);
+
+ byte[] plain1 = CryptographyHelper.AesDecrypt(cipher1, password);
+ byte[] plain2 = CryptographyHelper.AesDecrypt(cipher2, password);
+
+ // assert
+ CollectionAssert.AreNotEqual(cipher1, cipher2);
+ Assert.AreEqual(24, cipher1.Length);
+ Assert.AreEqual(24, cipher2.Length);
+ CollectionAssert.AreEqual(plain, plain1);
+ CollectionAssert.AreEqual(plain, plain2);
+ }
+
+ [TestMethod]
+ public void ShouldEncryptDecryptAesString()
+ {
+ // arrange
+ string plain = "ABC";
+ string password = "P@ssw0rd!";
+
+ // act
+ string cipher1 = CryptographyHelper.AesEncrypt(plain, password);
+ string cipher2 = CryptographyHelper.AesEncrypt(plain, password);
+
+ string plain1 = CryptographyHelper.AesDecrypt(cipher1, password);
+ string plain2 = CryptographyHelper.AesDecrypt(cipher2, password);
+
+ // assert
+ Assert.AreNotEqual(cipher1, cipher2);
+ Assert.AreEqual(32, cipher1.Length);
+ Assert.AreEqual(32, cipher2.Length);
+ Assert.AreEqual(plain, plain1);
+ Assert.AreEqual(plain, plain2);
+ }
+
+ #endregion AES
+
+ #region TripleDES
+
+ [TestMethod]
+ public void ShouldEncryptTdesWithoutSalt() // required to test the encryption itself
+ {
+ // arrange
+ using var disposable = CryptographyHelperSaltMock.Create(0);
+
+ byte[] bytes = new byte[] { 0xaf, 0xfe };
+ string str = "ABC";
+ string password1 = "P@ssw0rd!";
+ string password2 = "P@ssw0rd";
+
+ byte[] expectedBytes = new byte[] { 0xbf, 0x59, 0x1f, 0x48, 0x69, 0xab, 0x18, 0xc7 };
+
+ // act
+ byte[] cipherBytes1 = CryptographyHelper.TripleDesEncrypt(bytes, password1);
+ string cipherStr1 = CryptographyHelper.TripleDesEncrypt(str, password1);
+ byte[] cipherBytes2 = CryptographyHelper.TripleDesEncrypt(bytes, password2);
+ string cipherStr2 = CryptographyHelper.TripleDesEncrypt(str, password2);
+
+ // assert
+ CollectionAssert.AreEqual(expectedBytes, cipherBytes1);
+ Assert.AreEqual("1l74soBuuEI=", cipherStr1);
+
+ CollectionAssert.AreNotEqual(expectedBytes, cipherBytes2);
+ Assert.AreNotEqual("1l74soBuuEI=", cipherStr2);
+ }
+
+ [TestMethod]
+ public void ShouldDecryptTdesWithoutSalt() // required to test the decryption itself
+ {
+ // arrange
+ using var disposable = CryptographyHelperSaltMock.Create(0);
+
+ string cipherStr = "1l74soBuuEI=";
+ byte[] cipherBytes = new byte[] { 0xbf, 0x59, 0x1f, 0x48, 0x69, 0xab, 0x18, 0xc7 };
+
+ string password1 = "P@ssw0rd!";
+ string password2 = "P@ssw0rd";
+
+ byte[] expectedBytes = new byte[] { 0xaf, 0xfe };
+
+ // act
+ byte[] plainBytes1 = CryptographyHelper.TripleDesDecrypt(cipherBytes, password1);
+ string plainStr1 = CryptographyHelper.TripleDesDecrypt(cipherStr, password1);
+
+ try
+ {
+ CryptographyHelper.TripleDesDecrypt(cipherBytes, password2);
+ Assert.Fail();
+ }
+ catch (CryptographicException)
+ { }
+
+ try
+ {
+ CryptographyHelper.TripleDesDecrypt(cipherStr, password2);
+ Assert.Fail();
+ }
+ catch (CryptographicException)
+ { }
+
+ // assert
+ CollectionAssert.AreEqual(expectedBytes, plainBytes1);
+ Assert.AreEqual("ABC", plainStr1);
+ }
+
+ [TestMethod]
+ public void ShouldEncryptDecryptTdesBytes()
+ {
+ // arrange
+ byte[] plain = new byte[] { 0xaf, 0xfe };
+ string password = "P@ssw0rd!";
+
+ // act
+ byte[] cipher1 = CryptographyHelper.TripleDesEncrypt(plain, password);
+ byte[] cipher2 = CryptographyHelper.TripleDesEncrypt(plain, password);
+
+ byte[] plain1 = CryptographyHelper.TripleDesDecrypt(cipher1, password);
+ byte[] plain2 = CryptographyHelper.TripleDesDecrypt(cipher2, password);
+
+ // assert
+ CollectionAssert.AreNotEqual(cipher1, cipher2);
+ Assert.AreEqual(16, cipher1.Length);
+ Assert.AreEqual(16, cipher2.Length);
+ CollectionAssert.AreEqual(plain, plain1);
+ CollectionAssert.AreEqual(plain, plain2);
+ }
+
+ [TestMethod]
+ public void ShouldEncryptDecryptTdesString()
+ {
+ // arrange
+ string plain = "ABC";
+ string password = "P@ssw0rd!";
+
+ // act
+ string cipher1 = CryptographyHelper.TripleDesEncrypt(plain, password);
+ string cipher2 = CryptographyHelper.TripleDesEncrypt(plain, password);
+
+ string plain1 = CryptographyHelper.TripleDesDecrypt(cipher1, password);
+ string plain2 = CryptographyHelper.TripleDesDecrypt(cipher2, password);
+
+ // assert
+ Assert.AreNotEqual(cipher1, cipher2);
+ Assert.AreEqual(24, cipher1.Length);
+ Assert.AreEqual(24, cipher2.Length);
+ Assert.AreEqual(plain, plain1);
+ Assert.AreEqual(plain, plain2);
+ }
+
+ #endregion TripleDES
+
+ #endregion Encryption
+
+ #region Hash
+
+ [TestMethod]
+ public void ShouldReturnMd5Hash()
+ {
+ // arrange
+ string text = "Hello World!";
+ byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef };
+ string fileName = Path.GetTempFileName();
+
+ // act
+ string textHash = CryptographyHelper.Md5(text);
+ string bytesHash = CryptographyHelper.Md5(bytes);
+
+ File.WriteAllText(fileName, text);
+ string fileHash = CryptographyHelper.Md5File(fileName);
+ File.Delete(fileName);
+
+ // assert
+ Assert.AreEqual("ed076287532e86365e841e92bfc50d8c", textHash);
+ Assert.AreEqual("2f249230a8e7c2bf6005ccd2679259ec", bytesHash);
+ Assert.AreEqual("ed076287532e86365e841e92bfc50d8c", fileHash);
+ }
+
+ [TestMethod]
+ public void ShouldReturnSha1Hash()
+ {
+ // arrange
+ string text = "Hello World!";
+ byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef };
+ string fileName = Path.GetTempFileName();
+
+ // act
+ string textHash = CryptographyHelper.Sha1(text);
+ string bytesHash = CryptographyHelper.Sha1(bytes);
+
+ File.WriteAllText(fileName, text);
+ string fileHash = CryptographyHelper.Sha1File(fileName);
+ File.Delete(fileName);
+
+ // assert
+ Assert.AreEqual("2ef7bde608ce5404e97d5f042f95f89f1c232871", textHash);
+ Assert.AreEqual("d78f8bb992a56a597f6c7a1fb918bb78271367eb", bytesHash);
+ Assert.AreEqual("2ef7bde608ce5404e97d5f042f95f89f1c232871", fileHash);
+ }
+
+ [TestMethod]
+ public void ShouldReturnSha256Hash()
+ {
+ // arrange
+ string text = "Hello World!";
+ byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef };
+ string fileName = Path.GetTempFileName();
+
+ // act
+ string textHash = CryptographyHelper.Sha256(text);
+ string bytesHash = CryptographyHelper.Sha256(bytes);
+
+ File.WriteAllText(fileName, text);
+ string fileHash = CryptographyHelper.Sha256File(fileName);
+ File.Delete(fileName);
+
+ // assert
+ Assert.AreEqual("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", textHash);
+ Assert.AreEqual("5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953", bytesHash);
+ Assert.AreEqual("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", fileHash);
+ }
+
+ [TestMethod]
+ public void ShouldReturnSha512Hash()
+ {
+ // arrange
+ string text = "Hello World!";
+ byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef };
+ string fileName = Path.GetTempFileName();
+
+ // act
+ string textHash = CryptographyHelper.Sha512(text);
+ string bytesHash = CryptographyHelper.Sha512(bytes);
+
+ File.WriteAllText(fileName, text);
+ string fileHash = CryptographyHelper.Sha512File(fileName);
+ File.Delete(fileName);
+
+ // assert
+ Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", textHash);
+ Assert.AreEqual("1284b2d521535196f22175d5f558104220a6ad7680e78b49fa6f20e57ea7b185d71ec1edb137e70eba528dedb141f5d2f8bb53149d262932b27cf41fed96aa7f", bytesHash);
+ Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", fileHash);
+ }
+
+ #endregion Hash
+
+ #region Random
+
+ [TestMethod]
+ public void ShouldReturnRandomBytes()
+ {
+ // arrange
+ int length1 = 12;
+ int length2 = 12;
+ int length3 = 42;
+
+ // act
+ byte[] bytes1 = CryptographyHelper.GetRandomBytes(length1);
+ byte[] bytes2 = CryptographyHelper.GetRandomBytes(length2);
+ byte[] bytes3 = CryptographyHelper.GetRandomBytes(length3);
+
+ // assert
+ Assert.AreEqual(length1, bytes1.Length);
+ Assert.AreEqual(length2, bytes2.Length);
+ Assert.AreEqual(length3, bytes3.Length);
+
+ Assert.IsTrue(bytes1.Length == bytes2.Length);
+ CollectionAssert.AreNotEqual(bytes1, bytes2);
+ }
+
+ [TestMethod]
+ public void ShouldReturnRandomString()
+ {
+ // arrange
+ int length1 = 12;
+ int length2 = 12;
+ int length3 = 42;
+
+ // act
+ string str1 = CryptographyHelper.GetRandomString(length1);
+ string str2 = CryptographyHelper.GetRandomString(length2);
+ string str3 = CryptographyHelper.GetRandomString(length3);
+
+ // assert
+ Assert.AreEqual(length1, str1.Length);
+ Assert.AreEqual(length2, str2.Length);
+ Assert.AreEqual(length3, str3.Length);
+
+ Assert.IsTrue(str1.Length == str2.Length);
+ Assert.IsFalse(str1 == str2);
+ }
+
+ [TestMethod]
+ public void ShouldReturnRandomStringWithPool()
+ {
+ // arrange
+ int length = 12;
+ string pool = "0123456789abcdef";
+
+ // act
+ string str1 = CryptographyHelper.GetRandomString(length, pool);
+ string str2 = CryptographyHelper.GetRandomString(length, pool);
+
+ // assert
+ Assert.AreEqual(length, str1.Length);
+ Assert.AreEqual(length, str2.Length);
+ Assert.IsFalse(str1 == str2);
+ Assert.IsFalse(Regex.IsMatch(str1, "[^0-9a-f]"));
+ Assert.IsFalse(Regex.IsMatch(str2, "[^0-9a-f]"));
+ }
+
+ #endregion Random
+
+ #region Secure probing
+
+ [TestMethod]
+ public void ShouldTakeSameTimeToCompareString()
+ {
+ // arrange
+ string str1 = "Hello World!";
+ string str2 = "Hello World!";
+ string str3 = "Hallo World!";
+ string str4 = "Hello World?";
+
+ var sw = new Stopwatch();
+
+ // act
+ sw.Start();
+ bool compare1 = CryptographyHelper.SecureEquals(str1, str2);
+ long time1 = sw.ElapsedMilliseconds;
+
+ bool compare2 = CryptographyHelper.SecureEquals(str1, str3);
+ long time2 = sw.ElapsedMilliseconds;
+
+ bool compare3 = CryptographyHelper.SecureEquals(str1, str4);
+ long time3 = sw.ElapsedMilliseconds;
+ sw.Stop();
+
+ // assert
+ Assert.IsTrue(compare1);
+ Assert.IsFalse(compare2);
+ Assert.IsFalse(compare3);
+ // max. time delta: 2 ticks per character
+ Assert.AreEqual(time1, time2);
+ Assert.AreEqual(time1, time3);
+ Assert.AreEqual(time2, time3);
+ }
+
+ [TestMethod]
+ public void ShouldTakeSameTimeToCompareBytes()
+ {
+ // arrange
+ byte[] bytes1 = CryptographyHelper.GetRandomBytes(200);
+ byte[] bytes2 = new byte[bytes1.Length];
+ byte[] bytes3 = new byte[bytes1.Length];
+ byte[] bytes4 = new byte[bytes1.Length];
+
+ Array.Copy(bytes1, bytes2, bytes1.Length);
+ Array.Copy(bytes1, bytes3, bytes1.Length);
+ Array.Copy(bytes1, bytes4, bytes1.Length);
+
+ bytes3[10] = (byte)(bytes1[10] + 1);
+ bytes4[190] = (byte)(bytes1[190] + 1);
+
+ var sw = new Stopwatch();
+
+ // act
+ sw.Start();
+ bool compare1 = CryptographyHelper.SecureEquals(bytes1, bytes2);
+ long time1 = sw.ElapsedMilliseconds;
+
+ bool compare2 = CryptographyHelper.SecureEquals(bytes1, bytes3);
+ long time2 = sw.ElapsedMilliseconds;
+
+ bool compare3 = CryptographyHelper.SecureEquals(bytes1, bytes4);
+ long time3 = sw.ElapsedMilliseconds;
+ sw.Stop();
+
+ // assert
+ Assert.IsTrue(compare1);
+ Assert.IsFalse(compare2);
+ Assert.IsFalse(compare3);
+ // max. time delta: 2 ticks per byte
+ Assert.AreEqual(time1, time2);
+ Assert.AreEqual(time1, time3);
+ Assert.AreEqual(time2, time3);
+ }
+
+ #endregion Secure probing
+
+ #endregion Static
+
+ #region Instance
+
+ [TestMethod]
+ public void ShouldEncryptAesUsingKeyFile()
+ {
+ // arrange
+ string str = "Hello World!";
+ byte[] bytes = CryptographyHelper.GetRandomBytes(32);
+
+ string password = File.ReadAllText(keyFile);
+
+ // act
+ string cipherStr = cryptoHelper.EncryptAes(str);
+ byte[] cipherBytes = cryptoHelper.EncryptAes(bytes);
+
+ string plainStr = CryptographyHelper.AesDecrypt(cipherStr, password);
+ byte[] plainBytes = CryptographyHelper.AesDecrypt(cipherBytes, password);
+
+ // assert
+ Assert.AreNotEqual(str, cipherStr);
+ Assert.AreEqual(str, plainStr);
+ CollectionAssert.AreNotEqual(bytes, cipherBytes);
+ CollectionAssert.AreEqual(bytes, plainBytes);
+ }
+
+ [TestMethod]
+ public void ShouldDecryptAesUsingKeyFile()
+ {
+ // arrange
+ string str = "Hello World!";
+ byte[] bytes = CryptographyHelper.GetRandomBytes(32);
+
+ string password = File.ReadAllText(keyFile);
+
+ // act
+ string cipherStr = CryptographyHelper.AesEncrypt(str, password);
+ byte[] cipherBytes = CryptographyHelper.AesEncrypt(bytes, password);
+
+ string plainStr = cryptoHelper.DecryptAes(cipherStr);
+ byte[] plainBytes = cryptoHelper.DecryptAes(cipherBytes);
+
+ // assert
+ Assert.AreNotEqual(str, cipherStr);
+ Assert.AreEqual(str, plainStr);
+ CollectionAssert.AreNotEqual(bytes, cipherBytes);
+ CollectionAssert.AreEqual(bytes, plainBytes);
+ }
+
+ [TestMethod]
+ public void ShouldEncryptTdesUsingKeyFile()
+ {
+ // arrange
+ string str = "Hello World!";
+ byte[] bytes = CryptographyHelper.GetRandomBytes(32);
+
+ string password = File.ReadAllText(keyFile);
+
+ // act
+ string cipherStr = cryptoHelper.EncryptTripleDes(str);
+ byte[] cipherBytes = cryptoHelper.EncryptTripleDes(bytes);
+
+ string plainStr = CryptographyHelper.TripleDesDecrypt(cipherStr, password);
+ byte[] plainBytes = CryptographyHelper.TripleDesDecrypt(cipherBytes, password);
+
+ // assert
+ Assert.AreNotEqual(str, cipherStr);
+ Assert.AreEqual(str, plainStr);
+ CollectionAssert.AreNotEqual(bytes, cipherBytes);
+ CollectionAssert.AreEqual(bytes, plainBytes);
+ }
+
+ [TestMethod]
+ public void ShouldDecryptTdesUsingKeyFile()
+ {
+ // arrange
+ string str = "Hello World!";
+ byte[] bytes = CryptographyHelper.GetRandomBytes(32);
+
+ string password = File.ReadAllText(keyFile);
+
+ // act
+ string cipherStr = CryptographyHelper.TripleDesEncrypt(str, password);
+ byte[] cipherBytes = CryptographyHelper.TripleDesEncrypt(bytes, password);
+
+ string plainStr = cryptoHelper.DecryptTripleDes(cipherStr);
+ byte[] plainBytes = cryptoHelper.DecryptTripleDes(cipherBytes);
+
+ // assert
+ Assert.AreNotEqual(str, cipherStr);
+ Assert.AreEqual(str, plainStr);
+ CollectionAssert.AreNotEqual(bytes, cipherBytes);
+ CollectionAssert.AreEqual(bytes, plainBytes);
+ }
+
+ #endregion Instance
+ }
+}
diff --git a/AMWD.Common.Tests/Utilities/DelayedTaskTests.cs b/AMWD.Common.Tests/Utilities/DelayedTaskTests.cs
new file mode 100644
index 0000000..ad61841
--- /dev/null
+++ b/AMWD.Common.Tests/Utilities/DelayedTaskTests.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace AMWD.Common.Tests.Utilities
+{
+ [TestClass]
+ public class DelayedTaskTests
+ {
+ // TODO
+ }
+}
diff --git a/AMWD.Common.Tests/Utils/CryptographyHelperSaltMock.cs b/AMWD.Common.Tests/Utils/CryptographyHelperSaltMock.cs
new file mode 100644
index 0000000..e59c6b9
--- /dev/null
+++ b/AMWD.Common.Tests/Utils/CryptographyHelperSaltMock.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Security.Cryptography;
+using ReflectionMagic;
+
+namespace AMWD.Common.Tests.Utils
+{
+ [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ internal class CryptographyHelperSaltMock : IDisposable
+ {
+ private readonly int saltLength;
+
+ private CryptographyHelperSaltMock(int saltLength)
+ {
+ this.saltLength = typeof(CryptographyHelper).AsDynamicType().saltLength;
+ SetSaltLength(saltLength);
+ }
+
+ public static IDisposable Create(int saltLength)
+ => new CryptographyHelperSaltMock(saltLength);
+
+ public void Dispose()
+ => SetSaltLength(saltLength);
+
+ private static void SetSaltLength(int length)
+ => typeof(CryptographyHelper).AsDynamicType().saltLength = length;
+ }
+}
diff --git a/AMWD.Common.Tests/Utils/TimeZoneInfoLocalMock.cs b/AMWD.Common.Tests/Utils/TimeZoneInfoLocalMock.cs
index 1b6d5a9..ff14a14 100644
--- a/AMWD.Common.Tests/Utils/TimeZoneInfoLocalMock.cs
+++ b/AMWD.Common.Tests/Utils/TimeZoneInfoLocalMock.cs
@@ -4,7 +4,7 @@ using ReflectionMagic;
namespace AMWD.Common.Tests.Utils
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
- public class TimeZoneInfoLocalMock : IDisposable
+ internal class TimeZoneInfoLocalMock : IDisposable
{
private readonly TimeZoneInfo localTimeZoneInfo;
diff --git a/AMWD.Common/Utilities/CryptographyHelper.cs b/AMWD.Common/Utilities/CryptographyHelper.cs
index 71a83c7..f5bab86 100644
--- a/AMWD.Common/Utilities/CryptographyHelper.cs
+++ b/AMWD.Common/Utilities/CryptographyHelper.cs
@@ -9,7 +9,7 @@ namespace System.Security.Cryptography
///
public class CryptographyHelper
{
- private static readonly int saltLength = 8;
+ private static int saltLength = 8;
private readonly string masterKeyFile;
@@ -206,6 +206,19 @@ namespace System.Security.Cryptography
return ms.ToArray();
}
+ ///
+ /// Decrypts a Base64 string using the AES algorithm and a password to an UTF-8 string.
+ ///
+ /// The encrypted Base64 encoded string.
+ /// The password to use for decryption.
+ /// The decrypted UTF-8 string string.
+ public static string AesDecrypt(string cipherStr, string password)
+ {
+ byte[] cipher = Convert.FromBase64String(cipherStr);
+ byte[] plain = AesDecrypt(cipher, password);
+ return Encoding.UTF8.GetString(plain);
+ }
+
///
/// Encrypts data using the AES algorithm and a password.
///
@@ -234,19 +247,6 @@ namespace System.Security.Cryptography
return ms.ToArray();
}
- ///
- /// Decrypts a Base64 string using the AES algorithm and a password to an UTF-8 string.
- ///
- /// The encrypted Base64 encoded string.
- /// The password to use for decryption.
- /// The decrypted UTF-8 string string.
- public static string AesDecrypt(string cipherStr, string password)
- {
- byte[] cipher = Convert.FromBase64String(cipherStr);
- byte[] plain = AesDecrypt(cipher, password);
- return Encoding.UTF8.GetString(plain);
- }
-
///
/// Encrypts an UTF-8 string using the AES algorithm and a password to a Base64 string.
///
@@ -543,29 +543,7 @@ namespace System.Security.Cryptography
#endregion Random
- #region Probing security
-
- ///
- /// Determines whether two strings are equal in constant time. This method does not stop
- /// early if a difference was detected, unless the length differs.
- ///
- /// The first string.
- /// The second string.
- /// true, if both strings are equal; otherwise, false.
- public static bool SecureEquals(string a, string b)
- {
- if ((a == null) != (b == null))
- return false;
- if (a.Length != b.Length)
- return false;
-
- int differentBits = 0;
- for (int i = 0; i < a.Length; i++)
- {
- differentBits |= a[i] ^ b[i];
- }
- return differentBits == 0;
- }
+ #region Secure probing
///
/// Determines whether two byte arrays are equal in constant time. This method does not stop
@@ -589,7 +567,29 @@ namespace System.Security.Cryptography
return differentBits == 0;
}
- #endregion Probing security
+ ///
+ /// Determines whether two strings are equal in constant time. This method does not stop
+ /// early if a difference was detected, unless the length differs.
+ ///
+ /// The first string.
+ /// The second string.
+ /// true, if both strings are equal; otherwise, false.
+ public static bool SecureEquals(string a, string b)
+ {
+ if ((a == null) != (b == null))
+ return false;
+ if (a.Length != b.Length)
+ return false;
+
+ int differentBits = 0;
+ for (int i = 0; i < a.Length; i++)
+ {
+ differentBits |= a[i] ^ b[i];
+ }
+ return differentBits == 0;
+ }
+
+ #endregion Secure probing
#endregion Static methods
}
diff --git a/AMWD.Common/Utilities/DelayedTask.cs b/AMWD.Common/Utilities/DelayedTask.cs
index 8d20124..6fca87d 100644
--- a/AMWD.Common/Utilities/DelayedTask.cs
+++ b/AMWD.Common/Utilities/DelayedTask.cs
@@ -22,7 +22,7 @@ namespace AMWD.Common.Utilities
///
/// The synchronisation object.
///
- protected readonly object syncObj = new();
+ protected readonly object syncLock = new();
///
/// The exception handler.
@@ -122,7 +122,7 @@ namespace AMWD.Common.Utilities
///
public void Reset()
{
- lock (syncObj)
+ lock (syncLock)
{
if (!IsWaitingToRun && !IsRunning)
{
@@ -149,7 +149,7 @@ namespace AMWD.Common.Utilities
public void Cancel()
{
TaskCompletionSourceWrapper localTcs = null;
- lock (syncObj)
+ lock (syncLock)
{
IsWaitingToRun = false;
nextRunPending = false;
@@ -172,7 +172,7 @@ namespace AMWD.Common.Utilities
/// true, if an execution was started; otherwise, false.
public bool ExecutePending()
{
- lock (syncObj)
+ lock (syncLock)
{
if (!IsWaitingToRun && !IsRunning)
{
@@ -197,7 +197,7 @@ namespace AMWD.Common.Utilities
/// An awaiter instance.
public TaskAwaiter GetAwaiter()
{
- lock (syncObj)
+ lock (syncLock)
{
return tcs.Task.GetAwaiter();
}
@@ -211,7 +211,7 @@ namespace AMWD.Common.Utilities
{
get
{
- lock (syncObj)
+ lock (syncLock)
{
return tcs.Task;
}
@@ -273,7 +273,7 @@ namespace AMWD.Common.Utilities
/// Unused.
protected void OnTimerCallback(object state)
{
- lock (syncObj)
+ lock (syncLock)
{
if (!IsWaitingToRun)
{
@@ -304,7 +304,7 @@ namespace AMWD.Common.Utilities
catch (Exception ex)
{
exception = ex;
- lock (syncObj)
+ lock (syncLock)
{
runAgain = false;
IsRunning = false;
@@ -320,7 +320,7 @@ namespace AMWD.Common.Utilities
}
finally
{
- lock (syncObj)
+ lock (syncLock)
{
runAgain = nextRunPending;
IsRunning = runAgain;
@@ -484,7 +484,7 @@ namespace AMWD.Common.Utilities
/// An awaiter instance.
public new TaskAwaiter GetAwaiter()
{
- lock (syncObj)
+ lock (syncLock)
{
var myTcs = (TaskCompletionSourceWrapper)tcs;
var myTask = (Task)myTcs.Task;
@@ -499,7 +499,7 @@ namespace AMWD.Common.Utilities
{
get
{
- lock (syncObj)
+ lock (syncLock)
{
var myTcs = (TaskCompletionSourceWrapper)tcs;
var myTask = (Task)myTcs.Task;
diff --git a/AMWD.Common/Utilities/NetworkHelper.cs b/AMWD.Common/Utilities/NetworkHelper.cs
index 8896468..b52f1d7 100644
--- a/AMWD.Common/Utilities/NetworkHelper.cs
+++ b/AMWD.Common/Utilities/NetworkHelper.cs
@@ -10,6 +10,7 @@ namespace AMWD.Common.Utilities
///
/// Provides some network utils.
///
+ [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public static class NetworkHelper
{
///
@@ -49,7 +50,6 @@ namespace AMWD.Common.Utilities
/// The interface name to resolve.
/// An address family to use (available: and ).
/// The resolved es or an empty list.
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] // not possible to define interfaces on unit tests
public static List ResolveInterface(string interfaceName, AddressFamily addressFamily = default)
{
if (string.IsNullOrWhiteSpace(interfaceName))