1 module keccak;
2 
3 static const ubyte[24] rho = [
4     1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39,
5     61, 20, 44
6 ];
7 static const ubyte[24] pi = [
8     10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22,
9     9, 6, 1
10 ];
11 static const ulong[24] RC = [
12     1, 0x8082, 0x800000000000808a, 0x8000000080008000, 0x808b, 0x80000001,
13     0x8000000080008081, 0x8000000000008009, 0x8a, 0x88, 0x80008009,
14     0x8000000a, 0x8000808b, 0x800000000000008b, 0x8000000000008089,
15     0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x800a,
16     0x800000008000000a, 0x8000000080008081, 0x8000000000008080, 0x80000001,
17     0x8000000080008008
18 ];
19 
20 enum PLEN = 200;
21 
22 auto rol(T)(T x, T s)
23 {
24     return ((x) << s) | ((x) >> (64 - s));
25 }
26 
27 struct State
28 {
29     import std.bitmanip;
30 
31     ubyte[PLEN] a;
32 
33     void opIndexAssign(ulong v, size_t i)
34     {
35         i *= ulong.sizeof;
36         a[i .. i + ulong.sizeof] = v.nativeToLittleEndian;
37     }
38 
39     ulong opIndex(size_t i)
40     {
41         i *= ulong.sizeof;
42         ubyte[ulong.sizeof] t;
43         t[] = a[i .. i + ulong.sizeof];
44         return t.littleEndianToNative!ulong;
45 
46     }
47 
48     void opIndexOpAssign(string op)(ulong v, size_t i)
49     {
50         mixin(q{this[i] = this[i] } ~ op ~ q{ v;});
51     }
52 }
53 
54 /*** Keccak-f[1600] ***/
55 static void keccakf(ref State a)
56 {
57     ulong[5] b;
58     ulong t = 0;
59     ubyte x, y;
60     static foreach (i; 0 .. 24)
61     {
62         // Theta
63         x = 0;
64         static foreach (_; 0 .. 5)
65         {
66             b[x] = 0;
67             y = 0;
68             static foreach (_; 0 .. 5)
69             {
70                 b[x] ^= a[x + y];
71                 y += 5;
72             }
73             x++;
74         }
75         x = 0;
76         static foreach (_; 0 .. 5)
77         {
78             y = 0;
79             static foreach (_; 0 .. 5)
80             {
81                 a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1);
82                 y += 5;
83             }
84             x++;
85         }
86         // Rho and pi
87         t = a[1];
88         x = 0;
89         static foreach (_; 0 .. 24)
90         {
91             b[0] = a[pi[x]];
92             a[pi[x]] = rol(t, rho[x]);
93             t = b[0];
94             x++;
95         }
96         // Chi
97         y = 0;
98         static foreach (_; 0 .. 5)
99         {
100             x = 0;
101             static foreach (_; 0 .. 5)
102             {
103                 b[x] = a[y + x];
104                 x++;
105             }
106             x = 0;
107             static foreach (_; 0 .. 5)
108             {
109                 a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]);
110                 x++;
111             }
112             y += 5;
113         }
114         // Iota
115         a[0] ^= RC[i];
116     }
117 }
118 
119 static void xorin(ubyte* dst, const ubyte* src, ulong len)
120 {
121     foreach (i; 0 .. len)
122     {
123         dst[i] ^= src[i];
124     }
125 }
126 
127 static int hash(ubyte delim, ulong bits, bool checkSize = true)(ubyte* dst,
128         ulong outlen, ubyte* src, ulong inlen)
129 {
130     static immutable rate = PLEN - (bits / 4);
131     import core.stdc.string : memset, strncpy;
132 
133     static if (checkSize)
134     {
135         if (outlen > (bits / 8))
136         {
137             return -1;
138         }
139     }
140     static assert(rate < PLEN, "Rate should be less than 200");
141     if ((dst == null) || ((src == null) && inlen != 0))
142     {
143         return -1;
144     }
145 
146     State s;
147     // Absorb input.
148     while (inlen >= rate)
149     {
150         xorin(s.a.ptr, src, rate);
151         keccakf(s);
152         src += rate;
153         inlen -= rate;
154     }
155     // Xor in the DS and pad frame.
156     s.a[inlen] ^= delim;
157     s.a[rate - 1] ^= 0x80;
158     // Xor in the last block.
159     xorin(s.a.ptr, src, inlen);
160     // Apply P
161     keccakf(s);
162     // Squeeze output.
163     while (outlen >= rate)
164     {
165         dst[0 .. rate] = s.a.ptr[0 .. rate];
166         keccakf(s);
167         dst += rate;
168         outlen -= rate;
169     }
170     dst[0 .. outlen] = s.a.ptr[0 .. outlen];
171     s.a.ptr[0 .. PLEN] = 0;
172     return 0;
173 }
174 
175 alias keccak(ulong SIZE) = hash!(0x01, SIZE);
176 
177 alias keccak_256 = keccak!256;
178 alias keccak_224 = keccak!224;
179 alias keccak_384 = keccak!384;
180 alias keccak_512 = keccak!512;
181 
182 auto keccakD(ulong SIZE)(ubyte[] data)
183 {
184     ubyte[SIZE / 8] hash;
185     keccak!SIZE(hash.ptr, hash.length, data.ptr, data.length);
186     return hash;
187 }
188 
189 alias keccak256 = keccakD!256;
190 alias keccak224 = keccakD!224;
191 alias keccak384 = keccakD!384;
192 alias keccak512 = keccakD!512;
193 
194 unittest
195 {
196     import std;
197 
198     "keccak test started".writeln;
199 
200     ubyte[] a = cast(ubyte[]) "hello".to!(char[]);
201     ubyte[4] b;
202 
203     assert(keccak_224(b.ptr, b.length, a.ptr, a.length) != -1);
204     auto hash = b[].map!(e => e.to!string(16)).join;
205     assert("45524EC4" == hash);
206 
207     assert(keccak_256(b.ptr, b.length, a.ptr, a.length) != -1);
208     hash = b[].map!(e => e.to!string(16)).join;
209     assert("1C8AFF95" == hash);
210 
211     assert(keccak_384(b.ptr, b.length, a.ptr, a.length) != -1);
212     hash = b[].map!(e => e.to!string(16)).join;
213     assert("DCEF6FB7" == hash);
214 
215     assert(keccak_512(b.ptr, b.length, a.ptr, a.length) != -1);
216     hash = b[].map!(e => e.to!string(16)).join;
217     assert("52FA8066" == hash);
218 }
219 
220 unittest
221 {
222     import std;
223 
224     "keccak test started".writeln;
225 
226     ubyte[] a = cast(ubyte[]) "hello".to!(char[]);
227     {
228         auto b = keccak224(a);
229 
230         auto hash = b[0 .. 4].map!(e => e.to!string(16)).join;
231         assert("45524EC4" == hash);
232     }
233     {
234         auto b = keccak256(a);
235 
236         auto hash = b[0 .. 4].map!(e => e.to!string(16)).join;
237         assert("1C8AFF95" == hash);
238     }
239     {
240         auto b = keccak384(a);
241 
242         auto hash = b[0 .. 4].map!(e => e.to!string(16)).join;
243         assert("DCEF6FB7" == hash);
244     }
245     {
246         auto b = keccak512(a);
247 
248         auto hash = b[0 .. 4].map!(e => e.to!string(16)).join;
249         assert("52FA8066" == hash);
250     }
251 }
252 
253 unittest
254 {
255     import std.datetime.stopwatch : benchmark;
256     import std : uniform;
257     import std : writeln;
258 
259     string t = import(`testText.txt`);
260 
261     benchmark!({
262         ubyte[32] o;
263         keccak_256(o.ptr, o.length, cast(ubyte*) t.ptr, t.length);
264     })(1).writeln;
265     benchmark!({
266         ubyte[32] o;
267         immutable s = uniform(0, cast(long) t.length - 600);
268         auto slice = t[s .. s + 600];
269         keccak_256(o.ptr, o.length, cast(ubyte*) slice.ptr, slice.length);
270     })(10_000).writeln;
271 }
272 
273 static assert(keccak256(cast(ubyte[]) "hello")[0] == cast(ubyte) 28u);