1 // Written in the D programming language.
2 
3 module wrapper.sodium.crypto_box;
4 
5 import wrapper.sodium.core; // assure sodium got initialized
6 
7 public
8 import  deimos.sodium.crypto_box : crypto_box_SEEDBYTES,
9                                    crypto_box_seedbytes,
10                                    crypto_box_PUBLICKEYBYTES,
11                                    crypto_box_publickeybytes,
12                                    crypto_box_SECRETKEYBYTES,
13                                    crypto_box_secretkeybytes,
14                                    crypto_box_NONCEBYTES,
15                                    crypto_box_noncebytes,
16                                    crypto_box_MACBYTES,
17                                    crypto_box_macbytes,
18                                    crypto_box_MESSAGEBYTES_MAX,
19                                    crypto_box_messagebytes_max,
20                                    crypto_box_PRIMITIVE,
21 /*                                 crypto_box_primitive,
22                                    crypto_box_seed_keypair,
23                                    crypto_box_keypair,
24                                    crypto_box_easy,
25                                    crypto_box_open_easy,
26                                    crypto_box_detached,
27                                    crypto_box_open_detached, */
28                                    crypto_box_BEFORENMBYTES,
29                                    crypto_box_beforenmbytes,
30 /*                                 crypto_box_beforenm,
31                                    crypto_box_easy_afternm,
32                                    crypto_box_open_easy_afternm,
33                                    crypto_box_detached_afternm,
34                                    crypto_box_open_detached_afternm, */
35                                    crypto_box_SEALBYTES,
36                                    crypto_box_sealbytes;
37 /*                                 crypto_box_seal,
38                                    crypto_box_seal_open,
39                                    crypto_box_ZEROBYTES,
40                                    crypto_box_zerobytes,
41                                    crypto_box_BOXZEROBYTES,
42                                    crypto_box_boxzerobytes,
43                                    crypto_box,
44                                    crypto_box_open,
45                                    crypto_box_afternm,
46                                    crypto_box_open_afternm;  */
47 
48 
49 import std.exception : assertThrown;
50 import nogc.exception: enforce;
51 
52 
53 
54 string crypto_box_primitive() @nogc nothrow pure @trusted
55 {
56   import std..string : fromStringz;
57   static import deimos.sodium.crypto_box;
58   const(char)[] c_arr;
59   try
60     c_arr = fromStringz(deimos.sodium.crypto_box.crypto_box_primitive()); // strips terminating \0
61   catch (Exception t) { /* known not to throw */ }
62   return c_arr;
63 }
64 
65 // overloading some functions between module deimos.sodium.crypto_box and this module
66 
67 
68 alias crypto_box_seed_keypair   = deimos.sodium.crypto_box.crypto_box_seed_keypair;
69 
70 /**
71  * Using crypto_box_seed_keypair(), the key pair can be deterministically derived from a single key `seed`.
72  */
73 pragma(inline, true)
74 bool crypto_box_seed_keypair(out ubyte[crypto_box_PUBLICKEYBYTES] pkey, out ubyte[crypto_box_SECRETKEYBYTES] skey,
75                              const ubyte[crypto_box_SEEDBYTES] seed) pure @nogc @trusted
76 {
77   return  crypto_box_seed_keypair(pkey.ptr, skey.ptr, seed.ptr) == 0;
78 }
79 
80 alias crypto_box_keypair        = deimos.sodium.crypto_box.crypto_box_keypair;
81 
82 /**
83  * The crypto_box_keypair() function randomly generates a secret key and a corresponding public key.
84  * The public key is put into `pkey` and the secret key into `skey`.
85  */
86 pragma(inline, true)
87 bool crypto_box_keypair(out ubyte[crypto_box_PUBLICKEYBYTES] pkey, out ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted
88 {
89   return  crypto_box_keypair(pkey.ptr, skey.ptr) == 0;
90 }
91 
92 alias crypto_box_easy           = deimos.sodium.crypto_box.crypto_box_easy;
93 
94 /**
95  */
96 bool crypto_box_easy(scope ubyte[] c, scope const ubyte[] m, const ubyte[crypto_box_NONCEBYTES] n,
97                      const ubyte[crypto_box_PUBLICKEYBYTES] pkey, const ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted
98 {
99   const  c_expect_len = m.length + crypto_box_MACBYTES;
100 //  enforce(c.length == c_expect_len, "Expected c.length: ", c.length, " to be equal to m.length + crypto_box_MACBYTES: ", c_expect_len);
101   enforce(c.length == c_expect_len, "Expected c.length is not equal to m.length + crypto_box_MACBYTES");
102   return  crypto_box_easy(c.ptr, m.ptr, m.length, n.ptr, pkey.ptr, skey.ptr) == 0;
103 }
104 
105 alias crypto_box_open_easy      = deimos.sodium.crypto_box.crypto_box_open_easy;
106 
107 /**
108  */
109 bool crypto_box_open_easy(scope ubyte[] m, scope const ubyte[] c, const ubyte[crypto_box_NONCEBYTES] n,
110                           const ubyte[crypto_box_PUBLICKEYBYTES] pkey, const ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted
111 {
112 //  enforce(c.length >= crypto_box_MACBYTES, "Expected c.length: ", c.length, " to be greater_equal to crypto_box_MACBYTES: ", crypto_box_MACBYTES);
113   enforce(c.length >= crypto_box_MACBYTES, "Expected c.length is not greater_equal to crypto_box_MACBYTES");
114   const  m_expect_len = c.length - crypto_box_MACBYTES;
115 //  enforce(m.length == m_expect_len, "Expected m.length: ", m.length, " to be equal to c.length - crypto_box_MACBYTES: ", m_expect_len);
116   enforce(m.length == m_expect_len, "Expected m.length is not equal to c.length - crypto_box_MACBYTES");
117   return  crypto_box_open_easy(m.ptr, c.ptr, c.length, n.ptr, pkey.ptr, skey.ptr) == 0;
118 }
119 
120 alias crypto_box_detached       = deimos.sodium.crypto_box.crypto_box_detached;
121 
122 /**
123  */
124 bool crypto_box_detached(scope ubyte[] c, out ubyte[crypto_box_MACBYTES] mac, scope const ubyte[] m, const ubyte[crypto_box_NONCEBYTES] n,
125                          const ubyte[crypto_box_PUBLICKEYBYTES] pkey, const ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted
126 {
127 //  enforce(c.length == m.length, "Expected c.length: ", c.length, " to be equal to m.length: ", m.length);
128   enforce(c.length == m.length, "Expected c.length is not equal to m.length");
129   return  crypto_box_detached(c.ptr, mac.ptr, m.ptr, m.length, n.ptr, pkey.ptr, skey.ptr) == 0;
130 }
131 
132 alias crypto_box_open_detached  = deimos.sodium.crypto_box.crypto_box_open_detached;
133 
134 /**
135  */
136 pragma(inline, true)
137 bool crypto_box_open_detached(scope ubyte[] m, scope const ubyte[] c, const ubyte[crypto_box_MACBYTES] mac, const ubyte[crypto_box_NONCEBYTES] n,
138                               const ubyte[crypto_box_PUBLICKEYBYTES] pkey, const ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted
139 {
140   enforce(m.length >= c.length, "Error in crypto_box_open_detached: m.length < c.length");
141   return  crypto_box_open_detached(m.ptr, c.ptr, mac.ptr, c.length, n.ptr, pkey.ptr, skey.ptr) == 0;
142 }
143 
144 
145 /* -- Precomputation interface -- */
146 
147 alias crypto_box_beforenm  = deimos.sodium.crypto_box.crypto_box_beforenm;
148 
149 pragma(inline, true)
150 bool  crypto_box_beforenm(out ubyte[crypto_box_BEFORENMBYTES] k,
151                           const ubyte[crypto_box_PUBLICKEYBYTES] pk,
152                           const ubyte[crypto_box_SECRETKEYBYTES] sk) pure nothrow @nogc @trusted // __attribute__ ((warn_unused_result));
153 {
154   return  crypto_box_beforenm(k.ptr, pk.ptr, sk.ptr) == 0;
155 }
156 
157 alias crypto_box_easy_afternm  = deimos.sodium.crypto_box.crypto_box_easy_afternm;
158 
159 pragma(inline, true)
160 bool  crypto_box_easy_afternm(scope ubyte[] c,
161                               scope const ubyte[] m,
162                               const ubyte[crypto_box_NONCEBYTES] n,
163                               const ubyte[crypto_box_BEFORENMBYTES] k) @nogc @trusted
164 {
165   const  c_expect_len = m.length + crypto_box_MACBYTES;
166 //  enforce(c.length == c_expect_len, "Expected c.length: ", c.length, " to be equal to m.length + crypto_box_MACBYTES: ", c_expect_len);
167   enforce(c.length == c_expect_len, "Expected c.length is not equal to m.length + crypto_box_MACBYTES");
168   return  crypto_box_easy_afternm(c.ptr, m.ptr, m.length, n.ptr, k.ptr) == 0;
169 }
170 
171 alias crypto_box_open_easy_afternm  = deimos.sodium.crypto_box.crypto_box_open_easy_afternm;
172 
173 pragma(inline, true)
174 bool  crypto_box_open_easy_afternm(scope ubyte[] m,
175                                    scope const ubyte[] c,
176                                    const ubyte[crypto_box_NONCEBYTES] n,
177                                    const ubyte[crypto_box_BEFORENMBYTES] k) @nogc @trusted // __attribute__ ((warn_unused_result));
178 {
179 //  enforce(c.length >= crypto_box_MACBYTES, "Expected c.length: ", c.length, " to be greater_equal to crypto_box_MACBYTES: ", crypto_box_MACBYTES);
180   enforce(c.length >= crypto_box_MACBYTES, "Expected c.length is not greater_equal to crypto_box_MACBYTES");
181   const  m_expect_len = c.length - crypto_box_MACBYTES;
182 //  enforce(m.length == m_expect_len, "Expected m.length: ", m.length, " to be equal to c.length - crypto_box_MACBYTES: ", m_expect_len);
183   enforce(m.length == m_expect_len, "Expected m.length is not equal to c.length - crypto_box_MACBYTES");
184   return  crypto_box_open_easy_afternm(m.ptr, c.ptr, c.length, n.ptr, k.ptr) == 0;
185 }
186 
187 alias crypto_box_detached_afternm  = deimos.sodium.crypto_box.crypto_box_detached_afternm;
188 
189 pragma(inline, true)
190 bool  crypto_box_detached_afternm(scope ubyte[] c,
191                                   out ubyte[crypto_box_MACBYTES] mac,
192                                   scope const  ubyte[] m,
193                                   const ubyte[crypto_box_NONCEBYTES] n,
194                                   const ubyte[crypto_box_BEFORENMBYTES] k) @nogc @trusted
195 {
196 //  enforce(c.length == m.length, "Expected c.length: ", c.length, " to be equal to m.length: ", m.length);
197   enforce(c.length == m.length, "Expected c.length is not equal to m.length");
198   return  crypto_box_detached_afternm(c.ptr, mac.ptr, m.ptr, m.length, n.ptr, k.ptr) == 0;
199 }
200 
201 alias crypto_box_open_detached_afternm  = deimos.sodium.crypto_box.crypto_box_open_detached_afternm;
202 
203 pragma(inline, true)
204 bool  crypto_box_open_detached_afternm(scope ubyte[] m,
205                                        scope const ubyte[] c,
206                                        const ubyte[crypto_box_MACBYTES] mac,
207                                        const ubyte[crypto_box_NONCEBYTES] n,
208                                        const ubyte[crypto_box_BEFORENMBYTES] k) @nogc @trusted // __attribute__ ((warn_unused_result));
209 {
210 //  enforce(m.length == c.length, "Expected m.length: ", m.length, " to be equal to c.length: ", c.length);
211   enforce(m.length == c.length, "Expected m.length is not equal to c.length");
212   return  crypto_box_open_detached_afternm(m.ptr, c.ptr, mac.ptr, c.length, n.ptr, k.ptr) == 0;
213 }
214 
215 
216 /* -- Ephemeral SK interface -- */
217 
218 alias crypto_box_seal           = deimos.sodium.crypto_box.crypto_box_seal;
219 
220 /**
221  * The crypto_box_seal() function encrypts a message `m` for a recipient whose public key is `pkey`.
222  * It puts the ciphertext whose length is crypto_box_SEALBYTES + m.length into `c`.
223  * The function creates a new key pair for each message, and attaches the public key to the ciphertext.
224  * The secret key is overwritten and is not accessible after this function returns.
225  */
226 bool crypto_box_seal(scope ubyte[] c, scope const ubyte[] m, const ubyte[crypto_box_PUBLICKEYBYTES] pkey) @nogc @trusted
227 {
228   const  c_expect_len = m.length + crypto_box_SEALBYTES;
229 //  enforce(c.length == c_expect_len, "Expected c.length: ", c.length, " to be equal to m.length + crypto_box_SEALBYTES: ", c_expect_len);
230   enforce(c.length == c_expect_len, "Expected c.length is not equal to m.length + crypto_box_SEALBYTES");
231   return  crypto_box_seal(c.ptr, m.ptr, m.length, pkey.ptr) == 0;
232 }
233 
234 alias crypto_box_seal_open      = deimos.sodium.crypto_box.crypto_box_seal_open;
235 
236 /**
237  */
238 bool crypto_box_seal_open(scope ubyte[] m, scope const ubyte[] c,
239                           const ubyte[crypto_box_PUBLICKEYBYTES] pkey, const ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted
240 {
241 //  enforce(c.length >= crypto_box_SEALBYTES, "Expected c.length: ", c.length, " to be greater_equal to crypto_box_SEALBYTES: ", crypto_box_SEALBYTES);
242   enforce(c.length >= crypto_box_SEALBYTES, "Expected c.length is not greater_equal to crypto_box_SEALBYTES");
243   const  m_expect_len = c.length - crypto_box_SEALBYTES;
244 //  enforce(m.length == m_expect_len, "Expected m.length: ", m.length, " to be equal to c.length - crypto_box_SEALBYTES: ", m_expect_len);
245   enforce(m.length == m_expect_len, "Expected m.length is not equal to c.length - crypto_box_SEALBYTES");
246   return  crypto_box_seal_open(m.ptr, c.ptr, c.length, pkey.ptr, skey.ptr) == 0;
247 }
248 
249 /* No overloads for -- NaCl compatibility interface ; Requires padding -- */
250 
251 
252 @safe
253 unittest {
254   import std.stdio : writeln;
255   debug writeln("unittest block 1 from sodium.crypto_box.d");
256 }
257 
258 @safe
259 unittest {
260   import std..string : representation;
261   import wrapper.sodium.randombytes : randombytes;
262   import wrapper.sodium.utils : sodium_increment;
263 
264   assert(crypto_box_seedbytes()        == crypto_box_SEEDBYTES);
265   assert(crypto_box_publickeybytes()   == crypto_box_PUBLICKEYBYTES);
266   assert(crypto_box_secretkeybytes()   == crypto_box_SECRETKEYBYTES);
267   assert(crypto_box_noncebytes()       == crypto_box_NONCEBYTES);
268   assert(crypto_box_macbytes()         == crypto_box_MACBYTES);
269   assert(crypto_box_messagebytes_max() == crypto_box_MESSAGEBYTES_MAX);
270   assert(crypto_box_primitive()        == crypto_box_PRIMITIVE);
271   assert(crypto_box_beforenmbytes()    == crypto_box_BEFORENMBYTES);
272   assert(crypto_box_sealbytes()        == crypto_box_SEALBYTES);
273 
274   enum MESSAGE_LEN = 4;
275   ubyte[MESSAGE_LEN] message  = [116, 101, 115, 116]; //representation("test");
276   // avoid heap allocation, like in the example code
277   enum CIPHERTEXT_LEN = (crypto_box_MACBYTES + MESSAGE_LEN);
278   ubyte[crypto_box_SEEDBYTES]  seed = void;
279   randombytes(seed);
280 
281   ubyte[crypto_box_PUBLICKEYBYTES]  alice_publickey = void;
282   ubyte[crypto_box_SECRETKEYBYTES]  alice_secretkey = void;
283   assert(crypto_box_seed_keypair(alice_publickey, alice_secretkey, seed));
284   // check crypto_scalarmult_base (compute alice_publickey from alice_secretkey)
285   {
286     import wrapper.sodium.crypto_scalarmult;
287     assert(crypto_scalarmult_BYTES       == crypto_box_PUBLICKEYBYTES);
288     assert(crypto_scalarmult_SCALARBYTES == crypto_box_SECRETKEYBYTES);
289     ubyte[crypto_box_PUBLICKEYBYTES]  alice_publickey_computed;
290     assert(crypto_scalarmult_base(alice_publickey_computed, alice_secretkey));
291     assert(alice_publickey_computed == alice_publickey);
292   }
293 
294   ubyte[crypto_box_PUBLICKEYBYTES]  bob_publickey = void;
295   ubyte[crypto_box_SECRETKEYBYTES]  bob_secretkey = void;
296   crypto_box_keypair(bob_publickey, bob_secretkey);
297 
298   ubyte[crypto_box_NONCEBYTES] nonce = void;
299   ubyte[CIPHERTEXT_LEN] ciphertext1  = void;
300   randombytes(nonce);
301   // Alice encrypts message for Bob
302   assertThrown(crypto_box_easy(ciphertext1[0..$-1], message, nonce, bob_publickey, alice_secretkey));
303   assert(crypto_box_easy(ciphertext1, message, nonce, bob_publickey, alice_secretkey));
304 //  { /* error */ }
305 
306   ubyte[MESSAGE_LEN] decrypted = void;
307   // Bob decrypts ciphertext from Alice
308   assertThrown(crypto_box_open_easy(decrypted[0..$-1], ciphertext1, nonce, alice_publickey, bob_secretkey));
309   assert(crypto_box_open_easy(decrypted, ciphertext1, nonce, alice_publickey, bob_secretkey));
310   assert(decrypted == message);
311 //
312   ubyte[MESSAGE_LEN]  ciphertext2;
313   ubyte[crypto_box_MACBYTES]  mac = void;
314   decrypted  = ubyte.init;
315   sodium_increment(nonce);
316   assertThrown(crypto_box_detached(ciphertext2[0..$-1], mac, message, nonce, bob_publickey, alice_secretkey));
317   assert(crypto_box_detached(ciphertext2, mac, message, nonce, bob_publickey, alice_secretkey));
318 
319   assertThrown(crypto_box_open_detached(decrypted[0..$-1], ciphertext2, mac, nonce, alice_publickey, bob_secretkey));
320   assert(crypto_box_open_detached(decrypted, ciphertext2, mac, nonce, alice_publickey, bob_secretkey));
321   assert(decrypted == message);
322 //
323   ubyte[crypto_box_BEFORENMBYTES] k;
324   assert(crypto_box_beforenm(k, bob_publickey, alice_secretkey));
325   ciphertext1 = ubyte.init;
326   decrypted   = ubyte.init;
327   sodium_increment(nonce);
328   assert(crypto_box_easy_afternm(ciphertext1, message, nonce, k));
329   assert(crypto_box_open_easy_afternm(decrypted, ciphertext1, nonce, k));
330   assert(decrypted == message);
331 
332   ciphertext2 = ubyte.init;
333   decrypted   = ubyte.init;
334   mac         = ubyte.init;
335   sodium_increment(nonce);
336   assert(crypto_box_detached_afternm(ciphertext2, mac, message, nonce, k));
337   assert(crypto_box_open_detached_afternm(decrypted, ciphertext2, mac, nonce, k));
338   assert(decrypted == message);
339 //
340   enum CIPHERTEXTSEALED_LEN = (crypto_box_SEALBYTES + MESSAGE_LEN);
341   ubyte[CIPHERTEXTSEALED_LEN] ciphertext_sealed = void;
342   decrypted  = ubyte.init;
343 
344   assertThrown(crypto_box_seal(ciphertext_sealed[0..$-1], message, bob_publickey));
345   assert(crypto_box_seal(ciphertext_sealed, message, bob_publickey));
346 
347   assertThrown(crypto_box_seal_open(decrypted[0..$-1], ciphertext_sealed, bob_publickey, bob_secretkey));
348   assert(crypto_box_seal_open(decrypted, ciphertext_sealed, bob_publickey, bob_secretkey));
349   assert(decrypted == message);
350 
351 }