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 }