diff --git a/ServerMaint/Program.cs b/ServerMaint/Program.cs
index ca4cdbd..d43b232 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, "CTR", "NoPadding", 0);
// We have the data, let's scan it
ClamScanResult scanResult = clam.SendAndScanFile(aesStream);
diff --git a/Teknik.sln b/Teknik.sln
index 37e5d56..3e2c91e 100644
--- a/Teknik.sln
+++ b/Teknik.sln
@@ -26,6 +26,8 @@ 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
@@ -91,6 +93,14 @@ 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 741a3da..0982ae8 100644
--- a/Teknik/Areas/Upload/Controllers/UploadController.cs
+++ b/Teknik/Areas/Upload/Controllers/UploadController.cs
@@ -292,9 +292,12 @@ 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"), (int)length, Config.UploadConfig.ChunkSize),
+ (response) => ResponseHelper.StreamToOutput(response, true, new AESCryptoStream(fs, false, keyBytes, ivBytes, "CTR", "NoPadding", counterOffset), (int)length, Config.UploadConfig.ChunkSize),
false);
}
else // Otherwise just send it
diff --git a/Teknik/Teknik.csproj b/Teknik/Teknik.csproj
index 522a97d..232f7ac 100644
--- a/Teknik/Teknik.csproj
+++ b/Teknik/Teknik.csproj
@@ -802,6 +802,10 @@
{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
new file mode 100644
index 0000000..c9ffd51
--- /dev/null
+++ b/Utilities/Security/Cryptography/AesCounterMode.cs
@@ -0,0 +1,137 @@
+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
new file mode 100644
index 0000000..8d9c36f
--- /dev/null
+++ b/Utilities/Security/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+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
new file mode 100644
index 0000000..53c6c51
--- /dev/null
+++ b/Utilities/Security/Security.csproj
@@ -0,0 +1,55 @@
+
+
+
+
+ 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/StreamHelper.cs b/Utilities/Utilities/StreamHelper.cs
index bfb69ca..7609a75 100644
--- a/Utilities/Utilities/StreamHelper.cs
+++ b/Utilities/Utilities/StreamHelper.cs
@@ -1,22 +1,45 @@
-using Org.BouncyCastle.Crypto;
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
+using Teknik.Security.Cryptography;
namespace Teknik.Utilities
{
public class AESCryptoStream : Stream
{
private Stream _Inner;
- private IBufferedCipher _Cipher;
+ //private IBufferedCipher _Cipher;
+ private ICryptoTransform _Cipher;
- public AESCryptoStream(Stream stream, bool encrypt, byte[] key, byte[] iv, string mode, string padding)
+ public AESCryptoStream(Stream stream, bool encrypt, byte[] key, byte[] iv, string mode, string padding, int initCounter)
{
_Inner = stream;
- _Cipher = AES.CreateCipher(encrypt, key, iv, mode, padding);
+
+ // 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);
+ if (encrypt)
+ {
+ _Cipher = aes.CreateEncryptor(key, iv); // Encrypt
+ }
+ else
+ {
+ _Cipher = aes.CreateDecryptor(key, iv); // Decrypt
+ }
}
public override int Read(byte[] buffer, int offset, int count)
@@ -24,30 +47,63 @@ namespace Teknik.Utilities
if (_Inner != null && CanRead)
{
int bytesRead = 0;
+ int totalBytesRead = 0;
+ int bytesProcessed = 0;
long startPosition = _Inner.Position;
- int blockSize = _Cipher.GetBlockSize();
- long blockOffset = (startPosition % blockSize);
+ 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;
// Determine if we are at the start of a block, or not
- if (blockOffset != 0)
+ if (byteOffset != 0)
{
// We are not a multiple of the block size, so let's backup to get the current block
- _Inner.Seek(startPosition - blockOffset, SeekOrigin.Begin);
+ _Inner.Seek(startPosition - byteOffset, SeekOrigin.Begin);
}
- // Process the cipher
- int processed = AES.ProcessCipherBlock(_Cipher, _Inner, 0, count + (int)blockOffset, buffer, offset, out bytesRead);
+ // 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
- bytesRead -= (int)blockOffset;
+ totalBytesRead -= (int)byteOffset;
+ bytesProcessed -= (int)byteOffset;
- if (processed < bytesRead)
+ if (bytesProcessed < count)
{
// Finalize the cipher
- processed += AES.FinalizeCipherBlock(_Cipher, buffer, processed + offset);
+ byte[] finalBuf = _Cipher.TransformFinalBlock(readBuf, bytesProcessed, bytesRead);
+ finalBuf.Take(count - bytesProcessed).ToArray().CopyTo(buffer, bytesProcessed);
+ bytesProcessed += count - bytesProcessed;
}
- return bytesRead;
+ return bytesProcessed;
}
return -1;
}
@@ -58,10 +114,10 @@ namespace Teknik.Utilities
{
// Process the cipher
byte[] output = new byte[count];
- int processed = _Cipher.ProcessBytes(buffer, offset, count, output, 0);
+ //int processed = _Cipher.ProcessBytes(buffer, offset, count, output, 0);
// Finalize the cipher
- AES.FinalizeCipherBlock(_Cipher, output, processed);
+ //AES.FinalizeCipherBlock(_Cipher, output, processed);
_Inner.Write(output, 0, count);
}
diff --git a/Utilities/Utilities/Utilities.csproj b/Utilities/Utilities/Utilities.csproj
index ab32f22..3308f98 100644
--- a/Utilities/Utilities/Utilities.csproj
+++ b/Utilities/Utilities/Utilities.csproj
@@ -139,6 +139,12 @@
+
+
+ {2bb2f552-b80f-41f2-937d-5d73f15c6dc4}
+ Security
+
+