HMAC.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Security.Cryptography;
  6. namespace Renci.SshNet.Security.Cryptography
  7. {
  8. /// <summary>
  9. /// Provides HMAC algorithm implementation.
  10. /// </summary>
  11. /// <typeparam name="T">Class that implements <see cref="T:System.Security.Cryptography.HashAlgorithm" />.</typeparam>
  12. public class HMac<T> : KeyedHashAlgorithm where T : HashAlgorithm, new()
  13. {
  14. private HashAlgorithm _hash;
  15. private bool _isHashing;
  16. private byte[] _innerPadding;
  17. private byte[] _outerPadding;
  18. private byte[] _key;
  19. /// <summary>
  20. /// Gets the size of the block.
  21. /// </summary>
  22. /// <value>
  23. /// The size of the block.
  24. /// </value>
  25. protected int BlockSize
  26. {
  27. get
  28. {
  29. return this._hash.InputBlockSize;
  30. }
  31. }
  32. private HMac()
  33. {
  34. // Create the hash algorithms.
  35. this._hash = new T();
  36. }
  37. /// <summary>
  38. /// Rfc 2104.
  39. /// </summary>
  40. /// <param name="key">The key.</param>
  41. public HMac(byte[] key, int hashSizeValue)
  42. : this()
  43. {
  44. this.HashSizeValue = hashSizeValue;
  45. this._key = key;
  46. this.InternalInitialize();
  47. }
  48. public HMac(byte[] key)
  49. : this()
  50. {
  51. this.HashSizeValue = this._hash.HashSize;
  52. this._key = key;
  53. this.InternalInitialize();
  54. }
  55. /// <summary>
  56. /// Gets or sets the key to use in the hash algorithm.
  57. /// </summary>
  58. /// <returns>The key to use in the hash algorithm.</returns>
  59. public override byte[] Key
  60. {
  61. get
  62. {
  63. return (byte[])KeyValue.Clone();
  64. }
  65. set
  66. {
  67. this.SetKey(value);
  68. }
  69. }
  70. /// <summary>
  71. /// Initializes an implementation of the <see cref="T:System.Security.Cryptography.HashAlgorithm" /> class.
  72. /// </summary>
  73. public override void Initialize()
  74. {
  75. this.InternalInitialize();
  76. }
  77. /// <summary>
  78. /// Hashes the core.
  79. /// </summary>
  80. /// <param name="rgb">The RGB.</param>
  81. /// <param name="ib">The ib.</param>
  82. /// <param name="cb">The cb.</param>
  83. protected override void HashCore(byte[] rgb, int ib, int cb)
  84. {
  85. if (!this._isHashing)
  86. {
  87. this._hash.TransformBlock(this._innerPadding, 0, this.BlockSize, this._innerPadding, 0);
  88. this._isHashing = true;
  89. }
  90. this._hash.TransformBlock(rgb, ib, cb, rgb, ib);
  91. }
  92. /// <summary>
  93. /// Finalizes the hash computation after the last data is processed by the cryptographic stream object.
  94. /// </summary>
  95. /// <returns>
  96. /// The computed hash code.
  97. /// </returns>
  98. protected override byte[] HashFinal()
  99. {
  100. if (!this._isHashing)
  101. {
  102. this._hash.TransformBlock(this._innerPadding, 0, 64, this._innerPadding, 0);
  103. this._isHashing = true;
  104. }
  105. // Finalize the original hash.
  106. this._hash.TransformFinalBlock(new byte[0], 0, 0);
  107. var hashValue = this._hash.Hash;
  108. // Write the outer array.
  109. this._hash.TransformBlock(this._outerPadding, 0, this.BlockSize, this._outerPadding, 0);
  110. // Write the inner hash and finalize the hash.
  111. this._hash.TransformFinalBlock(hashValue, 0, hashValue.Length);
  112. this._isHashing = false;
  113. return this._hash.Hash.Take(12).ToArray();
  114. }
  115. private void InternalInitialize()
  116. {
  117. this._isHashing = false;
  118. this.SetKey(this._key);
  119. }
  120. private void SetKey(byte[] value)
  121. {
  122. if (this._isHashing)
  123. {
  124. throw new Exception("Cannot change key during hash operation");
  125. }
  126. if (value.Length > this.BlockSize)
  127. {
  128. this.KeyValue = this._hash.ComputeHash(value);
  129. // No need to call Initialize, ComputeHash does it automatically.
  130. }
  131. else
  132. {
  133. this.KeyValue = value.Clone() as byte[];
  134. }
  135. this._innerPadding = new byte[this.BlockSize];
  136. this._outerPadding = new byte[this.BlockSize];
  137. // Compute inner and outer padding.
  138. int i = 0;
  139. for (i = 0; i < this.BlockSize; i++)
  140. {
  141. this._innerPadding[i] = 0x36;
  142. this._outerPadding[i] = 0x5C;
  143. }
  144. for (i = 0; i < this.KeyValue.Length; i++)
  145. {
  146. this._innerPadding[i] ^= this.KeyValue[i];
  147. this._outerPadding[i] ^= this.KeyValue[i];
  148. }
  149. }
  150. /// <summary>
  151. /// Releases unmanaged and - optionally - managed resources
  152. /// </summary>
  153. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>
  154. protected override void Dispose(bool disposing)
  155. {
  156. base.Dispose(disposing);
  157. if (this._hash != null)
  158. {
  159. this._hash.Clear();
  160. this._hash = null;
  161. }
  162. }
  163. }
  164. }