1 /// Homemade SHA 1 and SHA 2 implementations. Beware of bugs - you should probably use [std.digest] instead. Kept more for historical curiosity than anything else.
2 module arsd.sha;
3 
4 /*
5 	By Adam D. Ruppe, 26 Nov 2009
6 	I release this file into the public domain
7 */
8 import std.stdio;
9 
10 version(GNU) {
11 	immutable(ubyte)[] SHA1(T)(T data) {
12 		import std.digest.sha;
13 		auto i = sha1Of(data);
14 		return i.idup;
15 	}
16 
17 	immutable(ubyte)[] SHA256(T)(T data) {
18 		import std.digest.sha;
19 		auto i = sha256Of(data);
20 		return i.idup;
21 	}
22 } else {
23 
24 immutable(ubyte)[/*20*/] SHA1(T)(T data) if(isInputRange!(T)) /*const(ubyte)[] data)*/ {
25 	uint[5] h = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
26 
27 	SHARange!(T) range;
28 	static if(is(data == SHARange))
29 		range = data;
30 	else {
31 		range.r = data;
32 	}
33 	/*
34 		ubyte[] message = data.dup;
35 		message ~= 0b1000_0000;
36 		while(((message.length+8) * 8) % 512)
37 			message ~= 0;
38 
39 		ulong originalLength = cast(ulong) data.length * 8;
40 
41 		for(int a = 7; a >= 0; a--)
42 			message ~= (originalLength >> (a*8)) & 0xff; // to big-endian
43 
44 		assert(((message.length * 8) % 512) == 0);
45 
46 		uint pos = 0;
47 		while(pos < message.length) {
48 	*/
49 		while(!range.empty) {
50 			uint[80] words;
51 
52 			for(int a = 0; a < 16; a++) {
53 				for(int b = 3; b >= 0; b--) {
54 					words[a] |= cast(uint)(range.front()) << (b*8);
55 					range.popFront;
56 				//	words[a] |= cast(uint)(message[pos]) << (b*8);
57 				//	pos++;
58 				}
59 			}
60 
61 			for(int a = 16; a < 80; a++) {
62 				uint t  = words[a-3];
63 				     t ^= words[a-8];
64 				     t ^= words[a-14];
65 				     t ^= words[a-16];
66 				asm { rol t, 1; }
67 				words[a] = t;
68 			}
69 
70 			uint a = h[0];
71 			uint b = h[1];
72 			uint c = h[2];
73 			uint d = h[3];
74 			uint e = h[4];
75 
76 			for(int i = 0; i < 80; i++) {
77 				uint f, k;
78 				if(i >= 0 && i < 20) {
79 					f = (b & c) | ((~b) & d);
80 					k = 0x5A827999;
81 				} else
82 				if(i >= 20 && i < 40) {
83 					f = b ^ c ^ d;
84 					k = 0x6ED9EBA1;
85 				} else
86 				if(i >= 40 && i < 60) {
87 					f = (b & c) | (b & d) | (c & d);
88 					k = 0x8F1BBCDC;
89 				} else
90 				if(i >= 60 && i < 80) {
91 					f = b ^ c ^ d;
92 					k = 0xCA62C1D6;
93 				} else assert(0);
94 
95 				uint temp;
96 				asm {
97 					mov EAX, a;
98 					rol EAX, 5;
99 					add EAX, f;
100 					add EAX, e;
101 					add EAX, k;
102 					mov temp, EAX;
103 				}
104 				temp += words[i];
105 				e = d;
106 				d = c;
107 				asm {
108 					mov EAX, b;
109 					rol EAX, 30;
110 					mov c, EAX;
111 				}
112 				b = a;
113 				a = temp;
114 			}
115 
116 			h[0] += a;
117 			h[1] += b;
118 			h[2] += c;
119 			h[3] += d;
120 			h[4] += e;
121 		}
122 
123 
124 		ubyte[] hash;
125 		for(int j = 0; j < 5; j++)
126 		for(int i = 3; i >= 0; i--) {
127 			hash ~= cast(ubyte)(h[j] >> (i*8))&0xff;
128 		}
129 
130 		return hash.idup;
131 }
132 
133 import core.stdc.stdio;
134 import std.string;
135 // i wish something like this was in phobos.
136 struct FileByByte {
137 	FILE* fp;
138 	this(string filename) {
139 		fp = fopen(toStringz(filename), "rb".ptr);
140 		if(fp is null)
141 			throw new Exception("couldn't open " ~ filename);
142 		popFront();
143 	}
144 
145 	// FIXME: this should prolly be recounted or something. blargh.
146 
147 	~this() {
148 		if(fp !is null)
149 			fclose(fp);
150 	}
151 
152 	void popFront() {
153 		f = cast(ubyte) fgetc(fp);
154 	}
155 
156 	@property ubyte front() {
157 		return f;
158 	}
159 
160 	@property bool empty() {
161 		return feof(fp) ? true : false;
162 	}
163 
164 	ubyte f;
165 }
166 
167 import std.range;
168 
169 // This does the preprocessing of input data, fetching one byte at a time of the data until it is empty, then the padding and length at the end
170 template SHARange(T) if(isInputRange!(T)) {
171 	struct SHARange {
172 		T r;
173 
174 		bool empty() {
175 			return state == 5;
176 		}
177 
178 		void popFront() {
179 			if(state == 0) {
180 				r.popFront;
181 				/*
182 				static if(__traits(compiles, r.front.length))
183 					length += r.front.length;
184 				else
185 					length += r.front().sizeof;
186 				*/
187 				length++; // FIXME
188 
189 				if(r.empty) {
190 					state = 1;
191 					position = 2;
192 					current = 0x80;
193 				}
194 			} else {
195 				bool hackforward = false;
196 				if(state == 1) {
197 					current = 0x0;
198 					state = 2;
199 					if((((position + length + 8) * 8) % 512) == 8) {
200 						position--;
201 						hackforward = true;
202 					}
203 					goto proceed;
204 				//	position++;
205 				} else if( state == 2) {
206 				proceed:
207 					if(!(((position + length + 8) * 8) % 512)) {
208 						state = 3;
209 						position = 7;
210 						length *= 8;
211 						if(hackforward)
212 							goto proceedmoar;
213 					} else
214 						position++;
215 				} else if (state == 3) {
216 				proceedmoar:
217 					current = (length >> (position*8)) & 0xff;
218 					if(position == 0)
219 						state = 4;
220 					else
221 						position--;
222 				} else if (state == 4) {
223 					current = 0xff;
224 					state = 5;
225 				}
226 			}
227 		}
228 
229 		ubyte front() {
230 			if(state == 0) {
231 				return cast(ubyte) r.front();
232 			}
233 			assert(state != 5);
234 			//writefln("%x", current);
235 			return current;
236 		}
237 
238 		ubyte current;
239 		uint position;
240 		ulong length;
241 		int state = 0; // reading range, reading appended bit, reading padding, reading length, done
242 	}
243 }
244 
245 immutable(ubyte)[] SHA256(T)(T data) if ( isInputRange!(T)) {
246 	uint[8] h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];
247 	immutable(uint[64]) k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
248 			0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
249 			0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
250 			0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
251 			0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
252 			0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
253 			0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
254 			0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
255 
256 	SHARange!(T) range;
257 	static if(is(data == SHARange))
258 		range = data;
259 	else {
260 		range.r = data;
261 	}
262 /*
263    	ubyte[] message = cast(ubyte[]) data.dup;
264 	message ~= 0b1000_0000;
265 	while(((message.length+8) * 8) % 512)
266 		message ~= 0;
267 
268 	ulong originalLength = cast(ulong) data.length * 8;
269 
270 	for(int a = 7; a >= 0; a--)
271 		message ~= (originalLength >> (a*8)) & 0xff; // to big-endian
272 
273 	assert(((message.length * 8) % 512) == 0);
274 */
275 //	uint pos = 0;
276 	while(!range.empty) {
277 //	while(pos < message.length) {
278 		uint[64] words;
279 
280 		for(int a = 0; a < 16; a++) {
281 			for(int b = 3; b >= 0; b--) {
282 				words[a] |= cast(uint)(range.front()) << (b*8);
283 				//words[a] |= cast(uint)(message[pos]) << (b*8);
284 				range.popFront;
285 //				pos++;
286 			}
287 		}
288 
289 		for(int a = 16; a < 64; a++) {
290 			uint t1 = words[a-15];
291 			asm {
292 				mov EAX, t1;
293 				mov EBX, EAX;
294 				mov ECX, EAX;
295 				ror EAX, 7;
296 				ror EBX, 18;
297 				shr ECX, 3;
298 				xor EAX, EBX;
299 				xor EAX, ECX;
300 				mov t1, EAX;
301 			}
302 			uint t2 = words[a-2];
303 			asm {
304 				mov EAX, t2;
305 				mov EBX, EAX;
306 				mov ECX, EAX;
307 				ror EAX, 17;
308 				ror EBX, 19;
309 				shr ECX, 10;
310 				xor EAX, EBX;
311 				xor EAX, ECX;
312 				mov t2, EAX;
313 			}
314 
315 			words[a] = words[a-16] + t1 + words[a-7] + t2;
316 		}
317 
318 		uint A = h[0];
319 		uint B = h[1];
320 		uint C = h[2];
321 		uint D = h[3];
322 		uint E = h[4];
323 		uint F = h[5];
324 		uint G = h[6];
325 		uint H = h[7];
326 
327 		for(int i = 0; i < 64; i++) {
328 			uint s0;
329 			asm {
330 				mov EAX, A;
331 				mov EBX, EAX;
332 				mov ECX, EAX;
333 				ror EAX, 2;
334 				ror EBX, 13;
335 				ror ECX, 22;
336 				xor EAX, EBX;
337 				xor EAX, ECX;
338 				mov s0, EAX;
339 			}
340 			uint maj = (A & B) ^ (A & C) ^ (B & C);
341 			uint t2 = s0 + maj;
342 			uint s1;
343 			asm {
344 				mov EAX, E;
345 				mov EBX, EAX;
346 				mov ECX, EAX;
347 				ror EAX, 6;
348 				ror EBX, 11;
349 				ror ECX, 25;
350 				xor EAX, EBX;
351 				xor EAX, ECX;
352 				mov s1, EAX;
353 			}
354 			uint ch = (E & F) ^ ((~E) & G);
355 			uint t1 = H + s1 + ch + k[i] + words[i];
356 
357 			H = G;
358 			G = F;
359 			F = E;
360 			E = D + t1;
361 			D = C;
362 			C = B;
363 			B = A;
364 			A = t1 + t2;
365 		}
366 
367 		h[0] += A;
368 		h[1] += B;
369 		h[2] += C;
370 		h[3] += D;
371 		h[4] += E;
372 		h[5] += F;
373 		h[6] += G;
374 		h[7] += H;
375 	}
376 
377 	ubyte[] hash;
378 	for(int j = 0; j < 8; j++)
379 	for(int i = 3; i >= 0; i--) {
380 		hash ~= cast(ubyte)(h[j] >> (i*8))&0xff;
381 	}
382 
383 	return hash.idup;
384 }
385 
386 }
387 
388 import std.exception;
389 
390 string hashToString(const(ubyte)[] hash) {
391 	char[] s;
392 
393 	s.length = hash.length * 2;
394 
395 	char toHex(int a) {
396 		if(a < 10)
397 			return cast(char) (a + '0');
398 		else
399 			return cast(char) (a + 'a' - 10);
400 	}
401 
402 	for(int a = 0; a < hash.length; a++) {
403 		s[a*2]   = toHex(hash[a] >> 4);
404 		s[a*2+1] = toHex(hash[a] & 0x0f);
405 	}
406 
407 	return assumeUnique(s);
408 }
409 /*
410 string tee(string t) {
411 	writefln("%s", t);
412 	return t;
413 }
414 */
415 unittest {
416 	assert(hashToString(SHA1("abc")) == "a9993e364706816aba3e25717850c26c9cd0d89d");
417 	assert(hashToString(SHA1("sdfj983yr2ih")) == "335f1f5a4af4aa2c8e93b88d69dda2c22baeb94d");
418 	assert(hashToString(SHA1("$%&^54ylkufg09fd7f09sa7udsiouhcx987yw98etf7yew98yfds987f632<F7>uw90ruds09fudsf09dsuhfoidschyds98fydovipsdaidsd9fsa GA UIA duisguifgsuifgusaufisgfuisafguisagasuidgsaufsauifhuisahfuisafaoisahasiosafhffdasasdisayhfdoisayf8saiuhgduifyds8fiydsufisafoisayf8sayfd98wqyr98wqy98sayd98sayd098sayd09sayd98sayd98saicxyhckxnvjbpovc pousa09cusa 09csau csa9 dusa90d usa9d0sau dsa90 as09posufpodsufodspufdspofuds 9tu sapfusaa daosjdoisajdsapoihdsaiodyhsaioyfg d98ytewq89rysa 98yc98sdxych sa89ydsa89dy sa98ydas98c ysx9v8y cxv89ysd f8ysa89f ysa89fd sg8yhds9g8 rfjcxhvslkhdaiosy09wq7r987t98e7ys98aIYOIYOIY)(*YE (*WY *A(YSA* HDUIHDUIAYT&*ATDAUID AUI DUIAT DUIAG saoidusaoid ysqoid yhsaduiayh UIZYzuI YUIYEDSA UIDYUIADYISA YTDGS UITGUID")) == "e38a1220eaf8103d6176df2e0dd0a933e2f52001");
419 
420 	assert(hashToString(SHA256("abc")) == "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
421 	assert(hashToString(SHA256("$%&^54ylkufg09fd7f09sa7udsiouhcx987yw98etf7yew98yfds987f632<F7>uw90ruds09fudsf09dsuhfoidschyds98fydovipsdaidsd9fsa GA UIA duisguifgsuifgusaufisgfuisafguisagasuidgsaufsauifhuisahfuisafaoisahasiosafhffdasasdisayhfdoisayf8saiuhgduifyds8fiydsufisafoisayf8sayfd98wqyr98wqy98sayd98sayd098sayd09sayd98sayd98saicxyhckxnvjbpovc pousa09cusa 09csau csa9 dusa90d usa9d0sau dsa90 as09posufpodsufodspufdspofuds 9tu sapfusaa daosjdoisajdsapoihdsaiodyhsaioyfg d98ytewq89rysa 98yc98sdxych sa89ydsa89dy sa98ydas98c ysx9v8y cxv89ysd f8ysa89f ysa89fd sg8yhds9g8 rfjcxhvslkhdaiosy09wq7r987t98e7ys98aIYOIYOIY)(*YE (*WY *A(YSA* HDUIHDUIAYT&*ATDAUID AUI DUIAT DUIAG saoidusaoid ysqoid yhsaduiayh UIZYzuI YUIYEDSA UIDYUIADYISA YTDGS UITGUID")) == "64ff79c67ad5ddf9ba5b2d83e07a6937ef9a5b4eb39c54fe1e913e21aad0e95c");
422 }
423 /*
424 void main() {
425 	auto hash = SHA256(InputByChar(stdin));
426 	writefln("%s", hashToString(hash));
427 }
428 */