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