The next generation of the Teknik Services. Written in ASP.NET. Fork for blog tags.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AesCounterMode.cs 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Security.Cryptography;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace Teknik.Utilities.Cryptography
  8. {
  9. public class AesCounterMode : SymmetricAlgorithm
  10. {
  11. // Internal Variables
  12. private const int _BlockSize = 16;
  13. private readonly byte[] _Counter;
  14. private readonly AesManaged _Algo;
  15. public AesCounterMode() : this(new byte[_BlockSize]) { }
  16. public AesCounterMode(byte[] counter)
  17. {
  18. if (counter == null) throw new ArgumentNullException("counter");
  19. if (counter.Length != _BlockSize)
  20. throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
  21. counter.Length, _BlockSize));
  22. // Generate a new instance of the Aes Algorithm in ECB mode with no padding
  23. _Algo = new AesManaged
  24. {
  25. Mode = CipherMode.ECB,
  26. Padding = PaddingMode.None
  27. };
  28. // Set the internal variables
  29. _Counter = new byte[counter.Length];
  30. counter.CopyTo(_Counter, 0);
  31. }
  32. public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv)
  33. {
  34. return new CounterModeCryptoTransform(_Algo, key, iv, _Counter);
  35. }
  36. public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv)
  37. {
  38. return new CounterModeCryptoTransform(_Algo, key, iv, _Counter);
  39. }
  40. public override void GenerateKey()
  41. {
  42. _Algo.GenerateKey();
  43. }
  44. public override void GenerateIV()
  45. {
  46. _Algo.GenerateIV();
  47. }
  48. }
  49. public class CounterModeCryptoTransform : ICryptoTransform
  50. {
  51. private readonly byte[] _IV;
  52. private readonly byte[] _Counter;
  53. private readonly ICryptoTransform _CounterEncryptor;
  54. private readonly SymmetricAlgorithm _SymmetricAlgorithm;
  55. // Stateful Fields
  56. private byte[] _EncryptedCounter;
  57. private int _Iterations;
  58. public int Iterations
  59. {
  60. get
  61. {
  62. return _Iterations;
  63. }
  64. }
  65. private int _CounterPosition;
  66. public int CounterPosition
  67. {
  68. get
  69. {
  70. return _CounterPosition;
  71. }
  72. set
  73. {
  74. if (value > 0 && value < _EncryptedCounter.Length)
  75. {
  76. _CounterPosition = value;
  77. }
  78. }
  79. }
  80. public CounterModeCryptoTransform(SymmetricAlgorithm symmetricAlgorithm, byte[] key, byte[] iv, byte[] counter)
  81. {
  82. if (symmetricAlgorithm == null) throw new ArgumentNullException("symmetricAlgorithm");
  83. if (key == null) throw new ArgumentNullException("key");
  84. if (iv == null) throw new ArgumentNullException("iv");
  85. if (counter == null) throw new ArgumentNullException("counter");
  86. // Check lengths
  87. if (counter.Length != symmetricAlgorithm.BlockSize / 8)
  88. throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
  89. counter.Length, symmetricAlgorithm.BlockSize / 8));
  90. _SymmetricAlgorithm = symmetricAlgorithm;
  91. _IV = new byte[iv.Length];
  92. iv.CopyTo(_IV, 0);
  93. _Counter = new byte[counter.Length];
  94. counter.CopyTo(_Counter, 0);
  95. _CounterEncryptor = symmetricAlgorithm.CreateEncryptor(key, iv);
  96. // Initialize State
  97. _CounterPosition = 0;
  98. _Iterations = 0;
  99. // Encrypt the counter
  100. EncryptCounter();
  101. // Initial Increment
  102. IncrementCounter();
  103. }
  104. public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
  105. {
  106. var output = new byte[inputCount];
  107. TransformBlock(inputBuffer, inputOffset, inputCount, output, 0);
  108. return output;
  109. }
  110. public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
  111. {
  112. for (var i = 0; i < inputCount; i++)
  113. {
  114. // Encrypt the counter if we have reached the end, or
  115. if (_CounterPosition >= _EncryptedCounter.Length)
  116. {
  117. //Reset current counter position
  118. _CounterPosition = 0;
  119. // Encrypt the counter
  120. EncryptCounter();
  121. // Increment the counter for the next batch
  122. IncrementCounter();
  123. }
  124. // XOR the encrypted counter with the input plain text
  125. outputBuffer[outputOffset + i] = (byte)(_EncryptedCounter[_CounterPosition] ^ inputBuffer[inputOffset + i]);
  126. // Move the counter position
  127. _CounterPosition++;
  128. }
  129. return inputCount;
  130. }
  131. public void EncryptCounter()
  132. {
  133. // Clear the encrypted counter
  134. _EncryptedCounter = new byte[_SymmetricAlgorithm.BlockSize / 8];
  135. // Encrypt the current counter to the encrypted counter
  136. _CounterEncryptor.TransformBlock(_Counter, 0, _Counter.Length, _EncryptedCounter, 0);
  137. }
  138. public void ResetCounter()
  139. {
  140. Array.Clear(_Counter, 0, _Counter.Length);
  141. _IV.CopyTo(_Counter, 0);
  142. _Iterations = 0;
  143. }
  144. public void IncrementCounter()
  145. {
  146. int j = _Counter.Length;
  147. while (--j >= 0 && ++_Counter[j] == 0)
  148. {
  149. }
  150. _Iterations++;
  151. }
  152. public int InputBlockSize { get { return _SymmetricAlgorithm.BlockSize / 8; } }
  153. public int OutputBlockSize { get { return _SymmetricAlgorithm.BlockSize / 8; } }
  154. public bool CanTransformMultipleBlocks { get { return true; } }
  155. public bool CanReuseTransform { get { return false; } }
  156. public void Dispose()
  157. {
  158. }
  159. }
  160. }