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