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 }