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 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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.Security.Cryptography
  8. {
  9. public class AesCounterMode : SymmetricAlgorithm
  10. {
  11. private const int _blockSize = 16;
  12. private readonly byte[] _counter;
  13. private readonly AesManaged _aes;
  14. public AesCounterMode(byte[] counter)
  15. {
  16. if (counter == null) throw new ArgumentNullException("counter");
  17. if (counter.Length != _blockSize)
  18. throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
  19. counter.Length, _blockSize));
  20. _aes = new AesManaged
  21. {
  22. Mode = CipherMode.ECB,
  23. Padding = PaddingMode.None
  24. };
  25. _counter = counter;
  26. }
  27. public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv)
  28. {
  29. return new CounterModeCryptoTransform(_aes, key, iv, _counter);
  30. }
  31. public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv)
  32. {
  33. return new CounterModeCryptoTransform(_aes, key, iv, _counter);
  34. }
  35. public override void GenerateKey()
  36. {
  37. _aes.GenerateKey();
  38. }
  39. public override void GenerateIV()
  40. {
  41. // IV not needed in Counter Mode
  42. }
  43. }
  44. public class CounterModeCryptoTransform : ICryptoTransform
  45. {
  46. private readonly byte[] _iv;
  47. private readonly byte[] _counter;
  48. private readonly ICryptoTransform _counterEncryptor;
  49. private readonly Queue<byte> _xorMask = new Queue<byte>();
  50. private readonly SymmetricAlgorithm _symmetricAlgorithm;
  51. public CounterModeCryptoTransform(SymmetricAlgorithm symmetricAlgorithm, byte[] key, byte[] iv, byte[] counter)
  52. {
  53. if (symmetricAlgorithm == null) throw new ArgumentNullException("symmetricAlgorithm");
  54. if (key == null) throw new ArgumentNullException("key");
  55. if (iv == null) throw new ArgumentNullException("counter");
  56. if (iv.Length != symmetricAlgorithm.BlockSize / 8)
  57. throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
  58. iv.Length, symmetricAlgorithm.BlockSize / 8));
  59. _symmetricAlgorithm = symmetricAlgorithm;
  60. _counter = counter;
  61. _iv = iv;
  62. _counterEncryptor = symmetricAlgorithm.CreateEncryptor(key, iv);
  63. }
  64. public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
  65. {
  66. var output = new byte[inputCount];
  67. TransformBlock(inputBuffer, inputOffset, inputCount, output, 0);
  68. return output;
  69. }
  70. public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
  71. {
  72. for (var i = 0; i < inputCount; i++)
  73. {
  74. if (NeedMoreXorMaskBytes()) EncryptCounterThenIncrement();
  75. var mask = _xorMask.Dequeue();
  76. outputBuffer[outputOffset + i] = (byte)(mask ^ inputBuffer[inputOffset + i]);
  77. }
  78. return inputCount;
  79. }
  80. private bool NeedMoreXorMaskBytes()
  81. {
  82. return _xorMask.Count == 0;
  83. }
  84. private void EncryptCounterThenIncrement()
  85. {
  86. var counterModeBlock = new byte[_symmetricAlgorithm.BlockSize / 8];
  87. _counterEncryptor.TransformBlock(_counter, 0, _counter.Length, counterModeBlock, 0);
  88. IncrementCounter();
  89. foreach (var b in counterModeBlock)
  90. {
  91. _xorMask.Enqueue(b);
  92. }
  93. }
  94. private void IncrementCounter()
  95. {
  96. int j = _counter.Length;
  97. while (--j >= 0 && ++_counter[j] == 0)
  98. {
  99. }
  100. //for (var i = _counter.Length - 1; i >= 0; i--)
  101. //{
  102. // if (++_counter[i] != 0)
  103. // break;
  104. //}
  105. }
  106. public int InputBlockSize { get { return _symmetricAlgorithm.BlockSize / 8; } }
  107. public int OutputBlockSize { get { return _symmetricAlgorithm.BlockSize / 8; } }
  108. public bool CanTransformMultipleBlocks { get { return true; } }
  109. public bool CanReuseTransform { get { return false; } }
  110. public void Dispose()
  111. {
  112. }
  113. }
  114. }