From f00a0d9ed741b7541f29f059ae429e720802cc45 Mon Sep 17 00:00:00 2001 From: SeaEagle1 Date: Wed, 8 Apr 2020 00:29:22 +0200 Subject: [PATCH 1/4] Allow en- & decryption of binary blobs --- Rijndael256/Rijndael.cs | 33 ++++++++++++++++++++++++++------- Rijndael256/RijndaelEtM.cs | 13 +++++++------ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/Rijndael256/Rijndael.cs b/Rijndael256/Rijndael.cs index 3f11a8d..9e76112 100644 --- a/Rijndael256/Rijndael.cs +++ b/Rijndael256/Rijndael.cs @@ -31,7 +31,9 @@ public class Rijndael /// The Base64 encoded ciphertext. public static string Encrypt(string plaintext, string password, KeySize keySize) { - return Encrypt(Encoding.UTF8.GetBytes(plaintext), password, keySize); + return Convert.ToBase64String( + Encrypt(Encoding.UTF8.GetBytes(plaintext), password, keySize) + ); } /// @@ -41,8 +43,8 @@ public static string Encrypt(string plaintext, string password, KeySize keySize) /// The plaintext to encrypt. /// The password to encrypt the plaintext with. /// The cipher key size. 256-bit is stronger, but slower. - /// The Base64 encoded ciphertext. - public static string Encrypt(byte[] plaintext, string password, KeySize keySize) + /// The encoded ciphertext. + public static byte[] Encrypt(byte[] plaintext, string password, KeySize keySize) { // Generate a random IV var iv = Rng.GenerateRandomBytes(InitializationVectorSize); @@ -51,7 +53,7 @@ public static string Encrypt(byte[] plaintext, string password, KeySize keySize) var ciphertext = Encrypt(plaintext, password, iv, keySize); // Encode the ciphertext - return Convert.ToBase64String(ciphertext); + return ciphertext; } /// @@ -139,7 +141,19 @@ public static void Encrypt(string plaintextFile, string ciphertextFile, string p /// The plaintext. public static string Decrypt(string ciphertext, string password, KeySize keySize) { - return Decrypt(Convert.FromBase64String(ciphertext), password, keySize); + using (var ms = new MemoryStream(Convert.FromBase64String(ciphertext))) + { + // Extract the IV from the ciphertext + var iv = new byte[InitializationVectorSize]; + ms.Read(iv, 0, iv.Length); + + // Create a CryptoStream to decrypt the ciphertext + using (var cs = new CryptoStream(ms, CreateDecryptor(password, iv, keySize), CryptoStreamMode.Read)) + { + // Decrypt the ciphertext + using (var sr = new StreamReader(cs, Encoding.UTF8)) return sr.ReadToEnd(); + } + } } /// @@ -149,7 +163,7 @@ public static string Decrypt(string ciphertext, string password, KeySize keySize /// The password to decrypt the ciphertext with. /// The size of the cipher key used to create the ciphertext. /// The plaintext. - public static string Decrypt(byte[] ciphertext, string password, KeySize keySize) + public static byte[] Decrypt(byte[] ciphertext, string password, KeySize keySize) { using (var ms = new MemoryStream(ciphertext)) { @@ -161,7 +175,12 @@ public static string Decrypt(byte[] ciphertext, string password, KeySize keySize using (var cs = new CryptoStream(ms, CreateDecryptor(password, iv, keySize), CryptoStreamMode.Read)) { // Decrypt the ciphertext - using (var sr = new StreamReader(cs, Encoding.UTF8)) return sr.ReadToEnd(); + using (var buffer = new MemoryStream()) + { + cs.CopyTo(buffer); + + return buffer.ToArray(); + } } } } diff --git a/Rijndael256/RijndaelEtM.cs b/Rijndael256/RijndaelEtM.cs index d41345b..4ec47bb 100644 --- a/Rijndael256/RijndaelEtM.cs +++ b/Rijndael256/RijndaelEtM.cs @@ -30,7 +30,7 @@ public class RijndaelEtM : Rijndael /// The Base64 encoded EtM ciphertext. public static new string Encrypt(string plaintext, string password, KeySize keySize) { - return Encrypt(Encoding.UTF8.GetBytes(plaintext), password, keySize); + return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(plaintext), password, keySize)); } /// @@ -41,8 +41,8 @@ public class RijndaelEtM : Rijndael /// The plaintext to encrypt. /// The password to encrypt the plaintext with. /// The cipher key size. 256-bit is stronger, but slower. - /// The Base64 encoded EtM ciphertext. - public static new string Encrypt(byte[] plaintext, string password, KeySize keySize) + /// The encoded EtM ciphertext. + public static new byte[] Encrypt(byte[] plaintext, string password, KeySize keySize) { // Generate a random IV var iv = Rng.GenerateRandomBytes(InitializationVectorSize); @@ -51,7 +51,7 @@ public class RijndaelEtM : Rijndael var etmCiphertext = Encrypt(plaintext, password, iv, keySize); // Encode the EtM ciphertext - return Convert.ToBase64String(etmCiphertext); + return etmCiphertext; } /// @@ -93,7 +93,8 @@ public class RijndaelEtM : Rijndael /// The plaintext. public static new string Decrypt(string etmCiphertext, string password, KeySize keySize) { - return Decrypt(Convert.FromBase64String(etmCiphertext), password, keySize); + return Encoding.UTF8.GetString( + Decrypt(Convert.FromBase64String(etmCiphertext), password, keySize)); } /// @@ -104,7 +105,7 @@ public class RijndaelEtM : Rijndael /// The password to decrypt the EtM ciphertext with. /// The size of the cipher key used to create the EtM ciphertext. /// The plaintext. - public static new string Decrypt(byte[] etmCiphertext, string password, KeySize keySize) + public static new byte[] Decrypt(byte[] etmCiphertext, string password, KeySize keySize) { // Generate AE keys var keyRing = AeKeyRing.Generate(password); From 3d6481d3b0e508d5e17874844e1e68d24606646c Mon Sep 17 00:00:00 2001 From: SeaEagle1 Date: Sun, 26 Apr 2020 23:44:11 +0200 Subject: [PATCH 2/4] Allow binary en- and decryption (mergeable with upstream) --- Rijndael256/Rijndael.cs | 48 ++++++++++++++++++++++++++++++++ Rijndael256/RijndaelEtM.cs | 56 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/Rijndael256/Rijndael.cs b/Rijndael256/Rijndael.cs index 3f11a8d..9769425 100644 --- a/Rijndael256/Rijndael.cs +++ b/Rijndael256/Rijndael.cs @@ -54,6 +54,26 @@ public static string Encrypt(byte[] plaintext, string password, KeySize keySize) return Convert.ToBase64String(ciphertext); } + /// + /// Encrypts plaintext using the Rijndael cipher in CBC mode with a password derived HMAC SHA-512 salt. + /// A random 128-bit Initialization Vector is generated for the cipher. + /// + /// The plaintext to encrypt. + /// The password to encrypt the plaintext with. + /// The cipher key size. 256-bit is stronger, but slower. + /// The encoded ciphertext. + public static byte[] EncryptBinary(byte[] plaintext, string password, KeySize keySize) + { + // Generate a random IV + var iv = Rng.GenerateRandomBytes(InitializationVectorSize); + + // Encrypt the plaintext + var ciphertext = Encrypt(plaintext, password, iv, keySize); + + // Encode the ciphertext + return ciphertext; + } + /// /// Encrypts plaintext using the Rijndael cipher in CBC mode with a password derived HMAC SHA-512 salt. /// @@ -142,6 +162,34 @@ public static string Decrypt(string ciphertext, string password, KeySize keySize return Decrypt(Convert.FromBase64String(ciphertext), password, keySize); } + /// + /// Decrypts ciphertext using the Rijndael cipher in CBC mode with a password derived HMAC SHA-512 salt. + /// + /// The ciphertext to decrypt. + /// The password to decrypt the ciphertext with. + /// The size of the cipher key used to create the ciphertext. + /// The plaintext. + public static byte[] DecryptBinary(byte[] ciphertext, string password, KeySize keySize) + { + using (var ms = new MemoryStream(ciphertext)) + { + // Extract the IV from the ciphertext + var iv = new byte[InitializationVectorSize]; + ms.Read(iv, 0, iv.Length); + + // Create a CryptoStream to decrypt the ciphertext + using (var cs = new CryptoStream(ms, CreateDecryptor(password, iv, keySize), CryptoStreamMode.Read)) + { + // Decrypt the ciphertext + using (var buffer = new MemoryStream()) + { + cs.CopyTo(buffer); + return buffer.ToArray(); + } + } + } + } + /// /// Decrypts ciphertext using the Rijndael cipher in CBC mode with a password derived HMAC SHA-512 salt. /// diff --git a/Rijndael256/RijndaelEtM.cs b/Rijndael256/RijndaelEtM.cs index d41345b..4468b83 100644 --- a/Rijndael256/RijndaelEtM.cs +++ b/Rijndael256/RijndaelEtM.cs @@ -54,6 +54,27 @@ public class RijndaelEtM : Rijndael return Convert.ToBase64String(etmCiphertext); } + /// + /// Encrypts plaintext using the Encrypt-then-MAC (EtM) mode via the Rijndael cipher in + /// CBC mode with a password derived HMAC SHA-512 salt. A random 128-bit Initialization + /// Vector is generated for the cipher. + /// + /// The plaintext to encrypt. + /// The password to encrypt the plaintext with. + /// The cipher key size. 256-bit is stronger, but slower. + /// The encoded EtM ciphertext. + public static new byte[] EncryptBinary(byte[] plaintext, string password, KeySize keySize) + { + // Generate a random IV + var iv = Rng.GenerateRandomBytes(InitializationVectorSize); + + // Encrypt the plaintext + var etmCiphertext = Encrypt(plaintext, password, iv, keySize); + + // Encode the EtM ciphertext + return etmCiphertext; + } + /// /// Encrypts plaintext using the Encrypt-then-MAC (EtM) mode via the Rijndael cipher in /// CBC mode with a password derived HMAC SHA-512 salt. @@ -96,6 +117,41 @@ public class RijndaelEtM : Rijndael return Decrypt(Convert.FromBase64String(etmCiphertext), password, keySize); } + /// + /// Decrypts authenticated ciphertext using the Rijndael cipher in CBC mode with a password derived + /// HMAC SHA-512 salt. + /// + /// The EtM ciphertext to decrypt. + /// The password to decrypt the EtM ciphertext with. + /// The size of the cipher key used to create the EtM ciphertext. + /// The plaintext. + public static new byte[] DecryptBinary(byte[] etmCiphertext, string password, KeySize keySize) + { + // Generate AE keys + var keyRing = AeKeyRing.Generate(password); + + // Extract the ciphertext and MAC from the EtM ciphertext + var mac = new byte[keyRing.MacKey.Length]; + var ciphertext = new byte[etmCiphertext.Length - mac.Length]; + using (var ms = new MemoryStream(etmCiphertext)) + { + // Extract the ciphertext + ms.Read(ciphertext, 0, ciphertext.Length); + + // Extract the MAC + ms.Read(mac, 0, mac.Length); + } + + // Calculate the MAC from the ciphertext + var newMac = CalculateMac(ciphertext, keyRing.MacKey); + + // Authenticate ciphertext + if (!mac.SequenceEqual(newMac)) throw new Exception("Authentication failed!"); + + // Decrypt the ciphertext + return Rijndael.DecryptBinary(ciphertext, keyRing.CipherKey, keySize); + } + /// /// Decrypts authenticated ciphertext using the Rijndael cipher in CBC mode with a password derived /// HMAC SHA-512 salt. From a4ddfa0001e674fa525073698475336675a02835 Mon Sep 17 00:00:00 2001 From: SeaEagle1 Date: Sun, 26 Apr 2020 23:47:09 +0200 Subject: [PATCH 3/4] Revert "Allow en- & decryption of binary blobs" This reverts commit f00a0d9ed741b7541f29f059ae429e720802cc45. --- Rijndael256/Rijndael.cs | 33 +++++++-------------------------- Rijndael256/RijndaelEtM.cs | 13 ++++++------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/Rijndael256/Rijndael.cs b/Rijndael256/Rijndael.cs index 9e76112..3f11a8d 100644 --- a/Rijndael256/Rijndael.cs +++ b/Rijndael256/Rijndael.cs @@ -31,9 +31,7 @@ public class Rijndael /// The Base64 encoded ciphertext. public static string Encrypt(string plaintext, string password, KeySize keySize) { - return Convert.ToBase64String( - Encrypt(Encoding.UTF8.GetBytes(plaintext), password, keySize) - ); + return Encrypt(Encoding.UTF8.GetBytes(plaintext), password, keySize); } /// @@ -43,8 +41,8 @@ public static string Encrypt(string plaintext, string password, KeySize keySize) /// The plaintext to encrypt. /// The password to encrypt the plaintext with. /// The cipher key size. 256-bit is stronger, but slower. - /// The encoded ciphertext. - public static byte[] Encrypt(byte[] plaintext, string password, KeySize keySize) + /// The Base64 encoded ciphertext. + public static string Encrypt(byte[] plaintext, string password, KeySize keySize) { // Generate a random IV var iv = Rng.GenerateRandomBytes(InitializationVectorSize); @@ -53,7 +51,7 @@ public static byte[] Encrypt(byte[] plaintext, string password, KeySize keySize) var ciphertext = Encrypt(plaintext, password, iv, keySize); // Encode the ciphertext - return ciphertext; + return Convert.ToBase64String(ciphertext); } /// @@ -141,19 +139,7 @@ public static void Encrypt(string plaintextFile, string ciphertextFile, string p /// The plaintext. public static string Decrypt(string ciphertext, string password, KeySize keySize) { - using (var ms = new MemoryStream(Convert.FromBase64String(ciphertext))) - { - // Extract the IV from the ciphertext - var iv = new byte[InitializationVectorSize]; - ms.Read(iv, 0, iv.Length); - - // Create a CryptoStream to decrypt the ciphertext - using (var cs = new CryptoStream(ms, CreateDecryptor(password, iv, keySize), CryptoStreamMode.Read)) - { - // Decrypt the ciphertext - using (var sr = new StreamReader(cs, Encoding.UTF8)) return sr.ReadToEnd(); - } - } + return Decrypt(Convert.FromBase64String(ciphertext), password, keySize); } /// @@ -163,7 +149,7 @@ public static string Decrypt(string ciphertext, string password, KeySize keySize /// The password to decrypt the ciphertext with. /// The size of the cipher key used to create the ciphertext. /// The plaintext. - public static byte[] Decrypt(byte[] ciphertext, string password, KeySize keySize) + public static string Decrypt(byte[] ciphertext, string password, KeySize keySize) { using (var ms = new MemoryStream(ciphertext)) { @@ -175,12 +161,7 @@ public static byte[] Decrypt(byte[] ciphertext, string password, KeySize keySize using (var cs = new CryptoStream(ms, CreateDecryptor(password, iv, keySize), CryptoStreamMode.Read)) { // Decrypt the ciphertext - using (var buffer = new MemoryStream()) - { - cs.CopyTo(buffer); - - return buffer.ToArray(); - } + using (var sr = new StreamReader(cs, Encoding.UTF8)) return sr.ReadToEnd(); } } } diff --git a/Rijndael256/RijndaelEtM.cs b/Rijndael256/RijndaelEtM.cs index 4ec47bb..d41345b 100644 --- a/Rijndael256/RijndaelEtM.cs +++ b/Rijndael256/RijndaelEtM.cs @@ -30,7 +30,7 @@ public class RijndaelEtM : Rijndael /// The Base64 encoded EtM ciphertext. public static new string Encrypt(string plaintext, string password, KeySize keySize) { - return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(plaintext), password, keySize)); + return Encrypt(Encoding.UTF8.GetBytes(plaintext), password, keySize); } /// @@ -41,8 +41,8 @@ public class RijndaelEtM : Rijndael /// The plaintext to encrypt. /// The password to encrypt the plaintext with. /// The cipher key size. 256-bit is stronger, but slower. - /// The encoded EtM ciphertext. - public static new byte[] Encrypt(byte[] plaintext, string password, KeySize keySize) + /// The Base64 encoded EtM ciphertext. + public static new string Encrypt(byte[] plaintext, string password, KeySize keySize) { // Generate a random IV var iv = Rng.GenerateRandomBytes(InitializationVectorSize); @@ -51,7 +51,7 @@ public class RijndaelEtM : Rijndael var etmCiphertext = Encrypt(plaintext, password, iv, keySize); // Encode the EtM ciphertext - return etmCiphertext; + return Convert.ToBase64String(etmCiphertext); } /// @@ -93,8 +93,7 @@ public class RijndaelEtM : Rijndael /// The plaintext. public static new string Decrypt(string etmCiphertext, string password, KeySize keySize) { - return Encoding.UTF8.GetString( - Decrypt(Convert.FromBase64String(etmCiphertext), password, keySize)); + return Decrypt(Convert.FromBase64String(etmCiphertext), password, keySize); } /// @@ -105,7 +104,7 @@ public class RijndaelEtM : Rijndael /// The password to decrypt the EtM ciphertext with. /// The size of the cipher key used to create the EtM ciphertext. /// The plaintext. - public static new byte[] Decrypt(byte[] etmCiphertext, string password, KeySize keySize) + public static new string Decrypt(byte[] etmCiphertext, string password, KeySize keySize) { // Generate AE keys var keyRing = AeKeyRing.Generate(password); From 04c572692555d79fe537cf2a2039948c8e23a266 Mon Sep 17 00:00:00 2001 From: SeaEagle1 Date: Mon, 27 Apr 2020 00:32:51 +0200 Subject: [PATCH 4/4] Binary encryption unittest --- Rijndael256.Tests/RijndaelEtMTests.cs | 11 +++++++++++ Rijndael256.Tests/RijndaelTests.cs | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/Rijndael256.Tests/RijndaelEtMTests.cs b/Rijndael256.Tests/RijndaelEtMTests.cs index f30ec1d..dbf1fbc 100644 --- a/Rijndael256.Tests/RijndaelEtMTests.cs +++ b/Rijndael256.Tests/RijndaelEtMTests.cs @@ -142,5 +142,16 @@ public void GenerateAeKeyRing() Assert.Equal(keyRing.CipherKey, AeKeyRingProof.CipherKey); Assert.True(keyRing.MacKey.SequenceEqual(AeKeyRingProof.MacKey)); } + + [Fact] + public void BinaryBlob() + { + var plainblob = UTF8Encoding.UTF8.GetBytes(Plaintext); + + var cipherblob = RijndaelEtM.EncryptBinary(plainblob, Password, KeySize.Aes128); + var plainresult = RijndaelEtM.DecryptBinary(cipherblob, Password, KeySize.Aes128); + + Assert.Equal(plainblob, plainresult); + } } } diff --git a/Rijndael256.Tests/RijndaelTests.cs b/Rijndael256.Tests/RijndaelTests.cs index c2e4b3e..45bc7f5 100644 --- a/Rijndael256.Tests/RijndaelTests.cs +++ b/Rijndael256.Tests/RijndaelTests.cs @@ -98,5 +98,16 @@ public void RandomIv256() Assert.Equal(plaintext, Plaintext); Assert.NotEqual(ciphertext1, ciphertext2); } + + [Fact] + public void BinaryBlob() + { + var plainblob = UTF8Encoding.UTF8.GetBytes(Plaintext); + + var cipherblob = Rijndael.EncryptBinary(plainblob, Password, KeySize.Aes128); + var plainresult = Rijndael.DecryptBinary(cipherblob, Password, KeySize.Aes128); + + Assert.Equal(plainblob, plainresult); + } } }