Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / hmac.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>Microsoft</OWNER>
7 // 
8
9 //
10 // HMAC.cs
11 //
12
13 //
14 // For test vectors, see RFC2104, e.g. http://www.faqs.org/rfcs/rfc2104.html
15 //
16
17 using System.Diagnostics.Contracts;
18
19 namespace System.Security.Cryptography {
20     [System.Runtime.InteropServices.ComVisible(true)]
21     public abstract class HMAC : KeyedHashAlgorithm {
22         //
23         // protected members
24         //
25
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
28         
29         private int blockSizeValue = 64;
30
31         protected int BlockSizeValue {
32             get {
33                 return blockSizeValue;
34             }
35             set {
36                 blockSizeValue = value;
37             }        
38         }       
39  
40         internal string m_hashName;
41
42         internal HashAlgorithm m_hash1;
43         internal HashAlgorithm m_hash2;
44
45         //
46         // private members
47         //
48
49         // m_inner = PaddedKey ^ {0x36,...,0x36}
50         // m_outer = PaddedKey ^ {0x5C,...,0x5C}
51         private byte[] m_inner;
52         private byte[] m_outer;
53
54         private bool m_hashing = false;
55
56         private void UpdateIOPadBuffers () {
57             if (m_inner == null)
58                 m_inner = new byte[BlockSizeValue];
59             if (m_outer == null)
60                 m_outer = new byte[BlockSizeValue];
61
62             int i;
63             for (i=0; i < BlockSizeValue; i++) {
64                 m_inner[i] = 0x36;
65                 m_outer[i] = 0x5C;
66             }
67             for (i=0; i < KeyValue.Length; i++) {
68                 m_inner[i] ^= KeyValue[i];
69                 m_outer[i] ^= KeyValue[i];
70             }
71         }
72
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.
77             m_inner = null;
78             m_outer = null;
79
80             if (key.Length > BlockSizeValue) {
81                 KeyValue = m_hash1.ComputeHash(key);
82                 // No need to call Initialize, ComputeHash will do it for us
83             } else {
84                 KeyValue = (byte[]) key.Clone();
85             }
86             UpdateIOPadBuffers();
87         }
88
89         //
90         // public properties
91         //
92
93         public override byte[] Key {
94             get { return (byte[]) KeyValue.Clone(); }
95             set {
96                 if (m_hashing)
97                     throw new CryptographicException(Environment.GetResourceString("Cryptography_HashKeySet"));
98                 InitializeKey(value);
99             }
100         }
101
102         public string HashName {
103             get { return m_hashName; }
104 #if FEATURE_CRYPTO
105             set { 
106                 if (m_hashing)
107                     throw new CryptographicException(Environment.GetResourceString("Cryptography_HashNameSet"));
108                 m_hashName = value; 
109                 // create the hash algorithms
110                 m_hash1 = HashAlgorithm.Create(m_hashName);
111                 m_hash2 = HashAlgorithm.Create(m_hashName);
112             }
113 #endif // FEATURE_CRYPTO
114         }
115
116         //
117         // public methods
118         //
119
120         new static public HMAC Create () {
121 #if FULL_AOT_RUNTIME
122             return new System.Security.Cryptography.HMACSHA1 ();
123 #else
124             return Create("System.Security.Cryptography.HMAC");
125 #endif
126         }
127
128         new static public HMAC Create (string algorithmName) {
129             return (HMAC) CryptoConfig.CreateFromName(algorithmName);
130         }
131
132         public override void Initialize () {
133             m_hash1.Initialize();
134             m_hash2.Initialize();
135             m_hashing = false;
136         }
137
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);
141                 m_hashing = true;
142             }
143             m_hash1.TransformBlock(rgb, ib, cb, rgb, ib);
144         }
145
146         protected override byte[] HashFinal () {
147             if (m_hashing == false) {
148                 m_hash1.TransformBlock(m_inner, 0, m_inner.Length, m_inner, 0);
149                 m_hashing = true;
150             }
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);
158             m_hashing = false;
159             m_hash2.TransformFinalBlock(EmptyArray<Byte>.Value, 0, 0);
160             return m_hash2.HashValue;
161         }
162
163         //
164         // IDisposable methods
165         //
166
167         protected override void Dispose (bool disposing) {
168             if (disposing) {
169                 if (m_hash1 != null)
170                     ((IDisposable)m_hash1).Dispose();
171                 if (m_hash2 != null)
172                     ((IDisposable)m_hash2).Dispose();
173                 if (m_inner != null)
174                     Array.Clear(m_inner, 0, m_inner.Length);
175                 if (m_outer != null)
176                     Array.Clear(m_outer, 0, m_outer.Length);
177             }
178             // call the base class's Dispose
179             base.Dispose(disposing);
180         }
181
182 #if FEATURE_CRYPTO
183         /// <summary>
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.
187         /// </summary>
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);
193
194             // Use the standard algorithm implementation by default - in FIPS mode try to fall back to the
195             // FIPS implementation.
196             if (CryptoConfig.AllowOnlyFipsAlgorithms) {
197                 try {
198                     return createFipsHashAlgorithmCallback();
199                 }
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);
204                 }
205             }
206             else {
207                 return createStandardHashAlgorithmCallback();
208             }
209         }
210 #endif // FEATURE_CRYPTO
211     }
212 }