1 module sha1ct; 2 import std.uuid : UUID; 3 4 private ubyte[4] ctfeUIntToBigEndian(uint u) @safe pure nothrow @nogc 5 { 6 return cast(ubyte[4])[(u >> 24) & 0xFF, (u >> 16) & 0xFF, (u >> 8) & 0xFF, u & 0xFF]; 7 } 8 9 private ubyte[8] ctfeULongToBigEndian(ulong u) @safe pure nothrow @nogc 10 { 11 return cast(ubyte[8])[(u >> 56) & 0xFF, (u >> 48) & 0xFF, (u >> 40) & 0xFF, 12 (u >> 32) & 0xFF, (u >> 24) & 0xFF, (u >> 16) & 0xFF, (u >> 8) & 0xFF, u & 0xFF]; 13 } 14 15 private uint rotateLeft(uint x, uint n) @safe pure nothrow @nogc 16 { 17 return (x << n) | (x >> (32 - n)); 18 } 19 20 private uint rotateRight(uint x, uint n) @safe pure nothrow @nogc 21 { 22 return (x >> n) | (x << (32 - n)); 23 } 24 25 struct SHA1 26 { 27 void put(ubyte[64] block) @safe pure @nogc nothrow 28 { 29 uint[80] w; 30 for (int i = 0; i < 16; i++) 31 { 32 ubyte[4] part; 33 part[0] = block[i * 4]; 34 part[1] = block[i * 4 + 1]; 35 part[2] = block[i * 4 + 2]; 36 part[3] = block[i * 4 + 3]; 37 w[i] = (part[0] << 24) | (part[1] << 16) | (part[2] << 8) | part[3]; 38 } 39 for (int i = 16; i < 80; i++) 40 w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotateLeft(1); 41 42 uint a = h[0]; 43 uint b = h[1]; 44 uint c = h[2]; 45 uint d = h[3]; 46 uint e = h[4]; 47 uint f; 48 uint k; 49 uint tmp; 50 51 for (int i = 0; i < 80; i++) 52 { 53 if (i < 20) 54 { 55 f = (b & c) | (~b & d); 56 k = 0x5A827999; 57 } 58 else if (i < 40) 59 { 60 f = b ^ c ^ d; 61 k = 0x6ED9EBA1; 62 } 63 else if (i < 60) 64 { 65 f = (b & c) | (b & d) | (c & d); 66 k = 0x8F1BBCDC; 67 } 68 else 69 { 70 f = b ^ c ^ d; 71 k = 0xCA62C1D6; 72 } 73 74 tmp = a.rotateLeft(5) + f + e + k + w[i]; 75 e = d; 76 d = c; 77 c = b.rotateLeft(30); 78 b = a; 79 a = tmp; 80 } 81 82 h[0] += a; 83 h[1] += b; 84 h[2] += c; 85 h[3] += d; 86 h[4] += e; 87 } 88 89 ubyte[20] sum() @safe pure @nogc nothrow 90 { 91 ubyte[20] ret; 92 ret[0 .. 4] = h[0].ctfeUIntToBigEndian; 93 ret[4 .. 8] = h[1].ctfeUIntToBigEndian; 94 ret[8 .. 12] = h[2].ctfeUIntToBigEndian; 95 ret[12 .. 16] = h[3].ctfeUIntToBigEndian; 96 ret[16 .. 20] = h[4].ctfeUIntToBigEndian; 97 return ret; 98 } 99 100 uint[5] h = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]; 101 } 102 103 ubyte[20] sha1Of(in char[] s) pure nothrow 104 { 105 return sha1Of(cast(ubyte[]) s); 106 } 107 108 ubyte[20] sha1Of(ubyte[] bytes) @safe pure nothrow 109 { 110 SHA1 builder; 111 auto len = bytes.length; 112 bytes ~= 0b1000_0000; 113 if (bytes.length % 64 > 56) 114 bytes.length = (bytes.length & ~63) + 128; 115 else 116 bytes.length = (bytes.length & ~63) + 64; 117 ubyte[8] b = (cast(ulong) len * 8).ctfeULongToBigEndian; 118 bytes[$ - 8 .. $] = b; 119 auto blocks = bytes.length / 64; 120 assert(bytes.length % 64 == 0); 121 for (int i = 0; i < blocks; i++) 122 builder.put(bytes[i * 64 .. i * 64 + 64][0 .. 64]); 123 return builder.sum; 124 } 125 126 UUID sha1UUID(in char[] data, const UUID namespace = UUID.init) pure nothrow 127 { 128 return sha1UUID(cast(ubyte[]) data, namespace); 129 } 130 131 UUID sha1UUID(ubyte[] data, const UUID namespace = UUID.init) @safe pure nothrow 132 { 133 auto hash = sha1Of(namespace.data ~ data); 134 auto u = UUID(); 135 u.data[] = hash[0 .. 16]; 136 137 //set variant 138 //must be 0b10xxxxxx 139 u.data[8] &= 0b10111111; 140 u.data[8] |= 0b10000000; 141 142 //set version 143 //must be 0b0101xxxx 144 u.data[6] &= 0b01011111; 145 u.data[6] |= 0b01010000; 146 147 return u; 148 } 149 150 unittest 151 { 152 import std.digest.digest; 153 import std.digest.sha : sha1Orig = sha1Of; 154 import std.uuid : origUUID = sha1UUID; 155 156 string longString() 157 { 158 char[] ret; 159 for (int i = 0; i < 1652; i++) 160 ret ~= cast(char) i; 161 return ret.idup; 162 } 163 164 enum test = longString; 165 enum result = sha1Of(test); 166 enum uuid = sha1UUID(test); 167 enum namespace = sha1UUID("my.app"); 168 enum namespacedUuid = sha1UUID(test, namespace); 169 assert(sha1Orig(test) == result, "\n" ~ sha1Orig(test) 170 .toHexString ~ " !=\n" ~ result.toHexString); 171 assert(origUUID(test) == uuid, "\n" ~ origUUID(test).toString ~ " !=\n" ~ uuid.toString); 172 assert(origUUID(test, namespace) == namespacedUuid, "\n" ~ origUUID(test, 173 namespace).toString ~ " !=\n" ~ namespacedUuid.toString); 174 } 175 176 /// README 177 unittest 178 { 179 enum hash = sha1Of(cast(ubyte[])[0, 1, 2, 3]); // Binary Data 180 enum hash2 = sha1Of("Hello World"); // String 181 enum namespace = sha1UUID("my.app"); // generates a std.uuid.UUID from string or binary 182 enum uuid = sha1UUID("interface1", namespace); // also with namespaces 183 }