Skip to content

Commit 6bd9eb4

Browse files
committed
Support keying
1 parent b75d9f3 commit 6bd9eb4

File tree

3 files changed

+92
-57
lines changed

3 files changed

+92
-57
lines changed

README.md

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ BLAKE2 was introduced in 2015 as IETF RFC 7693. You can visit
99
This module:
1010

1111
- [x] Supports BLAKE2b-512 and BLAKE2s-256.
12-
- [x] Supports custom hash sizes.
13-
- [ ] Supports HMAC. (WIP!)
12+
- [x] Supports custom digest sizes.
13+
- [x] Supports keying.
14+
- [ ] Supports BLAKE2bp and BLAKE2sp.
15+
16+
NOTE: May be incompatible with HMAC.
1417

1518
Compatible and tested with DMD, GDC, and LDC.
1619

@@ -39,13 +42,12 @@ BLAKE2b512 b2b512;
3942
b2b512.put("abc");
4043
assert(b2b512.finish() == cast(ubyte[]) hexString!(
4144
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~
42-
"7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"
43-
));
45+
"7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"));
4446
b2b512.start(); // reset
4547
b2b512.put("abcdef");
4648
assert(b2b512.finish() == cast(ubyte[]) hexString!(
4749
"dde410524e3569b303e494aa82a3afb3e426f9df24c1398e9ff87aafbc2f5b7b"~
48-
"3c1a4c9400409de3b45d37a00e5eae2a93cc9c4a108b00f05217d41a424d2b8a");
50+
"3c1a4c9400409de3b45d37a00e5eae2a93cc9c4a108b00f05217d41a424d2b8a"));
4951
```
5052

5153
### OOP API
@@ -59,18 +61,34 @@ Digest dgst = new BLAKE2b512Digest();
5961
dgst.put("abc");
6062
assert(dgst.finish() == cast(ubyte[]) hexString!(
6163
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~
62-
"7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"
63-
));
64+
"7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"));
6465
dgst.start(); // reset
6566
dgst.put("abcdef");
6667
assert(dgst.finish() == cast(ubyte[]) hexString!(
6768
"dde410524e3569b303e494aa82a3afb3e426f9df24c1398e9ff87aafbc2f5b7b"~
68-
"3c1a4c9400409de3b45d37a00e5eae2a93cc9c4a108b00f05217d41a424d2b8a");
69+
"3c1a4c9400409de3b45d37a00e5eae2a93cc9c4a108b00f05217d41a424d2b8a"));
6970
```
7071

7172
There are numerous ways to avoid GC allocation. For example when only using a
7273
digest for a one-time use in a short scope, there's `std.typecons.scoped`.
7374

75+
### Keying
76+
77+
Currently, the only way to supply a key is with the `key` function:
78+
79+
```d
80+
import std.string : representation;
81+
import std.conv : hexString;
82+
83+
auto secret2s = hexString!(
84+
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
85+
.representation;
86+
auto data = hexString!("000102").representation;
87+
88+
BLAKE2s256 b2s;
89+
b2s.key(secret);
90+
```
91+
7492
# License
7593

7694
Published under the Boost License 1.0.

dub.sdl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name "blake2-d"
2-
description "BLAKE2s and BLAKEb implementations."
2+
description "BLAKE2 implementation."
33
authors "dd86k <dd@dax.moe>"
44
copyright "Copyright © 2021, dd86k"
55
license "BSL-1.0"

source/blake2d.d

Lines changed: 65 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/// Authors: $(LINK2 github.com/dd86k, dd86k)
55
module blake2d;
66

7-
public enum BLAKE2D_VERSION_STRING = "0.1.0";
7+
public enum BLAKE2D_VERSION_STRING = "0.2.0";
88

99
private import std.digest;
1010
private import core.bitop : ror, bswap;
@@ -65,10 +65,7 @@ enum BLAKE2Variant {
6565
// key = HMAC key. This is a temporary hack to allow HMAC usage.
6666
struct BLAKE2(BLAKE2Variant var, uint digestSize/*, const(ubyte)[] key = null*/)
6767
{
68-
@safe:
69-
@nogc:
70-
nothrow:
71-
pure:
68+
@safe: @nogc: nothrow: pure:
7269

7370
static assert(digestSize > 0,
7471
"Digest size must be non-zero.");
@@ -80,8 +77,9 @@ struct BLAKE2(BLAKE2Variant var, uint digestSize/*, const(ubyte)[] key = null*/)
8077
static assert(digestSize >= 8 && digestSize <= 512,
8178
"BLAKE2b digest size must be between 8 and 512 bits.");
8279

80+
private enum MAXKEYSIZE = 32; // In bytes
8381
private enum MAXED = digestSize == 512;
84-
private enum BSIZE = 128; /// bb
82+
private enum BSIZE = 128; /// Buffer size, "bb" variable
8583
private enum ROUNDS = 12;
8684
private enum R1 = 32;
8785
private enum R2 = 24;
@@ -94,8 +92,9 @@ struct BLAKE2(BLAKE2Variant var, uint digestSize/*, const(ubyte)[] key = null*/)
9492
static assert(digestSize >= 8 && digestSize <= 256,
9593
"BLAKE2s digest size must be between 8 and 256 bits.");
9694

95+
private enum MAXKEYSIZE = 32; // In bytes
9796
private enum MAXED = digestSize == 256;
98-
private enum BSIZE = 64; /// bb
97+
private enum BSIZE = 64; /// Buffer size, "bb" variable
9998
private enum ROUNDS = 10;
10099
private enum R1 = 16;
101100
private enum R2 = 12;
@@ -113,6 +112,20 @@ struct BLAKE2(BLAKE2Variant var, uint digestSize/*, const(ubyte)[] key = null*/)
113112
this = typeof(this).init;
114113
}
115114

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+
116129
/// Feed the algorithm with data.
117130
/// Also implements the $(REF isOutputRange, std,range,primitives)
118131
/// interface for `ubyte` and `const(ubyte)[]`.
@@ -167,7 +180,7 @@ struct BLAKE2(BLAKE2Variant var, uint digestSize/*, const(ubyte)[] key = null*/)
167180
// Clear out possible sensitive data
168181
t[0] = t[1] = c = 0; // clear size information
169182
mz[] = 0; // clear input message buffer
170-
// Only clear remaining of state if digest not at maximum.
183+
// Only clear remaining of state if digest isn't fulled used.
171184
// e.g., BLAKE2b-512 has a digest size of 64 bytes
172185
// ulong[8] (8*8) is 64 bytes of state
173186
// So is BLAKE2x-256 is used, a digest of 32 bytes
@@ -179,22 +192,13 @@ struct BLAKE2(BLAKE2Variant var, uint digestSize/*, const(ubyte)[] key = null*/)
179192
}
180193

181194
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;
191195

192196
enum digestSizeBytes = digestSize / 8;
193197
// 3 2 1 0
194198
// p[0] = 0x0101kknn
195199
// kk - Key size. Set to zero since HMAC is done elsewhere.
196200
// nn - Digest size in bytes.
197-
enum p0 = 0x0101_0000 ^ (0 << 8) ^ digestSizeBytes;
201+
enum p0 = 0x0101_0000 ^ digestSizeBytes;
198202
enum msz = 16 * inner_t.sizeof; /// message size in bytes
199203
enum hsz = 8 * inner_t.sizeof; /// state size in bytes
200204

@@ -249,7 +253,7 @@ private:
249253
v[14] = last ? ~IV[6] : IV[6];
250254
v[15] = IV[7];
251255

252-
// See i=0 v[16]
256+
// Assert i=0 v[16]
253257

254258
for (size_t round; round < ROUNDS; ++round)
255259
{
@@ -263,7 +267,7 @@ private:
263267
G(v, 2, 7, 8, 13, m[SIGMA[round][12]], m[SIGMA[round][13]]);
264268
G(v, 3, 4, 9, 14, m[SIGMA[round][14]], m[SIGMA[round][15]]);
265269

266-
// See i=1..i=10/12 v[16]
270+
// Assert i=1..i=10/12 v[16]
267271
}
268272

269273
h[0] ^= v[0] ^ v[8];
@@ -275,7 +279,7 @@ private:
275279
h[6] ^= v[6] ^ v[14];
276280
h[7] ^= v[7] ^ v[15];
277281

278-
// See h[8]
282+
// Assert h[8]
279283
}
280284

281285
static void G(ref inner_t[16] v, uint a, uint b, uint c, uint d, inner_t x, inner_t y)
@@ -315,16 +319,20 @@ public alias BLAKE2s256Digest = WrapperDigest!BLAKE2s256;
315319
assert(isDigest!BLAKE2s256);
316320
}
317321

322+
/// Of course they have a blockSize!
323+
@safe unittest
324+
{
325+
assert(hasBlockSize!BLAKE2b512);
326+
assert(hasBlockSize!BLAKE2s256);
327+
}
328+
318329
/// Testing "Of" wrappers against digest wrappers.
319330
@safe unittest
320331
{
321332
enum TEXT = "abc";
322333

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));
334+
assert(blake2b_Of(TEXT) == digest!BLAKE2b512(TEXT));
335+
assert(blake2s_Of(TEXT) == digest!BLAKE2s256(TEXT));
328336
}
329337

330338
/// Testing template API
@@ -388,13 +396,13 @@ public alias BLAKE2s256Digest = WrapperDigest!BLAKE2s256;
388396

389397
ubyte[] s = ['a', 'b', 'c'];
390398

391-
BLAKE2b512Digest b2b = new BLAKE2b512Digest();
399+
Digest b2b = new BLAKE2b512Digest();
392400
b2b.put(s);
393401
assert(b2b.finish() == cast(ubyte[]) hexString!(
394402
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~
395403
"7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"));
396404

397-
BLAKE2s256Digest b2s = new BLAKE2s256Digest();
405+
Digest b2s = new BLAKE2s256Digest();
398406
b2s.put(s);
399407
assert(b2s.finish() == cast(ubyte[]) hexString!(
400408
"508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"));
@@ -425,28 +433,37 @@ public alias BLAKE2s256Digest = WrapperDigest!BLAKE2s256;
425433
"508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"));
426434
}
427435

428-
/// Testing with HMAC
429-
/*@system unittest
436+
/// Keying digests
437+
@system unittest
430438
{
439+
// NOTE: BLAKE2 is a keyed hash and therefore not compatible with
440+
// the hmac/HMAC templates, or OpenSSL does it differently.
441+
431442
import std.ascii : LetterCase;
432-
import std.digest.hmac : hmac;
433443
import std.string : representation;
444+
import std.conv : hexString;
434445

435-
auto secret = "secret".representation;
446+
auto secret2b = hexString!(
447+
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"~
448+
"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")
449+
.representation;
450+
auto secret2s = hexString!(
451+
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
452+
.representation;
453+
auto data = hexString!("000102").representation;
436454

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-
}*/
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+
}

0 commit comments

Comments
 (0)