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);