1 /// Port of C keccak-tiny library 
2 /// Implements keccakf-1600 functions
3 /// See_Also: https://github.com/IoTone/keccak-tiny
4 /// Authors: Tynuk
5 
6 module keccak;
7 
8 private static immutable ubyte[24] rho = [
9     1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39,
10     61, 20, 44
11 ];
12 private static immutable ubyte[24] pi = [
13     10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22,
14     9, 6, 1
15 ];
16 private static immutable ulong[24] RC = [
17     1, 0x8082, 0x800000000000808a, 0x8000000080008000, 0x808b, 0x80000001,
18     0x8000000080008081, 0x8000000000008009, 0x8a, 0x88, 0x80008009,
19     0x8000000a, 0x8000808b, 0x800000000000008b, 0x8000000000008089,
20     0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x800a,
21     0x800000008000000a, 0x8000000080008081, 0x8000000000008080, 0x80000001,
22     0x8000000080008008
23 ];
24 
25 private enum PLEN = 200;
26 
27 private auto rol(T)(T x, T s)
28 {
29     return ((x) << s) | ((x) >> (64 - s));
30 }
31 
32 pragma(inline, true) private struct State
33 {
34     import std.bitmanip;
35 
36     ubyte[PLEN] a;
37 
38     void opIndexAssign(ulong v, size_t i) pure nothrow @safe @nogc
39     {
40         i *= ulong.sizeof;
41         a[i .. i + ulong.sizeof] = v.nativeToLittleEndian;
42     }
43 
44     ulong opIndex(size_t i) pure nothrow @safe @nogc
45     {
46         i *= ulong.sizeof;
47         ubyte[ulong.sizeof] t;
48         t[] = a[i .. i + ulong.sizeof];
49         return t.littleEndianToNative!ulong;
50 
51     }
52 
53     void opIndexOpAssign(string op)(ulong v, size_t i) pure nothrow @safe @nogc
54     {
55         mixin(q{this[i] = this[i] } ~ op ~ q{ v;});
56     }
57 }
58 
59 /*** Keccak-f[1600] ***/
60 private void keccakf(ref State a) pure nothrow @safe @nogc
61 {
62     ulong[5] b;
63     ulong t = 0;
64     ubyte x, y;
65     static foreach (i; 0 .. 24)
66     {
67         // Theta
68         x = 0;
69         static foreach (_; 0 .. 5)
70         {
71             b[x] = 0;
72             y = 0;
73             static foreach (_; 0 .. 5)
74             {
75                 b[x] ^= a[x + y];
76                 y += 5;
77             }
78             x++;
79         }
80         x = 0;
81         static foreach (_; 0 .. 5)
82         {
83             y = 0;
84             static foreach (_; 0 .. 5)
85             {
86                 a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1);
87                 y += 5;
88             }
89             x++;
90         }
91         // Rho and pi
92         t = a[1];
93         x = 0;
94         static foreach (_; 0 .. 24)
95         {
96             b[0] = a[pi[x]];
97             a[pi[x]] = rol(t, rho[x]);
98             t = b[0];
99             x++;
100         }
101         // Chi
102         y = 0;
103         static foreach (_; 0 .. 5)
104         {
105             x = 0;
106             static foreach (_; 0 .. 5)
107             {
108                 b[x] = a[y + x];
109                 x++;
110             }
111             x = 0;
112             static foreach (_; 0 .. 5)
113             {
114                 a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]);
115                 x++;
116             }
117             y += 5;
118         }
119         // Iota
120         a[0] ^= RC[i];
121     }
122 }
123 
124 private void xorin(ubyte[] dst, const ubyte[] src, ulong len) pure nothrow @safe @nogc
125 {
126     foreach (i; 0 .. len)
127     {
128         dst[i] ^= src[i];
129     }
130 }
131 
132 /// used for create keccak family functions
133 /// Params:
134 ///   dst = destination for hash result
135 ///   outlen = hash size wanted
136 ///   src = value used for hash
137 ///   inlen = size of value
138 /// Returns: error code -1 if failed
139 /// Deprecated: use hashImpl instead
140 int hash(ubyte delim, ulong bits, bool checkSize = true)(ubyte* dst,
141     ulong outlen, ubyte* src, ulong inlen) pure nothrow @trusted @nogc
142 {
143     return hashImpl!(delim, bits, checkSize)(dst[0 .. outlen], src[0 .. inlen]);
144 }
145 
146 /// ditto
147 alias keccak(ulong SIZE) = hash!(0x01, SIZE);
148 
149 ///
150 @trusted unittest
151 {
152     ubyte[5] source = [0xff, 0xa1, 0x12, 0x22, 0xff];
153     ubyte[256 / 8] result;
154     ubyte[256 / 8] result2;
155     assert(hash!(0x01, 256)(result.ptr, result.length, source.ptr, source.length) == 0);
156     assert(keccak!(256)(result2.ptr, result2.length, source.ptr, source.length) == 0);
157     assert(result[] == result2[]);
158 }
159 
160 /// used for create keccak family functions
161 /// Params:
162 ///   dst = destination for hash result
163 ///   src = value used for hash
164 /// Returns: error code -1 if failed
165 pure nothrow @safe @nogc int hashImpl(ubyte delim, ulong bits, bool checkSize = true)(
166     ubyte[] dst, const(ubyte)[] src)
167 {
168     static immutable rate = PLEN - (bits / 4);
169     import core.stdc.string : memset, strncpy;
170 
171     static if (checkSize)
172     {
173         if (dst.length > (bits / 8))
174         {
175             return -1;
176         }
177     }
178     static assert(rate < PLEN, "Rate should be less than 200");
179     if ((dst == null) || ((src == null) && src.length != 0))
180     {
181         return -1;
182     }
183 
184     State s;
185     // Absorb input.
186     while (src.length >= rate)
187     {
188         xorin(s.a[], src, rate);
189         keccakf(s);
190         src = src[rate .. $];
191     }
192     // Xor in the DS and pad frame.
193     s.a[src.length] ^= delim;
194     s.a[rate - 1] ^= 0x80;
195     // Xor in the last block.
196     xorin(s.a[], src, src.length);
197     // Apply P
198     keccakf(s);
199     // Squeeze output.
200     while (dst.length >= rate)
201     {
202         dst[0 .. rate] = s.a[0 .. rate];
203         keccakf(s);
204         dst = dst[rate .. $];
205     }
206     dst[0 .. dst.length] = s.a[0 .. dst.length];
207     s.a[0 .. PLEN] = 0;
208     return 0;
209 }
210 
211 /// ditto
212 alias keccakImpl(ulong SIZE) = hashImpl!(0x01, SIZE);
213 
214 ///
215 @trusted unittest
216 {
217     ubyte[5] source = [0xff, 0xa1, 0x12, 0x22, 0xff];
218     ubyte[256 / 8] result;
219     ubyte[256 / 8] result2;
220     assert(hashImpl!(0x01, 256)(result[], source[]) == 0);
221     assert(keccakImpl!(256)(result2[], source[]) == 0);
222     assert(result[] == result2[]);
223 }
224 
225 /// Instance if keccak function
226 alias keccak_256 = keccak!256;
227 /// ditto
228 alias keccak_224 = keccak!224;
229 /// ditto
230 alias keccak_384 = keccak!384;
231 /// ditto
232 alias keccak_512 = keccak!512;
233 
234 ///
235 @safe unittest
236 {
237     ubyte[5] source = [0xff, 0xa1, 0x12, 0x22, 0xff];
238     ubyte[224 / 8] result;
239     ubyte[256 / 8] result2;
240     assert(keccak_224(result.ptr, result.length, source.ptr, source.length) == 0);
241     assert(keccak_256(result2.ptr, result2.length, source.ptr, source.length) == 0);
242 }
243 
244 
245 /// Used for create keccak functions
246 /// Params:
247 ///   data = The value used for hash
248 /// Returns: static array with result of hashing
249 auto keccakD(ulong SIZE)(const ubyte[] data) pure nothrow @safe @nogc
250 {
251     ubyte[SIZE / 8] hash;
252     keccakImpl!SIZE(hash[], data);
253     return hash;
254 }
255 ///
256 @safe unittest
257 {
258     ubyte[5] source = [0xff, 0xa1, 0x12, 0x22, 0xff];
259     ubyte[256 / 8] result;
260     result = keccakD!256(source[]);
261 }
262 
263 /// Instance of keccakD function
264 alias keccak256 = keccakD!256;
265 /// ditto
266 alias keccak224 = keccakD!224;
267 /// ditto
268 alias keccak384 = keccakD!384;
269 /// ditto
270 alias keccak512 = keccakD!512;
271 ///
272 @safe unittest
273 {
274     ubyte[5] source = [0xff, 0xa1, 0x12, 0x22, 0xff];
275     ubyte[256 / 8] result;
276     result = keccak256(source[]);
277 }
278 
279 
280 unittest
281 {
282     import std;
283 
284     ubyte[] a = cast(ubyte[]) "hello".to!(char[]);
285     ubyte[4] b;
286 
287     assert(keccak_224(b.ptr, b.length, a.ptr, a.length) != -1);
288     auto hash = b[].map!(e => e.to!string(16)).join;
289     assert("45524EC4" == hash);
290 
291     assert(keccak_256(b.ptr, b.length, a.ptr, a.length) != -1);
292     hash = b[].map!(e => e.to!string(16)).join;
293     assert("1C8AFF95" == hash);
294 
295     assert(keccak_384(b.ptr, b.length, a.ptr, a.length) != -1);
296     hash = b[].map!(e => e.to!string(16)).join;
297     assert("DCEF6FB7" == hash);
298 
299     assert(keccak_512(b.ptr, b.length, a.ptr, a.length) != -1);
300     hash = b[].map!(e => e.to!string(16)).join;
301     assert("52FA8066" == hash);
302 }
303 
304 unittest
305 {
306     import std;
307 
308     ubyte[] a = cast(ubyte[]) "hello".to!(char[]);
309     {
310         auto b = keccak224(a);
311 
312         auto hash = b[0 .. 4].map!(e => e.to!string(16)).join;
313         assert("45524EC4" == hash);
314     }
315     {
316         auto b = keccak256(a);
317 
318         auto hash = b[0 .. 4].map!(e => e.to!string(16)).join;
319         assert("1C8AFF95" == hash);
320     }
321     {
322         auto b = keccak384(a);
323 
324         auto hash = b[0 .. 4].map!(e => e.to!string(16)).join;
325         assert("DCEF6FB7" == hash);
326     }
327     {
328         auto b = keccak512(a);
329 
330         auto hash = b[0 .. 4].map!(e => e.to!string(16)).join;
331         assert("52FA8066" == hash);
332     }
333 }
334 
335 unittest
336 {
337     import std.datetime.stopwatch : benchmark;
338     import std : uniform;
339     import std : writeln;
340 
341     string t = import(`testText.txt`);
342 
343     benchmark!({
344         ubyte[32] o;
345         keccak_256(o.ptr, o.length, cast(ubyte*) t.ptr, t.length);
346     })(1).writeln;
347     benchmark!({
348         ubyte[32] o;
349         immutable s = uniform(0, cast(long) t.length - 600);
350         auto slice = t[s .. s + 600];
351         keccak_256(o.ptr, o.length, cast(ubyte*) slice.ptr, slice.length);
352     })(10_000).writeln;
353 }
354 
355 static assert(keccak256(cast(ubyte[]) "hello")[0] == cast(ubyte) 28u);