1 module mysql.sha1;
2 
3 import std.stdio;
4 import std.conv;
5 
6 enum
7 {
8     shaSuccess = 0,
9     shaNull,            /* Null pointer parameter */
10     shaInputTooLong,    /* input data too long */
11     shaStateError       /* called Input after Result */
12 }
13 
14 immutable uint SHA1HashSize = 20;
15 
16 /*
17  *  This structure will hold context information for the SHA-1
18  *  hashing operation
19  */
20 struct SHA1
21 {
22 private:
23    uint[SHA1HashSize/4] Intermediate_Hash; /* Message Digest  */
24 
25    uint Length_Low;            // Message length in bits
26    uint Length_High;           // Message length in bits
27 
28    // Index into message block array
29    short Message_Block_Index;
30    ubyte[64] Message_Block;      /* 512-bit message blocks      */
31 
32    bool Computed;               /* Is the digest computed?         */
33    bool Corrupted;             /* Is the message digest corrupted? */
34    int err;
35 
36    uint SHA1CircularShift(int bits, uint word)
37    {
38       return (((word) << (bits)) | ((word) >> (32-(bits))));
39    }
40 
41 
42    void ProcessMessageBlock()
43    {
44       const uint K[] =    [ 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 ];
45       int        t;                 /* Loop counter                */
46       uint      temp;              /* Temporary word value        */
47       uint[80] W;             /* Word sequence               */
48       uint      A, B, C, D, E;     /* Word buffers                */
49 
50       /*
51       *  Initialize the first 16 words in the array W
52       */
53       for(t = 0; t < 16; t++)
54       {
55          W[t] = Message_Block[t * 4] << 24;
56          W[t] |= Message_Block[t * 4 + 1] << 16;
57          W[t] |= Message_Block[t * 4 + 2] << 8;
58          W[t] |= Message_Block[t * 4 + 3];
59       }
60 
61       for(t = 16; t < 80; t++)
62       {
63          W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
64       }
65 
66       A = Intermediate_Hash[0];
67       B = Intermediate_Hash[1];
68       C = Intermediate_Hash[2];
69       D = Intermediate_Hash[3];
70       E = Intermediate_Hash[4];
71 
72       for(t = 0; t < 20; t++)
73       {
74          temp =  SHA1CircularShift(5,A) +
75                 ((B & C) | ((~B) & D)) + E + W[t] + K[0];
76          E = D;
77          D = C;
78          C = SHA1CircularShift(30,B);
79 
80          B = A;
81          A = temp;
82       }
83 
84       for(t = 20; t < 40; t++)
85       {
86          temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
87          E = D;
88          D = C;
89          C = SHA1CircularShift(30,B);
90          B = A;
91          A = temp;
92       }
93 
94       for(t = 40; t < 60; t++)
95       {
96          temp = SHA1CircularShift(5,A) +
97                ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
98          E = D;
99          D = C;
100          C = SHA1CircularShift(30,B);
101          B = A;
102          A = temp;
103       }
104 
105       for(t = 60; t < 80; t++)
106       {
107          temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
108          E = D;
109          D = C;
110          C = SHA1CircularShift(30,B);
111          B = A;
112          A = temp;
113       }
114 
115       Intermediate_Hash[0] += A;
116       Intermediate_Hash[1] += B;
117       Intermediate_Hash[2] += C;
118       Intermediate_Hash[3] += D;
119       Intermediate_Hash[4] += E;
120 
121       Message_Block_Index = 0;
122    }
123 
124    void PadMessage()
125    {
126       if (Message_Block_Index > 55)
127       {
128          Message_Block[Message_Block_Index++] = 0x80;
129          while(Message_Block_Index < 64)
130          {
131             Message_Block[Message_Block_Index++] = 0;
132          }
133 
134          ProcessMessageBlock();
135 
136          while(Message_Block_Index < 56)
137          {
138             Message_Block[Message_Block_Index++] = 0;
139          }
140       }
141       else
142       {
143          Message_Block[Message_Block_Index++] = 0x80;
144          while(Message_Block_Index < 56)
145          {
146 
147             Message_Block[Message_Block_Index++] = 0;
148          }
149       }
150 
151       /*
152       *  Store the message length as the last 8 octets
153       */
154       Message_Block[56] = cast(ubyte) (Length_High >> 24);
155       Message_Block[57] = cast(ubyte) (Length_High >> 16);
156       Message_Block[58] = cast(ubyte) (Length_High >> 8);
157       Message_Block[59] = cast(ubyte) Length_High;
158       Message_Block[60] = cast(ubyte) (Length_Low >> 24);
159       Message_Block[61] = cast(ubyte) (Length_Low >> 16);
160       Message_Block[62] = cast(ubyte) (Length_Low >> 8);
161       Message_Block[63] = cast(ubyte) Length_Low;
162 
163       ProcessMessageBlock();
164    }
165 
166 public:
167 
168    this(int dummy = 0) { reset(); dummy++; }
169 
170    @property error() { return err; }
171 
172    void reset()
173    {
174       Length_Low = 0;
175       Length_High = 0;
176       Message_Block_Index = 0;
177 
178       Intermediate_Hash = [ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 ];
179       Computed = false;
180       Corrupted = false;
181    }
182 
183    ubyte[] result()
184    {
185       if (Corrupted)
186          return null;
187 
188       ubyte[] rv;
189       rv.length = SHA1HashSize;
190 
191       if (!Computed)
192       {
193          PadMessage();
194          Message_Block[] = 0;
195          Length_Low = 0;    /* and clear length */
196          Length_High = 0;
197          Computed = true;
198       }
199 
200       for (int i = 0; i < SHA1HashSize; i++)
201       {
202          rv[i] = cast(ubyte)  (Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 )));
203       }
204 
205       return rv;
206    }
207 
208    bool input(const(ubyte)* message_array, size_t length)
209    {
210       if (!length)
211          return true;
212 
213       if (message_array is null)
214       {
215          err = shaNull;
216          return false;
217       }
218 
219       if (Computed)
220       {
221         err = shaStateError;
222         Corrupted = true;
223         return false;
224       }
225 
226       if (Corrupted)
227          return false;
228 
229       while(length-- && !Corrupted)
230       {
231          Message_Block[Message_Block_Index++] = (*message_array & 0xFF);
232 
233          Length_Low += 8;
234          if (Length_Low == 0)      // wrapped round
235          {
236             Length_High++;
237             if (Length_High == 0) // wrapped round
238             {
239                // Message is too long
240                Corrupted = true;
241                err = shaInputTooLong;
242                return false;
243             }
244          }
245 
246          if (Message_Block_Index == 64)
247             ProcessMessageBlock();
248 
249          message_array++;
250       }
251       return true;
252    }
253 }
254