1 /// Computes BLAKE2b and BLAKE2s hashes of arbitary data. 2 /// Reference: IETF RFC 7693 3 /// License: $(LINK2 www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 4 /// Authors: $(LINK2 github.com/dd86k, dd86k) 5 module blake2d; 6 7 private import std.digest; 8 private import core.bitop : ror, bswap; 9 10 // "For BLAKE2b, the two extra permutations for rounds 10 and 11 are 11 // SIGMA[10..11] = SIGMA[0..1]." 12 private immutable ubyte[16][12] SIGMA = [ 13 [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ], 14 [ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, ], 15 [ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, ], 16 [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, ], 17 [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, ], 18 [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, ], 19 [ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, ], 20 [ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, ], 21 [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, ], 22 [ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, ], 23 [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ], 24 [ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, ], 25 ]; 26 27 private 28 immutable ulong[8] B2B_IV = [ 29 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 30 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 31 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 32 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 33 ]; 34 35 private 36 immutable uint[8] B2S_IV = [ 37 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 38 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 39 ]; 40 41 /// Used with the BLAKE2 structure template to make the BLAKE2s and BLAKE2p 42 /// aliases. 43 enum BLAKE2Variant { 44 b, /// BLAKE2b (default) 45 s, /// BLAKE2s 46 } 47 48 /// BLAKE2 structure template. 49 /// 50 /// It is recommended to use the BLAKE2p512 and BLAKE2s256 aliases. 51 /// However, if you wish to use a custom digest size, this is the structure 52 /// to use. 53 /// 54 /// Example definitions for BLAKE2s-160: 55 /// --- 56 /// alias BLAKE2s160 = BLAKE2!(BLAKE2Variant.s, 160); 57 /// auto blake2s160_Of(T...)(T data) { return digest!(BLAKE2s160, T)(data); } 58 /// public alias BLAKE2s160Digest = WrapperDigest!BLAKE2s160; 59 /// --- 60 /// Params: 61 /// var = BLAKE2 hash variation. 62 /// digestSize = Digest size in bits. 63 // key = HMAC key. This is a temporary hack to allow HMAC usage. 64 struct BLAKE2(BLAKE2Variant var, uint digestSize/*, const(ubyte)[] key = null*/) 65 { 66 @safe: 67 @nogc: 68 nothrow: 69 pure: 70 71 static assert(digestSize > 0, 72 "Digest size must be non-zero."); 73 static assert(digestSize % 8 == 0, 74 "Digest size must be dividable by 8."); 75 76 static if (var == BLAKE2Variant.b) { // BLAKE2b 77 // 8 to 512 bits 78 static assert(digestSize >= 8 && digestSize <= 512, 79 "BLAKE2b digest size must be between 8 and 512 bits."); 80 81 private enum MAXED = digestSize == 512; 82 private enum BSIZE = 128; /// bb 83 private enum ROUNDS = 12; 84 private enum R1 = 32; 85 private enum R2 = 24; 86 private enum R3 = 16; 87 private enum R4 = 63; 88 alias IV = B2B_IV; 89 alias inner_t = ulong; 90 } else static if (var == BLAKE2Variant.s) { // BLAKE2s 91 // 8 to 256 bits 92 static assert(digestSize >= 8 && digestSize <= 256, 93 "BLAKE2s digest size must be between 8 and 256 bits."); 94 95 private enum MAXED = digestSize == 256; 96 private enum BSIZE = 64; /// bb 97 private enum ROUNDS = 10; 98 private enum R1 = 16; 99 private enum R2 = 12; 100 private enum R3 = 8; 101 private enum R4 = 7; 102 alias IV = B2S_IV; 103 alias inner_t = uint; 104 } else static assert(0, "Invalid BLAKE2 variant."); 105 106 enum blockSize = digestSize; /// Digest size in bits 107 108 /// Initiate or reset the state of the structure. 109 void start() 110 { 111 this = typeof(this).init; 112 } 113 114 /// Feed the algorithm with data. 115 /// Also implements the $(REF isOutputRange, std,range,primitives) 116 /// interface for `ubyte` and `const(ubyte)[]`. 117 /// Params: input = Input data to digest 118 void put(scope const(ubyte)[] input...) @trusted 119 { 120 version (BLAKE2Trace) writefln("put() len=%u", input.length); 121 122 // Process wordwise if properly aligned. 123 if ((c | cast(size_t) input.ptr) % size_t.alignof == 0) 124 { 125 foreach (const word; (cast(size_t*) input.ptr)[0 .. input.length / size_t.sizeof]) 126 { 127 if (c >= BSIZE) 128 { 129 t[0] += c; 130 if (t[0] < c) ++t[1]; // Overflow 131 compress(); 132 c = 0; 133 } 134 mz.ptr[c / size_t.sizeof] = word; 135 c += size_t.sizeof; 136 } 137 input = input.ptr[input.length - (input.length % size_t.sizeof) .. input.length]; 138 } 139 140 // Process remainder bytewise. 141 foreach (const i; input) 142 { 143 if (c >= BSIZE) 144 { 145 t[0] += c; 146 if (t[0] < c) ++t[1]; // Overflow 147 compress(); 148 c = 0; 149 } 150 m8[c++] = i; 151 } 152 } 153 154 /// Returns the finished hash. 155 /// Returns: Raw digest data. 156 ubyte[digestSizeBytes] finish() 157 { 158 // final counter update 159 t[0] += c; 160 if (t[0] < c) ++t[1]; 161 162 // 0-pad message buffer 163 m8[c..$] = 0; 164 last = 1; 165 compress(); 166 167 // Clear out possible sensitive data 168 t[0] = t[1] = c = 0; // clear size information 169 mz[] = 0; // clear input message buffer 170 // Only clear remaining of state if digest not at maximum. 171 // e.g., BLAKE2b-512 has a digest size of 64 bytes 172 // ulong[8] (8*8) is 64 bytes of state 173 // So is BLAKE2x-256 is used, a digest of 32 bytes 174 // state (h) will still be 64 bytes, so upper range is cleared 175 static if (MAXED == false) 176 h8[digestSizeBytes..$] = 0; // clear unused state space 177 178 return h8[0..digestSizeBytes]; 179 } 180 181 private: 182 183 /*static if (key) 184 { 185 enum KEYLEN = key.length; 186 } 187 else 188 enum KEYLEN = 0;*/ 189 190 public ubyte[32] key; 191 192 enum digestSizeBytes = digestSize / 8; 193 // 3 2 1 0 194 // p[0] = 0x0101kknn 195 // kk - Key size. Set to zero since HMAC is done elsewhere. 196 // nn - Digest size in bytes. 197 enum p0 = 0x0101_0000 ^ (0 << 8) ^ digestSizeBytes; 198 enum msz = 16 * inner_t.sizeof; /// message size in bytes 199 enum hsz = 8 * inner_t.sizeof; /// state size in bytes 200 201 static assert(msz == BSIZE); // e.g. 128 for b2b and 64 for b2s 202 static assert(hsz == BSIZE / 2); // e.g., 64 for b2b and 32 for b2s 203 204 union // input message buffer 205 { 206 size_t[BSIZE / size_t.sizeof] mz = void; 207 inner_t[16] m; /// Message 208 ubyte[16 * inner_t.sizeof] m8; /// Message in byte-size 209 } 210 union // state 211 { 212 struct // .init hack since start() can't cover this 213 { 214 inner_t h0 = IV[0] ^ p0; 215 inner_t h1 = IV[1]; 216 inner_t h2 = IV[2]; 217 inner_t h3 = IV[3]; 218 inner_t h4 = IV[4]; 219 inner_t h5 = IV[5]; 220 inner_t h6 = IV[6]; 221 inner_t h7 = IV[7]; 222 } 223 inner_t[8] h; /// State 224 ubyte[8 * inner_t.sizeof] h8; /// State in byte-size 225 } 226 inner_t[2] t; /// Total count of input size 227 size_t c; /// Counter, pointer for buffer 228 uint last; /// If set, this is the last block to compress. 229 230 void compress() 231 { 232 //TODO: bswap m on BigEndian platforms? 233 234 inner_t[16] v = void; 235 v[0] = h[0]; 236 v[1] = h[1]; 237 v[2] = h[2]; 238 v[3] = h[3]; 239 v[4] = h[4]; 240 v[5] = h[5]; 241 v[6] = h[6]; 242 v[7] = h[7]; 243 v[8] = IV[0]; 244 v[9] = IV[1]; 245 v[10] = IV[2]; 246 v[11] = IV[3]; 247 v[12] = t[0] ^ IV[4]; 248 v[13] = t[1] ^ IV[5]; 249 v[14] = last ? ~IV[6] : IV[6]; 250 v[15] = IV[7]; 251 252 // See i=0 v[16] 253 254 for (size_t round; round < ROUNDS; ++round) 255 { 256 // a b c d x y 257 G(v, 0, 4, 8, 12, m[SIGMA[round][ 0]], m[SIGMA[round][ 1]]); 258 G(v, 1, 5, 9, 13, m[SIGMA[round][ 2]], m[SIGMA[round][ 3]]); 259 G(v, 2, 6, 10, 14, m[SIGMA[round][ 4]], m[SIGMA[round][ 5]]); 260 G(v, 3, 7, 11, 15, m[SIGMA[round][ 6]], m[SIGMA[round][ 7]]); 261 G(v, 0, 5, 10, 15, m[SIGMA[round][ 8]], m[SIGMA[round][ 9]]); 262 G(v, 1, 6, 11, 12, m[SIGMA[round][10]], m[SIGMA[round][11]]); 263 G(v, 2, 7, 8, 13, m[SIGMA[round][12]], m[SIGMA[round][13]]); 264 G(v, 3, 4, 9, 14, m[SIGMA[round][14]], m[SIGMA[round][15]]); 265 266 // See i=1..i=10/12 v[16] 267 } 268 269 h[0] ^= v[0] ^ v[8]; 270 h[1] ^= v[1] ^ v[9]; 271 h[2] ^= v[2] ^ v[10]; 272 h[3] ^= v[3] ^ v[11]; 273 h[4] ^= v[4] ^ v[12]; 274 h[5] ^= v[5] ^ v[13]; 275 h[6] ^= v[6] ^ v[14]; 276 h[7] ^= v[7] ^ v[15]; 277 278 // See h[8] 279 } 280 281 static void G(ref inner_t[16] v, uint a, uint b, uint c, uint d, inner_t x, inner_t y) 282 { 283 v[a] = v[a] + v[b] + x; 284 v[d] = ror(v[d] ^ v[a], R1); 285 v[c] = v[c] + v[d]; 286 v[b] = ror(v[b] ^ v[c], R2); 287 v[a] = v[a] + v[b] + y; 288 v[d] = ror(v[d] ^ v[a], R3); 289 v[c] = v[c] + v[d]; 290 v[b] = ror(v[b] ^ v[c], R4); 291 } 292 } 293 294 /// Alias for BLAKE2b-512 295 public alias BLAKE2b512 = BLAKE2!(BLAKE2Variant.b, 512); 296 /// Alias for BLAKE2s-256 297 public alias BLAKE2s256 = BLAKE2!(BLAKE2Variant.s, 256); 298 299 /// Convience alias for $(REF digest, std,digest) using the BLAKE2b-512 implementation. 300 auto blake2b_Of(T...)(T data) { return digest!(BLAKE2b512, T)(data); } 301 /// Alias of blake2b_Of. 302 alias blake2_Of = blake2b_Of; 303 /// Convience alias for $(REF digest, std,digest) using the BLAKE2s-256 implementation. 304 auto blake2s_Of(T...)(T data) { return digest!(BLAKE2s256, T)(data); } 305 306 /// OOP API BLAKE2 implementation aliases. 307 public alias BLAKE2b512Digest = WrapperDigest!BLAKE2b512; 308 /// Ditto 309 public alias BLAKE2s256Digest = WrapperDigest!BLAKE2s256; 310 311 /// Of course they correspond to the digest API! 312 @safe unittest 313 { 314 assert(isDigest!BLAKE2b512); 315 assert(isDigest!BLAKE2s256); 316 } 317 318 /// Testing "Of" wrappers against digest wrappers. 319 @safe unittest 320 { 321 enum TEXT = "abc"; 322 323 ubyte[64] b2b = blake2b_Of(TEXT); 324 assert(b2b == digest!BLAKE2b512(TEXT)); 325 326 ubyte[32] b2s = blake2s_Of(TEXT); 327 assert(b2s == digest!BLAKE2s256(TEXT)); 328 } 329 330 /// Testing template API 331 @system unittest 332 { 333 import std.conv : hexString; 334 335 ubyte[] s = [ 'a', 'b', 'c' ]; 336 337 BLAKE2s256 b2s; 338 b2s.put(s); 339 assert(b2s.finish() == cast(ubyte[])hexString!( 340 "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982")); 341 } 342 343 /// Test against empty input 344 @safe unittest 345 { 346 assert(toHexString!(LetterCase.lower)(blake2b_Of("")) == 347 "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419"~ 348 "d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"); 349 assert(toHexString!(LetterCase.lower)(blake2s_Of("")) == 350 "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"); 351 } 352 353 /// Test against "abc" 354 @safe unittest 355 { 356 assert(toHexString!(LetterCase.lower)(blake2b_Of("abc")) == 357 "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~ 358 "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); 359 assert(toHexString!(LetterCase.lower)(blake2s_Of("abc")) == 360 "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"); 361 } 362 363 364 /// Testing template API against one million 'a' 365 @system unittest 366 { 367 import std.conv : hexString; 368 369 ubyte[] onemilliona = new ubyte[1_000_000]; 370 onemilliona[] = 'a'; 371 372 BLAKE2b512 b2b; 373 b2b.put(onemilliona); 374 assert(b2b.finish() == cast(ubyte[]) hexString!( 375 "98fb3efb7206fd19ebf69b6f312cf7b64e3b94dbe1a17107913975a793f177e1"~ 376 "d077609d7fba363cbba00d05f7aa4e4fa8715d6428104c0a75643b0ff3fd3eaf")); 377 378 BLAKE2s256 b2s; 379 b2s.put(onemilliona); 380 assert(b2s.finish() == cast(ubyte[]) hexString!( 381 "bec0c0e6cde5b67acb73b81f79a67a4079ae1c60dac9d2661af18e9f8b50dfa5")); 382 } 383 384 /// Testing OOP API 385 @system unittest 386 { 387 import std.conv : hexString; 388 389 ubyte[] s = ['a', 'b', 'c']; 390 391 BLAKE2b512Digest b2b = new BLAKE2b512Digest(); 392 b2b.put(s); 393 assert(b2b.finish() == cast(ubyte[]) hexString!( 394 "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~ 395 "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923")); 396 397 BLAKE2s256Digest b2s = new BLAKE2s256Digest(); 398 b2s.put(s); 399 assert(b2s.finish() == cast(ubyte[]) hexString!( 400 "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982")); 401 } 402 403 /// BLAKE2s Template usage 404 @system unittest 405 { 406 import std.conv : hexString; 407 408 // Let's use the template features: 409 // NOTE: When passing a digest to a function, it must be passed by reference! 410 void doSomething(T)(ref T hash) 411 if (isDigest!T) 412 { 413 hash.put([ 'a', 'b', 'c' ]); 414 } 415 BLAKE2b512 b2b; 416 b2b.start(); 417 doSomething(b2b); 418 assert(b2b.finish() == cast(ubyte[]) hexString!( 419 "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~ 420 "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923")); 421 BLAKE2s256 b2s; 422 b2s.start(); 423 doSomething(b2s); 424 assert(b2s.finish() == cast(ubyte[]) hexString!( 425 "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982")); 426 } 427 428 /// Testing with HMAC 429 /*@system unittest 430 { 431 import std.ascii : LetterCase; 432 import std.digest.hmac : hmac; 433 import std.string : representation; 434 435 auto secret = "secret".representation; 436 437 // Temporary hack 438 //alias HMACBLAKE2b512 = BLAKE2!(BLAKE2Variant.b, 512, "secret".representation); 439 //alias HMACBLAKE2s256 = BLAKE2!(BLAKE2Variant.s, 256, "secret".representation); 440 441 assert("The quick brown fox jumps over the lazy dog" 442 .representation 443 .hmac!BLAKE2b512(secret) 444 .toHexString!(LetterCase.lower) == 445 "97504d0493aaaa40b08cf700fd380f17fe32e26e008fa20f9f3f04901d9f5bf3"~ 446 "3e826ea234f93bedfe7c5c50a540ad61454eb011581194cd68bff57938760ae0"); 447 assert("The quick brown fox jumps over the lazy dog" 448 .representation 449 .hmac!BLAKE2s256(secret) 450 .toHexString!(LetterCase.lower) == 451 "e95b806f87e9477966cd5f0ca2d496bfdfa424c69e820d33e4f1007aeb6c9de1"); 452 }*/