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 */