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 }