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 }