1 #include <erl_nif.h>
2 #include <sodium.h>
3 
4 #include "enacl.h"
5 #include "secretstream.h"
6 
7 typedef struct enacl_secretstream_ctx {
8   ErlNifMutex *mtx;
9   crypto_secretstream_xchacha20poly1305_state
10       *state; // The underlying secretstream state
11   int alive;  // Is the context still valid for updates/finalization
12 } enacl_secretstream_ctx;
13 
14 ErlNifResourceType *enacl_secretstream_ctx_rtype = NULL;
15 
16 static void enacl_secretstream_ctx_dtor(ErlNifEnv *env,
17                                         enacl_secretstream_ctx *);
18 
enacl_init_secretstream_ctx(ErlNifEnv * env)19 int enacl_init_secretstream_ctx(ErlNifEnv *env) {
20   enacl_secretstream_ctx_rtype =
21       enif_open_resource_type(env, NULL, "enacl_secretstream_context",
22                               (ErlNifResourceDtor *)enacl_secretstream_ctx_dtor,
23                               ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL);
24 
25   if (enacl_secretstream_ctx_rtype == NULL)
26     return 0;
27 
28   return 1;
29 }
30 
enacl_secretstream_ctx_dtor(ErlNifEnv * env,enacl_secretstream_ctx * obj)31 static void enacl_secretstream_ctx_dtor(ErlNifEnv *env,
32                                         enacl_secretstream_ctx *obj) {
33   if (!obj->alive) {
34     return;
35   }
36 
37   if (obj->state)
38     sodium_memzero(obj->state,
39                    crypto_secretstream_xchacha20poly1305_statebytes());
40   enif_free(obj->state);
41 
42   if (obj->mtx != NULL)
43     enif_mutex_destroy(obj->mtx);
44 
45   return;
46 }
47 
48 /*
49  * Secretstream
50  */
51 
52 ERL_NIF_TERM
enacl_crypto_secretstream_xchacha20poly1305_ABYTES(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])53 enacl_crypto_secretstream_xchacha20poly1305_ABYTES(ErlNifEnv *env, int argc,
54                                                    const ERL_NIF_TERM argv[]) {
55   return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_ABYTES);
56 }
57 
enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])58 ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES(
59     ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
60   return enif_make_int64(env,
61                          crypto_secretstream_xchacha20poly1305_HEADERBYTES);
62 }
63 
enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])64 ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES(
65     ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
66   return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_KEYBYTES);
67 }
68 
enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])69 ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX(
70     ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
71   return enif_make_int64(
72       env, crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX);
73 }
74 
enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])75 ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE(
76     ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
77 
78   return enif_make_int64(env,
79                          crypto_secretstream_xchacha20poly1305_TAG_MESSAGE);
80 }
81 
enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])82 ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH(
83     ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
84 
85   return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_PUSH);
86 }
87 
enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])88 ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY(
89     ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
90 
91   return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_REKEY);
92 }
93 
enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])94 ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL(
95     ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
96 
97   return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_FINAL);
98 }
99 
100 ERL_NIF_TERM
enacl_crypto_secretstream_xchacha20poly1305_keygen(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])101 enacl_crypto_secretstream_xchacha20poly1305_keygen(ErlNifEnv *env, int argc,
102                                                    const ERL_NIF_TERM argv[]) {
103 
104   ErlNifBinary key;
105 
106   if (argc != 0) {
107     return enif_make_badarg(env);
108   }
109 
110   if (!enif_alloc_binary(crypto_secretstream_xchacha20poly1305_KEYBYTES,
111                          &key)) {
112     return enacl_internal_error(env);
113   }
114 
115   crypto_secretstream_xchacha20poly1305_keygen(key.data);
116 
117   return enif_make_binary(env, &key);
118 }
119 
120 /*
121    int crypto_secretstream_xchacha20poly1305_init_push
122       (crypto_secretstream_xchacha20poly1305_state *state,
123       unsigned char out[crypto_secretstream_xchacha20poly1305_HEADERBYTES],
124       const unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES])
125 */
enacl_crypto_secretstream_xchacha20poly1305_init_push(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])126 ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_push(
127     ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
128   ERL_NIF_TERM ret;
129   ErlNifBinary key, header;
130   enacl_secretstream_ctx *obj = NULL;
131 
132   if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &key))) {
133     goto bad_arg;
134   }
135 
136   if (key.size != crypto_secretstream_xchacha20poly1305_KEYBYTES) {
137     goto bad_arg;
138   }
139 
140   if (!enif_alloc_binary(crypto_secretstream_xchacha20poly1305_HEADERBYTES,
141                          &header)) {
142     ret = enacl_internal_error(env);
143     goto done;
144   }
145 
146   if ((obj = enif_alloc_resource(enacl_secretstream_ctx_rtype,
147                                  sizeof(enacl_secretstream_ctx))) == NULL) {
148     ret = enacl_internal_error(env);
149     goto release_header;
150   }
151   obj->alive = 0;
152   obj->state = enif_alloc(crypto_secretstream_xchacha20poly1305_statebytes());
153 
154   if (obj->state == NULL) {
155     ret = enacl_internal_error(env);
156     goto release;
157   }
158   obj->alive = 1;
159 
160   if ((obj->mtx = enif_mutex_create("enacl.secretstream")) == NULL) {
161     ret = enacl_internal_error(env);
162     goto free;
163   }
164 
165   crypto_secretstream_xchacha20poly1305_init_push(obj->state, header.data,
166                                                   key.data);
167 
168   ret = enif_make_tuple2(env, enif_make_binary(env, &header),
169                          enif_make_resource(env, obj));
170 
171   goto release;
172 bad_arg:
173   return enif_make_badarg(env);
174 free:
175   if (obj->alive)
176     if (obj->state != NULL) {
177       sodium_memzero(obj->state,
178                      crypto_secretstream_xchacha20poly1305_statebytes());
179       enif_free(obj->state);
180       obj->state = NULL;
181     }
182 release_header:
183   enif_release_binary(&header);
184 release:
185   // This also frees the mutex via the destructor
186   enif_release_resource(obj);
187 done:
188   return ret;
189 }
190 
191 /*
192 crypto_secretstream_xchacha20poly1305_init_pull
193    (crypto_secretstream_xchacha20poly1305_state *state,
194     const unsigned char in[crypto_secretstream_xchacha20poly1305_HEADERBYTES],
195     const unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES])
196 */
enacl_crypto_secretstream_xchacha20poly1305_init_pull(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])197 ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_pull(
198     ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
199   ERL_NIF_TERM ret;
200   ErlNifBinary header, key;
201   enacl_secretstream_ctx *obj = NULL;
202 
203   if (argc != 2) {
204     goto bad_arg;
205   }
206 
207   if (!enif_inspect_binary(env, argv[0], &header)) {
208     goto bad_arg;
209   }
210 
211   if (!enif_inspect_binary(env, argv[1], &key)) {
212     goto bad_arg;
213   }
214 
215   if ((key.size != crypto_secretstream_xchacha20poly1305_KEYBYTES) ||
216       (header.size != crypto_secretstream_xchacha20poly1305_HEADERBYTES)) {
217     goto bad_arg;
218   }
219 
220   if ((obj = enif_alloc_resource(enacl_secretstream_ctx_rtype,
221                                  sizeof(enacl_secretstream_ctx))) == NULL) {
222     ret = enacl_internal_error(env);
223     goto done;
224   }
225 
226   obj->alive = 0;
227   obj->state = enif_alloc(crypto_secretstream_xchacha20poly1305_statebytes());
228 
229   if (obj->state == NULL) {
230     goto release;
231   }
232   obj->alive = 1;
233 
234   if ((obj->mtx = enif_mutex_create("enacl.secretstream")) == NULL) {
235     goto free;
236   }
237 
238   crypto_secretstream_xchacha20poly1305_init_pull(obj->state, header.data,
239                                                   key.data);
240 
241   ret = enif_make_resource(env, obj);
242 
243   goto release;
244 
245 bad_arg:
246   return enif_make_badarg(env);
247 free:
248   if (obj->alive)
249     if (obj->state != NULL) {
250       sodium_memzero(obj->state,
251                      crypto_secretstream_xchacha20poly1305_statebytes());
252       enif_free(obj->state);
253       obj->state = NULL;
254     }
255 release:
256   // This also frees the mutex via the destructor
257   enif_release_resource(obj);
258 done:
259   return ret;
260 }
261 
262 /*
263 void
264 crypto_secretstream_xchacha20poly1305_rekey
265     (crypto_secretstream_xchacha20poly1305_state *state)
266 */
267 ERL_NIF_TERM
enacl_crypto_secretstream_xchacha20poly1305_rekey(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])268 enacl_crypto_secretstream_xchacha20poly1305_rekey(ErlNifEnv *env, int argc,
269                                                   const ERL_NIF_TERM argv[]) {
270   ERL_NIF_TERM ret;
271   enacl_secretstream_ctx *obj = NULL;
272 
273   if (argc != 1) {
274     goto bad_arg;
275   }
276 
277   if (!enif_get_resource(env, argv[0],
278                          (ErlNifResourceType *)enacl_secretstream_ctx_rtype,
279                          (void **)&obj)) {
280     goto bad_arg;
281   }
282 
283   enif_mutex_lock(obj->mtx);
284   if (!obj->alive) {
285     goto err;
286   }
287 
288   crypto_secretstream_xchacha20poly1305_rekey(obj->state);
289 
290   ret = enif_make_atom(env, ATOM_OK);
291 
292   goto done;
293 
294 bad_arg:
295   return enif_make_badarg(env);
296 err:
297   ret = enacl_error_finalized(env);
298 done:
299   enif_mutex_unlock(obj->mtx);
300   return ret;
301 }
302 
303 /*
304 int
305 crypto_secretstream_xchacha20poly1305_push
306    (crypto_secretstream_xchacha20poly1305_state *state,
307     unsigned char *out, unsigned long long *outlen_p,
308     const unsigned char *m, unsigned long long mlen,
309     const unsigned char *ad, unsigned long long adlen, unsigned char tag)
310 */
311 ERL_NIF_TERM
enacl_crypto_secretstream_xchacha20poly1305_push(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])312 enacl_crypto_secretstream_xchacha20poly1305_push(ErlNifEnv *env, int argc,
313                                                  const ERL_NIF_TERM argv[]) {
314   ERL_NIF_TERM ret;
315   ErlNifBinary m, ad, out;
316   ErlNifUInt64 tag;
317   enacl_secretstream_ctx *obj = NULL;
318 
319   if (argc != 4) {
320     goto bad_arg;
321   }
322 
323   if (!enif_get_resource(env, argv[0],
324                          (ErlNifResourceType *)enacl_secretstream_ctx_rtype,
325                          (void **)&obj)) {
326     goto bad_arg;
327   }
328 
329   if (!enif_inspect_binary(env, argv[1], &m)) {
330     goto bad_arg;
331   }
332 
333   if (!enif_inspect_binary(env, argv[2], &ad)) {
334     goto bad_arg;
335   }
336 
337   if (!enif_get_uint64(env, argv[3], &tag)) {
338     goto bad_arg;
339   }
340 
341   if (!enif_alloc_binary(m.size + crypto_secretstream_xchacha20poly1305_ABYTES,
342                          &out)) {
343     return enacl_internal_error(env);
344   }
345 
346   enif_mutex_lock(obj->mtx);
347   if (!obj->alive) {
348     goto err;
349   }
350 
351   crypto_secretstream_xchacha20poly1305_push(obj->state, out.data, NULL, m.data,
352                                              m.size, ad.data, ad.size, tag);
353 
354   if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
355     if (obj->state) {
356       obj->alive = 0;
357       sodium_memzero(obj->state,
358                      crypto_secretstream_xchacha20poly1305_statebytes());
359       enif_free(obj->state);
360       obj->state = NULL;
361     }
362   }
363 
364   ret = enif_make_binary(env, &out);
365 
366   goto done;
367 
368 bad_arg:
369   return enif_make_badarg(env);
370 err:
371   ret = enacl_error_finalized(env);
372   enif_release_binary(&out);
373 done:
374   enif_mutex_unlock(obj->mtx);
375   return ret;
376 }
377 
378 /*
379    crypto_secretstream_xchacha20poly1305_pull
380    (crypto_secretstream_xchacha20poly1305_state *state,
381    unsigned char *m, unsigned long long *mlen_p, unsigned char *tag_p,
382    const unsigned char *in, unsigned long long inlen,
383    const unsigned char *ad, unsigned long long adlen)
384    */
385 ERL_NIF_TERM
enacl_crypto_secretstream_xchacha20poly1305_pull(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])386 enacl_crypto_secretstream_xchacha20poly1305_pull(ErlNifEnv *env, int argc,
387                                                  const ERL_NIF_TERM argv[]) {
388   ERL_NIF_TERM ret;
389   ErlNifBinary m, in, ad;
390   unsigned char tag;
391   enacl_secretstream_ctx *obj = NULL;
392 
393   if (argc != 3) {
394     goto bad_arg;
395   }
396 
397   if (!enif_get_resource(env, argv[0],
398                          (ErlNifResourceType *)enacl_secretstream_ctx_rtype,
399                          (void **)&obj)) {
400     goto bad_arg;
401   }
402 
403   if (!enif_inspect_binary(env, argv[1], &in)) {
404     goto bad_arg;
405   }
406 
407   if (in.size < crypto_secretstream_xchacha20poly1305_ABYTES) {
408     goto bad_arg;
409   }
410 
411   if (!enif_inspect_binary(env, argv[2], &ad)) {
412     goto bad_arg;
413   }
414 
415   if (in.size < crypto_secretstream_xchacha20poly1305_ABYTES) {
416     goto bad_arg;
417   }
418 
419   if (!enif_alloc_binary(in.size - crypto_secretstream_xchacha20poly1305_ABYTES,
420                          &m)) {
421     return enacl_internal_error(env);
422   }
423 
424   enif_mutex_lock(obj->mtx);
425   if (!obj->alive) {
426     goto err;
427   }
428 
429   if (0 != crypto_secretstream_xchacha20poly1305_pull(obj->state, m.data, NULL,
430                                                       &tag, in.data, in.size,
431                                                       ad.data, ad.size)) {
432     ret = enacl_error_tuple(env, "failed_verification");
433     goto release;
434   }
435 
436   if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
437     if (obj->state) {
438       obj->alive = 0;
439       sodium_memzero(obj->state,
440                      crypto_secretstream_xchacha20poly1305_statebytes());
441       enif_free(obj->state);
442       obj->state = NULL;
443     }
444   }
445 
446   ret = enif_make_tuple2(env, enif_make_binary(env, &m),
447                          enif_make_int64(env, tag));
448 
449   goto done;
450 
451 bad_arg:
452   return enif_make_badarg(env);
453 err:
454   ret = enacl_error_finalized(env);
455 release:
456   enif_release_binary(&m);
457 done:
458   enif_mutex_unlock(obj->mtx);
459   return ret;
460 }
461