1 /** BLAKE3 wrapper around BLAKE3 C API.
2  */
3 module blake3;
4 
5 @safe:
6 
7 /** BLAKE3 wrapper around BLAKE3 C API.
8  */
9 @safe struct BLAKE3 {
10 pure nothrow @nogc:
11     void start() @trusted => blake3_hasher_init(&_hasher);
12 
13     void put(scope const(ubyte)[] data...) @trusted
14 		=> blake3_hasher_update(&_hasher, data.ptr, data.length);
15 
16     ubyte[32] finish() const @trusted {
17 		typeof(return) result = void;
18 		blake3_hasher_finalize(&_hasher, result.ptr, result.length);
19         return result;
20     }
21 	private blake3_hasher _hasher;
22 }
23 
24 /// verify digest of empty input
25 @safe pure nothrow @nogc unittest {
26 	// output of b3sum on empty file:
27 	static immutable ubyte[digestLength] expected = [
28 		0xaf, 0x13, 0x49, 0xb9, 0xf5, 0xf9, 0xa1, 0xa6,
29 	    0xa0, 0x40, 0x4d, 0xea, 0x36, 0xdc, 0xc9, 0x49,
30 	    0x9b, 0xcb, 0x25, 0xc9, 0xad, 0xc1, 0x12, 0xb7,
31 		0xcc, 0x9a, 0x93, 0xca, 0xe4, 0x1f, 0x32, 0x62,
32 	];
33 	BLAKE3 h;
34 	h.start();
35 	assert(h.finish == expected);
36 }
37 
38 /// verify digest of non-empty input
39 @safe pure nothrow @nogc unittest {
40 	const input = "Hello world!";
41 	// output of b3sum on file containing `input`:
42 	static immutable ubyte[digestLength] expected = [
43 	    0x79, 0x3c, 0x10, 0xbc, 0x0b, 0x28, 0xc3, 0x78,
44 	    0x33, 0x0d, 0x39, 0xed, 0xac, 0xe7, 0x26, 0x0a,
45 	    0xf9, 0xda, 0x81, 0xd6, 0x03, 0xb8, 0xff, 0xed,
46 	    0xe2, 0x70, 0x6a, 0x21, 0xed, 0xa8, 0x93, 0xf4
47 	];
48 	BLAKE3 h;
49 	h.start();
50 	h.put(cast(immutable(ubyte)[])input);
51 	assert(h.finish == expected);
52 }
53 
54 version (unittest) {
55 	private enum digestLength = typeof(BLAKE3.init.finish()).sizeof;
56 }
57 
58 import core.stdc.stdint : uint8_t, uint32_t, uint64_t;
59 
60 // Copied from BLAKE3/c/blake3.h
61 extern(C) pure nothrow @nogc {
62 private enum BLAKE3_VERSION_STRING = "1.5.1";
63 private enum BLAKE3_KEY_LEN = 32;
64 private enum BLAKE3_OUT_LEN = 32;
65 private enum BLAKE3_BLOCK_LEN = 64;
66 private enum BLAKE3_CHUNK_LEN = 1024;
67 private enum BLAKE3_MAX_DEPTH = 54;
68 
69 // This struct is a private implementation detail. It has to be here because
70 // it's part of blake3_hasher below.
71 struct blake3_chunk_state {
72   uint32_t[8] cv;
73   uint64_t chunk_counter;
74   uint8_t[BLAKE3_BLOCK_LEN] buf;
75   uint8_t buf_len;
76   uint8_t blocks_compressed;
77   uint8_t flags;
78 }
79 
80 struct blake3_hasher {
81   uint32_t[8] key;
82   blake3_chunk_state chunk;
83   uint8_t cv_stack_len;
84   // The stack size is MAX_DEPTH + 1 because we do lazy merging. For example,
85   // with 7 chunks, we have 3 entries in the stack. Adding an 8th chunk
86   // requires a 4th entry, rather than merging everything down to 1, because we
87   // don't know whether more input is coming. This is different from how the
88   // reference implementation does things.
89   uint8_t[(BLAKE3_MAX_DEPTH + 1) * BLAKE3_OUT_LEN] cv_stack;
90 }
91 
92 const(char) *blake3_version();
93 void blake3_hasher_init(blake3_hasher *self);
94 void blake3_hasher_init_keyed(blake3_hasher *self, const uint8_t[BLAKE3_KEY_LEN] key);
95 void blake3_hasher_init_derive_key(blake3_hasher *self, const char *context);
96 void blake3_hasher_init_derive_key_raw(blake3_hasher *self, const void *context,
97                                                   size_t context_len);
98 void blake3_hasher_update(blake3_hasher *self, const void *input,
99                                      size_t input_len);
100 void blake3_hasher_finalize(const blake3_hasher *self, uint8_t *out_, size_t out_len);
101 void blake3_hasher_finalize_seek(const blake3_hasher *self, uint64_t seek,
102                                             uint8_t *out_, size_t out_len);
103 void blake3_hasher_reset(blake3_hasher *self);
104 }