enacl_nif.c 29.1 KB
Newer Older
Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
1
2
#include "erl_nif.h"

3
4
#include <string.h>

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
5
6
#include <sodium.h>

7
8
9
10
#ifndef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
# error Requires dirty schedulers
#endif

11
/* Errors */
Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
12
13
14
15
16
static
ERL_NIF_TERM nacl_error_tuple(ErlNifEnv *env, char *error_atom) {
	return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, error_atom));
}

17
18
19
20
21
22
23
/* Initialization */
static
int enif_crypto_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
	sodium_init();
	return 0;
}

24
/* Low-level functions (Hashing, String Equality, ...) */
25

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
26
27
28
29
static
ERL_NIF_TERM enif_crypto_hash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary input;
	ErlNifBinary result;
30

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
31
32
33
	if ((argc != 1) || (!enif_inspect_iolist_as_binary(env, argv[0], &input))) {
		return enif_make_badarg(env);
	}
34

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
35
36
37
	if (!enif_alloc_binary(crypto_hash_BYTES, &result)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
38

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
39
	crypto_hash(result.data, input.data, input.size);
40

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
41
42
43
	return enif_make_binary(env, &result);
}

44
45
46
47
static
ERL_NIF_TERM enif_crypto_verify_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary x,y;

48
	if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x))
49
	  || (!enif_inspect_binary(env, argv[1], &y))) {
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
		return enif_make_badarg(env);
	}

	if (x.size != 16 || y.size != 16) {
		return enif_make_badarg(env);
	}

	if (0 == crypto_verify_16(x.data, y.data)) {
		return enif_make_atom(env, "true");
	} else {
		return enif_make_atom(env, "false");
	}
}

static
ERL_NIF_TERM enif_crypto_verify_32(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary x,y;

68
69
	if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x))
	  || (!enif_inspect_binary(env, argv[1], &y))) {
70
71
72
73
74
75
76
77
78
79
80
81
82
83
		return enif_make_badarg(env);
	}

	if (x.size != 32 || y.size != 32) {
		return enif_make_badarg(env);
	}

	if (0 == crypto_verify_32(x.data, y.data)) {
		return enif_make_atom(env, "true");
	} else {
		return enif_make_atom(env, "false");
	}
}

84
85
86
/* Curve 25519 */
static
ERL_NIF_TERM enif_crypto_curve25519_scalarmult(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
87
	ERL_NIF_TERM result;
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
	ErlNifBinary secret, basepoint, output;
	uint8_t bp[crypto_scalarmult_curve25519_BYTES];

	if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &secret))
			|| (!enif_inspect_binary(env, argv[1], &basepoint))
			|| (secret.size != crypto_scalarmult_curve25519_BYTES)
			|| (basepoint.size != crypto_scalarmult_curve25519_BYTES)) {
		return enif_make_badarg(env);
	}

	memcpy(bp, basepoint.data, crypto_scalarmult_curve25519_BYTES);

	/* Clear the high-bit. Better safe than sorry. */
	bp[31] &= 0x7f;

103
104
105
106
107
108
	do
	{
		if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &output)) {
			result = nacl_error_tuple(env, "alloc_failed");
			continue;
		}
109

110
111
112
113
114
115
116
117
118
		if (crypto_scalarmult_curve25519(output.data, secret.data, bp) < 0) {
			result = nacl_error_tuple(env, "scalarmult_curve25519_failed");
			continue;
		}

		result = enif_make_binary(env, &output);
	} while (0);

	sodium_memzero(bp, crypto_scalarmult_curve25519_BYTES);
119

120
	return result;
121
}
122

Alexander Færøy's avatar
Alexander Færøy committed
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* Ed 25519 */
static
ERL_NIF_TERM enif_crypto_sign_ed25519_keypair(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary pk, sk;

	if (argc != 0) {
		return enif_make_badarg(env);
	}

	if (!enif_alloc_binary(crypto_sign_ed25519_PUBLICKEYBYTES, &pk)) {
		return nacl_error_tuple(env, "alloc_failed");
	}

	if (!enif_alloc_binary(crypto_sign_ed25519_SECRETKEYBYTES, &sk)) {
		return nacl_error_tuple(env, "alloc_failed");
	}

	crypto_sign_ed25519_keypair(pk.data, sk.data);

	return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk));
}

static
ERL_NIF_TERM enif_crypto_sign_ed25519_public_to_curve25519(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary curve25519_pk, ed25519_pk;

	if ((argc != 1)
			|| (!enif_inspect_binary(env, argv[0], &ed25519_pk))
			|| (ed25519_pk.size != crypto_sign_ed25519_PUBLICKEYBYTES)) {
		return enif_make_badarg(env);
	}

	if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_pk)) {
		return nacl_error_tuple(env, "alloc_failed");
	}

	if (crypto_sign_ed25519_pk_to_curve25519(curve25519_pk.data, ed25519_pk.data) != 0) {
		return nacl_error_tuple(env, "ed25519_public_to_curve25519_failed");
	}

	return enif_make_binary(env, &curve25519_pk);
}

static
ERL_NIF_TERM enif_crypto_sign_ed25519_secret_to_curve25519(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary curve25519_sk, ed25519_sk;

	if ((argc != 1)
			|| (!enif_inspect_binary(env, argv[0], &ed25519_sk))
			|| (ed25519_sk.size != crypto_sign_ed25519_SECRETKEYBYTES)) {
		return enif_make_badarg(env);
	}

	if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_sk)) {
		return nacl_error_tuple(env, "alloc_failed");
	}

	if (crypto_sign_ed25519_sk_to_curve25519(curve25519_sk.data, ed25519_sk.data) != 0) {
		return nacl_error_tuple(env, "ed25519_secret_to_curve25519_failed");
	}

	return enif_make_binary(env, &curve25519_sk);
}

static
ERL_NIF_TERM enif_crypto_sign_ed25519_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_sign_ed25519_PUBLICKEYBYTES);
}

static
ERL_NIF_TERM enif_crypto_sign_ed25519_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_sign_ed25519_SECRETKEYBYTES);
}

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/* Public-key cryptography */
static
ERL_NIF_TERM enif_crypto_box_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_box_NONCEBYTES);
}

static
ERL_NIF_TERM enif_crypto_box_ZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_box_ZEROBYTES);
}

static
ERL_NIF_TERM enif_crypto_box_BOXZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_box_BOXZEROBYTES);
}

213
214
215
216
217
218
219
220
221
222
static
ERL_NIF_TERM enif_crypto_box_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_box_PUBLICKEYBYTES);
}

static
ERL_NIF_TERM enif_crypto_box_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_box_SECRETKEYBYTES);
}

223
224
225
226
227
static
ERL_NIF_TERM enif_crypto_box_BEFORENMBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_box_BEFORENMBYTES);
}

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
228
229
230
static
ERL_NIF_TERM enif_crypto_box_keypair(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary pk, sk;
231

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
232
233
234
	if (argc != 0) {
		return enif_make_badarg(env);
	}
235

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
236
237
238
	if (!enif_alloc_binary(crypto_box_PUBLICKEYBYTES, &pk)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
239

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
240
241
242
	if (!enif_alloc_binary(crypto_box_SECRETKEYBYTES, &sk)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
243

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
244
	crypto_box_keypair(pk.data, sk.data);
245

246
	return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk));
Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
247
248
}

249
250
251
static
ERL_NIF_TERM enif_crypto_box(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary padded_msg, nonce, pk, sk, result;
252

253
254
255
	if (
	  (argc != 4) ||
	  (!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg)) ||
256
257
258
	  (!enif_inspect_binary(env, argv[1], &nonce)) ||
	  (!enif_inspect_binary(env, argv[2], &pk)) ||
	  (!enif_inspect_binary(env, argv[3], &sk))) {
259
260
	  	return enif_make_badarg(env);
	}
261

262
263
264
265
266
267
268
	if (
	    (nonce.size != crypto_box_NONCEBYTES) ||
	    (pk.size != crypto_box_PUBLICKEYBYTES) ||
	    (sk.size != crypto_box_SECRETKEYBYTES) ||
	    (padded_msg.size < crypto_box_ZEROBYTES)) {
		return enif_make_badarg(env);
	}
269

270
271
272
	if (!enif_alloc_binary(padded_msg.size, &result)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
273

274
	crypto_box(result.data, padded_msg.data, padded_msg.size, nonce.data, pk.data, sk.data);
275

276
277
278
279
280
281
282
283
284
285
	return enif_make_sub_binary(
		env,
		enif_make_binary(env, &result),
		crypto_box_BOXZEROBYTES,
		padded_msg.size - crypto_box_BOXZEROBYTES);
}

static
ERL_NIF_TERM enif_crypto_box_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary padded_ciphertext, nonce, pk, sk, result;
286

287
288
289
	if (
	  (argc != 4) ||
	  (!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) ||
290
291
292
	  (!enif_inspect_binary(env, argv[1], &nonce)) ||
	  (!enif_inspect_binary(env, argv[2], &pk)) ||
	  (!enif_inspect_binary(env, argv[3], &sk))) {
293
294
		return enif_make_badarg(env);
	}
295

296
297
298
299
300
301
302
	if (
	  (nonce.size != crypto_box_NONCEBYTES) ||
	  (pk.size != crypto_box_PUBLICKEYBYTES) ||
	  (sk.size != crypto_box_SECRETKEYBYTES) ||
	  (padded_ciphertext.size < crypto_box_BOXZEROBYTES)) {
		return enif_make_badarg(env);
	}
303

304
305
306
	if (!enif_alloc_binary(padded_ciphertext.size, &result)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
307

308
	if (0 != crypto_box_open(result.data, padded_ciphertext.data, padded_ciphertext.size, nonce.data, pk.data, sk.data)) {
309
		enif_release_binary(&result);
310
311
		return nacl_error_tuple(env, "failed_verification");
	}
312

313
314
315
316
317
318
	return enif_make_sub_binary(
		env,
		enif_make_binary(env, &result),
		crypto_box_ZEROBYTES,
		padded_ciphertext.size - crypto_box_ZEROBYTES);
}
319

320
321
322
323
324
/* Precomputed crypto boxes */

static
ERL_NIF_TERM enif_crypto_box_beforenm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary k, pk, sk;
325

326
327
328
329
330
331
332
333
	if (
	    (argc != 2) ||
	    (!enif_inspect_binary(env, argv[0], &pk)) ||
	    (!enif_inspect_binary(env, argv[1], &sk)) ||
	    (pk.size != crypto_box_PUBLICKEYBYTES) ||
	    (sk.size != crypto_box_SECRETKEYBYTES)) {
		return enif_make_badarg(env);
	}
334

335
336
337
	if (!enif_alloc_binary(crypto_box_BEFORENMBYTES, &k)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
338

339
	crypto_box_beforenm(k.data, pk.data, sk.data);
340

341
342
343
344
345
346
	return enif_make_binary(env, &k);
}

static
ERL_NIF_TERM enif_crypto_box_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary result, m, nonce, k;
347

348
349
350
351
352
353
354
355
356
357
	if (
	    (argc != 3) ||
	    (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
	    (!enif_inspect_binary(env, argv[1], &nonce)) ||
	    (!enif_inspect_binary(env, argv[2], &k)) ||
	    (m.size < crypto_box_ZEROBYTES) ||
	    (nonce.size != crypto_box_NONCEBYTES) ||
	    (k.size != crypto_box_BEFORENMBYTES)) {
		return enif_make_badarg(env);
	}
358

359
360
361
	if (!enif_alloc_binary(m.size, &result)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
362

363
	crypto_box_afternm(result.data, m.data, m.size, nonce.data, k.data);
364

365
366
367
368
369
370
371
372
373
374
	return enif_make_sub_binary(
		env,
		enif_make_binary(env, &result),
		crypto_box_BOXZEROBYTES,
		m.size - crypto_box_BOXZEROBYTES);
}

static
ERL_NIF_TERM enif_crypto_box_open_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary result, m, nonce, k;
375

376
377
378
379
380
381
382
383
384
385
	if (
	    (argc != 3) ||
	    (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
	    (!enif_inspect_binary(env, argv[1], &nonce)) ||
	    (!enif_inspect_binary(env, argv[2], &k)) ||
	    (m.size < crypto_box_BOXZEROBYTES) ||
	    (nonce.size != crypto_box_NONCEBYTES) ||
	    (k.size != crypto_box_BEFORENMBYTES)) {
		return enif_make_badarg(env);
	}
386

387
388
389
	if (!enif_alloc_binary(m.size, &result)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
390

391
	if (0 != crypto_box_open_afternm(result.data, m.data, m.size, nonce.data, k.data)) {
392
393
394
		enif_release_binary(&result);
		return nacl_error_tuple(env, "failed_verification");
	}
395

396
397
398
399
400
401
402
	return enif_make_sub_binary(
		env,
		enif_make_binary(env, &result),
		crypto_box_ZEROBYTES,
		m.size - crypto_box_ZEROBYTES);
}

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
/* Signing */
static
ERL_NIF_TERM enif_crypto_sign_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_sign_PUBLICKEYBYTES);
}

static
ERL_NIF_TERM enif_crypto_sign_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_sign_SECRETKEYBYTES);
}

static
ERL_NIF_TERM enif_crypto_sign_keypair(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary pk, sk;

	if (argc != 0) {
		return enif_make_badarg(env);
	}

	if (!enif_alloc_binary(crypto_sign_PUBLICKEYBYTES, &pk)) {
		return nacl_error_tuple(env, "alloc_failed");
	}

	if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
		return nacl_error_tuple(env, "alloc_failed");
	}

	crypto_sign_keypair(pk.data, sk.data);

432
	return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk));
433
434
}

435
436
437
438
439
/*
int crypto_sign(unsigned char *sm, unsigned long long *smlen,
                const unsigned char *m, unsigned long long mlen,
                const unsigned char *sk);
 */
440
441
442
443
444
445
446
447
static
ERL_NIF_TERM enif_crypto_sign(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary m, sk, sm;
	unsigned long long smlen;

	if (
	  (argc != 2) ||
	  (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
448
	  (!enif_inspect_binary(env, argv[1], &sk))) {
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
		return enif_make_badarg(env);
	}

	if (sk.size != crypto_sign_SECRETKEYBYTES) {
		return enif_make_badarg(env);
	}

	if (!enif_alloc_binary(m.size + crypto_sign_BYTES, &sm)) {
		return nacl_error_tuple(env, "alloc_failed");
	}

	crypto_sign(sm.data, &smlen, m.data, m.size, sk.data);

	return enif_make_sub_binary(env, enif_make_binary(env, &sm), 0, smlen);
}

465
466
467
468
469
/*
int crypto_sign_open(unsigned char *m, unsigned long long *mlen,
                     const unsigned char *sm, unsigned long long smlen,
                     const unsigned char *pk);
 */
470
471
472
473
474
475
476
477
static
ERL_NIF_TERM enif_crypto_sign_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary m, sm, pk;
	unsigned long long mlen;

	if (
	  (argc != 2) ||
	  (!enif_inspect_iolist_as_binary(env, argv[0], &sm)) ||
478
	  (!enif_inspect_binary(env, argv[1], &pk))) {
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
		return enif_make_badarg(env);
	}

	if (pk.size != crypto_sign_PUBLICKEYBYTES) {
		return enif_make_badarg(env);
	}

	if (!enif_alloc_binary(sm.size, &m)) {
		return nacl_error_tuple(env, "alloc_failed");
	}

	if (0 == crypto_sign_open(m.data, &mlen, sm.data, sm.size, pk.data)) {
		return enif_make_sub_binary(env, enif_make_binary(env, &m), 0, mlen);
	} else {
		enif_release_binary(&m);
		return nacl_error_tuple(env, "failed_verification");
	}
}

498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
/*
int crypto_sign_detached(unsigned char *sig, unsigned long long *siglen,
                         const unsigned char *m, unsigned long long mlen,
                         const unsigned char *sk);
 */
static
ERL_NIF_TERM enif_crypto_sign_detached(ErlNifEnv* env, int argc, ERL_NIF_TERM const argv[]) {
        ErlNifBinary m, sk, sig;
        unsigned long long siglen;

        if (
            (argc != 2) ||
            (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
            (!enif_inspect_binary(env, argv[1], &sk))) {
            return enif_make_badarg(env);
        }

        if (sk.size != crypto_sign_SECRETKEYBYTES) {
            return enif_make_badarg(env);
        }

        if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) {
            return nacl_error_tuple(env, "alloc_failed");
        }

        crypto_sign_detached(sig.data, &siglen, m.data, m.size, sk.data);

        return enif_make_sub_binary(env, enif_make_binary(env, &sig), 0, siglen);
}

/*
int crypto_sign_verify_detached(const unsigned char *sig,
                                const unsigned char *m,
                                unsigned long long mlen,
                                const unsigned char *pk);
 */
static
ERL_NIF_TERM enif_crypto_sign_verify_detached(ErlNifEnv* env, int argc, ERL_NIF_TERM const argv[]) {
        ErlNifBinary m, sig, pk;

        if (
            (argc != 3) ||
            (!enif_inspect_binary(env, argv[0], &sig)) ||
            (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
            (!enif_inspect_binary(env, argv[2], &pk))) {
            return enif_make_badarg(env);
        }

        if (pk.size != crypto_sign_PUBLICKEYBYTES) {
            return enif_make_badarg(env);
        }

        if (0 == crypto_sign_verify_detached(sig.data, m.data, m.size, pk.data)) {
            return enif_make_atom(env, "true");
        } else {
            return enif_make_atom(env, "false");
        }
}

557
/* Secret key cryptography */
558

559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
static
ERL_NIF_TERM enif_crypto_secretbox_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_secretbox_NONCEBYTES);
}

static
ERL_NIF_TERM enif_crypto_secretbox_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_secretbox_KEYBYTES);
}

static
ERL_NIF_TERM enif_crypto_secretbox_ZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_secretbox_ZEROBYTES);
}

static
ERL_NIF_TERM enif_crypto_secretbox_BOXZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_secretbox_BOXZEROBYTES);
}

579
580
581
582
583
584
585
586
587
588
static
ERL_NIF_TERM enif_crypto_stream_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_stream_KEYBYTES);
}

static
ERL_NIF_TERM enif_crypto_stream_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_stream_NONCEBYTES);
}

589
590
591
592
593
static
ERL_NIF_TERM enif_crypto_auth_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_auth_BYTES);
}

594
595
596
597
598
static
ERL_NIF_TERM enif_crypto_auth_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_auth_KEYBYTES);
}

599
600
601
602
603
static
ERL_NIF_TERM enif_crypto_onetimeauth_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_onetimeauth_BYTES);
}

604
605
606
607
608
static
ERL_NIF_TERM enif_crypto_onetimeauth_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	return enif_make_int64(env, crypto_onetimeauth_KEYBYTES);
}

609
610
611
static
ERL_NIF_TERM enif_crypto_secretbox(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary key, nonce, padded_msg, padded_ciphertext;
612

613
614
615
	if (
	  (argc != 3) ||
	  (!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg)) ||
616
617
	  (!enif_inspect_binary(env, argv[1], &nonce)) ||
	  (!enif_inspect_binary(env, argv[2], &key))) {
618
619
		return enif_make_badarg(env);
	}
620

621
622
623
624
625
626
	if (
	  (key.size != crypto_secretbox_KEYBYTES) ||
	  (nonce.size != crypto_secretbox_NONCEBYTES) ||
	  (padded_msg.size < crypto_secretbox_ZEROBYTES)) {
		return enif_make_badarg(env);
	}
627

628
629
630
	if (!enif_alloc_binary(padded_msg.size, &padded_ciphertext)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
631

632
633
634
635
636
	crypto_secretbox(
	  padded_ciphertext.data,
	  padded_msg.data, padded_msg.size,
	  nonce.data,
	  key.data);
637

638
639
640
641
642
643
644
645
646
	return enif_make_sub_binary(env,
		enif_make_binary(env, &padded_ciphertext),
		crypto_secretbox_BOXZEROBYTES,
		padded_msg.size - crypto_secretbox_BOXZEROBYTES);
}

static
ERL_NIF_TERM enif_crypto_secretbox_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary key, nonce, padded_ciphertext, padded_msg;
647

648
649
650
	if (
	  (argc != 3) ||
	  (!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) ||
651
652
	  (!enif_inspect_binary(env, argv[1], &nonce)) ||
	  (!enif_inspect_binary(env, argv[2], &key))) {
653
654
		return enif_make_badarg(env);
	}
655

656
657
658
659
660
661
	if (
	  (key.size != crypto_secretbox_KEYBYTES) ||
	  (nonce.size != crypto_secretbox_NONCEBYTES) ||
	  (padded_ciphertext.size < crypto_secretbox_BOXZEROBYTES)) {
		return enif_make_badarg(env);
	}
662

663
664
665
	if (!enif_alloc_binary(padded_ciphertext.size, &padded_msg)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
666

667
668
669
670
671
672
673
674
675
	if (crypto_secretbox_open(
	    padded_msg.data,
	    padded_ciphertext.data,
	    padded_ciphertext.size,
	    nonce.data,
	    key.data) != 0) {
		enif_release_binary(&padded_msg);
		return nacl_error_tuple(env, "failed_verification");
	}
676

677
678
679
680
681
682
	return enif_make_sub_binary(
	    env,
	    enif_make_binary(env, &padded_msg),
	    crypto_secretbox_ZEROBYTES,
	    padded_ciphertext.size - crypto_secretbox_ZEROBYTES);
}
683
684
685
686
687
688
689
690
691

static
ERL_NIF_TERM enif_crypto_stream(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary c, n, k;
	ErlNifUInt64 clen;

	if (
	  (argc != 3) ||
	  (!enif_get_uint64(env, argv[0], &clen)) ||
692
693
	  (!enif_inspect_binary(env, argv[1], &n)) ||
	  (!enif_inspect_binary(env, argv[2], &k))) {
694
695
		return enif_make_badarg(env);
	}
696

697
698
699
700
701
	if (
	  (k.size != crypto_stream_KEYBYTES) ||
	  (n.size != crypto_stream_NONCEBYTES)) {
		return enif_make_badarg(env);
	}
702

703
704
705
	if (!enif_alloc_binary(clen, &c)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
706

707
	crypto_stream(c.data, c.size, n.data, k.data);
708

709
710
711
712
713
714
	return enif_make_binary(env, &c);
}

static
ERL_NIF_TERM enif_crypto_stream_xor(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary c, m, n, k;
715

716
717
718
	if (
	  (argc != 3) ||
	  (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
719
720
	  (!enif_inspect_binary(env, argv[1], &n)) ||
	  (!enif_inspect_binary(env, argv[2], &k))) {
721
722
		return enif_make_badarg(env);
	}
723

724
725
726
727
728
	if (
	  (k.size != crypto_stream_KEYBYTES) ||
	  (n.size != crypto_stream_NONCEBYTES)) {
		return enif_make_badarg(env);
	}
729

730
731
732
	if (!enif_alloc_binary(m.size, &c)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
733

734
	crypto_stream_xor(c.data, m.data, m.size, n.data, k.data);
735

736
737
738
	return enif_make_binary(env, &c);
}

739
740
741
742
743
744
745
static
ERL_NIF_TERM enif_crypto_auth(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary a,m,k;

	if (
	  (argc != 2) ||
	  (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
746
	  (!enif_inspect_binary(env, argv[1], &k))) {
747
748
		return enif_make_badarg(env);
	}
749

750
751
752
	if (k.size != crypto_auth_KEYBYTES) {
		return enif_make_badarg(env);
	}
753

754
755
756
	if (!enif_alloc_binary(crypto_auth_BYTES, &a)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
757

758
	crypto_auth(a.data, m.data, m.size, k.data);
759

760
761
762
763
764
765
	return enif_make_binary(env, &a);
}

static
ERL_NIF_TERM enif_crypto_auth_verify(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary a, m, k;
766

767
768
	if (
	  (argc != 3) ||
769
	  (!enif_inspect_binary(env, argv[0], &a)) ||
770
	  (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
771
	  (!enif_inspect_binary(env, argv[2], &k))) {
772
773
		return enif_make_badarg(env);
	}
774

775
776
777
778
779
	if (
	  (k.size != crypto_auth_KEYBYTES) ||
	  (a.size != crypto_auth_BYTES)) {
		return enif_make_badarg(env);
	}
780

781
782
783
784
785
786
	if (0 == crypto_auth_verify(a.data, m.data, m.size, k.data)) {
		return enif_make_atom(env, "true");
	} else {
		return enif_make_atom(env, "false");
	}
}
787
788
789
790
791
792
793
794

static
ERL_NIF_TERM enif_crypto_onetimeauth(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary a,m,k;

	if (
	  (argc != 2) ||
	  (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
795
	  (!enif_inspect_binary(env, argv[1], &k))) {
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
		return enif_make_badarg(env);
	}

	if (k.size != crypto_onetimeauth_KEYBYTES) {
		return enif_make_badarg(env);
	}

	if (!enif_alloc_binary(crypto_onetimeauth_BYTES, &a)) {
		return nacl_error_tuple(env, "alloc_failed");
	}

	crypto_onetimeauth(a.data, m.data, m.size, k.data);

	return enif_make_binary(env, &a);
}

static
ERL_NIF_TERM enif_crypto_onetimeauth_verify(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
	ErlNifBinary a, m, k;

	if (
	  (argc != 3) ||
818
	  (!enif_inspect_binary(env, argv[0], &a)) ||
819
	  (!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
820
	  (!enif_inspect_binary(env, argv[2], &k))) {
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
		return enif_make_badarg(env);
	}

	if (
	  (k.size != crypto_onetimeauth_KEYBYTES) ||
	  (a.size != crypto_onetimeauth_BYTES)) {
		return enif_make_badarg(env);
	}

	if (0 == crypto_onetimeauth_verify(a.data, m.data, m.size, k.data)) {
		return enif_make_atom(env, "true");
	} else {
		return enif_make_atom(env, "false");
	}
}
836

837
838
839
840
841
static
ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
{
	size_t req_size;
	ErlNifBinary result;
842

843
844
845
	if ((argc != 1) || (!enif_get_uint64(env, argv[0], &req_size))) {
		return enif_make_badarg(env);
	}
846

847
848
849
	if (!enif_alloc_binary(req_size, &result)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
850

851
	randombytes(result.data, result.size);
852

853
854
855
	return enif_make_binary(env, &result);
}

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
856
/* Various other helper functions */
857
static
Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
858
859
860
861
862
863
864
865
866
867
868
869
void uint64_pack(unsigned char *y, ErlNifUInt64 x)
{
	*y++ = x; x >>= 8;
	*y++ = x; x >>= 8;
	*y++ = x; x >>= 8;
	*y++ = x; x >>= 8;
	*y++ = x; x >>= 8;
	*y++ = x; x >>= 8;
	*y++ = x; x >>= 8;
	*y++ = x; x >>= 8;
}

870
871
static
ErlNifUInt64 uint64_unpack(const unsigned char *x)
Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
872
873
874
875
876
877
878
879
880
881
882
883
884
885
{
	ErlNifUInt64 result;

	result = x[7];
	result <<= 8; result |= x[6];
	result <<= 8; result |= x[5];
	result <<= 8; result |= x[4];
	result <<= 8; result |= x[3];
	result <<= 8; result |= x[2];
	result <<= 8; result |= x[1];
	result <<= 8; result |= x[0];
	return result;
}

886
887
static
int crypto_block(unsigned char *out, const unsigned char *in, const unsigned char *k)
Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
{
	ErlNifUInt64 v0 = uint64_unpack(in + 0);
	ErlNifUInt64 v1 = uint64_unpack(in + 8);
	ErlNifUInt64 k0 = uint64_unpack(k + 0);
	ErlNifUInt64 k1 = uint64_unpack(k + 8);
	ErlNifUInt64 k2 = uint64_unpack(k + 16);
	ErlNifUInt64 k3 = uint64_unpack(k + 24);
	ErlNifUInt64 sum = 0;
	ErlNifUInt64 delta = 0x9e3779b97f4a7c15;
	int i;
	for (i = 0;i < 32;++i) {
		sum += delta;
		v0 += ((v1<<7) + k0) ^ (v1 + sum) ^ ((v1>>12) + k1);
		v1 += ((v0<<16) + k2) ^ (v0 + sum) ^ ((v0>>8) + k3);
	}
	uint64_pack(out + 0,v0);
	uint64_pack(out + 8,v1);

	return 0;
}

static
ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
{
	ErlNifBinary in, out, key;
913

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
914
915
916
917
918
919
920
	if (
	  (argc != 2) ||
	  (!enif_inspect_binary(env, argv[0], &in)) ||
	  (!enif_inspect_binary(env, argv[1], &key)) ||
	  (in.size != 16) || (key.size != 32)) {
		return enif_make_badarg(env);
	}
921

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
922
923
924
	if (!enif_alloc_binary(in.size, &out)) {
		return nacl_error_tuple(env, "alloc_failed");
	}
925

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
926
	crypto_block(out.data, in.data, key.data);
927

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
928
929
930
	return enif_make_binary(env, &out);
}

931
/* Tie the knot to the Erlang world */
Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
932
static ErlNifFunc nif_funcs[] = {
933
934
935
	{"crypto_box_NONCEBYTES", 0, enif_crypto_box_NONCEBYTES},
	{"crypto_box_ZEROBYTES", 0, enif_crypto_box_ZEROBYTES},
	{"crypto_box_BOXZEROBYTES", 0, enif_crypto_box_BOXZEROBYTES},
936
937
	{"crypto_box_PUBLICKEYBYTES", 0, enif_crypto_box_PUBLICKEYBYTES},
	{"crypto_box_SECRETKEYBYTES", 0, enif_crypto_box_SECRETKEYBYTES},
938
	{"crypto_box_BEFORENMBYTES", 0, enif_crypto_box_BEFORENMBYTES},
Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
939
	{"crypto_box_keypair", 0, enif_crypto_box_keypair},
940
	{"crypto_box_b", 4, enif_crypto_box},
941
	{"crypto_box", 4, enif_crypto_box, ERL_NIF_DIRTY_JOB_CPU_BOUND},
942
	{"crypto_box_open_b", 4, enif_crypto_box_open},
943
	{"crypto_box_open", 4, enif_crypto_box_open, ERL_NIF_DIRTY_JOB_CPU_BOUND},
944
	{"crypto_box_beforenm", 2, enif_crypto_box_beforenm},
945
946
947
948
	{"crypto_box_afternm_b", 3, enif_crypto_box_afternm},
	{"crypto_box_afternm", 3, enif_crypto_box_afternm, ERL_NIF_DIRTY_JOB_CPU_BOUND},
	{"crypto_box_open_afternm_b", 3, enif_crypto_box_open_afternm},
	{"crypto_box_open_afternm", 3, enif_crypto_box_open_afternm, ERL_NIF_DIRTY_JOB_CPU_BOUND},
949

950
951
952
	{"crypto_sign_PUBLICKEYBYTES", 0, enif_crypto_sign_PUBLICKEYBYTES},
	{"crypto_sign_SECRETKEYBYTES", 0, enif_crypto_sign_SECRETKEYBYTES},
	{"crypto_sign_keypair", 0, enif_crypto_sign_keypair},
953
	{"crypto_sign_b", 2, enif_crypto_sign},
954
	{"crypto_sign", 2, enif_crypto_sign, ERL_NIF_DIRTY_JOB_CPU_BOUND},
955
	{"crypto_sign_open_b", 2, enif_crypto_sign_open},
956
	{"crypto_sign_open", 2, enif_crypto_sign_open, ERL_NIF_DIRTY_JOB_CPU_BOUND},
957

958
959
960
        {"crypto_sign_detached", 2, enif_crypto_sign_detached, ERL_NIF_DIRTY_JOB_CPU_BOUND},
        {"crypto_sign_verify_detached", 3, enif_crypto_sign_verify_detached, ERL_NIF_DIRTY_JOB_CPU_BOUND},

961
962
963
964
	{"crypto_secretbox_NONCEBYTES", 0, enif_crypto_secretbox_NONCEBYTES},
	{"crypto_secretbox_ZEROBYTES", 0, enif_crypto_secretbox_ZEROBYTES},
	{"crypto_secretbox_BOXZEROBYTES", 0, enif_crypto_secretbox_BOXZEROBYTES},
	{"crypto_secretbox_KEYBYTES", 0, enif_crypto_secretbox_KEYBYTES},
965
	{"crypto_secretbox_b", 3, enif_crypto_secretbox},
966
	{"crypto_secretbox", 3, enif_crypto_secretbox, ERL_NIF_DIRTY_JOB_CPU_BOUND},
967
	{"crypto_secretbox_open_b", 3, enif_crypto_secretbox_open},
968
969
	{"crypto_secretbox_open", 3, enif_crypto_secretbox_open, ERL_NIF_DIRTY_JOB_CPU_BOUND},

970
971
	{"crypto_stream_KEYBYTES", 0, enif_crypto_stream_KEYBYTES},
	{"crypto_stream_NONCEBYTES", 0, enif_crypto_stream_NONCEBYTES},
972
	{"crypto_stream_b", 3, enif_crypto_stream},
973
	{"crypto_stream", 3, enif_crypto_stream, ERL_NIF_DIRTY_JOB_CPU_BOUND},
974
	{"crypto_stream_xor_b", 3, enif_crypto_stream_xor},
975
976
	{"crypto_stream_xor", 3, enif_crypto_stream_xor, ERL_NIF_DIRTY_JOB_CPU_BOUND},

977
	{"crypto_auth_BYTES", 0, enif_crypto_auth_BYTES},
978
	{"crypto_auth_KEYBYTES", 0, enif_crypto_auth_KEYBYTES},
979
	{"crypto_auth_b", 2, enif_crypto_auth},
980
	{"crypto_auth", 2, enif_crypto_auth, ERL_NIF_DIRTY_JOB_CPU_BOUND},
981
	{"crypto_auth_verify_b", 3, enif_crypto_auth_verify},
982
983
	{"crypto_auth_verify", 3, enif_crypto_auth_verify, ERL_NIF_DIRTY_JOB_CPU_BOUND},

984
	{"crypto_onetimeauth_BYTES", 0, enif_crypto_onetimeauth_BYTES},
985
	{"crypto_onetimeauth_KEYBYTES", 0, enif_crypto_onetimeauth_KEYBYTES},
986
	{"crypto_onetimeauth_b", 2, enif_crypto_onetimeauth},
987
	{"crypto_onetimeauth", 2, enif_crypto_onetimeauth, ERL_NIF_DIRTY_JOB_CPU_BOUND},
988
	{"crypto_onetimeauth_verify_b", 3, enif_crypto_onetimeauth_verify},
989
990
	{"crypto_onetimeauth_verify", 3, enif_crypto_onetimeauth_verify, ERL_NIF_DIRTY_JOB_CPU_BOUND},

991
	{"crypto_hash_b", 1, enif_crypto_hash},
992
993
	{"crypto_hash", 1, enif_crypto_hash, ERL_NIF_DIRTY_JOB_CPU_BOUND},
	{"crypto_verify_16", 2, enif_crypto_verify_16},
994
	{"crypto_verify_32", 2, enif_crypto_verify_32},
995
996
997

	{"crypto_curve25519_scalarmult", 2, enif_crypto_curve25519_scalarmult},

Alexander Færøy's avatar
Alexander Færøy committed
998
999
1000
1001
1002
1003
	{"crypto_sign_ed25519_keypair", 0, enif_crypto_sign_ed25519_keypair},
	{"crypto_sign_ed25519_public_to_curve25519", 1, enif_crypto_sign_ed25519_public_to_curve25519},
	{"crypto_sign_ed25519_secret_to_curve25519", 1, enif_crypto_sign_ed25519_secret_to_curve25519},
	{"crypto_sign_ed25519_PUBLICKEYBYTES", 0, enif_crypto_sign_ed25519_PUBLICKEYBYTES},
	{"crypto_sign_ed25519_SECRETKEYBYTES", 0, enif_crypto_sign_ed25519_SECRETKEYBYTES},

1004
	{"randombytes_b", 1, enif_randombytes},
Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
1005
	{"randombytes", 1, enif_randombytes, ERL_NIF_DIRTY_JOB_CPU_BOUND},
1006

Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
1007
	{"scramble_block_16", 2, enif_scramble_block_16}
Jesper Louis Andersen's avatar
Jesper Louis Andersen committed
1008
1009
};

1010
1011


1012
ERL_NIF_INIT(enacl_nif, nif_funcs, enif_crypto_load, NULL, NULL, NULL);