From 72e5f4b62e28c8521f00ed05e4ba93590cfd5ced Mon Sep 17 00:00:00 2001 From: Uncled1023 Date: Tue, 11 Apr 2017 22:01:42 -0700 Subject: [PATCH] Made AesStream handle syncing with the counter of the cipher. --- ServerMaint/Program.cs | 2 +- Teknik.sln | 10 - .../Upload/Controllers/UploadController.cs | 5 +- Teknik/Teknik.csproj | 4 - .../Security/Cryptography/AesCounterMode.cs | 137 ------------- Utilities/Security/Properties/AssemblyInfo.cs | 36 ---- Utilities/Security/Security.csproj | 55 ------ .../Utilities/Cryptography/AesCounterMode.cs | 187 ++++++++++++++++++ Utilities/Utilities/StreamHelper.cs | 132 ++++++------- Utilities/Utilities/Utilities.csproj | 7 +- 10 files changed, 253 insertions(+), 322 deletions(-) delete mode 100644 Utilities/Security/Cryptography/AesCounterMode.cs delete mode 100644 Utilities/Security/Properties/AssemblyInfo.cs delete mode 100644 Utilities/Security/Security.csproj create mode 100644 Utilities/Utilities/Cryptography/AesCounterMode.cs diff --git a/ServerMaint/Program.cs b/ServerMaint/Program.cs index d43b232..ca4cdbd 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", 0); + AESCryptoStream aesStream = new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding"); // We have the data, let's scan it ClamScanResult scanResult = clam.SendAndScanFile(aesStream); diff --git a/Teknik.sln b/Teknik.sln index 3e2c91e..37e5d56 100644 --- a/Teknik.sln +++ b/Teknik.sln @@ -26,8 +26,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Configuration", "Utilities\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeknikStreaming", "TeknikStreaming\TeknikStreaming.csproj", "{7695CE9A-A0DB-4D73-BC9B-2206481F0254}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Security", "Utilities\Security\Security.csproj", "{2BB2F552-B80F-41F2-937D-5D73F15C6DC4}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -93,14 +91,6 @@ Global {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Release|Any CPU.Build.0 = Release|Any CPU {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Release|x64.ActiveCfg = Release|Any CPU {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Release|x64.Build.0 = Release|Any CPU - {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Debug|x64.ActiveCfg = Debug|Any CPU - {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Debug|x64.Build.0 = Debug|Any CPU - {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Release|Any CPU.Build.0 = Release|Any CPU - {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Release|x64.ActiveCfg = Release|Any CPU - {2BB2F552-B80F-41F2-937D-5D73F15C6DC4}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Teknik/Areas/Upload/Controllers/UploadController.cs b/Teknik/Areas/Upload/Controllers/UploadController.cs index 0982ae8..741a3da 100644 --- a/Teknik/Areas/Upload/Controllers/UploadController.cs +++ b/Teknik/Areas/Upload/Controllers/UploadController.cs @@ -292,12 +292,9 @@ namespace Teknik.Areas.Upload.Controllers byte[] keyBytes = Encoding.UTF8.GetBytes(key); byte[] ivBytes = Encoding.UTF8.GetBytes(iv); - // Calculate the block offset needed for the counter - int counterOffset = (int)Math.Floor(startByte / 16.0); - return new FileGenerateResult(url, contentType, - (response) => ResponseHelper.StreamToOutput(response, true, new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding", counterOffset), (int)length, Config.UploadConfig.ChunkSize), + (response) => ResponseHelper.StreamToOutput(response, true, new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding"), (int)length, Config.UploadConfig.ChunkSize), false); } else // Otherwise just send it diff --git a/Teknik/Teknik.csproj b/Teknik/Teknik.csproj index 232f7ac..522a97d 100644 --- a/Teknik/Teknik.csproj +++ b/Teknik/Teknik.csproj @@ -802,10 +802,6 @@ {c492c2c6-d45a-498b-84a2-6d4c8bf9de77} Piwik - - {2bb2f552-b80f-41f2-937d-5d73f15c6dc4} - Security - {f45de6fc-3754-4954-a20a-4277362cc6c1} Utilities diff --git a/Utilities/Security/Cryptography/AesCounterMode.cs b/Utilities/Security/Cryptography/AesCounterMode.cs deleted file mode 100644 index c9ffd51..0000000 --- a/Utilities/Security/Cryptography/AesCounterMode.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; - -namespace Teknik.Security.Cryptography -{ - public class AesCounterMode : SymmetricAlgorithm - { - private const int _blockSize = 16; - private readonly byte[] _counter; - private readonly AesManaged _aes; - - public AesCounterMode(byte[] counter) - { - if (counter == null) throw new ArgumentNullException("counter"); - if (counter.Length != _blockSize) - throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})", - counter.Length, _blockSize)); - - _aes = new AesManaged - { - Mode = CipherMode.ECB, - Padding = PaddingMode.None - }; - - _counter = counter; - } - - public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv) - { - return new CounterModeCryptoTransform(_aes, key, iv, _counter); - } - - public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv) - { - return new CounterModeCryptoTransform(_aes, key, iv, _counter); - } - - public override void GenerateKey() - { - _aes.GenerateKey(); - } - - public override void GenerateIV() - { - // IV not needed in Counter Mode - } - } - - public class CounterModeCryptoTransform : ICryptoTransform - { - private readonly byte[] _iv; - private readonly byte[] _counter; - private readonly ICryptoTransform _counterEncryptor; - private readonly Queue _xorMask = new Queue(); - private readonly SymmetricAlgorithm _symmetricAlgorithm; - - public CounterModeCryptoTransform(SymmetricAlgorithm symmetricAlgorithm, byte[] key, byte[] iv, byte[] counter) - { - if (symmetricAlgorithm == null) throw new ArgumentNullException("symmetricAlgorithm"); - if (key == null) throw new ArgumentNullException("key"); - if (iv == null) throw new ArgumentNullException("counter"); - if (iv.Length != symmetricAlgorithm.BlockSize / 8) - throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})", - iv.Length, symmetricAlgorithm.BlockSize / 8)); - - _symmetricAlgorithm = symmetricAlgorithm; - _counter = counter; - _iv = iv; - - _counterEncryptor = symmetricAlgorithm.CreateEncryptor(key, iv); - } - - public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) - { - var output = new byte[inputCount]; - TransformBlock(inputBuffer, inputOffset, inputCount, output, 0); - return output; - } - - public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - for (var i = 0; i < inputCount; i++) - { - if (NeedMoreXorMaskBytes()) EncryptCounterThenIncrement(); - - var mask = _xorMask.Dequeue(); - outputBuffer[outputOffset + i] = (byte)(mask ^ inputBuffer[inputOffset + i]); - } - - return inputCount; - } - - private bool NeedMoreXorMaskBytes() - { - return _xorMask.Count == 0; - } - - private void EncryptCounterThenIncrement() - { - var counterModeBlock = new byte[_symmetricAlgorithm.BlockSize / 8]; - - _counterEncryptor.TransformBlock(_counter, 0, _counter.Length, counterModeBlock, 0); - IncrementCounter(); - - foreach (var b in counterModeBlock) - { - _xorMask.Enqueue(b); - } - } - - private void IncrementCounter() - { - int j = _counter.Length; - while (--j >= 0 && ++_counter[j] == 0) - { - } - //for (var i = _counter.Length - 1; i >= 0; i--) - //{ - // if (++_counter[i] != 0) - // break; - //} - } - - public int InputBlockSize { get { return _symmetricAlgorithm.BlockSize / 8; } } - public int OutputBlockSize { get { return _symmetricAlgorithm.BlockSize / 8; } } - public bool CanTransformMultipleBlocks { get { return true; } } - public bool CanReuseTransform { get { return false; } } - - public void Dispose() - { - } - } -} diff --git a/Utilities/Security/Properties/AssemblyInfo.cs b/Utilities/Security/Properties/AssemblyInfo.cs deleted file mode 100644 index 8d9c36f..0000000 --- a/Utilities/Security/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Teknik Security Utilities")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Teknik")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("2bb2f552-b80f-41f2-937d-5d73f15c6dc4")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Utilities/Security/Security.csproj b/Utilities/Security/Security.csproj deleted file mode 100644 index 53c6c51..0000000 --- a/Utilities/Security/Security.csproj +++ /dev/null @@ -1,55 +0,0 @@ - - - - - Debug - AnyCPU - {2BB2F552-B80F-41F2-937D-5D73F15C6DC4} - Library - Properties - Teknik.Security - Teknik.Security - v4.6.2 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Utilities/Utilities/Cryptography/AesCounterMode.cs b/Utilities/Utilities/Cryptography/AesCounterMode.cs new file mode 100644 index 0000000..9533913 --- /dev/null +++ b/Utilities/Utilities/Cryptography/AesCounterMode.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Teknik.Utilities.Cryptography +{ + public class AesCounterMode : SymmetricAlgorithm + { + // Internal Variables + private const int _BlockSize = 16; + private readonly byte[] _Counter; + private readonly AesManaged _Algo; + + public AesCounterMode() : this(new byte[_BlockSize]) { } + + public AesCounterMode(byte[] counter) + { + if (counter == null) throw new ArgumentNullException("counter"); + if (counter.Length != _BlockSize) + throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})", + counter.Length, _BlockSize)); + + // Generate a new instance of the Aes Algorithm in ECB mode with no padding + _Algo = new AesManaged + { + Mode = CipherMode.ECB, + Padding = PaddingMode.None + }; + + // Set the internal variables + _Counter = counter; + } + + public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv) + { + return new CounterModeCryptoTransform(_Algo, key, iv, _Counter); + } + + public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv) + { + return new CounterModeCryptoTransform(_Algo, key, iv, _Counter); + } + + public override void GenerateKey() + { + _Algo.GenerateKey(); + } + + public override void GenerateIV() + { + _Algo.GenerateIV(); + } + } + + public class CounterModeCryptoTransform : ICryptoTransform + { + private readonly byte[] _IV; + private readonly byte[] _Counter; + private readonly ICryptoTransform _CounterEncryptor; + private readonly SymmetricAlgorithm _SymmetricAlgorithm; + + // Stateful Fields + private byte[] _EncryptedCounter; + + private int _Iterations; + public int Iterations + { + get + { + return _Iterations; + } + } + + private int _CounterPosition; + public int CounterPosition + { + get + { + return _CounterPosition; + } + set + { + if (value > 0 && value < _EncryptedCounter.Length) + { + _CounterPosition = value; + } + } + } + + public CounterModeCryptoTransform(SymmetricAlgorithm symmetricAlgorithm, byte[] key, byte[] iv, byte[] counter) + { + if (symmetricAlgorithm == null) throw new ArgumentNullException("symmetricAlgorithm"); + if (key == null) throw new ArgumentNullException("key"); + if (iv == null) throw new ArgumentNullException("iv"); + if (counter == null) throw new ArgumentNullException("counter"); + + // Check lengths + if (counter.Length != symmetricAlgorithm.BlockSize / 8) + throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})", + counter.Length, symmetricAlgorithm.BlockSize / 8)); + + _SymmetricAlgorithm = symmetricAlgorithm; + _IV = iv; + _Counter = counter; + + _CounterEncryptor = symmetricAlgorithm.CreateEncryptor(key, iv); + + // Initialize State + _CounterPosition = 0; + _Iterations = 0; + + // Encrypt the counter + EncryptCounter(); + } + + public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) + { + var output = new byte[inputCount]; + TransformBlock(inputBuffer, inputOffset, inputCount, output, 0); + return output; + } + + public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + for (var i = 0; i < inputCount; i++) + { + // 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 + IncrementCounter(); + } + + // XOR the encrypted counter with the input plain text + outputBuffer[outputOffset + i] = (byte)(_EncryptedCounter[_CounterPosition] ^ inputBuffer[inputOffset + i]); + + // Move the counter position + _CounterPosition++; + } + + return inputCount; + } + + public void EncryptCounter() + { + // Clear the encrypted counter + _EncryptedCounter = new byte[_SymmetricAlgorithm.BlockSize / 8]; + + // Encrypt the current counter to the encrypted counter + _CounterEncryptor.TransformBlock(_Counter, 0, _Counter.Length, _EncryptedCounter, 0); + } + + public void ResetCounter() + { + Array.Clear(_Counter, 0, _Counter.Length); + Array.Copy(_IV, 0, _Counter, 0, _IV.Length); + _Iterations = 0; + } + + public void IncrementCounter() + { + int j = _Counter.Length; + while (--j >= 0 && ++_Counter[j] == 0) + { + } + _Iterations++; + } + + public int InputBlockSize { get { return _SymmetricAlgorithm.BlockSize / 8; } } + public int OutputBlockSize { get { return _SymmetricAlgorithm.BlockSize / 8; } } + public bool CanTransformMultipleBlocks { get { return true; } } + public bool CanReuseTransform { get { return false; } } + + public void Dispose() + { + } + } +} diff --git a/Utilities/Utilities/StreamHelper.cs b/Utilities/Utilities/StreamHelper.cs index 7609a75..b02928f 100644 --- a/Utilities/Utilities/StreamHelper.cs +++ b/Utilities/Utilities/StreamHelper.cs @@ -5,105 +5,59 @@ using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; -using Teknik.Security.Cryptography; +using Teknik.Utilities.Cryptography; namespace Teknik.Utilities { public class AESCryptoStream : Stream { private Stream _Inner; - //private IBufferedCipher _Cipher; - private ICryptoTransform _Cipher; + private CounterModeCryptoTransform _Cipher; - public AESCryptoStream(Stream stream, bool encrypt, byte[] key, byte[] iv, string mode, string padding, int initCounter) + public AESCryptoStream(Stream stream, bool encrypt, byte[] key, byte[] iv, string mode, string padding) { _Inner = stream; - // Create initial counter value from IV - byte[] counter = new byte[iv.Length]; - iv.CopyTo(counter, 0); - - // Increment the counter depending on the init counter - for (int i = 0; i < initCounter; i++) - { - int j = counter.Length; - while (--j >= 0 && ++counter[j] == 0) { } - } - // Create the Aes Cipher - AesCounterMode aes = new AesCounterMode(counter); + AesCounterMode aes = new AesCounterMode(iv); if (encrypt) { - _Cipher = aes.CreateEncryptor(key, iv); // Encrypt + _Cipher = (CounterModeCryptoTransform)aes.CreateEncryptor(key, iv); // Encrypt } else { - _Cipher = aes.CreateDecryptor(key, iv); // Decrypt + _Cipher = (CounterModeCryptoTransform)aes.CreateDecryptor(key, iv); // Decrypt } + + // Sync the counter + SyncCounter(); } public override int Read(byte[] buffer, int offset, int count) { if (_Inner != null && CanRead) { - int bytesRead = 0; - int totalBytesRead = 0; - int bytesProcessed = 0; - long startPosition = _Inner.Position; - int blockSize = _Cipher.InputBlockSize; - long byteOffset = (startPosition % blockSize); - int initialOffset = (int)byteOffset; - int blockOffset = (byteOffset == 0) ? 0 : 1; - int blocksToProcess = (int)Math.Ceiling(count / (double)blockSize) + blockOffset; + byte[] readBuf = new byte[count]; + int processed = 0; - // Determine if we are at the start of a block, or not - if (byteOffset != 0) + // Read the data from the stream + int bytesRead = _Inner.Read(readBuf, 0, count); + if (bytesRead > 0) { - // We are not a multiple of the block size, so let's backup to get the current block - _Inner.Seek(startPosition - byteOffset, SeekOrigin.Begin); + // Process the + processed = _Cipher.TransformBlock(readBuf, 0, bytesRead, buffer, 0); } - // Initialize buffers - byte[] readBuf = new byte[blockSize]; - byte[] outBuf = new byte[blockSize]; - - // Iterate through each block of the ammount we want to read - for (int i = 0; i < blocksToProcess; i++) - { - // Read the next block of data - totalBytesRead += bytesRead = _Inner.Read(readBuf, 0, blockSize); - if (bytesRead > 0) - { - // process the cipher for the read block and add it to the output - int processed = _Cipher.TransformBlock(readBuf, 0, bytesRead, outBuf, 0); - - // copy the values to the output - outBuf.Skip(initialOffset).ToArray().CopyTo(buffer, bytesProcessed + offset); - - // Reset initial offset and calibrate - bytesProcessed += processed - initialOffset; - - // Reset initial offset - initialOffset = 0; - } - - // Clear the read buffer - Array.Clear(readBuf, 0, blockSize); - } - - // Adjust bytes read by the block offset - totalBytesRead -= (int)byteOffset; - bytesProcessed -= (int)byteOffset; - - if (bytesProcessed < count) + // Do we have more? + if (processed < bytesRead) { // Finalize the cipher - byte[] finalBuf = _Cipher.TransformFinalBlock(readBuf, bytesProcessed, bytesRead); - finalBuf.Take(count - bytesProcessed).ToArray().CopyTo(buffer, bytesProcessed); - bytesProcessed += count - bytesProcessed; + byte[] finalBuf = _Cipher.TransformFinalBlock(readBuf, processed, bytesRead); + finalBuf.CopyTo(buffer, processed); + processed += finalBuf.Length; } - return bytesProcessed; + return processed; } return -1; } @@ -187,6 +141,9 @@ namespace Teknik.Utilities if (_Inner != null) { _Inner.Position = value; + + // Sync the counter + SyncCounter(); } } } @@ -203,7 +160,12 @@ namespace Teknik.Utilities { if (_Inner != null) { - return _Inner.Seek(offset, origin); + long newPos = _Inner.Seek(offset, origin); + + // Sync the counter + SyncCounter(); + + return newPos; } return -1; } @@ -215,5 +177,37 @@ namespace Teknik.Utilities _Inner.SetLength(value); } } + + private void SyncCounter() + { + if (_Cipher != null) + { + // Calculate the counter iterations and position needed + int iterations = (int)Math.Floor(_Inner.Position / (decimal)_Cipher.InputBlockSize); + int counterPos = (int)(_Inner.Position % _Cipher.InputBlockSize); + + // Are we out of sync with the cipher? + if (_Cipher.Iterations != iterations || _Cipher.CounterPosition != counterPos) + { + // Reset the current counter + _Cipher.ResetCounter(); + + // Iterate the counter to the current position + for (int i = 0; i < iterations; i++) + { + _Cipher.IncrementCounter(); + } + + // Encrypt the counter + _Cipher.EncryptCounter(); + + // Set the current position of the counter + _Cipher.CounterPosition = counterPos; + + // Increment the counter for the next time + _Cipher.IncrementCounter(); + } + } + } } } diff --git a/Utilities/Utilities/Utilities.csproj b/Utilities/Utilities/Utilities.csproj index 3308f98..321b658 100644 --- a/Utilities/Utilities/Utilities.csproj +++ b/Utilities/Utilities/Utilities.csproj @@ -106,6 +106,7 @@ + @@ -139,12 +140,6 @@ - - - {2bb2f552-b80f-41f2-937d-5d73f15c6dc4} - Security - -