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