1 // Written in the D programming language. 2 3 module wrapper.sodium.crypto_generichash; 4 5 import wrapper.sodium.core; // assure sodium got initialized 6 7 public 8 import deimos.sodium.crypto_generichash : crypto_generichash_BYTES_MIN, 9 crypto_generichash_bytes_min, 10 crypto_generichash_BYTES_MAX, 11 crypto_generichash_bytes_max, 12 crypto_generichash_BYTES, 13 crypto_generichash_bytes, 14 crypto_generichash_KEYBYTES_MIN, 15 crypto_generichash_keybytes_min, 16 crypto_generichash_KEYBYTES_MAX, 17 crypto_generichash_keybytes_max, 18 crypto_generichash_KEYBYTES, 19 crypto_generichash_keybytes, 20 crypto_generichash_PRIMITIVE, 21 // crypto_generichash_primitive, 22 crypto_generichash_state, 23 crypto_generichash_statebytes, 24 // crypto_generichash, 25 // crypto_generichash_init, 26 // crypto_generichash_update, 27 // crypto_generichash_final; 28 crypto_generichash_keygen; 29 30 import std..string : fromStringz; 31 import nogc.exception: enforce; 32 33 34 pragma(inline, true) 35 string crypto_generichash_primitive() @nogc nothrow pure @trusted 36 { 37 static import deimos.sodium.crypto_generichash; 38 return fromStringz(deimos.sodium.crypto_generichash.crypto_generichash_primitive()); // strips terminating \0 ; compiler infers assumeUnique 39 } 40 41 void crypto_generichash_multi(scope ubyte[] out_, scope const ubyte[][] in_multi, scope const ubyte[] key = null) @nogc @trusted 42 { 43 enforce(out_.length >= crypto_generichash_BYTES_MIN && out_.length <= crypto_generichash_BYTES_MAX, "wrong length allocated for hash"); 44 if (key.length) 45 enforce(key.length >= crypto_generichash_KEYBYTES_MIN && key.length <= crypto_generichash_KEYBYTES_MAX, "wrong length of key: Must be either 0 or 16<=length<=64"); 46 crypto_generichash_state state; 47 crypto_generichash_init(state, key, out_.length); 48 foreach (arr; in_multi) 49 crypto_generichash_update(state, arr); 50 crypto_generichash_final(state, out_); 51 } 52 53 54 /* overload */ 55 56 alias crypto_generichash = deimos.sodium.crypto_generichash.crypto_generichash; 57 58 bool crypto_generichash(scope ubyte[] out_, scope const ubyte[] in_, scope const ubyte[] key = null) @nogc @trusted 59 { 60 enforce(out_.length >= crypto_generichash_BYTES_MIN && out_.length <= crypto_generichash_BYTES_MAX, "wrong length allocated for hash"); 61 if (key.length) 62 enforce(key.length >= crypto_generichash_KEYBYTES_MIN && key.length <= crypto_generichash_KEYBYTES_MAX, "wrong length of key: Must be either 0 or 16<=length<=64"); 63 return crypto_generichash(out_.ptr, out_.length, in_.ptr, in_.length, key.ptr, key.length) == 0; 64 } 65 66 alias crypto_generichash_init = deimos.sodium.crypto_generichash.crypto_generichash_init; 67 68 bool crypto_generichash_init(out crypto_generichash_state state, 69 scope const ubyte[] key, const size_t outlen) @nogc @trusted 70 { 71 enforce(outlen >= crypto_generichash_BYTES_MIN && outlen <= crypto_generichash_BYTES_MAX, "wrong length allocated for hash"); 72 if (key.length) 73 enforce(key.length >= crypto_generichash_KEYBYTES_MIN && key.length <= crypto_generichash_KEYBYTES_MAX, "wrong length allocated for key"); 74 return crypto_generichash_init(&state, key.ptr, key.length, outlen) == 0; 75 } 76 77 alias crypto_generichash_update = deimos.sodium.crypto_generichash.crypto_generichash_update; 78 79 //pragma(inline, true) 80 bool crypto_generichash_update(ref crypto_generichash_state state, 81 scope const ubyte[] in_) @nogc pure @trusted 82 { 83 return crypto_generichash_update(&state, in_.ptr, in_.length) == 0; 84 } 85 86 alias crypto_generichash_final = deimos.sodium.crypto_generichash.crypto_generichash_final; 87 88 bool crypto_generichash_final(ref crypto_generichash_state state, 89 scope ubyte[] out_) @nogc @trusted 90 { 91 enforce(out_.length >= crypto_generichash_BYTES_MIN && out_.length <= crypto_generichash_BYTES_MAX, "wrong length allocated for hash"); 92 return crypto_generichash_final(&state, out_.ptr, out_.length) == 0; 93 } 94 95 @system 96 unittest 97 { 98 import std.stdio : writeln, writefln; 99 import std.algorithm.comparison : equal; 100 import std..string : representation; 101 import wrapper.sodium.randombytes : randombytes; 102 debug writeln("unittest block 1 from sodium.crypto_generichash.d"); 103 /+ 104 writeln("crypto_generichash_state.alignof: ", crypto_generichash_state.alignof); // 8, the default for this struct; but actually the alignment is 64 as declared 105 writeln("crypto_generichash_statebytes(): ", crypto_generichash_statebytes()); // 384 = 64*6 106 writeln("crypto_generichash_state.sizeof: ", crypto_generichash_state.sizeof); // 384 107 writeln("crypto_generichash_state.last_node.offsetof: ", crypto_generichash_state.last_node.offsetof); // 360, thus I use 361 bytes and 384-361=23 are padding 108 +/ 109 assert(crypto_generichash_bytes_min() == crypto_generichash_BYTES_MIN); 110 assert(crypto_generichash_bytes_max() == crypto_generichash_BYTES_MAX); 111 assert(crypto_generichash_bytes() == crypto_generichash_BYTES); 112 assert(crypto_generichash_keybytes_min() == crypto_generichash_KEYBYTES_MIN); 113 assert(crypto_generichash_keybytes_max() == crypto_generichash_KEYBYTES_MAX); 114 assert(crypto_generichash_keybytes() == crypto_generichash_KEYBYTES); 115 assert(crypto_generichash_primitive() == crypto_generichash_PRIMITIVE); 116 117 assert(crypto_generichash_statebytes() == 64*6); 118 assert(crypto_generichash_statebytes() == crypto_generichash_state.sizeof); 119 120 //Single-part example without a key 121 122 //crypto_generichash 123 auto MESSAGE = representation("Arbitrary data to hash"); 124 ubyte[crypto_generichash_BYTES] hash; 125 { 126 crypto_generichash(hash.ptr, hash.length, MESSAGE.ptr, MESSAGE.length, null, 0); 127 // writefln("0x%(%02x%)", hash); // 0x3dc7925e13e4c5f0f8756af2cc71d5624b58833bb92fa989c3e87d734ee5a600 128 } 129 130 //Multi-part example with a key 131 132 auto MESSAGE_PART1 = representation("Arbitrary data to hash"); 133 auto MESSAGE_PART2 = representation("is longer than expected"); 134 135 hash = ubyte.init; 136 ubyte[crypto_generichash_KEYBYTES] key; 137 randombytes(key); 138 // writefln("0x%(%02x%)", key); // 0x516efcdd0db3fa9ececd98400dbd70df50fbe1ec2cfaf6a9cde509c7b306fcab 139 140 crypto_generichash_state state; 141 crypto_generichash_init (state, key, hash.length); 142 crypto_generichash_update(state, MESSAGE_PART1); 143 crypto_generichash_update(state, MESSAGE_PART2); 144 crypto_generichash_final (state, hash); 145 // writefln("0x%(%02x%)", hash); // 0xbd4ec735d9b885a60e0a2b1f8e61842795126a77db60e4fa962290f29e63fde7 146 147 } 148 149 @safe 150 unittest 151 { 152 import std.stdio : writeln, writefln; 153 import std.algorithm.comparison : equal; 154 import std..string : representation; 155 import std.conv : hexString; 156 import wrapper.sodium.randombytes : randombytes; 157 debug writeln("unittest block 2 from sodium.crypto_generichash.d"); 158 159 //Single-part example without a key 160 161 //crypto_generichash 162 ubyte[crypto_generichash_BYTES] hash; 163 auto MESSAGE = representation("Arbitrary data to hash"); 164 crypto_generichash(hash, MESSAGE); 165 166 auto hash_output = cast(immutable(ubyte)[]) hexString!"3dc7925e13e4c5f0f8756af2cc71d5624b58833bb92fa989c3e87d734ee5a600"; // the result from invoking the C version 167 assert(equal(hash[], hash_output)); 168 169 ubyte[crypto_generichash_KEYBYTES] key32 = void; 170 randombytes(key32); 171 crypto_generichash(hash, MESSAGE, key32); 172 173 //Multi-part example with a key 174 175 //crypto_generichash_multi 176 hash = ubyte.init; 177 auto MESSAGE_multi = [ 178 representation("Arbitrary data to hash"), 179 representation("is longer than expected") 180 ]; 181 auto key = cast(immutable(ubyte)[]) hexString!"516efcdd0db3fa9ececd98400dbd70df50fbe1ec2cfaf6a9cde509c7b306fcab"; // a key chosen in a run of block 1 182 crypto_generichash_multi(hash, MESSAGE_multi, key); 183 184 hash_output = cast(immutable(ubyte)[]) hexString!"bd4ec735d9b885a60e0a2b1f8e61842795126a77db60e4fa962290f29e63fde7"; // a hash calculated in a run of block 1 for same key and input 185 assert(equal(hash[], hash_output)); 186 187 ubyte[crypto_generichash_KEYBYTES] k; 188 crypto_generichash_keygen(k); 189 }