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.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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 = counter;
  30. }
  31. public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv)
  32. {
  33. return new CounterModeCryptoTransform(_Algo, key, iv, _Counter);
  34. }
  35. public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv)
  36. {
  37. return new CounterModeCryptoTransform(_Algo, key, iv, _Counter);
  38. }
  39. public override void GenerateKey()
  40. {
  41. _Algo.GenerateKey();
  42. }
  43. public override void GenerateIV()
  44. {
  45. _Algo.GenerateIV();
  46. }
  47. }
  48. public class CounterModeCryptoTransform : ICryptoTransform
  49. {
  50. private readonly byte[] _IV;
  51. private readonly byte[] _Counter;
  52. private readonly ICryptoTransform _CounterEncryptor;
  53. private readonly SymmetricAlgorithm _SymmetricAlgorithm;
  54. // Stateful Fields
  55. private byte[] _EncryptedCounter;
  56. private int _Iterations;
  57. public int Iterations
  58. {
  59. get
  60. {
  61. return _Iterations;
  62. }
  63. }
  64. private int _CounterPosition;
  65. public int CounterPosition
  66. {
  67. get
  68. {
  69. return _CounterPosition;
  70. }
  71. set
  72. {
  73. if (value > 0 && value < _EncryptedCounter.Length)
  74. {
  75. _CounterPosition = value;
  76. }
  77. }
  78. }
  79. public CounterModeCryptoTransform(SymmetricAlgorithm symmetricAlgorithm, byte[] key, byte[] iv, byte[] counter)
  80. {
  81. if (symmetricAlgorithm == null) throw new ArgumentNullException("symmetricAlgorithm");
  82. if (key == null) throw new ArgumentNullException("key");
  83. if (iv == null) throw new ArgumentNullException("iv");
  84. if (counter == null) throw new ArgumentNullException("counter");
  85. // Check lengths
  86. if (counter.Length != symmetricAlgorithm.BlockSize / 8)
  87. throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
  88. counter.Length, symmetricAlgorithm.BlockSize / 8));
  89. _SymmetricAlgorithm = symmetricAlgorithm;
  90. _IV = iv;
  91. _Counter = counter;
  92. _CounterEncryptor = symmetricAlgorithm.CreateEncryptor(key, iv);
  93. // Initialize State
  94. _CounterPosition = 0;
  95. _Iterations = 0;
  96. // Encrypt the counter
  97. EncryptCounter();
  98. }
  99. public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
  100. {
  101. var output = new byte[inputCount];
  102. TransformBlock(inputBuffer, inputOffset, inputCount, output, 0);
  103. return output;
  104. }
  105. public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
  106. {
  107. for (var i = 0; i < inputCount; i++)
  108. {
  109. // Encrypt the counter if we have reached the end, or
  110. if (_CounterPosition >= _EncryptedCounter.Length)
  111. {
  112. // Encrypt the counter
  113. EncryptCounter();
  114. //Reset current counter position
  115. _CounterPosition = 0;
  116. // Increment the counter for the next run
  117. IncrementCounter();
  118. }
  119. // XOR the encrypted counter with the input plain text
  120. outputBuffer[outputOffset + i] = (byte)(_EncryptedCounter[_CounterPosition] ^ inputBuffer[inputOffset + i]);
  121. // Move the counter position
  122. _CounterPosition++;
  123. }
  124. return inputCount;
  125. }
  126. public void EncryptCounter()
  127. {
  128. // Clear the encrypted counter
  129. _EncryptedCounter = new byte[_SymmetricAlgorithm.BlockSize / 8];
  130. // Encrypt the current counter to the encrypted counter
  131. _CounterEncryptor.TransformBlock(_Counter, 0, _Counter.Length, _EncryptedCounter, 0);
  132. }
  133. public void ResetCounter()
  134. {
  135. Array.Clear(_Counter, 0, _Counter.Length);
  136. Array.Copy(_IV, 0, _Counter, 0, _IV.Length);
  137. _Iterations = 0;
  138. }
  139. public void IncrementCounter()
  140. {
  141. int j = _Counter.Length;
  142. while (--j >= 0 && ++_Counter[j] == 0)
  143. {
  144. }
  145. _Iterations++;
  146. }
  147. public int InputBlockSize { get { return _SymmetricAlgorithm.BlockSize / 8; } }
  148. public int OutputBlockSize { get { return _SymmetricAlgorithm.BlockSize / 8; } }
  149. public bool CanTransformMultipleBlocks { get { return true; } }
  150. public bool CanReuseTransform { get { return false; } }
  151. public void Dispose()
  152. {
  153. }
  154. }
  155. }