Skip to content

Commit 29b9325

Browse files
authored
runtime: Use substitute SHA-1 impl in wasm (#1904)
1 parent be5787e commit 29b9325

File tree

1 file changed

+391
-0
lines changed

1 file changed

+391
-0
lines changed
Lines changed: 391 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,391 @@
1+
From eadad2b4d7d2ef556a2329dd955a2e7f04f56db6 Mon Sep 17 00:00:00 2001
2+
From: Levi Broderick <[email protected]>
3+
Date: Fri, 20 Nov 2020 07:38:00 -0800
4+
Subject: [PATCH] Use substitute SHA-1 implementation in wasm (#44982)
5+
6+
* Use different managed SHA-1 implementation
7+
8+
* Add missing call to Start
9+
---
10+
.../Cryptography/SHAHashProvider.Browser.cs | 328 ++++++++----------
11+
1 file changed, 151 insertions(+), 177 deletions(-)
12+
13+
diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs
14+
index 9fd4ab1a476..4515b8ae75c 100644
15+
--- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs
16+
+++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs
17+
@@ -4,6 +4,7 @@
18+
using System;
19+
using System.IO;
20+
using System.Diagnostics;
21+
+using System.Numerics;
22+
using System.Security.Cryptography;
23+
24+
namespace Internal.Cryptography
25+
@@ -84,213 +85,186 @@ private abstract class SHAManagedImplementationBase
26+
public abstract byte[] HashFinal();
27+
}
28+
29+
- // ported from https://github.com/microsoft/referencesource/blob/a48449cb48a9a693903668a71449ac719b76867c/mscorlib/system/security/cryptography/sha1managed.cs
30+
+ // Ported from src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs.
31+
+ // n.b. It's ok to use a "non-secret purposes" hashing implementation here, as this is only
32+
+ // used in wasm scenarios, and as of the current release we don't make any security guarantees
33+
+ // about our crypto primitives in wasm environments.
34+
private class SHA1ManagedImplementation : SHAManagedImplementationBase
35+
{
36+
- private byte[] _buffer;
37+
- private long _count; // Number of bytes in the hashed message
38+
- private uint[] _stateSHA1;
39+
- private uint[] _expandedBuffer;
40+
+ private Sha1ForNonSecretPurposes _state; // mutable struct - don't make readonly
41+
42+
- public SHA1ManagedImplementation()
43+
+ public override void Initialize()
44+
{
45+
- _stateSHA1 = new uint[5];
46+
- _buffer = new byte[64];
47+
- _expandedBuffer = new uint[80];
48+
-
49+
- InitializeState();
50+
+ _state = default;
51+
+ _state.Start();
52+
}
53+
54+
- public override void Initialize()
55+
+ public override void HashCore(byte[] partIn, int ibStart, int cbSize)
56+
{
57+
- InitializeState();
58+
-
59+
- // Zeroize potentially sensitive information.
60+
- Array.Clear(_buffer, 0, _buffer.Length);
61+
- Array.Clear(_expandedBuffer, 0, _expandedBuffer.Length);
62+
+ _state.Append(partIn.AsSpan(ibStart, cbSize));
63+
}
64+
65+
- private void InitializeState()
66+
+ public override byte[] HashFinal()
67+
{
68+
- _count = 0;
69+
-
70+
- _stateSHA1[0] = 0x67452301;
71+
- _stateSHA1[1] = 0xefcdab89;
72+
- _stateSHA1[2] = 0x98badcfe;
73+
- _stateSHA1[3] = 0x10325476;
74+
- _stateSHA1[4] = 0xc3d2e1f0;
75+
+ byte[] output = new byte[20];
76+
+ _state.Finish(output);
77+
+ return output;
78+
}
79+
80+
- /* Copyright (C) RSA Data Security, Inc. created 1993. This is an
81+
- unpublished work protected as such under copyright law. This work
82+
- contains proprietary, confidential, and trade secret information of
83+
- RSA Data Security, Inc. Use, disclosure or reproduction without the
84+
- express written authorization of RSA Data Security, Inc. is
85+
- prohibited.
86+
- */
87+
-
88+
- /* SHA block update operation. Continues an SHA message-digest
89+
- operation, processing another message block, and updating the
90+
- context.
91+
- */
92+
- public override unsafe void HashCore(byte[] partIn, int ibStart, int cbSize)
93+
+ /// <summary>
94+
+ /// Implements the SHA1 hashing algorithm. Note that this
95+
+ /// implementation is for hashing public information. Do not
96+
+ /// use this code to hash private data, as this implementation does
97+
+ /// not take any steps to avoid information disclosure.
98+
+ /// </summary>
99+
+ private struct Sha1ForNonSecretPurposes
100+
{
101+
- int bufferLen;
102+
- int partInLen = cbSize;
103+
- int partInBase = ibStart;
104+
-
105+
- /* Compute length of buffer */
106+
- bufferLen = (int)(_count & 0x3f);
107+
-
108+
- /* Update number of bytes */
109+
- _count += partInLen;
110+
+ private long length; // Total message length in bits
111+
+ private uint[] w; // Workspace
112+
+ private int pos; // Length of current chunk in bytes
113+
+
114+
+ /// <summary>
115+
+ /// Call Start() to initialize the hash object.
116+
+ /// </summary>
117+
+ public void Start()
118+
+ {
119+
+ this.w ??= new uint[85];
120+
+
121+
+ this.length = 0;
122+
+ this.pos = 0;
123+
+ this.w[80] = 0x67452301;
124+
+ this.w[81] = 0xEFCDAB89;
125+
+ this.w[82] = 0x98BADCFE;
126+
+ this.w[83] = 0x10325476;
127+
+ this.w[84] = 0xC3D2E1F0;
128+
+ }
129+
130+
- fixed (uint* stateSHA1 = _stateSHA1)
131+
+ /// <summary>
132+
+ /// Adds an input byte to the hash.
133+
+ /// </summary>
134+
+ /// <param name="input">Data to include in the hash.</param>
135+
+ public void Append(byte input)
136+
{
137+
- fixed (byte* buffer = _buffer)
138+
+ this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
139+
+ if (64 == ++this.pos)
140+
{
141+
- fixed (uint* expandedBuffer = _expandedBuffer)
142+
- {
143+
- if ((bufferLen > 0) && (bufferLen + partInLen >= 64))
144+
- {
145+
- Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, 64 - bufferLen);
146+
- partInBase += (64 - bufferLen);
147+
- partInLen -= (64 - bufferLen);
148+
- SHATransform(expandedBuffer, stateSHA1, buffer);
149+
- bufferLen = 0;
150+
- }
151+
-
152+
- /* Copy input to temporary buffer and hash */
153+
- while (partInLen >= 64)
154+
- {
155+
- Buffer.BlockCopy(partIn, partInBase, _buffer, 0, 64);
156+
- partInBase += 64;
157+
- partInLen -= 64;
158+
- SHATransform(expandedBuffer, stateSHA1, buffer);
159+
- }
160+
-
161+
- if (partInLen > 0)
162+
- {
163+
- Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, partInLen);
164+
- }
165+
- }
166+
+ this.Drain();
167+
}
168+
}
169+
- }
170+
171+
- /* SHA finalization. Ends an SHA message-digest operation, writing
172+
- the message digest.
173+
- */
174+
- public override byte[] HashFinal()
175+
- {
176+
- byte[] pad;
177+
- int padLen;
178+
- long bitCount;
179+
- byte[] hash = new byte[20];
180+
-
181+
- /* Compute padding: 80 00 00 ... 00 00 <bit count>
182+
- */
183+
-
184+
- padLen = 64 - (int)(_count & 0x3f);
185+
- if (padLen <= 8)
186+
- padLen += 64;
187+
-
188+
- pad = new byte[padLen];
189+
- pad[0] = 0x80;
190+
-
191+
- // Convert count to bit count
192+
- bitCount = _count * 8;
193+
-
194+
- pad[padLen - 8] = (byte)((bitCount >> 56) & 0xff);
195+
- pad[padLen - 7] = (byte)((bitCount >> 48) & 0xff);
196+
- pad[padLen - 6] = (byte)((bitCount >> 40) & 0xff);
197+
- pad[padLen - 5] = (byte)((bitCount >> 32) & 0xff);
198+
- pad[padLen - 4] = (byte)((bitCount >> 24) & 0xff);
199+
- pad[padLen - 3] = (byte)((bitCount >> 16) & 0xff);
200+
- pad[padLen - 2] = (byte)((bitCount >> 8) & 0xff);
201+
- pad[padLen - 1] = (byte)((bitCount >> 0) & 0xff);
202+
-
203+
- /* Digest padding */
204+
- HashCore(pad, 0, pad.Length);
205+
-
206+
- /* Store digest */
207+
- SHAUtils.DWORDToBigEndian(hash, _stateSHA1, 5);
208+
-
209+
- return hash;
210+
- }
211+
-
212+
- private unsafe void SHATransform(uint* expandedBuffer, uint* state, byte* block)
213+
- {
214+
- uint a = state[0];
215+
- uint b = state[1];
216+
- uint c = state[2];
217+
- uint d = state[3];
218+
- uint e = state[4];
219+
-
220+
- int i;
221+
-
222+
- SHAUtils.DWORDFromBigEndian(expandedBuffer, 16, block);
223+
- SHAExpand(expandedBuffer);
224+
-
225+
- /* Round 1 */
226+
- for (i = 0; i < 20; i += 5)
227+
+ /// <summary>
228+
+ /// Adds input bytes to the hash.
229+
+ /// </summary>
230+
+ /// <param name="input">
231+
+ /// Data to include in the hash. Must not be null.
232+
+ /// </param>
233+
+ public void Append(ReadOnlySpan<byte> input)
234+
{
235+
- { (e) += (((((a)) << (5)) | (((a)) >> (32 - (5)))) + ((d) ^ ((b) & ((c) ^ (d)))) + (expandedBuffer[i]) + 0x5a827999); (b) = ((((b)) << (30)) | (((b)) >> (32 - (30)))); }
236+
- { (d) += (((((e)) << (5)) | (((e)) >> (32 - (5)))) + ((c) ^ ((a) & ((b) ^ (c)))) + (expandedBuffer[i + 1]) + 0x5a827999); (a) = ((((a)) << (30)) | (((a)) >> (32 - (30)))); }
237+
- { (c) += (((((d)) << (5)) | (((d)) >> (32 - (5)))) + ((b) ^ ((e) & ((a) ^ (b)))) + (expandedBuffer[i + 2]) + 0x5a827999); (e) = ((((e)) << (30)) | (((e)) >> (32 - (30)))); }; ;
238+
- { (b) += (((((c)) << (5)) | (((c)) >> (32 - (5)))) + ((a) ^ ((d) & ((e) ^ (a)))) + (expandedBuffer[i + 3]) + 0x5a827999); (d) = ((((d)) << (30)) | (((d)) >> (32 - (30)))); }; ;
239+
- { (a) += (((((b)) << (5)) | (((b)) >> (32 - (5)))) + ((e) ^ ((c) & ((d) ^ (e)))) + (expandedBuffer[i + 4]) + 0x5a827999); (c) = ((((c)) << (30)) | (((c)) >> (32 - (30)))); }; ;
240+
+ foreach (byte b in input)
241+
+ {
242+
+ this.Append(b);
243+
+ }
244+
}
245+
246+
- /* Round 2 */
247+
- for (; i < 40; i += 5)
248+
+ /// <summary>
249+
+ /// Retrieves the hash value.
250+
+ /// Note that after calling this function, the hash object should
251+
+ /// be considered uninitialized. Subsequent calls to Append or
252+
+ /// Finish will produce useless results. Call Start() to
253+
+ /// reinitialize.
254+
+ /// </summary>
255+
+ /// <param name="output">
256+
+ /// Buffer to receive the hash value. Must not be null.
257+
+ /// Up to 20 bytes of hash will be written to the output buffer.
258+
+ /// If the buffer is smaller than 20 bytes, the remaining hash
259+
+ /// bytes will be lost. If the buffer is larger than 20 bytes, the
260+
+ /// rest of the buffer is left unmodified.
261+
+ /// </param>
262+
+ public void Finish(byte[] output)
263+
{
264+
- { (e) += (((((a)) << (5)) | (((a)) >> (32 - (5)))) + ((b) ^ (c) ^ (d)) + (expandedBuffer[i]) + 0x6ed9eba1); (b) = ((((b)) << (30)) | (((b)) >> (32 - (30)))); }; ;
265+
- { (d) += (((((e)) << (5)) | (((e)) >> (32 - (5)))) + ((a) ^ (b) ^ (c)) + (expandedBuffer[i + 1]) + 0x6ed9eba1); (a) = ((((a)) << (30)) | (((a)) >> (32 - (30)))); }; ;
266+
- { (c) += (((((d)) << (5)) | (((d)) >> (32 - (5)))) + ((e) ^ (a) ^ (b)) + (expandedBuffer[i + 2]) + 0x6ed9eba1); (e) = ((((e)) << (30)) | (((e)) >> (32 - (30)))); }; ;
267+
- { (b) += (((((c)) << (5)) | (((c)) >> (32 - (5)))) + ((d) ^ (e) ^ (a)) + (expandedBuffer[i + 3]) + 0x6ed9eba1); (d) = ((((d)) << (30)) | (((d)) >> (32 - (30)))); }; ;
268+
- { (a) += (((((b)) << (5)) | (((b)) >> (32 - (5)))) + ((c) ^ (d) ^ (e)) + (expandedBuffer[i + 4]) + 0x6ed9eba1); (c) = ((((c)) << (30)) | (((c)) >> (32 - (30)))); }; ;
269+
- }
270+
+ long l = this.length + 8 * this.pos;
271+
+ this.Append(0x80);
272+
+ while (this.pos != 56)
273+
+ {
274+
+ this.Append(0x00);
275+
+ }
276+
277+
- /* Round 3 */
278+
- for (; i < 60; i += 5)
279+
- {
280+
- { (e) += (((((a)) << (5)) | (((a)) >> (32 - (5)))) + (((b) & (c)) | ((d) & ((b) | (c)))) + (expandedBuffer[i]) + 0x8f1bbcdc); (b) = ((((b)) << (30)) | (((b)) >> (32 - (30)))); }; ;
281+
- { (d) += (((((e)) << (5)) | (((e)) >> (32 - (5)))) + (((a) & (b)) | ((c) & ((a) | (b)))) + (expandedBuffer[i + 1]) + 0x8f1bbcdc); (a) = ((((a)) << (30)) | (((a)) >> (32 - (30)))); }; ;
282+
- { (c) += (((((d)) << (5)) | (((d)) >> (32 - (5)))) + (((e) & (a)) | ((b) & ((e) | (a)))) + (expandedBuffer[i + 2]) + 0x8f1bbcdc); (e) = ((((e)) << (30)) | (((e)) >> (32 - (30)))); }; ;
283+
- { (b) += (((((c)) << (5)) | (((c)) >> (32 - (5)))) + (((d) & (e)) | ((a) & ((d) | (e)))) + (expandedBuffer[i + 3]) + 0x8f1bbcdc); (d) = ((((d)) << (30)) | (((d)) >> (32 - (30)))); }; ;
284+
- { (a) += (((((b)) << (5)) | (((b)) >> (32 - (5)))) + (((c) & (d)) | ((e) & ((c) | (d)))) + (expandedBuffer[i + 4]) + 0x8f1bbcdc); (c) = ((((c)) << (30)) | (((c)) >> (32 - (30)))); }; ;
285+
+ unchecked
286+
+ {
287+
+ this.Append((byte)(l >> 56));
288+
+ this.Append((byte)(l >> 48));
289+
+ this.Append((byte)(l >> 40));
290+
+ this.Append((byte)(l >> 32));
291+
+ this.Append((byte)(l >> 24));
292+
+ this.Append((byte)(l >> 16));
293+
+ this.Append((byte)(l >> 8));
294+
+ this.Append((byte)l);
295+
+
296+
+ int end = output.Length < 20 ? output.Length : 20;
297+
+ for (int i = 0; i != end; i++)
298+
+ {
299+
+ uint temp = this.w[80 + i / 4];
300+
+ output[i] = (byte)(temp >> 24);
301+
+ this.w[80 + i / 4] = temp << 8;
302+
+ }
303+
+ }
304+
}
305+
306+
- /* Round 4 */
307+
- for (; i < 80; i += 5)
308+
+ /// <summary>
309+
+ /// Called when this.pos reaches 64.
310+
+ /// </summary>
311+
+ private void Drain()
312+
{
313+
- { (e) += (((((a)) << (5)) | (((a)) >> (32 - (5)))) + ((b) ^ (c) ^ (d)) + (expandedBuffer[i]) + 0xca62c1d6); (b) = ((((b)) << (30)) | (((b)) >> (32 - (30)))); }; ;
314+
- { (d) += (((((e)) << (5)) | (((e)) >> (32 - (5)))) + ((a) ^ (b) ^ (c)) + (expandedBuffer[i + 1]) + 0xca62c1d6); (a) = ((((a)) << (30)) | (((a)) >> (32 - (30)))); }; ;
315+
- { (c) += (((((d)) << (5)) | (((d)) >> (32 - (5)))) + ((e) ^ (a) ^ (b)) + (expandedBuffer[i + 2]) + 0xca62c1d6); (e) = ((((e)) << (30)) | (((e)) >> (32 - (30)))); }; ;
316+
- { (b) += (((((c)) << (5)) | (((c)) >> (32 - (5)))) + ((d) ^ (e) ^ (a)) + (expandedBuffer[i + 3]) + 0xca62c1d6); (d) = ((((d)) << (30)) | (((d)) >> (32 - (30)))); }; ;
317+
- { (a) += (((((b)) << (5)) | (((b)) >> (32 - (5)))) + ((c) ^ (d) ^ (e)) + (expandedBuffer[i + 4]) + 0xca62c1d6); (c) = ((((c)) << (30)) | (((c)) >> (32 - (30)))); }; ;
318+
- }
319+
+ for (int i = 16; i != 80; i++)
320+
+ {
321+
+ this.w[i] = BitOperations.RotateLeft(this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16], 1);
322+
+ }
323+
324+
- state[0] += a;
325+
- state[1] += b;
326+
- state[2] += c;
327+
- state[3] += d;
328+
- state[4] += e;
329+
- }
330+
+ unchecked
331+
+ {
332+
+ uint a = this.w[80];
333+
+ uint b = this.w[81];
334+
+ uint c = this.w[82];
335+
+ uint d = this.w[83];
336+
+ uint e = this.w[84];
337+
338+
- /* Expands x[0..15] into x[16..79], according to the recurrence
339+
- x[i] = x[i-3] ^ x[i-8] ^ x[i-14] ^ x[i-16].
340+
- */
341+
- private unsafe void SHAExpand(uint* x)
342+
- {
343+
- int i;
344+
- uint tmp;
345+
+ for (int i = 0; i != 20; i++)
346+
+ {
347+
+ const uint k = 0x5A827999;
348+
+ uint f = (b & c) | ((~b) & d);
349+
+ uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
350+
+ }
351+
352+
- for (i = 16; i < 80; i++)
353+
- {
354+
- tmp = (x[i - 3] ^ x[i - 8] ^ x[i - 14] ^ x[i - 16]);
355+
- x[i] = ((tmp << 1) | (tmp >> 31));
356+
+ for (int i = 20; i != 40; i++)
357+
+ {
358+
+ uint f = b ^ c ^ d;
359+
+ const uint k = 0x6ED9EBA1;
360+
+ uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
361+
+ }
362+
+
363+
+ for (int i = 40; i != 60; i++)
364+
+ {
365+
+ uint f = (b & c) | (b & d) | (c & d);
366+
+ const uint k = 0x8F1BBCDC;
367+
+ uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
368+
+ }
369+
+
370+
+ for (int i = 60; i != 80; i++)
371+
+ {
372+
+ uint f = b ^ c ^ d;
373+
+ const uint k = 0xCA62C1D6;
374+
+ uint temp = BitOperations.RotateLeft(a, 5) + f + e + k + this.w[i]; e = d; d = c; c = BitOperations.RotateLeft(b, 30); b = a; a = temp;
375+
+ }
376+
+
377+
+ this.w[80] += a;
378+
+ this.w[81] += b;
379+
+ this.w[82] += c;
380+
+ this.w[83] += d;
381+
+ this.w[84] += e;
382+
+ }
383+
+
384+
+ this.length += 512; // 64 bytes == 512 bits
385+
+ this.pos = 0;
386+
}
387+
}
388+
}
389+
--
390+
2.25.2
391+

0 commit comments

Comments
 (0)