3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <OWNER>Microsoft</OWNER>
14 // For test vectors, see RFC2104, e.g. http://www.faqs.org/rfcs/rfc2104.html
17 using System.Diagnostics.Contracts;
19 namespace System.Security.Cryptography {
20 [System.Runtime.InteropServices.ComVisible(true)]
21 public abstract class HMAC : KeyedHashAlgorithm {
26 // an HMAC uses a hash function where data is hashed by iterating a basic compression
27 // function on blocks of data. BlockSizeValue is the byte size of such a block
29 private int blockSizeValue = 64;
31 protected int BlockSizeValue {
33 return blockSizeValue;
36 blockSizeValue = value;
40 internal string m_hashName;
42 internal HashAlgorithm m_hash1;
43 internal HashAlgorithm m_hash2;
49 // m_inner = PaddedKey ^ {0x36,...,0x36}
50 // m_outer = PaddedKey ^ {0x5C,...,0x5C}
51 private byte[] m_inner;
52 private byte[] m_outer;
54 private bool m_hashing = false;
56 private void UpdateIOPadBuffers () {
58 m_inner = new byte[BlockSizeValue];
60 m_outer = new byte[BlockSizeValue];
63 for (i=0; i < BlockSizeValue; i++) {
67 for (i=0; i < KeyValue.Length; i++) {
68 m_inner[i] ^= KeyValue[i];
69 m_outer[i] ^= KeyValue[i];
73 internal void InitializeKey (byte[] key) {
74 // When we change the key value, we'll need to update the initial values of the inner and outter
75 // computation buffers. In the case of correct HMAC vs Whidbey HMAC, these buffers could get
76 // generated to a different size than when we started.
80 if (key.Length > BlockSizeValue) {
81 KeyValue = m_hash1.ComputeHash(key);
82 // No need to call Initialize, ComputeHash will do it for us
84 KeyValue = (byte[]) key.Clone();
93 public override byte[] Key {
94 get { return (byte[]) KeyValue.Clone(); }
97 throw new CryptographicException(Environment.GetResourceString("Cryptography_HashKeySet"));
102 public string HashName {
103 get { return m_hashName; }
107 throw new CryptographicException(Environment.GetResourceString("Cryptography_HashNameSet"));
109 // create the hash algorithms
110 m_hash1 = HashAlgorithm.Create(m_hashName);
111 m_hash2 = HashAlgorithm.Create(m_hashName);
113 #endif // FEATURE_CRYPTO
120 new static public HMAC Create () {
122 return new System.Security.Cryptography.HMACSHA1 ();
124 return Create("System.Security.Cryptography.HMAC");
128 new static public HMAC Create (string algorithmName) {
129 return (HMAC) CryptoConfig.CreateFromName(algorithmName);
132 public override void Initialize () {
133 m_hash1.Initialize();
134 m_hash2.Initialize();
138 protected override void HashCore (byte[] rgb, int ib, int cb) {
139 if (m_hashing == false) {
140 m_hash1.TransformBlock(m_inner, 0, m_inner.Length, m_inner, 0);
143 m_hash1.TransformBlock(rgb, ib, cb, rgb, ib);
146 protected override byte[] HashFinal () {
147 if (m_hashing == false) {
148 m_hash1.TransformBlock(m_inner, 0, m_inner.Length, m_inner, 0);
151 // finalize the original hash
152 m_hash1.TransformFinalBlock(EmptyArray<Byte>.Value, 0, 0);
153 byte[] hashValue1 = m_hash1.HashValue;
154 // write the outer array
155 m_hash2.TransformBlock(m_outer, 0, m_outer.Length, m_outer, 0);
156 // write the inner hash and finalize the hash
157 m_hash2.TransformBlock(hashValue1, 0, hashValue1.Length, hashValue1, 0);
159 m_hash2.TransformFinalBlock(EmptyArray<Byte>.Value, 0, 0);
160 return m_hash2.HashValue;
164 // IDisposable methods
167 protected override void Dispose (bool disposing) {
170 ((IDisposable)m_hash1).Dispose();
172 ((IDisposable)m_hash2).Dispose();
174 Array.Clear(m_inner, 0, m_inner.Length);
176 Array.Clear(m_outer, 0, m_outer.Length);
178 // call the base class's Dispose
179 base.Dispose(disposing);
184 /// Get a hash algorithm instance falling back to a second algorithm in FIPS mode. For instance,
185 /// use SHA256Managed by default but fall back to SHA256CryptoServiceProvider which is FIPS
186 /// certified if FIPS is enabled.
188 /// <returns></returns>
189 internal static HashAlgorithm GetHashAlgorithmWithFipsFallback(Func<HashAlgorithm> createStandardHashAlgorithmCallback,
190 Func<HashAlgorithm> createFipsHashAlgorithmCallback) {
191 Contract.Requires(createStandardHashAlgorithmCallback != null);
192 Contract.Requires(createFipsHashAlgorithmCallback != null);
194 // Use the standard algorithm implementation by default - in FIPS mode try to fall back to the
195 // FIPS implementation.
196 if (CryptoConfig.AllowOnlyFipsAlgorithms) {
198 return createFipsHashAlgorithmCallback();
200 catch (PlatformNotSupportedException e) {
201 // We need to wrap the PlatformNotSupportedException into an InvalidOperationException to
202 // remain compatible with the error that would be triggered in previous runtimes.
203 throw new InvalidOperationException(e.Message, e);
207 return createStandardHashAlgorithmCallback();
210 #endif // FEATURE_CRYPTO