diff --git a/Readme.md b/Readme.md index 039bc14..dd25898 100644 --- a/Readme.md +++ b/Readme.md @@ -101,8 +101,8 @@ string plainText = aes256.Decrypt(cipherText); #### 1.4.2.3. Encryption/Decryption methods without hashing ```csharp -byte[] cipherText = aes256.Encrypt("your-plaintext", false); -string plainText = aes256.Decrypt(cipherText, false); +byte[] cipherText = aes256.EncryptWithout("your-plaintext"); +string plainText = aes256.DecryptWithout(cipherText); ``` #### 1.4.2.4. Encryption/Decryption methods with custom key (overriding options for one time) diff --git a/src/Pandatech.Crypto/Aes256.cs b/src/Pandatech.Crypto/Aes256.cs index cc4b019..6b40420 100644 --- a/src/Pandatech.Crypto/Aes256.cs +++ b/src/Pandatech.Crypto/Aes256.cs @@ -9,34 +9,56 @@ public class Aes256(Aes256Options options) private const int IvSize = 16; private const int HashSize = 64; - public byte[] Encrypt(string? plainText, bool addHashToBytes = true) + public byte[] Encrypt(string plainText) { - if (string.IsNullOrEmpty(plainText)) return []; - return addHashToBytes ? EncryptWithHash(plainText) : Encrypt(plainText); + return EncryptWithHashInner(plainText); } - public byte[] Encrypt(string? plainText, string key, bool addHashToBytes = true) + public byte[] EncryptWithoutHash(string plainText) + { + return EncryptWithoutHashInner(plainText, null); + } + + public byte[] Encrypt(string plainText, string key) { ValidateKey(key); - if (string.IsNullOrEmpty(plainText)) return []; - return addHashToBytes ? EncryptWithHash(plainText, key) : Encrypt(plainText, key); + + return EncryptWithHashInner(plainText, key); } - public string? Decrypt(byte[]? cipherText, bool includesHash = true) + public byte[] EncryptWithoutHash(string plainText, string key) { - if (cipherText == null || cipherText.Length == 0) return ""; - return includesHash ? DecryptIgnoringHash(cipherText) : Decrypt(cipherText); + ValidateKey(key); + + return EncryptWithoutHashInner(plainText, key); } - public string Decrypt(byte[] cipherText, string key, bool bytesIncludeHash = true) + public void Encrypt(Stream inputStream, Stream outputStream, string? key = null) { + key ??= _options.Key; ValidateKey(key); - if (cipherText.Length == 0) return ""; - return bytesIncludeHash ? DecryptIgnoringHash(cipherText, key) : Decrypt(cipherText, key); + using var aesAlg = Aes.Create(); + aesAlg.KeySize = KeySize; + aesAlg.Padding = PaddingMode.PKCS7; + aesAlg.Key = Convert.FromBase64String(key); + aesAlg.GenerateIV(); + + outputStream.Write(aesAlg.IV, 0, aesAlg.IV.Length); + + using var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); + using var cryptoStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write, leaveOpen: true); + inputStream.CopyTo(cryptoStream); } + private byte[] EncryptWithHashInner(string plainText, string? key = null) + { + key ??= _options.Key; + var encryptedBytes = EncryptWithoutHashInner(plainText, key); + var hashBytes = Sha3.Hash(plainText); + return hashBytes.Concat(encryptedBytes).ToArray(); + } - private byte[] Encrypt(string plainText, string? key) + private byte[] EncryptWithoutHashInner(string plainText, string? key) { key ??= _options.Key; ValidateText(plainText); @@ -59,26 +81,58 @@ private byte[] Encrypt(string plainText, string? key) var result = aesAlg.IV.Concat(encryptedPasswordByte).ToArray(); return result; } + + public string Decrypt(byte[] cipherText) + { + return cipherText.Length == 0 + ? "" + : DecryptSkippingHashInner(cipherText); + } + + public string DecryptWithoutHash(byte[] cipherText) + { + return cipherText.Length == 0 + ? "" + : DecryptWithoutSkippingHashInner(cipherText, null); + } + + public string Decrypt(byte[] cipherText, string key) + { + ValidateKey(key); + return cipherText.Length == 0 + ? "" + : DecryptSkippingHashInner(cipherText, key); + } + + public string DecryptWithoutHash(byte[] cipherText, string key) + { + ValidateKey(key); + return cipherText.Length == 0 + ? "" + : DecryptWithoutSkippingHashInner(cipherText, key); + } - public void EncryptStream(Stream inputStream, Stream outputStream, string? key = null) + public void Decrypt(Stream inputStream, Stream outputStream, string? key = null) { key ??= _options.Key; ValidateKey(key); + + var iv = new byte[IvSize]; + if (inputStream.Read(iv, 0, IvSize) != IvSize) + throw new ArgumentException("Input stream does not contain a complete IV."); + using var aesAlg = Aes.Create(); aesAlg.KeySize = KeySize; aesAlg.Padding = PaddingMode.PKCS7; aesAlg.Key = Convert.FromBase64String(key); - aesAlg.GenerateIV(); - - outputStream.Write(aesAlg.IV, 0, aesAlg.IV.Length); + aesAlg.IV = iv; - using var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); - using var cryptoStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write, leaveOpen: true); - inputStream.CopyTo(cryptoStream); + using var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); + using var cryptoStream = new CryptoStream(inputStream, decryptor, CryptoStreamMode.Read, leaveOpen: true); + cryptoStream.CopyTo(outputStream); } - - private string Decrypt(byte[] cipherText, string? key) + private string DecryptWithoutSkippingHashInner(byte[] cipherText, string? key) { key ??= _options.Key; ValidateCipherText(cipherText); @@ -99,39 +153,11 @@ private string Decrypt(byte[] cipherText, string? key) return srDecrypt.ReadToEnd(); } - private byte[] EncryptWithHash(string plainText, string? key = null) - { - key ??= _options.Key; - var encryptedBytes = Encrypt(plainText, key); - var hashBytes = Sha3.Hash(plainText); - return hashBytes.Concat(encryptedBytes).ToArray(); - } - - private string DecryptIgnoringHash(IEnumerable cipherTextWithHash, string? key = null) + private string DecryptSkippingHashInner(IEnumerable cipherTextWithHash, string? key = null) { key ??= _options.Key; var cipherText = cipherTextWithHash.Skip(HashSize).ToArray(); - return Decrypt(cipherText, key); - } - - public void DecryptStream(Stream inputStream, Stream outputStream, string? key = null) - { - key ??= _options.Key; - ValidateKey(key); - - var iv = new byte[IvSize]; - if (inputStream.Read(iv, 0, IvSize) != IvSize) - throw new ArgumentException("Input stream does not contain a complete IV."); - - using var aesAlg = Aes.Create(); - aesAlg.KeySize = KeySize; - aesAlg.Padding = PaddingMode.PKCS7; - aesAlg.Key = Convert.FromBase64String(key); - aesAlg.IV = iv; - - using var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); - using var cryptoStream = new CryptoStream(inputStream, decryptor, CryptoStreamMode.Read, leaveOpen: true); - cryptoStream.CopyTo(outputStream); + return DecryptWithoutSkippingHashInner(cipherText, key); } private static void ValidateKey(string key) diff --git a/src/Pandatech.Crypto/Pandatech.Crypto.csproj b/src/Pandatech.Crypto/Pandatech.Crypto.csproj index b9556d1..fdfcbf7 100644 --- a/src/Pandatech.Crypto/Pandatech.Crypto.csproj +++ b/src/Pandatech.Crypto/Pandatech.Crypto.csproj @@ -8,12 +8,12 @@ MIT pandatech.png Readme.md - 2.3.2 + 2.4.0 Pandatech.Crypto Pandatech, library, encryption, hash, algorythms, security PandaTech.Crypto is a .NET library simplifying common cryptograhic functions. https://github.com/PandaTechAM/be-lib-pandatech-crypto - Zip and Aes stream overload + Aes256 public api has changed which causes breaking changes. Now instead of parameter of adding hash it is introduced totally new method diff --git a/test/Pandatech.Crypto.Tests/Aes256Tests.cs b/test/Pandatech.Crypto.Tests/Aes256Tests.cs index 7289640..c957629 100644 --- a/test/Pandatech.Crypto.Tests/Aes256Tests.cs +++ b/test/Pandatech.Crypto.Tests/Aes256Tests.cs @@ -5,33 +5,32 @@ namespace Pandatech.Crypto.Tests; public class Aes256Tests { [Fact] - public void EncryptDecryptWithParameter_ShouldReturnOriginalString() + public void EncryptDecryptWithHash_ShouldReturnOriginalString() { var aes256 = new Aes256(new Aes256Options()); var key = Random.GenerateAes256KeyString(); const string original = "MySensitiveData"; - var encrypted = aes256.Encrypt(original, key, false); - var decrypted = aes256.Decrypt(encrypted, key, false); + var encrypted = aes256.Encrypt(original, key); + var decrypted = aes256.Decrypt(encrypted, key); Assert.Equal(original, decrypted); } [Fact] - public void EncryptDecryptWithoutParameter_ShouldReturnOriginalString() + public void EncryptDecryptWithoutHash_ShouldReturnOriginalString() { var aes256Options = new Aes256Options { Key = Random.GenerateAes256KeyString() }; var aes256 = new Aes256(aes256Options); - Environment.SetEnvironmentVariable("AES_KEY", Random.GenerateAes256KeyString()); const string original = "MySensitiveData"; - var encrypted = aes256.Encrypt(original, false); - var decrypted = aes256.Decrypt(encrypted, false); + var encrypted = aes256.EncryptWithoutHash(original); + var decrypted = aes256.DecryptWithoutHash(encrypted); Assert.Equal(original, decrypted); } [Fact] - public void EncryptWithParameterAndHash_ShouldReturnByteArrayWithHash() + public void EncryptWithHash_ShouldReturnByteArrayWithHash() { var aes256 = new Aes256(new Aes256Options()); var key = Random.GenerateAes256KeyString(); @@ -44,11 +43,10 @@ public void EncryptWithParameterAndHash_ShouldReturnByteArrayWithHash() } [Fact] - public void EncryptWithoutParameterAndHash_ShouldReturnByteArrayWithHash() + public void EncryptAndHash_ShouldReturnByteArrayWithHash() { var aes256Options = new Aes256Options { Key = Random.GenerateAes256KeyString() }; var aes256 = new Aes256(aes256Options); - Environment.SetEnvironmentVariable("AES_KEY", Random.GenerateAes256KeyString()); const string original = "MySensitiveData"; var encryptedWithHash = aes256.Encrypt(original); @@ -74,7 +72,6 @@ public void DecryptWithoutParameterAndIgnoringHash_ShouldReturnOriginalString() { var aes256Options = new Aes256Options { Key = Random.GenerateAes256KeyString() }; var aes256 = new Aes256(aes256Options); - Environment.SetEnvironmentVariable("AES_KEY", Random.GenerateAes256KeyString()); const string original = "MySensitiveData"; var encryptedWithHash = aes256.Encrypt(original); var decrypted = aes256.Decrypt(encryptedWithHash); @@ -113,17 +110,6 @@ public void EncryptDecryptWithShortKey_ShouldThrowException() Assert.Throws(() => aes256.Decrypt([], shortKey)); } - [Fact] - public void EncryptDecryptWithEmptyText_ShouldReturnEmptyString() - { - var aes256 = new Aes256(new Aes256Options()); - var key = Random.GenerateAes256KeyString(); - var original = string.Empty; - var encrypted = aes256.Encrypt(original, key); - var decrypted = aes256.Decrypt(encrypted, key); - Assert.Equal(original, decrypted); - } - [Fact] public void EncryptDecryptWithNullCipher_ShouldReturnEmptyString() { @@ -159,11 +145,11 @@ public void EncryptDecryptStream_ShouldReturnOriginalData() var outputStream = new MemoryStream(); // Act - aes256.EncryptStream(inputStream, outputStream, aes256Options.Key); + aes256.Encrypt(inputStream, outputStream, aes256Options.Key); outputStream.Seek(0, SeekOrigin.Begin); var resultStream = new MemoryStream(); - aes256.DecryptStream(outputStream, resultStream, aes256Options.Key); + aes256.Decrypt(outputStream, resultStream, aes256Options.Key); resultStream.Seek(0, SeekOrigin.Begin); var decryptedData = new StreamReader(resultStream).ReadToEnd(); @@ -181,11 +167,11 @@ public void EncryptDecryptStreamWithEmptyContent_ShouldHandleGracefully() var outputStream = new MemoryStream(); // Act - aes256.EncryptStream(inputStream, outputStream, aes256Options.Key); + aes256.Encrypt(inputStream, outputStream, aes256Options.Key); outputStream.Seek(0, SeekOrigin.Begin); // Reset the position for reading. var resultStream = new MemoryStream(); - aes256.DecryptStream(outputStream, resultStream, aes256Options.Key); + aes256.Decrypt(outputStream, resultStream, aes256Options.Key); resultStream.Seek(0, SeekOrigin.Begin); var decryptedData = new StreamReader(resultStream).ReadToEnd(); @@ -203,7 +189,7 @@ public void EncryptStreamWithInvalidKey_ShouldThrowException() var outputStream = new MemoryStream(); // Act & Assert - Assert.Throws(() => aes256.EncryptStream(inputStream, outputStream, invalidKey)); + Assert.Throws(() => aes256.Encrypt(inputStream, outputStream, invalidKey)); } [Fact] @@ -216,6 +202,6 @@ public void DecryptStreamWithInvalidKey_ShouldThrowException() var outputStream = new MemoryStream(); // Act & Assert - Assert.Throws(() => aes256.DecryptStream(inputStream, outputStream, invalidKey)); + Assert.Throws(() => aes256.Decrypt(inputStream, outputStream, invalidKey)); } } \ No newline at end of file diff --git a/test/Pandatech.Crypto.Tests/Pandatech.Crypto.Tests.csproj b/test/Pandatech.Crypto.Tests/Pandatech.Crypto.Tests.csproj index 58c8056..463bd2b 100644 --- a/test/Pandatech.Crypto.Tests/Pandatech.Crypto.Tests.csproj +++ b/test/Pandatech.Crypto.Tests/Pandatech.Crypto.Tests.csproj @@ -11,9 +11,9 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all