242bfff1b172424f2615ba4f2dd3e796e115699a
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / sha256managed.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>Microsoft</OWNER>
7 // 
8
9 //
10 // SHA256Managed.cs
11 //
12 // C# implementation of the proposed SHA-256 hash algorithm
13 //
14
15 namespace System.Security.Cryptography {
16     using System;
17     using System.Security;
18     using System.Diagnostics.Contracts;
19
20     [System.Runtime.InteropServices.ComVisible(true)]
21     public class SHA256Managed : SHA256
22     {
23         private byte[]   _buffer;
24         private long     _count; // Number of bytes in the hashed message
25         private UInt32[] _stateSHA256;
26         private UInt32[] _W;
27
28         //
29         // public constructors
30         //
31
32         public SHA256Managed()
33         {
34 #if FEATURE_CRYPTO
35             if (CryptoConfig.AllowOnlyFipsAlgorithms)
36                 throw new InvalidOperationException(Environment.GetResourceString("Cryptography_NonCompliantFIPSAlgorithm"));
37             Contract.EndContractBlock();
38 #endif // FEATURE_CRYPTO
39
40             _stateSHA256 = new UInt32[8];
41             _buffer = new byte[64];
42             _W = new UInt32[64];
43
44             InitializeState();
45         }
46
47         //
48         // public methods
49         //
50
51         public override void Initialize() {
52             InitializeState();
53
54             // Zeroize potentially sensitive information.
55             Array.Clear(_buffer, 0, _buffer.Length);
56             Array.Clear(_W, 0, _W.Length);
57         }
58
59         protected override void HashCore(byte[] rgb, int ibStart, int cbSize) {
60             _HashData(rgb, ibStart, cbSize);
61         }
62
63         protected override byte[] HashFinal() {
64             return _EndHash();
65         }
66
67         //
68         // private methods
69         //
70
71         private void InitializeState() {
72             _count = 0;
73
74             _stateSHA256[0] = 0x6a09e667;
75             _stateSHA256[1] = 0xbb67ae85;
76             _stateSHA256[2] = 0x3c6ef372;
77             _stateSHA256[3] = 0xa54ff53a;
78             _stateSHA256[4] = 0x510e527f;
79             _stateSHA256[5] = 0x9b05688c;
80             _stateSHA256[6] = 0x1f83d9ab;
81             _stateSHA256[7] = 0x5be0cd19;
82         }
83
84         /* SHA256 block update operation. Continues an SHA message-digest
85            operation, processing another message block, and updating the
86            context.
87            */
88
89         [System.Security.SecuritySafeCritical]  // auto-generated
90         private unsafe void _HashData(byte[] partIn, int ibStart, int cbSize)
91         {
92             int bufferLen;
93             int partInLen = cbSize;
94             int partInBase = ibStart;
95
96             /* Compute length of buffer */
97             bufferLen = (int) (_count & 0x3f);
98
99             /* Update number of bytes */
100             _count += partInLen;
101
102             fixed (uint* stateSHA256 = _stateSHA256) {
103                 fixed (byte* buffer = _buffer) {
104                     fixed (uint* expandedBuffer = _W) {
105                         if ((bufferLen > 0) && (bufferLen + partInLen >= 64)) {
106                             Buffer.InternalBlockCopy(partIn, partInBase, _buffer, bufferLen, 64 - bufferLen);
107                             partInBase += (64 - bufferLen);
108                             partInLen -= (64 - bufferLen);
109                             SHATransform(expandedBuffer, stateSHA256, buffer);
110                             bufferLen = 0;
111                         }
112
113                         /* Copy input to temporary buffer and hash */
114                         while (partInLen >= 64) {
115                             Buffer.InternalBlockCopy(partIn, partInBase, _buffer, 0, 64);
116                             partInBase += 64;
117                             partInLen -= 64;
118                             SHATransform(expandedBuffer, stateSHA256, buffer);
119                         }
120
121                         if (partInLen > 0) {
122                             Buffer.InternalBlockCopy(partIn, partInBase, _buffer, bufferLen, partInLen);
123                         }
124                     }
125                 }
126             }
127         }
128
129         /* SHA256 finalization. Ends an SHA256 message-digest operation, writing
130            the message digest.
131            */
132
133         private byte[] _EndHash()
134         {
135             byte[]         pad;
136             int            padLen;
137             long           bitCount;
138             byte[]         hash = new byte[32]; // HashSizeValue = 256
139
140             /* Compute padding: 80 00 00 ... 00 00 <bit count>
141              */
142
143             padLen = 64 - (int)(_count & 0x3f);
144             if (padLen <= 8)
145                 padLen += 64;
146
147             pad = new byte[padLen];
148             pad[0] = 0x80;
149
150             //  Convert count to bit count
151             bitCount = _count * 8;
152
153             pad[padLen-8] = (byte) ((bitCount >> 56) & 0xff);
154             pad[padLen-7] = (byte) ((bitCount >> 48) & 0xff);
155             pad[padLen-6] = (byte) ((bitCount >> 40) & 0xff);
156             pad[padLen-5] = (byte) ((bitCount >> 32) & 0xff);
157             pad[padLen-4] = (byte) ((bitCount >> 24) & 0xff);
158             pad[padLen-3] = (byte) ((bitCount >> 16) & 0xff);
159             pad[padLen-2] = (byte) ((bitCount >> 8) & 0xff);
160             pad[padLen-1] = (byte) ((bitCount >> 0) & 0xff);
161
162             /* Digest padding */
163             _HashData(pad, 0, pad.Length);
164
165             /* Store digest */
166             Utils.DWORDToBigEndian (hash, _stateSHA256, 8);
167
168             HashValue = hash;
169             return hash;
170         }
171
172         private readonly static UInt32[] _K = {
173             0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
174             0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
175             0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
176             0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
177             0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
178             0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
179             0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
180             0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
181             0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
182             0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
183             0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
184             0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
185             0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
186             0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
187             0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
188             0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
189         };
190
191         [System.Security.SecurityCritical]  // auto-generated
192         private static unsafe void SHATransform (uint* expandedBuffer, uint* state, byte* block)
193         {
194             UInt32 a, b, c, d, e, f, h, g;
195             UInt32 aa, bb, cc, dd, ee, ff, hh, gg;
196             UInt32 T1;
197
198             a = state[0];
199             b = state[1];
200             c = state[2];
201             d = state[3];
202             e = state[4];
203             f = state[5];
204             g = state[6];
205             h = state[7];
206
207             // fill in the first 16 bytes of W.
208             Utils.DWORDFromBigEndian(expandedBuffer, 16, block);
209             SHA256Expand(expandedBuffer);
210
211             /* Apply the SHA256 compression function */
212             // We are trying to be smart here and avoid as many copies as we can
213             // The perf gain with this method over the straightforward modify and shift 
214             // forward is >= 20%, so it's worth the pain
215             for (int j=0; j<64; ) {
216                 T1 = h + Sigma_1(e) + Ch(e,f,g) + _K[j] + expandedBuffer[j];
217                 ee = d + T1;
218                 aa = T1 + Sigma_0(a) + Maj(a,b,c);
219                 j++;
220
221                 T1 = g + Sigma_1(ee) + Ch(ee,e,f) + _K[j] + expandedBuffer[j];
222                 ff = c + T1;
223                 bb = T1 + Sigma_0(aa) + Maj(aa,a,b);
224                 j++;
225
226                 T1 = f + Sigma_1(ff) + Ch(ff,ee,e) + _K[j] + expandedBuffer[j];
227                 gg = b + T1;
228                 cc = T1 + Sigma_0(bb) + Maj(bb,aa,a);
229                 j++;
230
231                 T1 = e + Sigma_1(gg) + Ch(gg,ff,ee) + _K[j] + expandedBuffer[j];
232                 hh = a + T1;
233                 dd = T1 + Sigma_0(cc) + Maj(cc,bb,aa);
234                 j++;
235
236                 T1 = ee + Sigma_1(hh) + Ch(hh,gg,ff) + _K[j] + expandedBuffer[j];
237                 h = aa + T1;
238                 d = T1 + Sigma_0(dd) + Maj(dd,cc,bb);
239                 j++;
240
241                 T1 = ff + Sigma_1(h) + Ch(h,hh,gg) + _K[j] + expandedBuffer[j];
242                 g = bb + T1;
243                 c = T1 + Sigma_0(d) + Maj(d,dd,cc);
244                 j++;
245
246                 T1 = gg + Sigma_1(g) + Ch(g,h,hh) + _K[j] + expandedBuffer[j];
247                 f = cc + T1;
248                 b = T1 + Sigma_0(c) + Maj(c,d,dd);
249                 j++;
250
251                 T1 = hh + Sigma_1(f) + Ch(f,g,h) + _K[j] + expandedBuffer[j];
252                 e = dd + T1;
253                 a = T1 + Sigma_0(b) + Maj(b,c,d);
254                 j++;
255             }
256
257             state[0] += a;
258             state[1] += b;
259             state[2] += c;
260             state[3] += d;
261             state[4] += e;
262             state[5] += f;
263             state[6] += g;
264             state[7] += h;
265         }
266
267         private static UInt32 RotateRight(UInt32 x, int n) {
268             return (((x) >> (n)) | ((x) << (32-(n))));
269         }
270
271         private static UInt32 Ch(UInt32 x, UInt32 y, UInt32 z) {
272             return ((x & y) ^ ((x ^ 0xffffffff) & z));
273         }
274
275         private static UInt32 Maj(UInt32 x, UInt32 y, UInt32 z) {
276             return ((x & y) ^ (x & z) ^ (y & z));
277         }
278
279         private static UInt32 sigma_0(UInt32 x) {
280             return (RotateRight(x,7) ^ RotateRight(x,18) ^ (x >> 3));
281         }
282
283         private static UInt32 sigma_1(UInt32 x) {
284             return (RotateRight(x,17) ^ RotateRight(x,19) ^ (x >> 10));
285         }
286
287         private static UInt32 Sigma_0(UInt32 x) {
288             return (RotateRight(x,2) ^ RotateRight(x,13) ^ RotateRight(x,22));
289         }
290
291         private static UInt32 Sigma_1(UInt32 x) {
292             return (RotateRight(x,6) ^ RotateRight(x,11) ^ RotateRight(x,25));
293         }
294
295         /* This function creates W_16,...,W_63 according to the formula
296            W_j <- sigma_1(W_{j-2}) + W_{j-7} + sigma_0(W_{j-15}) + W_{j-16};
297         */
298
299         [System.Security.SecurityCritical]  // auto-generated
300         private static unsafe void SHA256Expand (uint* x)
301         {
302             for (int i = 16; i < 64; i++) {
303                 x[i] = sigma_1(x[i-2]) + x[i-7] + sigma_0(x[i-15]) + x[i-16];
304             }
305         }
306     }
307 }