From cfa226fa6a126c2a920e437f8b216383462527dc Mon Sep 17 00:00:00 2001 From: Uncled1023 Date: Wed, 12 Apr 2017 00:10:39 -0700 Subject: [PATCH] Finalized new AES cipher wrapper and removed old instance. --- ServerMaint/Program.cs | 2 +- .../Paste/Controllers/PasteController.cs | 1 + Teknik/Areas/Paste/PasteHelper.cs | 1 + .../Upload/Controllers/UploadController.cs | 2 +- Teknik/Areas/Upload/Uploader.cs | 3 +- Utilities/Utilities/Crypto.cs | 156 ------------------ Utilities/Utilities/Cryptography/AES.cs | 129 +++++++++++++++ .../Utilities/Cryptography/AesCounterMode.cs | 24 ++- Utilities/Utilities/ResponseHelper.cs | 77 --------- Utilities/Utilities/StreamHelper.cs | 31 +++- Utilities/Utilities/Utilities.csproj | 1 + 11 files changed, 176 insertions(+), 251 deletions(-) create mode 100644 Utilities/Utilities/Cryptography/AES.cs diff --git a/ServerMaint/Program.cs b/ServerMaint/Program.cs index ca4cdbd..9eb6349 100644 --- a/ServerMaint/Program.cs +++ b/ServerMaint/Program.cs @@ -168,7 +168,7 @@ namespace ServerMaint byte[] keyBytes = Encoding.UTF8.GetBytes(upload.Key); byte[] ivBytes = Encoding.UTF8.GetBytes(upload.IV); FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); - AESCryptoStream aesStream = new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding"); + AESCryptoStream aesStream = new AESCryptoStream(fs, false, keyBytes, ivBytes); // We have the data, let's scan it ClamScanResult scanResult = clam.SendAndScanFile(aesStream); diff --git a/Teknik/Areas/Paste/Controllers/PasteController.cs b/Teknik/Areas/Paste/Controllers/PasteController.cs index 157b53e..3173a69 100644 --- a/Teknik/Areas/Paste/Controllers/PasteController.cs +++ b/Teknik/Areas/Paste/Controllers/PasteController.cs @@ -15,6 +15,7 @@ using Teknik.Filters; using Teknik.Utilities; using Teknik.Models; using Teknik.Attributes; +using Teknik.Utilities.Cryptography; namespace Teknik.Areas.Paste.Controllers { diff --git a/Teknik/Areas/Paste/PasteHelper.cs b/Teknik/Areas/Paste/PasteHelper.cs index d03a843..c251e1e 100644 --- a/Teknik/Areas/Paste/PasteHelper.cs +++ b/Teknik/Areas/Paste/PasteHelper.cs @@ -6,6 +6,7 @@ using System.Web; using Teknik.Configuration; using Teknik.Utilities; using Teknik.Models; +using Teknik.Utilities.Cryptography; namespace Teknik.Areas.Paste { diff --git a/Teknik/Areas/Upload/Controllers/UploadController.cs b/Teknik/Areas/Upload/Controllers/UploadController.cs index 741a3da..d4a832c 100644 --- a/Teknik/Areas/Upload/Controllers/UploadController.cs +++ b/Teknik/Areas/Upload/Controllers/UploadController.cs @@ -294,7 +294,7 @@ namespace Teknik.Areas.Upload.Controllers return new FileGenerateResult(url, contentType, - (response) => ResponseHelper.StreamToOutput(response, true, new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding"), (int)length, Config.UploadConfig.ChunkSize), + (response) => ResponseHelper.StreamToOutput(response, true, new AESCryptoStream(fs, false, keyBytes, ivBytes), (int)length, Config.UploadConfig.ChunkSize), false); } else // Otherwise just send it diff --git a/Teknik/Areas/Upload/Uploader.cs b/Teknik/Areas/Upload/Uploader.cs index 2cc39ab..a92a5e2 100644 --- a/Teknik/Areas/Upload/Uploader.cs +++ b/Teknik/Areas/Upload/Uploader.cs @@ -8,6 +8,7 @@ using Teknik.Models; using Teknik.Utilities; using System.Text; using Org.BouncyCastle.Utilities.Encoders; +using Teknik.Utilities.Cryptography; namespace Teknik.Areas.Upload { @@ -54,7 +55,7 @@ namespace Teknik.Areas.Upload byte[] ivBytes = Encoding.UTF8.GetBytes(iv); // Encrypt the file to disk - AES.EncryptToFile(filePath, file, config.UploadConfig.ChunkSize, keyBytes, ivBytes, "CTR", "NoPadding"); + AES.EncryptToFile(filePath, file, config.UploadConfig.ChunkSize, keyBytes, ivBytes); } else { diff --git a/Utilities/Utilities/Crypto.cs b/Utilities/Utilities/Crypto.cs index 8820c95..3daf1d9 100644 --- a/Utilities/Utilities/Crypto.cs +++ b/Utilities/Utilities/Crypto.cs @@ -3,15 +3,8 @@ using SecurityDriven.Inferno.Hash; using SecurityDriven.Inferno.Mac; using System.IO; using System.Security.Cryptography; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Modes; -using Org.BouncyCastle.Crypto.Paddings; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.Bcpg.OpenPgp; -using Org.BouncyCastle.Utilities.IO; using System; using System.Collections.Generic; using System.IO.MemoryMappedFiles; @@ -154,155 +147,6 @@ namespace Teknik.Utilities } } - public class AES - { - public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv) - { - return Decrypt(data, key, iv, "CTR", "NoPadding"); - } - public static byte[] Decrypt(byte[] data, string key, string iv) - { - byte[] keyBytes = Encoding.UTF8.GetBytes(key); - byte[] ivBytes = Encoding.UTF8.GetBytes(iv); - return Decrypt(data, keyBytes, ivBytes, "CTR", "NoPadding"); - } - public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv, string mode, string padding) - { - using (MemoryStream stream = new MemoryStream(data)) - { - return ProcessCipher(false, stream, 1024, key, iv, mode, padding); - } - } - - - public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv) - { - return Encrypt(data, key, iv, "CTR", "NoPadding"); - } - public static byte[] Encrypt(byte[] data, string key, string iv) - { - byte[] keyBytes = Encoding.UTF8.GetBytes(key); - byte[] ivBytes = Encoding.UTF8.GetBytes(iv); - return Encrypt(data, keyBytes, ivBytes, "CTR", "NoPadding"); - } - public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv, string mode, string padding) - { - using (MemoryStream stream = new MemoryStream(data)) - { - return ProcessCipher(true, stream, 1024, key, iv, mode, padding); - } - } - - public static byte[] ProcessCipher(bool encrypt, Stream input, int chunkSize, byte[] key, byte[] iv, string mode, string padding) - { - // Create the cipher we are going to use - IBufferedCipher cipher = CreateCipher(encrypt, key, iv, mode, padding); - - // Make sure the input stream is at the beginning - input.Seek(0, SeekOrigin.Begin); - - // Initialize variables - byte[] output = new byte[input.Length]; - int cipherOffset = 0; - int bytesRead = 0; - - // Process the stream and save the bytes to the output - do - { - int processedBytes = ProcessCipherBlock(cipher, input, 0, chunkSize, output, cipherOffset, out bytesRead); - cipherOffset += processedBytes; - } - while (bytesRead > 0); - - // Finalize processing of the cipher - FinalizeCipherBlock(cipher, output, cipherOffset); - - return output; - } - - public static void EncryptToFile(string filePath, Stream input, int chunkSize, byte[] key, byte[] iv, string mode, string padding) - { - IBufferedCipher cipher = CreateCipher(true, key, iv, mode, padding); - - // Make sure the input stream is at the beginning - input.Seek(0, SeekOrigin.Begin); - - using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - { - int processedBytes = 0; - byte[] buffer = new byte[chunkSize]; - int bytesRead = 0; - do - { - processedBytes = ProcessCipherBlock(cipher, input, 0, chunkSize, buffer, 0, out bytesRead); - if (processedBytes > 0) - { - // We have bytes, lets write them to the file - fileStream.Write(buffer, 0, processedBytes); - - // Clear the buffer - Array.Clear(buffer, 0, chunkSize); - } - } - while (processedBytes > 0); - - // Clear the buffer - Array.Clear(buffer, 0, chunkSize); - - // Finalize processing of the cipher - processedBytes = FinalizeCipherBlock(cipher, buffer, 0); - if (processedBytes > 0) - { - // We have bytes, lets write them to the file - fileStream.Write(buffer, 0, processedBytes); - } - } - } - - public static IBufferedCipher CreateCipher(bool encrypt, byte[] key, byte[] iv, string mode, string padding) - { - IBufferedCipher cipher = CipherUtilities.GetCipher("AES/" + mode + "/" + padding); - - cipher.Init(encrypt, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", key), iv)); - - return cipher; - } - - public static int ProcessCipherBlock(IBufferedCipher cipher, Stream input, int inputOffset, int chunkSize, byte[] output, int outputOffset, out int bytesRead) - { - // Initialize buffer - byte[] buffer = new byte[chunkSize + inputOffset]; - - // Read the next block of data - bytesRead = input.Read(buffer, 0, chunkSize + inputOffset); - if (bytesRead > 0) - { - // process the cipher for the read block and add it to the output - return cipher.ProcessBytes(buffer, inputOffset, bytesRead - inputOffset, output, outputOffset); - } - - return 0; - } - - public static int FinalizeCipherBlock(IBufferedCipher cipher, byte[] output, int outputOffset) - { - // perform final action on cipher - return cipher.DoFinal(output, outputOffset); - } - - public static byte[] CreateKey(string password, string iv, int keySize = 256) - { - byte[] ivBytes = Encoding.UTF8.GetBytes(iv); - return CreateKey(password, ivBytes, keySize); - } - public static byte[] CreateKey(string password, byte[] iv, int keySize = 256) - { - const int Iterations = 300; - var keyGenerator = new Rfc2898DeriveBytes(password, iv, Iterations); - return keyGenerator.GetBytes(keySize / 8); - } - } - public static class PGP { public static bool IsPublicKey(string key) diff --git a/Utilities/Utilities/Cryptography/AES.cs b/Utilities/Utilities/Cryptography/AES.cs new file mode 100644 index 0000000..e7016dd --- /dev/null +++ b/Utilities/Utilities/Cryptography/AES.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Teknik.Utilities.Cryptography +{ + + public class AES + { + public static byte[] Decrypt(byte[] data, string key, string iv) + { + byte[] keyBytes = Encoding.UTF8.GetBytes(key); + byte[] ivBytes = Encoding.UTF8.GetBytes(iv); + return Decrypt(data, keyBytes, ivBytes); + } + public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv) + { + using (MemoryStream stream = new MemoryStream(data)) + { + return ProcessCipher(false, stream, 1024, key, iv); + } + } + + public static byte[] Encrypt(byte[] data, string key, string iv) + { + byte[] keyBytes = Encoding.UTF8.GetBytes(key); + byte[] ivBytes = Encoding.UTF8.GetBytes(iv); + return Encrypt(data, keyBytes, ivBytes); + } + public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv) + { + using (MemoryStream stream = new MemoryStream(data)) + { + return ProcessCipher(true, stream, 1024, key, iv); + } + } + + public static byte[] ProcessCipher(bool encrypt, Stream input, int chunkSize, byte[] key, byte[] iv) + { + // Make sure the input stream is at the beginning + input.Seek(0, SeekOrigin.Begin); + + AESCryptoStream cryptoStream = new AESCryptoStream(input, encrypt, key, iv); + + // Initialize variables + byte[] output = new byte[input.Length]; + + // Process the stream and save the bytes to the output + int curByte = 0; + int processedBytes = 0; + byte[] buffer = new byte[chunkSize]; + int bytesRemaining = (int)input.Length; + int bytesToRead = chunkSize; + do + { + if (chunkSize > bytesRemaining) + { + bytesToRead = bytesRemaining; + } + + processedBytes = cryptoStream.Read(buffer, 0, bytesToRead); + if (processedBytes > 0) + { + buffer.Take(processedBytes).ToArray().CopyTo(output, curByte); + + // Clear the buffer + Array.Clear(buffer, 0, chunkSize); + } + curByte += processedBytes; + bytesRemaining -= processedBytes; + } + while (processedBytes > 0 && bytesRemaining > 0); + + return output; + } + + public static void EncryptToFile(string filePath, Stream input, int chunkSize, byte[] key, byte[] iv) + { + // Make sure the input stream is at the beginning + input.Seek(0, SeekOrigin.Begin); + + AESCryptoStream cryptoStream = new AESCryptoStream(input, true, key, iv); + + using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + int curByte = 0; + int processedBytes = 0; + byte[] buffer = new byte[chunkSize]; + int bytesRemaining = (int)input.Length; + int bytesToRead = chunkSize; + do + { + if (chunkSize > bytesRemaining) + { + bytesToRead = bytesRemaining; + } + + processedBytes = cryptoStream.Read(buffer, 0, bytesToRead); + if (processedBytes > 0) + { + fileStream.Write(buffer, 0, processedBytes); + + // Clear the buffer + Array.Clear(buffer, 0, chunkSize); + } + curByte += processedBytes; + bytesRemaining -= processedBytes; + } + while (processedBytes > 0 && bytesRemaining > 0); + } + } + + public static byte[] CreateKey(string password, string iv, int keySize = 256) + { + byte[] ivBytes = Encoding.UTF8.GetBytes(iv); + return CreateKey(password, ivBytes, keySize); + } + public static byte[] CreateKey(string password, byte[] iv, int keySize = 256) + { + const int Iterations = 300; + var keyGenerator = new Rfc2898DeriveBytes(password, iv, Iterations); + return keyGenerator.GetBytes(keySize / 8); + } + } +} diff --git a/Utilities/Utilities/Cryptography/AesCounterMode.cs b/Utilities/Utilities/Cryptography/AesCounterMode.cs index 9533913..42a034a 100644 --- a/Utilities/Utilities/Cryptography/AesCounterMode.cs +++ b/Utilities/Utilities/Cryptography/AesCounterMode.cs @@ -31,7 +31,8 @@ namespace Teknik.Utilities.Cryptography }; // Set the internal variables - _Counter = counter; + _Counter = new byte[counter.Length]; + counter.CopyTo(_Counter, 0); } public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv) @@ -103,8 +104,12 @@ namespace Teknik.Utilities.Cryptography counter.Length, symmetricAlgorithm.BlockSize / 8)); _SymmetricAlgorithm = symmetricAlgorithm; - _IV = iv; - _Counter = counter; + + _IV = new byte[iv.Length]; + iv.CopyTo(_IV, 0); + + _Counter = new byte[counter.Length]; + counter.CopyTo(_Counter, 0); _CounterEncryptor = symmetricAlgorithm.CreateEncryptor(key, iv); @@ -114,6 +119,9 @@ namespace Teknik.Utilities.Cryptography // Encrypt the counter EncryptCounter(); + + // Initial Increment + IncrementCounter(); } public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) @@ -130,13 +138,13 @@ namespace Teknik.Utilities.Cryptography // Encrypt the counter if we have reached the end, or if (_CounterPosition >= _EncryptedCounter.Length) { - // Encrypt the counter - EncryptCounter(); - //Reset current counter position _CounterPosition = 0; - // Increment the counter for the next run + // Encrypt the counter + EncryptCounter(); + + // Increment the counter for the next batch IncrementCounter(); } @@ -162,7 +170,7 @@ namespace Teknik.Utilities.Cryptography public void ResetCounter() { Array.Clear(_Counter, 0, _Counter.Length); - Array.Copy(_IV, 0, _Counter, 0, _IV.Length); + _IV.CopyTo(_Counter, 0); _Iterations = 0; } diff --git a/Utilities/Utilities/ResponseHelper.cs b/Utilities/Utilities/ResponseHelper.cs index dcf2ec8..35d74be 100644 --- a/Utilities/Utilities/ResponseHelper.cs +++ b/Utilities/Utilities/ResponseHelper.cs @@ -77,82 +77,5 @@ namespace Teknik.Utilities } } } - - public static void DecryptStreamToOutput(HttpResponseBase response, bool flush, Stream stream, int length, byte[] key, byte[] iv, string mode, string padding, int chunkSize) - { - try - { - if (flush) - { - response.Flush(); - } - IBufferedCipher cipher = AES.CreateCipher(false, key, iv, mode, padding); - - int curByte = 0; - int processedBytes = 0; - byte[] buffer = new byte[chunkSize]; - int bytesRemaining = length; - int bytesToRead = chunkSize; - int bytesRead = 0; - do - { - if (chunkSize > bytesRemaining) - { - bytesToRead = bytesRemaining; - } - processedBytes = AES.ProcessCipherBlock(cipher, stream, 0, bytesToRead, buffer, 0, out bytesRead); - if (processedBytes > 0) - { - response.OutputStream.Write(buffer, 0, processedBytes); - if (flush) - { - response.Flush(); - } - - // Clear the buffer - Array.Clear(buffer, 0, chunkSize); - } - curByte += bytesRead; - bytesRemaining -= bytesRead; - } - while (bytesRemaining > 0); - - // Clear the buffer - Array.Clear(buffer, 0, chunkSize); - - // Finalize processing of the cipher - processedBytes = AES.FinalizeCipherBlock(cipher, buffer, 0); - if (processedBytes > 0) - { - // We have bytes, lets write them to the output - response.OutputStream.Write(buffer, 0, processedBytes); - if (flush) - { - response.Flush(); - } - } - } - catch (HttpException httpEx) - { - // If we lost connection, that's fine - if (httpEx.ErrorCode == -2147023667) - { - // do nothing - } - else - { - //throw httpEx; - } - } - catch (Exception ex) - { - throw ex; - } - finally - { - // dispose of file stream - stream.Dispose(); - } - } } } diff --git a/Utilities/Utilities/StreamHelper.cs b/Utilities/Utilities/StreamHelper.cs index b02928f..36e6c0d 100644 --- a/Utilities/Utilities/StreamHelper.cs +++ b/Utilities/Utilities/StreamHelper.cs @@ -14,7 +14,16 @@ namespace Teknik.Utilities private Stream _Inner; private CounterModeCryptoTransform _Cipher; - public AESCryptoStream(Stream stream, bool encrypt, byte[] key, byte[] iv, string mode, string padding) + /// + /// Performs Encryption or Decryption on a stream with the given Key and IV + /// + /// Cipher is AES-256 in CTR mode with no padding + /// + /// + /// + /// + /// + public AESCryptoStream(Stream stream, bool encrypt, byte[] key, byte[] iv) { _Inner = stream; @@ -44,7 +53,7 @@ namespace Teknik.Utilities int bytesRead = _Inner.Read(readBuf, 0, count); if (bytesRead > 0) { - // Process the + // Process the read buffer processed = _Cipher.TransformBlock(readBuf, 0, bytesRead, buffer, 0); } @@ -68,11 +77,19 @@ namespace Teknik.Utilities { // Process the cipher byte[] output = new byte[count]; - //int processed = _Cipher.ProcessBytes(buffer, offset, count, output, 0); - // Finalize the cipher - //AES.FinalizeCipherBlock(_Cipher, output, processed); - + // Process the buffer + int processed = _Cipher.TransformBlock(buffer, 0, count, output, 0); + + // Do we have more? + if (processed < count) + { + // Finalize the cipher + byte[] finalBuf = _Cipher.TransformFinalBlock(buffer, processed, count); + finalBuf.CopyTo(output, processed); + processed += finalBuf.Length; + } + _Inner.Write(output, 0, count); } } @@ -187,7 +204,7 @@ namespace Teknik.Utilities int counterPos = (int)(_Inner.Position % _Cipher.InputBlockSize); // Are we out of sync with the cipher? - if (_Cipher.Iterations != iterations || _Cipher.CounterPosition != counterPos) + if (_Cipher.Iterations != iterations + 1 || _Cipher.CounterPosition != counterPos) { // Reset the current counter _Cipher.ResetCounter(); diff --git a/Utilities/Utilities/Utilities.csproj b/Utilities/Utilities/Utilities.csproj index 321b658..b102bbb 100644 --- a/Utilities/Utilities/Utilities.csproj +++ b/Utilities/Utilities/Utilities.csproj @@ -106,6 +106,7 @@ +