using System;
using System.IO;
using System.Diagnostics;
+using System.Numerics;
using System.Security.Cryptography;
namespace Internal.Cryptography
public abstract byte[] HashFinal();
}
- // ported from https://github.com/microsoft/referencesource/blob/a48449cb48a9a693903668a71449ac719b76867c/mscorlib/system/security/cryptography/sha1managed.cs
+ // Ported from src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs.
+ // n.b. It's ok to use a "non-secret purposes" hashing implementation here, as this is only
+ // used in wasm scenarios, and as of the current release we don't make any security guarantees
+ // about our crypto primitives in wasm environments.
private class SHA1ManagedImplementation : SHAManagedImplementationBase
{
- private byte[] _buffer;
- private long _count; // Number of bytes in the hashed message
- private uint[] _stateSHA1;
- private uint[] _expandedBuffer;
+ private Sha1ForNonSecretPurposes _state; // mutable struct - don't make readonly
- public SHA1ManagedImplementation()
+ public override void Initialize()
{
- _stateSHA1 = new uint[5];
- _buffer = new byte[64];
- _expandedBuffer = new uint[80];
-
- InitializeState();
+ _state = default;
+ _state.Start();
}
- public override void Initialize()
+ public override void HashCore(byte[] partIn, int ibStart, int cbSize)
{
- InitializeState();
-
- // Zeroize potentially sensitive information.
- Array.Clear(_buffer, 0, _buffer.Length);
- Array.Clear(_expandedBuffer, 0, _expandedBuffer.Length);
+ _state.Append(partIn.AsSpan(ibStart, cbSize));
}
- private void InitializeState()
+ public override byte[] HashFinal()
{
- _count = 0;
-
- _stateSHA1[0] = 0x67452301;
- _stateSHA1[1] = 0xefcdab89;
- _stateSHA1[2] = 0x98badcfe;
- _stateSHA1[3] = 0x10325476;
- _stateSHA1[4] = 0xc3d2e1f0;
+ byte[] output = new byte[20];
+ _state.Finish(output);
+ return output;
}
- /* Copyright (C) RSA Data Security, Inc. created 1993. This is an
- unpublished work protected as such under copyright law. This work
- contains proprietary, confidential, and trade secret information of
- RSA Data Security, Inc. Use, disclosure or reproduction without the
- express written authorization of RSA Data Security, Inc. is
- prohibited.
- */
-
- /* SHA block update operation. Continues an SHA message-digest
- operation, processing another message block, and updating the
- context.
- */
- public override unsafe void HashCore(byte[] partIn, int ibStart, int cbSize)
+ /// <summary>
+ /// Implements the SHA1 hashing algorithm. Note that this
+ /// implementation is for hashing public information. Do not
+ /// use this code to hash private data, as this implementation does
+ /// not take any steps to avoid information disclosure.
+ /// </summary>
+ private struct Sha1ForNonSecretPurposes
{
- int bufferLen;
- int partInLen = cbSize;
- int partInBase = ibStart;
-
- /* Compute length of buffer */
- bufferLen = (int)(_count & 0x3f);
-
- /* Update number of bytes */
- _count += partInLen;
+ private long length; // Total message length in bits
+ private uint[] w; // Workspace
+ private int pos; // Length of current chunk in bytes
+
+ /// <summary>
+ /// Call Start() to initialize the hash object.
+ /// </summary>
+ public void Start()
+ {
+ this.w ??= new uint[85];
+
+ this.length = 0;
+ this.pos = 0;
+ this.w[80] = 0x67452301;
+ this.w[81] = 0xEFCDAB89;
+ this.w[82] = 0x98BADCFE;
+ this.w[83] = 0x10325476;
+ this.w[84] = 0xC3D2E1F0;
+ }
- fixed (uint* stateSHA1 = _stateSHA1)
+ /// <summary>
+ /// Adds an input byte to the hash.
+ /// </summary>
+ /// <param name="input">Data to include in the hash.</param>
+ public void Append(byte input)
{
- fixed (byte* buffer = _buffer)
+ this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
+ if (64 == ++this.pos)
{
- fixed (uint* expandedBuffer = _expandedBuffer)
- {
- if ((bufferLen > 0) && (bufferLen + partInLen >= 64))
- {
- Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, 64 - bufferLen);
- partInBase += (64 - bufferLen);
- partInLen -= (64 - bufferLen);
- SHATransform(expandedBuffer, stateSHA1, buffer);
- bufferLen = 0;
- }
-
- /* Copy input to temporary buffer and hash */
- while (partInLen >= 64)
- {
- Buffer.BlockCopy(partIn, partInBase, _buffer, 0, 64);
- partInBase += 64;
- partInLen -= 64;
- SHATransform(expandedBuffer, stateSHA1, buffer);
- }
-
- if (partInLen > 0)
- {
- Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, partInLen);
- }
- }
+ this.Drain();
}
}
- }
- /* SHA finalization. Ends an SHA message-digest operation, writing
- the message digest.
- */
- public override byte[] HashFinal()
- {
- byte[] pad;
- int padLen;
- long bitCount;
- byte[] hash = new byte[20];
-
- /* Compute padding: 80 00 00 ... 00 00 <bit count>
- */
-
- padLen = 64 - (int)(_count & 0x3f);
- if (padLen <= 8)
- padLen += 64;
-
- pad = new byte[padLen];
- pad[0] = 0x80;
-
- // Convert count to bit count
- bitCount = _count * 8;
-
- pad[padLen - 8] = (byte)((bitCount >> 56) & 0xff);
- pad[padLen - 7] = (byte)((bitCount >> 48) & 0xff);
- pad[padLen - 6] = (byte)((bitCount >> 40) & 0xff);
- pad[padLen - 5] = (byte)((bitCount >> 32) & 0xff);
- pad[padLen - 4] = (byte)((bitCount >> 24) & 0xff);
- pad[padLen - 3] = (byte)((bitCount >> 16) & 0xff);
- pad[padLen - 2] = (byte)((bitCount >> 8) & 0xff);
- pad[padLen - 1] = (byte)((bitCount >> 0) & 0xff);
-
- /* Digest padding */
- HashCore(pad, 0, pad.Length);
-
- /* Store digest */
- SHAUtils.DWORDToBigEndian(hash, _stateSHA1, 5);
-
- return hash;
- }
-
- private unsafe void SHATransform(uint* expandedBuffer, uint* state, byte* block)
- {
- uint a = state[0];
- uint b = state[1];
- uint c = state[2];
- uint d = state[3];
- uint e = state[4];
-
- int i;
-
- SHAUtils.DWORDFromBigEndian(expandedBuffer, 16, block);
- SHAExpand(expandedBuffer);
-
- /* Round 1 */
- for (i = 0; i < 20; i += 5)
+ /// <summary>
+ /// Adds input bytes to the hash.
+ /// </summary>
+ /// <param name="input">
+ /// Data to include in the hash. Must not be null.
+ /// </param>
+ public void Append(ReadOnlySpan<byte> input)
{
- { (e) += (((((a)) << (5)) | (((a)) >> (32 - (5)))) + ((d) ^ ((b) & ((c) ^ (d)))) + (expandedBuffer[i]) + 0x5a827999); (b) = ((((b)) << (30)) | (((b)) >> (32 - (30)))); }
- { (d) += (((((e)) << (5)) | (((e)) >> (32 - (5)))) + ((c) ^ ((a) & ((b) ^ (c)))) + (expandedBuffer[i + 1]) + 0x5a827999); (a) = ((((a)) << (30)) | (((a)) >> (32 - (30)))); }
- { (c) += (((((d)) << (5)) | (((d)) >> (32 - (5)))) + ((b) ^ ((e) & ((a) ^ (b)))) + (expandedBuffer[i + 2]) + 0x5a827999); (e) = ((((e)) << (30)) | (((e)) >> (32 - (30)))); }; ;
- { (b) += (((((c)) << (5)) | (((c)) >> (32 - (5)))) + ((a) ^ ((d) & ((e) ^ (a)))) + (expandedBuffer[i + 3]) + 0x5a827999); (d) = ((((d)) << (30)) | (((d)) >> (32 - (30)))); }; ;
- { (a) += (((((b)) << (5)) | (((b)) >> (32 - (5)))) + ((e) ^ ((c) & ((d) ^ (e)))) + (expandedBuffer[i + 4]) + 0x5a827999); (c) = ((((c)) << (30)) | (((c)) >> (32 - (30)))); }; ;
+ foreach (byte b in input)
+ {
+ this.Append(b);
+ }
}
- /* Round 2 */
- for (; i < 40; i += 5)
+ /// <summary>
+ /// Retrieves the hash value.
+ /// Note that after calling this function, the hash object should
+ /// be considered uninitialized. Subsequent calls to Append or
+ /// Finish will produce useless results. Call Start() to
+ /// reinitialize.
+ /// </summary>
+ /// <param name="output">
+ /// Buffer to receive the hash value. Must not be null.
+ /// Up to 20 bytes of hash will be written to the output buffer.
+ /// If the buffer is smaller than 20 bytes, the remaining hash
+ /// bytes will be lost. If the buffer is larger than 20 bytes, the
+ /// rest of the buffer is left unmodified.
+ /// </param>
+ public void Finish(byte[] output)
{
- { (e) += (((((a)) << (5)) | (((a)) >> (32 - (5)))) + ((b) ^ (c) ^ (d)) + (expandedBuffer[i]) + 0x6ed9eba1); (b) = ((((b)) << (30)) | (((b)) >> (32 - (30)))); }; ;
- { (d) += (((((e)) << (5)) | (((e)) >> (32 - (5)))) + ((a) ^ (b) ^ (c)) + (expandedBuffer[i + 1]) + 0x6ed9eba1); (a) = ((((a)) << (30)) | (((a)) >> (32 - (30)))); }; ;
- { (c) += (((((d)) << (5)) | (((d)) >> (32 - (5)))) + ((e) ^ (a) ^ (b)) + (expandedBuffer[i + 2]) + 0x6ed9eba1); (e) = ((((e)) << (30)) | (((e)) >> (32 - (30)))); }; ;
- { (b) += (((((c)) << (5)) | (((c)) >> (32 - (5)))) + ((d) ^ (e) ^ (a)) + (expandedBuffer[i + 3]) + 0x6ed9eba1); (d) = ((((d)) << (30)) | (((d)) >> (32 - (30)))); }; ;
- { (a) += (((((b)) << (5)) | (((b)) >> (32 - (5)))) + ((c) ^ (d) ^ (e)) + (expandedBuffer[i + 4]) + 0x6ed9eba1); (c) = ((((c)) << (30)) | (((c)) >> (32 - (30)))); }; ;
- }
+ long l = this.length + 8 * this.pos;
+ this.Append(0x80);
+ while (this.pos != 56)
+ {
+ this.Append(0x00);
+ }
- /* Round 3 */
- for (; i < 60; i += 5)
- {
- { (e) += (((((a)) << (5)) | (((a)) >> (32 - (5)))) + (((b) & (c)) | ((d) & ((b) | (c)))) + (expandedBuffer[i]) + 0x8f1bbcdc); (b) = ((((b)) << (30)) | (((b)) >> (32 - (30)))); }; ;
- { (d) += (((((e)) << (5)) | (((e)) >> (32 - (5)))) + (((a) & (b)) | ((c) & ((a) | (b)))) + (expandedBuffer[i + 1]) + 0x8f1bbcdc); (a) = ((((a)) << (30)) | (((a)) >> (32 - (30)))); }; ;
- { (c) += (((((d)) << (5)) | (((d)) >> (32 - (5)))) + (((e) & (a)) | ((b) & ((e) | (a)))) + (expandedBuffer[i + 2]) + 0x8f1bbcdc); (e) = ((((e)) << (30)) | (((e)) >> (32 - (30)))); }; ;
- { (b) += (((((c)) << (5)) | (((c)) >> (32 - (5)))) + (((d) & (e)) | ((a) & ((d) | (e)))) + (expandedBuffer[i + 3]) + 0x8f1bbcdc); (d) = ((((d)) << (30)) | (((d)) >> (32 - (30)))); }; ;
- { (a) += (((((b)) << (5)) | (((b)) >> (32 - (5)))) + (((c) & (d)) | ((e) & ((c) | (d)))) + (expandedBuffer[i + 4]) + 0x8f1bbcdc); (c) = ((((c)) << (30)) | (((c)) >> (32 - (30)))); }; ;
+ unchecked
+ {
+ this.Append((byte)(l >> 56));
+ this.Append((byte)(l >> 48));
+ this.Append((byte)(l >> 40));
+ this.Append((byte)(l >> 32));
+ this.Append((byte)(l >> 24));
+ this.Append((byte)(l >> 16));
+ this.Append((byte)(l >> 8));
+ this.Append((byte)l);
+
+ int end = output.Length < 20 ? output.Length : 20;
+ for (int i = 0; i != end; i++)
+ {
+ uint temp = this.w[80 + i / 4];
+ output[i] = (byte)(temp >> 24);
+ this.w[80 + i / 4] = temp << 8;
+ }
+ }
}
- /* Round 4 */
- for (; i < 80; i += 5)
+ /// <summary>
+ /// Called when this.pos reaches 64.
+ /// </summary>
+ private void Drain()
{
- { (e) += (((((a)) << (5)) | (((a)) >> (32 - (5)))) + ((b) ^ (c) ^ (d)) + (expandedBuffer[i]) + 0xca62c1d6); (b) = ((((b)) << (30)) | (((b)) >> (32 - (30)))); }; ;
- { (d) += (((((e)) << (5)) | (((e)) >> (32 - (5)))) + ((a) ^ (b) ^ (c)) + (expandedBuffer[i + 1]) + 0xca62c1d6); (a) = ((((a)) << (30)) | (((a)) >> (32 - (30)))); }; ;
- { (c) += (((((d)) << (5)) | (((d)) >> (32 - (5)))) + ((e) ^ (a) ^ (b)) + (expandedBuffer[i + 2]) + 0xca62c1d6); (e) = ((((e)) << (30)) | (((e)) >> (32 - (30)))); }; ;
- { (b) += (((((c)) << (5)) | (((c)) >> (32 - (5)))) + ((d) ^ (e) ^ (a)) + (expandedBuffer[i + 3]) + 0xca62c1d6); (d) = ((((d)) << (30)) | (((d)) >> (32 - (30)))); }; ;
- { (a) += (((((b)) << (5)) | (((b)) >> (32 - (5)))) + ((c) ^ (d) ^ (e)) + (expandedBuffer[i + 4]) + 0xca62c1d6); (c) = ((((c)) << (30)) | (((c)) >> (32 - (30)))); }; ;
- }
+ for (int i = 16; i != 80; i++)
+ {
+ this.w[i] = BitOperations.RotateLeft(this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16], 1);
+ }
- state[0] += a;
- state[1] += b;
- state[2] += c;
- state[3] += d;
- state[4] += e;
- }
+ unchecked
+ {
+ uint a = this.w[80];
+ uint b = this.w[81];
+ uint c = this.w[82];
+ uint d = this.w[83];
+ uint e = this.w[84];
- /* Expands x[0..15] into x[16..79], according to the recurrence
- x[i] = x[i-3] ^ x[i-8] ^ x[i-14] ^ x[i-16].
- */
- private unsafe void SHAExpand(uint* x)
- {
- int i;
- uint tmp;
+ for (int i = 0; i != 20; i++)
+ {
+ const uint k = 0x5A827999;
+ uint f = (b & c) | ((~b) & d);
+ 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;
+ }
- for (i = 16; i < 80; i++)
- {
- tmp = (x[i - 3] ^ x[i - 8] ^ x[i - 14] ^ x[i - 16]);
- x[i] = ((tmp << 1) | (tmp >> 31));
+ for (int i = 20; i != 40; i++)
+ {
+ uint f = b ^ c ^ d;
+ const uint k = 0x6ED9EBA1;
+ 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;
+ }
+
+ for (int i = 40; i != 60; i++)
+ {
+ uint f = (b & c) | (b & d) | (c & d);
+ const uint k = 0x8F1BBCDC;
+ 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;
+ }
+
+ for (int i = 60; i != 80; i++)
+ {
+ uint f = b ^ c ^ d;
+ const uint k = 0xCA62C1D6;
+ 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;
+ }
+
+ this.w[80] += a;
+ this.w[81] += b;
+ this.w[82] += c;
+ this.w[83] += d;
+ this.w[84] += e;
+ }
+
+ this.length += 512; // 64 bytes == 512 bits
+ this.pos = 0;
}
}
}