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