1 module dcrypt.digests.sha1;
2 
3 import dcrypt.digest;
4 import dcrypt.bitmanip;
5 
6 unittest {
7 	// test vectors from http://www.di-mgt.com.au/sha_testvectors.html
8 		
9 	immutable string[] plaintexts = [
10 		x"616263",
11 		x"",
12 		"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
13 	];
14 	
15 	immutable string[] hashes = [
16 		x"a9993e364706816aba3e25717850c26c9cd0d89d",
17 		x"da39a3ee5e6b4b0d3255bfef95601890afd80709",
18 		x"a49b2446a02c645bf419f995b67091253a04a259"
19 	];
20 	
21 	testDigest(new SHA1Digest(), plaintexts, hashes);
22 }
23 
24 alias WrapperDigest!SHA1 SHA1Digest;
25 
26 static assert(isDigest!SHA1);
27 
28 /**
29  * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349.
30  *
31  * It is interesting to ponder why the, apart from the extra IV, the other difference here from MD5
32  * is the "endianness" of the word processing!
33  */
34 @safe
35 public struct SHA1
36 {
37 
38 public:
39 
40 	enum name = "SHA1";
41 	enum digestLength = 20;
42 	enum blockSize = 64;
43 
44 	void put(in ubyte[] input...) nothrow @nogc
45 	{
46 		uint inOff = 0;
47 		size_t len = input.length;
48 		//
49 		// fill the current word
50 		//
51 		while ((xBufOff != 0) && (len > 0))
52 		{
53 			putSingleByte(input[inOff]);
54 			
55 			inOff++;
56 			len--;
57 		}
58 		
59 		//
60 		// process whole words.
61 		//
62 		while (len > xBuf.length)
63 		{
64 			processWord(input[inOff .. inOff + 4]);
65 			
66 			inOff += xBuf.length;
67 			len -= xBuf.length;
68 			byteCount += xBuf.length;
69 		}
70 		
71 		//
72 		// load in the remainder.
73 		//
74 		while (len > 0)
75 		{
76 			putSingleByte(input[inOff]);
77 			
78 			inOff++;
79 			len--;
80 		}
81 	}
82 
83 	/// Returns: The final hash value.
84 	ubyte[digestLength] finish() nothrow @nogc
85 	{
86 		ubyte[digestLength] output;
87 		immutable size_t bitLen = byteCount * 8;
88 		// add the pad bytes.
89 		put(128);
90 		while (xBufOff != 0)
91 		{
92 			put(0);
93 		}
94 		processLength(bitLen);
95 		processBlock();
96 
97 		// pack the integers into a byte array
98 		//toBigEndian!uint([H1,H2,H3,H4,H5], output);
99 
100 		toBigEndian!uint(H1, output[0..4]);
101 		toBigEndian!uint(H2, output[4..8]);
102 		toBigEndian!uint(H3, output[8..12]);
103 		toBigEndian!uint(H4, output[12..16]);
104 		toBigEndian!uint(H5, output[16..20]);
105 
106 		start();
107 
108 		return output;
109 	}
110 
111 	/// Reset SHA1.
112 	void start() nothrow @nogc
113 	{
114 		H1 = H10;
115 		H2 = H20;
116 		H3 = H30;
117 		H4 = H40;
118 		H5 = H50;
119 
120 		xOff = 0;
121 		X[] = 0;
122 
123 		byteCount = 0;
124 		
125 		xBufOff = 0;
126 		xBuf[] = 0;
127 	}
128 
129 	private void putSingleByte(ubyte input) nothrow @nogc
130 	{
131 		xBuf[xBufOff++] = input;
132 		
133 		if (xBufOff == xBuf.length)
134 		{
135 			processWord(xBuf);
136 			xBufOff = 0;
137 		}
138 		
139 		byteCount++;
140 	}
141 
142 	private void processWord(in ubyte[]  input) nothrow @nogc
143 	in {
144 		assert(input.length == 4);
145 	} body {
146 		X[xOff] = fromBigEndian!uint(input);
147 
148 		if (++xOff == 16)
149 		{
150 			processBlock();
151 		}
152 	}
153 
154 	private void processLength(ulong bitLength) nothrow @nogc
155 	{
156 		if (xOff > 14)
157 		{
158 			processBlock();
159 		}
160 
161 		X[14] = cast(uint)(bitLength >>> 32);
162 		X[15] = cast(uint) bitLength;
163 	}
164 
165 private:
166 	//
167 	// Additive constants
168 	//
169 	static {
170 		enum uint Y1 = 0x5a827999;
171 		enum uint Y2 = 0x6ed9eba1;
172 		enum uint Y3 = 0x8f1bbcdc;
173 		enum uint Y4 = 0xca62c1d6;
174 
175 		enum uint H10 = 0x67452301;
176 		enum uint H20 = 0xefcdab89;
177 		enum uint H30 = 0x98badcfe;
178 		enum uint H40 = 0x10325476;
179 		enum uint H50 = 0xc3d2e1f0;
180 	}
181 
182 	uint     H1 = H10, H2 = H20, H3 = H30, H4 = H40, H5 = H50;
183 
184 	uint[80]   X;
185 	uint     xOff;
186 
187 	ubyte[4]  xBuf;
188 	uint     xBufOff;
189 	size_t    byteCount;
190 	
191 
192 	uint f(uint u, uint v, uint w) pure nothrow @nogc
193 	{
194 		return ((u & v) | ((~u) & w));
195 	}
196 
197 	uint g(uint u, uint v, uint w)  pure nothrow @nogc
198 	{
199 		return ((u & v) | (u & w) | (v & w));
200 	}
201 
202 	uint h(uint u, uint v, uint w) pure nothrow @nogc
203 	{
204 		return (u ^ v ^ w);
205 	}
206 
207 	private void processBlock() nothrow @nogc
208 	{
209 		//
210 		// expand 16 word block into 80 word block.
211 		//
212 		foreach (uint i; 16 .. 80)
213 		{
214 			uint t = X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16];
215 			X[i] = t << 1 | t >>> 31;
216 		}
217 
218 		//
219 		// set up working variables.
220 		//
221 		uint     A = H1;
222 		uint     B = H2;
223 		uint     C = H3;
224 		uint     D = H4;
225 		uint     E = H5;
226 
227 		//
228 		// round 1
229 		//
230 		uint idx = 0;
231 
232 		for (uint j = 0; j < 4; j++)
233 		{
234 			// E = rotateLeft(A, 5) + f(B, C, D) + E + X[idx++] + Y1
235 			// B = rotateLeft(B, 30)
236 			E += (A << 5 | A >>> 27) + f(B, C, D) + X[idx++] + Y1;
237 			B = B << 30 | B >>> 2;
238 
239 			D += (E << 5 | E >>> 27) + f(A, B, C) + X[idx++] + Y1;
240 			A = A << 30 | A >>> 2;
241 
242 			C += (D << 5 | D >>> 27) + f(E, A, B) + X[idx++] + Y1;
243 			E = E << 30 | E >>> 2;
244 
245 			B += (C << 5 | C >>> 27) + f(D, E, A) + X[idx++] + Y1;
246 			D = D << 30 | D >>> 2;
247 
248 			A += (B << 5 | B >>> 27) + f(C, D, E) + X[idx++] + Y1;
249 			C = C << 30 | C >>> 2;
250 		}
251 
252 		//
253 		// round 2
254 		//
255 		for (uint j = 0; j < 4; j++)
256 		{
257 			// E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y2
258 			// B = rotateLeft(B, 30)
259 			E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y2;
260 			B = B << 30 | B >>> 2;
261 
262 			D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y2;
263 			A = A << 30 | A >>> 2;
264 
265 			C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y2;
266 			E = E << 30 | E >>> 2;
267 
268 			B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y2;
269 			D = D << 30 | D >>> 2;
270 
271 			A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y2;
272 			C = C << 30 | C >>> 2;
273 		}
274 
275 		//
276 		// round 3
277 		//
278 		for (uint j = 0; j < 4; j++)
279 		{
280 			// E = rotateLeft(A, 5) + g(B, C, D) + E + X[idx++] + Y3
281 			// B = rotateLeft(B, 30)
282 			E += (A << 5 | A >>> 27) + g(B, C, D) + X[idx++] + Y3;
283 			B = B << 30 | B >>> 2;
284 
285 			D += (E << 5 | E >>> 27) + g(A, B, C) + X[idx++] + Y3;
286 			A = A << 30 | A >>> 2;
287 
288 			C += (D << 5 | D >>> 27) + g(E, A, B) + X[idx++] + Y3;
289 			E = E << 30 | E >>> 2;
290 
291 			B += (C << 5 | C >>> 27) + g(D, E, A) + X[idx++] + Y3;
292 			D = D << 30 | D >>> 2;
293 
294 			A += (B << 5 | B >>> 27) + g(C, D, E) + X[idx++] + Y3;
295 			C = C << 30 | C >>> 2;
296 		}
297 
298 		//
299 		// round 4
300 		//
301 		for (uint j = 0; j < 4; j++)
302 		{
303 			// E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y4
304 			// B = rotateLeft(B, 30)
305 			E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y4;
306 			B = B << 30 | B >>> 2;
307 
308 			D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y4;
309 			A = A << 30 | A >>> 2;
310 
311 			C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y4;
312 			E = E << 30 | E >>> 2;
313 
314 			B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y4;
315 			D = D << 30 | D >>> 2;
316 
317 			A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y4;
318 			C = C << 30 | C >>> 2;
319 		}
320 
321 		
322 		H1 += A;
323 		H2 += B;
324 		H3 += C;
325 		H4 += D;
326 		H5 += E;
327 
328 		//
329 		// reset start of the buffer.
330 		//
331 		xOff = 0;
332 		X[] = 0;
333 	}
334 }
335 
336 
337