1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.Collections;
  8 using System.IO;
  9 
 10 namespace System.Security
 11 {
 12     public inline nothrow uint LeftRotate(uint xuint n)
 13     {
 14         return (x << n) ^ (x >> (32u - n));
 15     }
 16     
 17     public class Sha1
 18     {
 19         public nothrow Sha1()
 20         {
 21             Reset();
 22         }
 23         public nothrow void Reset()
 24         {
 25             digest[0] = 0x67452301u;
 26             digest[1] = 0xEFCDAB89u;
 27             digest[2] = 0x98BADCFEu;
 28             digest[3] = 0x10325476u;
 29             digest[4] = 0xC3D2E1F0u;
 30             byteIndex = 0u;
 31             bitCount = 0u;
 32         }
 33         public nothrow void Process(byte x)
 34         {
 35             ProcessByte(x);
 36             bitCount = bitCount + 8u;
 37         }
 38         public nothrow void Process(byte* beginbyte* end)
 39         {
 40             while (begin != end)
 41             {
 42                 Process(*begin);
 43                 ++begin;
 44             }
 45         }
 46         public nothrow void Process(byte* buflong count)
 47         {
 48             Process(bufbuf + count);
 49         }
 50         public nothrow string GetDigest()
 51         {
 52             ProcessByte(0x80u);
 53             if (byteIndex > 56u)
 54             {
 55                 while (byteIndex != 0u)
 56                 {
 57                     ProcessByte(0u);
 58                 }
 59                 while (byteIndex < 56u)
 60                 {
 61                     ProcessByte(0u);
 62                 }
 63             }
 64             else
 65             {
 66                 while (byteIndex < 56u)
 67                 {
 68                     ProcessByte(0u);
 69                 }
 70             }
 71             ProcessByte(cast<byte>((bitCount >> 56u) & 0xFFu));
 72             ProcessByte(cast<byte>((bitCount >> 48u) & 0xFFu));
 73             ProcessByte(cast<byte>((bitCount >> 40u) & 0xFFu));
 74             ProcessByte(cast<byte>((bitCount >> 32u) & 0xFFu));
 75             ProcessByte(cast<byte>((bitCount >> 24u) & 0xFFu));
 76             ProcessByte(cast<byte>((bitCount >> 16u) & 0xFFu));
 77             ProcessByte(cast<byte>((bitCount >> 8u) & 0xFFu));
 78             ProcessByte(cast<byte>(bitCount & 0xFFu));
 79             string s = ToHexString(digest[0]);
 80             s.Append(ToHexString(digest[1]));
 81             s.Append(ToHexString(digest[2]));
 82             s.Append(ToHexString(digest[3]));
 83             s.Append(ToHexString(digest[4]));
 84             return s;
 85         }
 86         private nothrow void ProcessByte(byte x)
 87         {
 88             block[byteIndex++] = x;
 89             if (byteIndex == 64u)
 90             {
 91                 byteIndex = 0u;
 92                 ProcessBlock();
 93             }
 94         }
 95         private nothrow void ProcessBlock()
 96         {
 97             uint[80] w;
 98             for (int i = 0; i < 16; ++i;)
 99             {
100                 w[i] = cast<uint>(block[4 * i]) << 24u;
101                 w[i] = w[i] | cast<uint>(block[4 * i + 1]) << 16u;
102                 w[i] = w[i] | cast<uint>(block[4 * i + 2]) << 8u;
103                 w[i] = w[i] | cast<uint>(block[4 * i + 3]);
104             }
105             for (int i = 16; i < 80; ++i;)
106             {
107                 w[i] = LeftRotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]1u);
108             }
109             uint a = digest[0];
110             uint b = digest[1];
111             uint c = digest[2];
112             uint d = digest[3];
113             uint e = digest[4];
114             for (int i = 0; i < 80; ++i;)
115             {
116                 uint f;
117                 uint k;
118                 if (i < 20)
119                 {
120                     f = (b & c) | (~b & d);
121                     k = 0x5A827999u;
122                 }
123                 else if (i < 40)
124                 {
125                     f = b ^ c ^ d;
126                     k = 0x6ED9EBA1u;
127                 }
128                 else if (i < 60)
129                 {
130                     f = (b & c) | (b & d) | (c & d);
131                     k = 0x8F1BBCDCu;
132                 }
133                 else
134                 {
135                     f = b ^ c ^ d;
136                     k = 0xCA62C1D6u;
137                 }
138                 uint temp = LeftRotate(a5u) + f + e + k + w[i];
139                 e = d;
140                 d = c;
141                 c = LeftRotate(b30u);
142                 b = a;
143                 a = temp;
144             }
145             digest[0] = digest[0] + a;
146             digest[1] = digest[1] + b;
147             digest[2] = digest[2] + c;
148             digest[3] = digest[3] + d;
149             digest[4] = digest[4] + e;
150         }
151         private uint[5] digest;
152         private byte[64] block;
153         private byte byteIndex;
154         private ulong bitCount;
155     }
156 
157     public nothrow string GetSha1MessageDigest(const string& message)
158     {
159         byte* data = cast<byte*>(cast<void*>(message.Chars()));
160         long count = message.Length();
161         return GetSha1MessageDigest(datacount);
162     }
163     
164     public nothrow string GetSha1MessageDigest(byte* datalong count)
165     {
166         Sha1 sha1;
167         sha1.Process(datacount);
168         return sha1.GetDigest();
169     }
170     
171     public string GetSha1FileDigest(const string& filePath)
172     {
173         Sha1 sha1;
174         long n = File.Size(filePath);
175         if (n > 0)
176         {
177             BinaryReader reader = File.OpenBinary(filePath);
178             for (long i = 0; i < n; ++i;)
179             {
180                 sha1.Process(reader.ReadByte());
181             }
182         }
183         return sha1.GetDigest();
184     }
185 
186     public string GetCumulativeSha1FileDigest(const List<string>& filePaths)
187     {
188         Sha1 sha1;
189         for (const string& filePath : filePaths)
190         {
191             long n = File.Size(filePath);
192             if (n > 0)
193             {
194                 BinaryReader reader = File.OpenBinary(filePath);
195                 for (long i = 0; i < n; ++i;)
196                 {
197                     sha1.Process(reader.ReadByte());
198                 }
199             }
200         }
201         return sha1.GetDigest();
202     }
203 }