1 // =================================
  2 // Copyright (c) 2024 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 uint LeftRotate(uint xuint n)
 13     {
 14         return (x << n) ^ (x >> (32u - n));
 15     }
 16 
 17     public class Sha1
 18     {
 19         public Sha1()
 20         {
 21             Reset();
 22         }
 23         public void Reset()
 24         {
 25             digest[0] = 1732584193u;
 26             digest[1] = 4023233417u;
 27             digest[2] = 2562383102u;
 28             digest[3] = 271733878u;
 29             digest[4] = 3285377520u;
 30             byteIndex = 0u;
 31             bitCount = 0u;
 32         }
 33         public void Process(byte x)
 34         {
 35             ProcessByte(x);
 36             bitCount = bitCount + 8u;
 37         }
 38         public void Process(byte* beginbyte* end)
 39         {
 40             while (begin != end)
 41             {
 42                 Process(*begin);
 43                 ++begin;
 44             }
 45         }
 46         public void Process(byte* buflong count)
 47         {
 48             Process(bufbuf + count);
 49         }
 50         [nodiscard]
 51         public Result<string> GetDigest()
 52         {
 53             ProcessByte(128u);
 54             if (byteIndex > 56u)
 55             {
 56                 while (byteIndex != 0u)
 57                 {
 58                     ProcessByte(0u);
 59                 }
 60                 while (byteIndex < 56u)
 61                 {
 62                     ProcessByte(0u);
 63                 }
 64             }
 65             else
 66             {
 67                 while (byteIndex < 56u)
 68                 {
 69                     ProcessByte(0u);
 70                 }
 71             }
 72             ProcessByte(cast<byte>((bitCount >> 56u) & 255u));
 73             ProcessByte(cast<byte>((bitCount >> 48u) & 255u));
 74             ProcessByte(cast<byte>((bitCount >> 40u) & 255u));
 75             ProcessByte(cast<byte>((bitCount >> 32u) & 255u));
 76             ProcessByte(cast<byte>((bitCount >> 24u) & 255u));
 77             ProcessByte(cast<byte>((bitCount >> 16u) & 255u));
 78             ProcessByte(cast<byte>((bitCount >> 8u) & 255u));
 79             ProcessByte(cast<byte>(bitCount & 255u));
 80             auto result = ToHexString(digest[0]);
 81             if (result.Error())
 82             {
 83                 return Result<string>(ErrorId(result.GetErrorId()));
 84             }
 85             string s = result.Value();
 86             result = ToHexString(digest[1]);
 87             if (result.Error())
 88             {
 89                 return Result<string>(ErrorId(result.GetErrorId()));
 90             }
 91             s.Append(result.Value());
 92             result = ToHexString(digest[2]);
 93             if (result.Error())
 94             {
 95                 return Result<string>(ErrorId(result.GetErrorId()));
 96             }
 97             s.Append(result.Value());
 98             result = ToHexString(digest[3]);
 99             if (result.Error())
100             {
101                 return Result<string>(ErrorId(result.GetErrorId()));
102             }
103             s.Append(result.Value());
104             result = ToHexString(digest[4]);
105             if (result.Error())
106             {
107                 return Result<string>(ErrorId(result.GetErrorId()));
108             }
109             s.Append(result.Value());
110             return Result<string>(s);
111         }
112         private void ProcessByte(byte x)
113         {
114             block[byteIndex++] = x;
115             if (byteIndex == 64u)
116             {
117                 byteIndex = 0u;
118                 ProcessBlock();
119             }
120         }
121         private void ProcessBlock()
122         {
123             uint[80] w;
124             for (int i = 0; i < 16; ++i;)
125             {
126                 w[i] = cast<uint>(block[4 * i]) << 24u;
127                 w[i] = w[i] | cast<uint>(block[4 * i + 1]) << 16u;
128                 w[i] = w[i] | cast<uint>(block[4 * i + 2]) << 8u;
129                 w[i] = w[i] | cast<uint>(block[4 * i + 3]);
130             }
131             for (int i = 16; i < 80; ++i;)
132             {
133                 w[i] = LeftRotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]1u);
134             }
135             uint a = digest[0];
136             uint b = digest[1];
137             uint c = digest[2];
138             uint d = digest[3];
139             uint e = digest[4];
140             for (int i = 0; i < 80; ++i;)
141             {
142                 uint f;
143                 uint k;
144                 if (i < 20)
145                 {
146                     f = (b & c) | (~b & d);
147                     k = 1518500249u;
148                 }
149                 else if (i < 40)
150                 {
151                     f = b ^ c ^ d;
152                     k = 1859775393u;
153                 }
154                 else if (i < 60)
155                 {
156                     f = (b & c) | (b & d) | (c & d);
157                     k = 2400959708u;
158                 }
159                 else
160                 {
161                     f = b ^ c ^ d;
162                     k = 3395469782u;
163                 }
164                 uint temp = LeftRotate(a5u) + f + e + k + w[i];
165                 e = d;
166                 d = c;
167                 c = LeftRotate(b30u);
168                 b = a;
169                 a = temp;
170             }
171             digest[0] = digest[0] + a;
172             digest[1] = digest[1] + b;
173             digest[2] = digest[2] + c;
174             digest[3] = digest[3] + d;
175             digest[4] = digest[4] + e;
176         }
177         private uint[5] digest;
178         private byte[64] block;
179         private byte byteIndex;
180         private ulong bitCount;
181     }
182 
183     [nodiscard]
184     public Result<string> GetSha1MessageDigest(const string& message)
185     {
186         byte* data = cast<byte*>(cast<void*>(message.Chars()));
187         long count = message.Length();
188         return GetSha1MessageDigest(datacount);
189     }
190 
191     [nodiscard]
192     public Result<string> GetSha1MessageDigest(byte* datalong count)
193     {
194         Sha1 sha1;
195         sha1.Process(datacount);
196         return sha1.GetDigest();
197     }
198 
199     [nodiscard]
200     public Result<string> GetSha1FileDigest(const string& filePath)
201     {
202         Sha1 sha1;
203         auto nr = File.Size(filePath);
204         if (nr.Error())
205         {
206             return Result<string>(ErrorId(nr.GetErrorId()));
207         }
208         long n = nr.Value();
209         if (n > 0)
210         {
211             auto readerResult = File.OpenBinary(filePath);
212             if (readerResult.Error())
213             {
214                 return Result<string>(ErrorId(readerResult.GetErrorId()));
215             }
216             BinaryReader& reader = readerResult.Value();
217             for (long i = 0; i < n; ++i;)
218             {
219                 auto result = reader.ReadByte();
220                 if (result.Error())
221                 {
222                     return Result<string>(ErrorId(result.GetErrorId()));
223                 }
224                 sha1.Process(result.Value());
225             }
226         }
227         return sha1.GetDigest();
228     }
229 
230     [nodiscard]
231     public Result<string> GetCumulativeSha1FileDigest(const List<string>& filePaths)
232     {
233         Sha1 sha1;
234         for (const string& filePath : filePaths)
235         {
236             auto nr = File.Size(filePath);
237             if (nr.Error())
238             {
239                 return Result<string>(ErrorId(nr.GetErrorId()));
240             }
241             long n = nr.Value();
242             if (n > 0)
243             {
244                 auto readerResult = File.OpenBinary(filePath);
245                 if (readerResult.Error())
246                 {
247                     return Result<string>(ErrorId(readerResult.GetErrorId()));
248                 }
249                 BinaryReader& reader = readerResult.Value();
250                 for (long i = 0; i < n; ++i;)
251                 {
252                     auto result = reader.ReadByte();
253                     if (result.Error())
254                     {
255                         return Result<string>(ErrorId(result.GetErrorId()));
256                     }
257                     sha1.Process(result.Value());
258                 }
259             }
260         }
261         return sha1.GetDigest();
262     }