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.2.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: @nogc: nothrow: pure: 69 70 static assert(digestSize > 0, 71 "Digest size must be non-zero."); 72 static assert(digestSize % 8 == 0, 73 "Digest size must be dividable by 8."); 74 75 static if (var == BLAKE2Variant.b) { // BLAKE2b 76 // 8 to 512 bits 77 static assert(digestSize >= 8 && digestSize <= 512, 78 "BLAKE2b digest size must be between 8 and 512 bits."); 79 80 private enum MAXKEYSIZE = 32; // In bytes 81 private enum MAXED = digestSize == 512; 82 private enum BSIZE = 128; /// Buffer size, "bb" variable 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 private alias IV = B2B_IV; 89 private 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 MAXKEYSIZE = 32; // In bytes 96 private enum MAXED = digestSize == 256; 97 private enum BSIZE = 64; /// Buffer size, "bb" variable 98 private enum ROUNDS = 10; 99 private enum R1 = 16; 100 private enum R2 = 12; 101 private enum R3 = 8; 102 private enum R4 = 7; 103 private alias IV = B2S_IV; 104 private alias inner_t = uint; 105 } else static assert(0, "Invalid BLAKE2 variant."); 106 107 enum blockSize = digestSize; /// Digest size in bits 108 109 /// Initiate or reset the state of the structure. 110 void start() 111 { 112 this = typeof(this).init; 113 } 114 115 /// Initiates a key with digest. 116 /// This is meant to be used after the digest initiation. 117 /// The key limit is 64 bytes for BLAKE2b and 32 bytes for 118 /// BLAKE2s. If the limit is reached, it fails silenty by truncating 119 /// key data. 120 /// Params: input = Key. 121 void key(scope const(ubyte)[] input) 122 { 123 enum MASK = BSIZE - 1; 124 h[0] ^= ((input.length & MASK) << 8); 125 put(input.length > BSIZE ? input[0..BSIZE] : input); 126 c = BSIZE; 127 } 128 129 /// Feed the algorithm with data. 130 /// Also implements the $(REF isOutputRange, std,range,primitives) 131 /// interface for `ubyte` and `const(ubyte)[]`. 132 /// Params: input = Input data to digest 133 void put(scope const(ubyte)[] input...) @trusted 134 { 135 // Process wordwise if properly aligned. 136 if ((c | cast(size_t) input.ptr) % size_t.alignof == 0) 137 { 138 foreach (const word; (cast(size_t*) input.ptr)[0 .. input.length / size_t.sizeof]) 139 { 140 if (c >= BSIZE) 141 { 142 t[0] += c; 143 if (t[0] < c) ++t[1]; // Overflow 144 compress(); 145 c = 0; 146 } 147 mz.ptr[c / size_t.sizeof] = word; 148 c += size_t.sizeof; 149 } 150 input = input.ptr[input.length - (input.length % size_t.sizeof) .. input.length]; 151 } 152 153 // Process remainder bytewise. 154 foreach (const i; input) 155 { 156 if (c >= BSIZE) 157 { 158 t[0] += c; 159 if (t[0] < c) ++t[1]; // Overflow 160 compress(); 161 c = 0; 162 } 163 m8[c++] = i; 164 } 165 } 166 167 /// Returns the finished hash. 168 /// Returns: Raw digest data. 169 ubyte[digestSizeBytes] finish() 170 { 171 // final counter update 172 t[0] += c; 173 if (t[0] < c) ++t[1]; 174 175 // 0-pad message buffer 176 m8[c..$] = 0; 177 last = 1; 178 compress(); 179 180 // Clear out possible sensitive data 181 t[0] = t[1] = c = 0; // clear size information 182 mz[] = 0; // clear input message buffer 183 // Only clear remaining of state if digest isn't fulled used. 184 // e.g., BLAKE2b-512 has a digest size of 64 bytes 185 // ulong[8] (8*8) is 64 bytes of state 186 // So is BLAKE2x-256 is used, a digest of 32 bytes 187 // state (h) will still be 64 bytes, so upper range is cleared 188 static if (MAXED == false) 189 h8[digestSizeBytes..$] = 0; // clear unused state space 190 191 return h8[0..digestSizeBytes]; 192 } 193 194 private: 195 196 enum digestSizeBytes = digestSize / 8; 197 // 3 2 1 0 198 // p[0] = 0x0101kknn 199 // kk - Key size. Set to zero since HMAC is done elsewhere. 200 // nn - Digest size in bytes. 201 enum p0 = 0x0101_0000 ^ digestSizeBytes; 202 enum msz = 16 * inner_t.sizeof; /// message size in bytes 203 enum hsz = 8 * inner_t.sizeof; /// state size in bytes 204 205 static assert(msz == BSIZE); // e.g. 128 for b2b and 64 for b2s 206 static assert(hsz == BSIZE / 2); // e.g., 64 for b2b and 32 for b2s 207 208 union // input message buffer 209 { 210 size_t[BSIZE / size_t.sizeof] mz = void; 211 inner_t[16] m; /// Message 212 ubyte[16 * inner_t.sizeof] m8; /// Message in byte-size 213 } 214 union // state 215 { 216 struct // .init hack since start() can't cover this 217 { 218 inner_t h0 = IV[0] ^ p0; 219 inner_t h1 = IV[1]; 220 inner_t h2 = IV[2]; 221 inner_t h3 = IV[3]; 222 inner_t h4 = IV[4]; 223 inner_t h5 = IV[5]; 224 inner_t h6 = IV[6]; 225 inner_t h7 = IV[7]; 226 } 227 inner_t[8] h; /// State 228 ubyte[8 * inner_t.sizeof] h8; /// State in byte-size 229 } 230 inner_t[2] t; /// Total count of input size 231 size_t c; /// Counter, pointer for buffer 232 uint last; /// If set, this is the last block to compress. 233 234 void compress() 235 { 236 //TODO: bswap m on BigEndian platforms? 237 238 inner_t[16] v = void; 239 v[0] = h[0]; 240 v[1] = h[1]; 241 v[2] = h[2]; 242 v[3] = h[3]; 243 v[4] = h[4]; 244 v[5] = h[5]; 245 v[6] = h[6]; 246 v[7] = h[7]; 247 v[8] = IV[0]; 248 v[9] = IV[1]; 249 v[10] = IV[2]; 250 v[11] = IV[3]; 251 v[12] = t[0] ^ IV[4]; 252 v[13] = t[1] ^ IV[5]; 253 v[14] = last ? ~IV[6] : IV[6]; 254 v[15] = IV[7]; 255 256 // Assert i=0 v[16] 257 258 for (size_t round; round < ROUNDS; ++round) 259 { 260 // a b c d x y 261 G(v, 0, 4, 8, 12, m[SIGMA[round][ 0]], m[SIGMA[round][ 1]]); 262 G(v, 1, 5, 9, 13, m[SIGMA[round][ 2]], m[SIGMA[round][ 3]]); 263 G(v, 2, 6, 10, 14, m[SIGMA[round][ 4]], m[SIGMA[round][ 5]]); 264 G(v, 3, 7, 11, 15, m[SIGMA[round][ 6]], m[SIGMA[round][ 7]]); 265 G(v, 0, 5, 10, 15, m[SIGMA[round][ 8]], m[SIGMA[round][ 9]]); 266 G(v, 1, 6, 11, 12, m[SIGMA[round][10]], m[SIGMA[round][11]]); 267 G(v, 2, 7, 8, 13, m[SIGMA[round][12]], m[SIGMA[round][13]]); 268 G(v, 3, 4, 9, 14, m[SIGMA[round][14]], m[SIGMA[round][15]]); 269 270 // Assert i=1..i=10/12 v[16] 271 } 272 273 h[0] ^= v[0] ^ v[8]; 274 h[1] ^= v[1] ^ v[9]; 275 h[2] ^= v[2] ^ v[10]; 276 h[3] ^= v[3] ^ v[11]; 277 h[4] ^= v[4] ^ v[12]; 278 h[5] ^= v[5] ^ v[13]; 279 h[6] ^= v[6] ^ v[14]; 280 h[7] ^= v[7] ^ v[15]; 281 282 // Assert h[8] 283 } 284 285 static void G(ref inner_t[16] v, uint a, uint b, uint c, uint d, inner_t x, inner_t y) 286 { 287 v[a] = v[a] + v[b] + x; 288 v[d] = ror(v[d] ^ v[a], R1); 289 v[c] = v[c] + v[d]; 290 v[b] = ror(v[b] ^ v[c], R2); 291 v[a] = v[a] + v[b] + y; 292 v[d] = ror(v[d] ^ v[a], R3); 293 v[c] = v[c] + v[d]; 294 v[b] = ror(v[b] ^ v[c], R4); 295 } 296 } 297 298 /// Alias for BLAKE2b-512 299 public alias BLAKE2b512 = BLAKE2!(BLAKE2Variant.b, 512); 300 /// Alias for BLAKE2s-256 301 public alias BLAKE2s256 = BLAKE2!(BLAKE2Variant.s, 256); 302 303 /// Convience alias for $(REF digest, std,digest) using the BLAKE2b-512 implementation. 304 auto blake2b_Of(T...)(T data) { return digest!(BLAKE2b512, T)(data); } 305 /// Alias of blake2b_Of. 306 alias blake2_Of = blake2b_Of; 307 /// Convience alias for $(REF digest, std,digest) using the BLAKE2s-256 implementation. 308 auto blake2s_Of(T...)(T data) { return digest!(BLAKE2s256, T)(data); } 309 310 /// OOP API BLAKE2 implementation aliases. 311 public alias BLAKE2b512Digest = WrapperDigest!BLAKE2b512; 312 /// Ditto 313 public alias BLAKE2s256Digest = WrapperDigest!BLAKE2s256; 314 315 /// Of course they correspond to the digest API! 316 @safe unittest 317 { 318 assert(isDigest!BLAKE2b512); 319 assert(isDigest!BLAKE2s256); 320 } 321 322 /// Of course they have a blockSize! 323 @safe unittest 324 { 325 assert(hasBlockSize!BLAKE2b512); 326 assert(hasBlockSize!BLAKE2s256); 327 } 328 329 /// Testing "Of" wrappers against digest wrappers. 330 @safe unittest 331 { 332 enum TEXT = "abc"; 333 334 assert(blake2b_Of(TEXT) == digest!BLAKE2b512(TEXT)); 335 assert(blake2s_Of(TEXT) == digest!BLAKE2s256(TEXT)); 336 } 337 338 /// Testing template API 339 @system unittest 340 { 341 import std.conv : hexString; 342 343 ubyte[] s = [ 'a', 'b', 'c' ]; 344 345 BLAKE2s256 b2s; 346 b2s.put(s); 347 assert(b2s.finish() == cast(ubyte[])hexString!( 348 "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982")); 349 } 350 351 /// Test against empty input 352 @safe unittest 353 { 354 assert(toHexString!(LetterCase.lower)(blake2b_Of("")) == 355 "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419"~ 356 "d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"); 357 assert(toHexString!(LetterCase.lower)(blake2s_Of("")) == 358 "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"); 359 } 360 361 /// Test against "abc" 362 @safe unittest 363 { 364 assert(toHexString!(LetterCase.lower)(blake2b_Of("abc")) == 365 "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~ 366 "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); 367 assert(toHexString!(LetterCase.lower)(blake2s_Of("abc")) == 368 "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"); 369 } 370 371 372 /// Testing template API against one million 'a' 373 @system unittest 374 { 375 import std.conv : hexString; 376 377 ubyte[] onemilliona = new ubyte[1_000_000]; 378 onemilliona[] = 'a'; 379 380 BLAKE2b512 b2b; 381 b2b.put(onemilliona); 382 assert(b2b.finish() == cast(ubyte[]) hexString!( 383 "98fb3efb7206fd19ebf69b6f312cf7b64e3b94dbe1a17107913975a793f177e1"~ 384 "d077609d7fba363cbba00d05f7aa4e4fa8715d6428104c0a75643b0ff3fd3eaf")); 385 386 BLAKE2s256 b2s; 387 b2s.put(onemilliona); 388 assert(b2s.finish() == cast(ubyte[]) hexString!( 389 "bec0c0e6cde5b67acb73b81f79a67a4079ae1c60dac9d2661af18e9f8b50dfa5")); 390 } 391 392 /// Testing OOP API 393 @system unittest 394 { 395 import std.conv : hexString; 396 397 ubyte[] s = ['a', 'b', 'c']; 398 399 Digest b2b = new BLAKE2b512Digest(); 400 b2b.put(s); 401 assert(b2b.finish() == cast(ubyte[]) hexString!( 402 "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~ 403 "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923")); 404 405 Digest b2s = new BLAKE2s256Digest(); 406 b2s.put(s); 407 assert(b2s.finish() == cast(ubyte[]) hexString!( 408 "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982")); 409 } 410 411 /// BLAKE2s Template usage 412 @system unittest 413 { 414 import std.conv : hexString; 415 416 // Let's use the template features: 417 // NOTE: When passing a digest to a function, it must be passed by reference! 418 void doSomething(T)(ref T hash) 419 if (isDigest!T) 420 { 421 hash.put([ 'a', 'b', 'c' ]); 422 } 423 BLAKE2b512 b2b; 424 b2b.start(); 425 doSomething(b2b); 426 assert(b2b.finish() == cast(ubyte[]) hexString!( 427 "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~ 428 "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923")); 429 BLAKE2s256 b2s; 430 b2s.start(); 431 doSomething(b2s); 432 assert(b2s.finish() == cast(ubyte[]) hexString!( 433 "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982")); 434 } 435 436 /// Keying digests 437 @system unittest 438 { 439 // NOTE: BLAKE2 is a keyed hash and therefore not compatible with 440 // the hmac/HMAC templates, or OpenSSL does it differently. 441 442 import std.ascii : LetterCase; 443 import std.string : representation; 444 import std.conv : hexString; 445 446 auto secret2b = hexString!( 447 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"~ 448 "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f") 449 .representation; 450 auto secret2s = hexString!( 451 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") 452 .representation; 453 auto data = hexString!("000102").representation; 454 455 BLAKE2b512 b2b; 456 b2b.key(secret2b); 457 b2b.put(data); 458 assert(b2b.finish().toHexString!(LetterCase.lower) == 459 "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5"~ 460 "a17e364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1" 461 , "BLAKE2b+secret failed"); 462 463 BLAKE2s256 b2s; 464 b2s.key(secret2s); 465 b2s.put(data); 466 assert(b2s.finish().toHexString!(LetterCase.lower) == 467 "1d220dbe2ee134661fdf6d9e74b41704710556f2f6e5a091b227697445dbea6b" 468 , "BLAKE2s+secret failed"); 469 }