Merge pull request #3389 from lambdageek/bug-43099
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / sha512managed.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>[....]</OWNER>
7 // 
8
9 //
10 // SHA512Managed.cs
11 //
12 // C# implementation of the proposed SHA-512 hash algorithm
13 //
14
15 namespace System.Security.Cryptography {
16     using System;
17     using System.Diagnostics.Contracts;
18
19     [System.Runtime.InteropServices.ComVisible(true)]
20     public class SHA512Managed : SHA512
21     {
22         private byte[]   _buffer;
23         private ulong    _count; // Number of bytes in the hashed message
24         private UInt64[] _stateSHA512;
25         private UInt64[] _W;
26
27         //
28         // public constructors
29         //
30
31         public SHA512Managed()
32         {
33             if (CryptoConfig.AllowOnlyFipsAlgorithms)
34                 throw new InvalidOperationException(Environment.GetResourceString("Cryptography_NonCompliantFIPSAlgorithm"));
35             Contract.EndContractBlock();
36
37             _stateSHA512 = new UInt64[8];
38             _buffer = new byte[128];
39             _W = new UInt64[80];
40
41             InitializeState();
42         }
43
44         //
45         // public methods
46         //
47
48         public override void Initialize() {
49             InitializeState();
50
51             // Zeroize potentially sensitive information.
52             Array.Clear(_buffer, 0, _buffer.Length);
53             Array.Clear(_W, 0, _W.Length);
54         }
55
56         [System.Security.SecuritySafeCritical]  // auto-generated
57         protected override void HashCore(byte[] rgb, int ibStart, int cbSize) {
58             _HashData(rgb, ibStart, cbSize);
59         }
60
61         [System.Security.SecuritySafeCritical]  // auto-generated
62         protected override byte[] HashFinal() {
63             return _EndHash();
64         }
65
66         //
67         // private methods
68         //
69
70         private void InitializeState() {
71             _count = 0;
72
73             _stateSHA512[0] = 0x6a09e667f3bcc908;
74             _stateSHA512[1] = 0xbb67ae8584caa73b;
75             _stateSHA512[2] = 0x3c6ef372fe94f82b;
76             _stateSHA512[3] = 0xa54ff53a5f1d36f1;
77             _stateSHA512[4] = 0x510e527fade682d1;
78             _stateSHA512[5] = 0x9b05688c2b3e6c1f;
79             _stateSHA512[6] = 0x1f83d9abfb41bd6b;
80             _stateSHA512[7] = 0x5be0cd19137e2179;
81         }
82
83         /* SHA512 block update operation. Continues an SHA message-digest
84            operation, processing another message block, and updating the
85            context.
86            */
87
88         [System.Security.SecurityCritical]  // auto-generated
89         private unsafe void _HashData(byte[] partIn, int ibStart, int cbSize)
90         {
91             int bufferLen;
92             int partInLen = cbSize;
93             int partInBase = ibStart;
94
95             /* Compute length of buffer */
96             bufferLen = (int) (_count & 0x7f);
97
98             /* Update number of bytes */
99             _count += (ulong) partInLen;
100
101             fixed (UInt64* stateSHA512 = _stateSHA512) {
102                 fixed (byte* buffer = _buffer) {
103                     fixed (UInt64* expandedBuffer = _W) {
104                         if ((bufferLen > 0) && (bufferLen + partInLen >= 128)) {
105                             Buffer.InternalBlockCopy(partIn, partInBase, _buffer, bufferLen, 128 - bufferLen);
106                             partInBase += (128 - bufferLen);
107                             partInLen -= (128 - bufferLen);
108                             SHATransform(expandedBuffer, stateSHA512, buffer);
109                             bufferLen = 0;
110                         }
111
112                         /* Copy input to temporary buffer and hash */
113                         while (partInLen >= 128) {
114                             Buffer.InternalBlockCopy(partIn, partInBase, _buffer, 0, 128);
115                             partInBase += 128;
116                             partInLen -= 128;
117                             SHATransform(expandedBuffer, stateSHA512, buffer);
118                         }
119
120                         if (partInLen > 0) {
121                             Buffer.InternalBlockCopy(partIn, partInBase, _buffer, bufferLen, partInLen);
122                         }
123                     }
124                 }
125             }
126         }
127
128         /* SHA512 finalization. Ends an SHA512 message-digest operation, writing
129            the message digest.
130            */
131
132         [System.Security.SecurityCritical]  // auto-generated
133         private byte[] _EndHash()
134         {
135             byte[]         pad;
136             int            padLen;
137             ulong          bitCount;
138             byte[]         hash = new byte[64]; // HashSizeValue = 512
139
140             /* Compute padding: 80 00 00 ... 00 00 <bit count>
141              */
142
143             padLen = 128 - (int)(_count & 0x7f);
144             if (padLen <= 16)
145                 padLen += 128;
146
147             pad = new byte[padLen];
148             pad[0] = 0x80;
149
150             //  Convert count to bit count
151             bitCount = _count * 8;
152
153             // If we ever have UInt128 for bitCount, then these need to be uncommented.
154             // Note that C# only looks at the low 6 bits of the shift value for ulongs,
155             // so >>0 and >>64 are equal!
156
157             //pad[padLen-16] = (byte) ((bitCount >> 120) & 0xff);
158             //pad[padLen-15] = (byte) ((bitCount >> 112) & 0xff);
159             //pad[padLen-14] = (byte) ((bitCount >> 104) & 0xff);
160             //pad[padLen-13] = (byte) ((bitCount >> 96) & 0xff);
161             //pad[padLen-12] = (byte) ((bitCount >> 88) & 0xff);
162             //pad[padLen-11] = (byte) ((bitCount >> 80) & 0xff);
163             //pad[padLen-10] = (byte) ((bitCount >> 72) & 0xff);
164             //pad[padLen-9] = (byte) ((bitCount >> 64) & 0xff);
165             pad[padLen-8] = (byte) ((bitCount >> 56) & 0xff);
166             pad[padLen-7] = (byte) ((bitCount >> 48) & 0xff);
167             pad[padLen-6] = (byte) ((bitCount >> 40) & 0xff);
168             pad[padLen-5] = (byte) ((bitCount >> 32) & 0xff);
169             pad[padLen-4] = (byte) ((bitCount >> 24) & 0xff);
170             pad[padLen-3] = (byte) ((bitCount >> 16) & 0xff);
171             pad[padLen-2] = (byte) ((bitCount >> 8) & 0xff);
172             pad[padLen-1] = (byte) ((bitCount >> 0) & 0xff);
173
174             /* Digest padding */
175             _HashData(pad, 0, pad.Length);
176
177             /* Store digest */
178             Utils.QuadWordToBigEndian (hash, _stateSHA512, 8);
179
180             HashValue = hash;
181             return hash;
182         }
183
184         private readonly static UInt64[] _K = {
185             0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
186             0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
187             0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
188             0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
189             0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
190             0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
191             0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
192             0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
193             0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
194             0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
195             0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
196             0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
197             0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
198             0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
199             0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
200             0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
201             0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
202             0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
203             0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
204             0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
205         };
206
207         [System.Security.SecurityCritical]  // auto-generated
208         private static unsafe void SHATransform (UInt64* expandedBuffer, UInt64* state, byte* block)
209         {
210             UInt64 a, b, c, d, e, f, g, h;
211             UInt64 aa, bb, cc, dd, ee, ff, hh, gg;
212             UInt64 T1;
213
214             a = state[0];
215             b = state[1];
216             c = state[2];
217             d = state[3];
218             e = state[4];
219             f = state[5];
220             g = state[6];
221             h = state[7];
222
223             // fill in the first 16 blocks of W.
224             Utils.QuadWordFromBigEndian (expandedBuffer, 16, block);
225             SHA512Expand (expandedBuffer);
226
227             /* Apply the SHA512 compression function */
228             // We are trying to be smart here and avoid as many copies as we can
229             // The perf gain with this method over the straightforward modify and shift 
230             // forward is >= 20%, so it's worth the pain
231             for (int j=0; j<80; ) {
232                 T1 = h + Sigma_1(e) + Ch(e,f,g) + _K[j] + expandedBuffer[j];
233                 ee = d + T1;
234                 aa = T1 + Sigma_0(a) + Maj(a,b,c);
235                 j++;
236
237                 T1 = g + Sigma_1(ee) + Ch(ee,e,f) + _K[j] + expandedBuffer[j];
238                 ff = c + T1;
239                 bb = T1 + Sigma_0(aa) + Maj(aa,a,b);
240                 j++;
241
242                 T1 = f + Sigma_1(ff) + Ch(ff,ee,e) + _K[j] + expandedBuffer[j];
243                 gg = b + T1;
244                 cc = T1 + Sigma_0(bb) + Maj(bb,aa,a);
245                 j++;
246
247                 T1 = e + Sigma_1(gg) + Ch(gg,ff,ee) + _K[j] + expandedBuffer[j];
248                 hh = a + T1;
249                 dd = T1 + Sigma_0(cc) + Maj(cc,bb,aa);
250                 j++;
251
252                 T1 = ee + Sigma_1(hh) + Ch(hh,gg,ff) + _K[j] + expandedBuffer[j];
253                 h = aa + T1;
254                 d = T1 + Sigma_0(dd) + Maj(dd,cc,bb);
255                 j++;
256
257                 T1 = ff + Sigma_1(h) + Ch(h,hh,gg) + _K[j] + expandedBuffer[j];
258                 g = bb + T1;
259                 c = T1 + Sigma_0(d) + Maj(d,dd,cc);
260                 j++;
261
262                 T1 = gg + Sigma_1(g) + Ch(g,h,hh) + _K[j] + expandedBuffer[j];
263                 f = cc + T1;
264                 b = T1 + Sigma_0(c) + Maj(c,d,dd);
265                 j++;
266
267                 T1 = hh + Sigma_1(f) + Ch(f,g,h) + _K[j] + expandedBuffer[j];
268                 e = dd + T1;
269                 a = T1 + Sigma_0(b) + Maj(b,c,d);
270                 j++;
271             }
272
273             state[0] += a;
274             state[1] += b;
275             state[2] += c;
276             state[3] += d;
277             state[4] += e;
278             state[5] += f;
279             state[6] += g;
280             state[7] += h;
281         }
282
283         private static UInt64 RotateRight(UInt64 x, int n) {
284             return (((x) >> (n)) | ((x) << (64-(n))));
285         }
286
287         private static UInt64 Ch(UInt64 x, UInt64 y, UInt64 z) {
288             return ((x & y) ^ ((x ^ 0xffffffffffffffff) & z));
289         }
290
291         private static UInt64 Maj(UInt64 x, UInt64 y, UInt64 z) {
292             return ((x & y) ^ (x & z) ^ (y & z));
293         }
294
295         private static UInt64 Sigma_0(UInt64 x) {
296             return (RotateRight(x,28) ^ RotateRight(x,34) ^ RotateRight(x,39));
297         }
298
299         private static UInt64 Sigma_1(UInt64 x) {
300             return (RotateRight(x,14) ^ RotateRight(x,18) ^ RotateRight(x,41));
301         }
302
303         private static UInt64 sigma_0(UInt64 x) {
304             return (RotateRight(x,1) ^ RotateRight(x,8) ^ (x >> 7));
305         }
306
307         private static UInt64 sigma_1(UInt64 x) {
308             return (RotateRight(x,19) ^ RotateRight(x,61) ^ (x >> 6));
309         }
310
311         /* This function creates W_16,...,W_79 according to the formula
312            W_j <- sigma_1(W_{j-2}) + W_{j-7} + sigma_0(W_{j-15}) + W_{j-16};
313         */
314
315         [System.Security.SecurityCritical]  // auto-generated
316         private static unsafe void SHA512Expand (UInt64* x)
317         {
318             for (int i = 16; i < 80; i++) {
319                 x[i] = sigma_1(x[i-2]) + x[i-7] + sigma_0(x[i-15]) + x[i-16];
320             }
321         }
322     }
323 }