1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2010-2020. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21 #include "api_ng.h"
22 #include "aes.h"
23 #include "cipher.h"
24
25 /*
26 * A unified set of functions for encryption/decryption.
27 *
28 */
29 ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
30 ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
31
32 /*************************************************************************/
33 /* Compatibility functions. */
34 /*************************************************************************/
35 #ifdef HAVE_ECB_IVEC_BUG
36 /* <= 0.9.8l returns faulty ivec length */
37 # define GET_IV_LEN(Ciph) ((Ciph)->flags & ECB_BUG_0_9_8L) ? 0 : EVP_CIPHER_iv_length((Ciph)->cipher.p)
38 #else
39 # define GET_IV_LEN(Ciph) EVP_CIPHER_iv_length((Ciph)->cipher.p)
40 #endif
41
42 #if !defined(HAVE_EVP_CIPHER_CTX_COPY)
43 /*
44 The EVP_CIPHER_CTX_copy is not available in older cryptolibs although
45 the function is needed.
46 Instead of implement it in-place, we have a copy here as a compatibility
47 function
48 */
49
50 int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in);
51
EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX * out,const EVP_CIPHER_CTX * in)52 int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in)
53 {
54 if ((in == NULL) || (in->cipher == NULL))
55 {
56 return 0;
57 }
58 #ifdef HAS_ENGINE_SUPPORT
59 /* Make sure it's safe to copy a cipher context using an ENGINE */
60 if (in->engine && !ENGINE_init(in->engine))
61 return 0;
62 #endif
63
64 EVP_CIPHER_CTX_cleanup(out);
65 memcpy(out,in,sizeof *out);
66
67 if (in->cipher_data && in->cipher->ctx_size)
68 {
69 out->cipher_data=OPENSSL_malloc(in->cipher->ctx_size);
70 if (!out->cipher_data)
71 return 0;
72 memcpy(out->cipher_data,in->cipher_data,in->cipher->ctx_size);
73 }
74
75 #if defined(EVP_CIPH_CUSTOM_COPY) && defined(EVP_CTRL_COPY)
76 if (in->cipher->flags & EVP_CIPH_CUSTOM_COPY)
77 return in->cipher->ctrl((EVP_CIPHER_CTX *)in, EVP_CTRL_COPY, 0, out);
78 #endif
79 return 1;
80 }
81 /****** End of !defined(HAVE_EVP_CIPHER_CTX_COPY) ******/
82 #endif
83
84 /*************************************************************************/
85 /* Get the arguments for the initialization of the EVP_CIPHER_CTX. Check */
86 /* them and initialize that context. */
87 /*************************************************************************/
get_init_args(ErlNifEnv * env,struct evp_cipher_ctx * ctx_res,const ERL_NIF_TERM cipher_arg,const ERL_NIF_TERM key_arg,const ERL_NIF_TERM ivec_arg,const ERL_NIF_TERM encflg_arg,const ERL_NIF_TERM padding_arg,const struct cipher_type_t ** cipherp,ERL_NIF_TERM * return_term)88 static int get_init_args(ErlNifEnv* env,
89 struct evp_cipher_ctx *ctx_res,
90 const ERL_NIF_TERM cipher_arg,
91 const ERL_NIF_TERM key_arg,
92 const ERL_NIF_TERM ivec_arg,
93 const ERL_NIF_TERM encflg_arg,
94 const ERL_NIF_TERM padding_arg,
95 const struct cipher_type_t **cipherp,
96 ERL_NIF_TERM *return_term)
97 {
98 int ivec_len;
99 ErlNifBinary key_bin;
100 ErlNifBinary ivec_bin;
101
102 ctx_res->ctx = NULL; /* For testing if *ctx should be freed after errors */
103 #if !defined(HAVE_EVP_AES_CTR)
104 ctx_res->env = NULL; /* For testing if *env should be freed after errors */
105 #endif
106 ctx_res->padding = atom_undefined;
107 ctx_res->padded_size = -1;
108 ctx_res->size = 0;
109
110 /* Fetch the flag telling if we are going to encrypt (=true) or decrypt (=false) */
111 if (encflg_arg == atom_true)
112 ctx_res->encflag = 1;
113 else if (encflg_arg == atom_false)
114 ctx_res->encflag = 0;
115 else if (encflg_arg == atom_undefined)
116 /* For compat funcs in crypto.erl */
117 ctx_res->encflag = -1;
118 else
119 {
120 *return_term = EXCP_BADARG(env, "Bad enc flag");
121 goto err;
122 }
123
124 /* Fetch the key */
125 if (!enif_inspect_iolist_as_binary(env, key_arg, &key_bin))
126 {
127 *return_term = EXCP_BADARG(env, "Bad key");
128 goto err;
129 }
130
131 /* Fetch cipher type */
132 if (!enif_is_atom(env, cipher_arg))
133 {
134 *return_term = EXCP_BADARG(env, "Cipher id is not an atom");
135 goto err;
136 }
137
138 if (!(*cipherp = get_cipher_type(cipher_arg, key_bin.size)))
139 {
140 if (!get_cipher_type_no_key(cipher_arg))
141 *return_term = EXCP_BADARG(env, "Unknown cipher");
142 else
143 *return_term = EXCP_BADARG(env, "Bad key size");
144 goto err;
145 }
146
147 if ((*cipherp)->flags & AEAD_CIPHER)
148 {
149 *return_term = EXCP_BADARG(env, "Missing arguments for this cipher");
150 goto err;
151 }
152
153
154 if (CIPHER_FORBIDDEN_IN_FIPS(*cipherp))
155 {
156 *return_term = EXCP_NOTSUP(env, "Forbidden in FIPS");
157 goto err;
158 }
159
160 /* Get ivec_len for this cipher (if we found one) */
161 #if !defined(HAVE_EVP_AES_CTR)
162 /* This code is for historic OpenSSL where EVP_aes_*_ctr is not defined.... */
163 if ((*cipherp)->cipher.p) {
164 /* Not aes_ctr compatibility code since EVP_*
165 was defined and assigned to (*cipherp)->cipher.p */
166 ivec_len = GET_IV_LEN(*cipherp);
167 } else {
168 /* No EVP_* was found */
169 if ((*cipherp)->flags & AES_CTR_COMPAT)
170 /* Use aes_ctr compatibility code later */
171 ivec_len = 16;
172 else {
173 /* Unsupported crypto */
174 *return_term = EXCP_NOTSUP(env, "Cipher not supported in this libcrypto version");
175 goto err;
176 }
177 }
178 #else
179 /* Normal code */
180 if (!((*cipherp)->cipher.p)) {
181 *return_term = EXCP_NOTSUP(env, "Cipher not supported in this libcrypto version");
182 goto err;
183 }
184 ivec_len = GET_IV_LEN(*cipherp);
185 #endif
186
187 /* (*cipherp)->cipher.p != NULL and ivec_len has a value */
188
189 /* Fetch IV */
190 if (ivec_len && (ivec_arg != atom_undefined)) {
191 if (!enif_inspect_iolist_as_binary(env, ivec_arg, &ivec_bin))
192 {
193 *return_term = EXCP_BADARG(env, "Bad iv type");
194 goto err;
195 }
196
197 if (ivec_len != ivec_bin.size)
198 {
199 *return_term = EXCP_BADARG(env, "Bad iv size");
200 goto err;
201 }
202 }
203
204 ctx_res->iv_len = ivec_len;
205
206 #if !defined(HAVE_EVP_AES_CTR)
207 if (!((*cipherp)->cipher.p)
208 && ((*cipherp)->flags & AES_CTR_COMPAT)
209 ) {
210 /* Must use aes_ctr compatibility code */
211 ERL_NIF_TERM ecount_bin;
212 unsigned char *outp;
213 if ((outp = enif_make_new_binary(env, AES_BLOCK_SIZE, &ecount_bin)) == NULL) {
214 *return_term = EXCP_ERROR(env, "Can't allocate ecount_bin");
215 goto err;
216 }
217 memset(outp, 0, AES_BLOCK_SIZE);
218
219 ctx_res->env = enif_alloc_env();
220 if (!ctx_res->env) {
221 *return_term = EXCP_ERROR(env, "Can't allocate env");
222 goto err;
223 }
224 ctx_res->state =
225 enif_make_copy(ctx_res->env,
226 enif_make_tuple4(env, key_arg, ivec_arg, ecount_bin, enif_make_int(env, 0)));
227 goto success;
228 } else {
229 /* Flag for subsequent calls that no aes_ctr compatibility code should be called */
230 ctx_res->state = atom_undefined;
231 ctx_res->env = NULL;
232 }
233 #endif
234
235 /* Initialize the EVP_CIPHER_CTX */
236
237 ctx_res->ctx = EVP_CIPHER_CTX_new();
238 if (! ctx_res->ctx)
239 {
240 *return_term = EXCP_ERROR(env, "Can't allocate context");
241 goto err;
242 }
243
244 if (!EVP_CipherInit_ex(ctx_res->ctx, (*cipherp)->cipher.p, NULL, NULL, NULL, ctx_res->encflag))
245 {
246 *return_term = EXCP_ERROR(env, "Can't initialize context, step 1");
247 goto err;
248 }
249
250 if (!EVP_CIPHER_CTX_set_key_length(ctx_res->ctx, (int)key_bin.size))
251 {
252 *return_term = EXCP_ERROR(env, "Can't initialize context, key_length");
253 goto err;
254 }
255
256 #ifdef HAVE_RC2
257 if (EVP_CIPHER_type((*cipherp)->cipher.p) == NID_rc2_cbc) {
258 if (key_bin.size > INT_MAX / 8) {
259 *return_term = EXCP_BADARG(env, "To large rc2_cbc key");
260 goto err;
261 }
262 if (!EVP_CIPHER_CTX_ctrl(ctx_res->ctx, EVP_CTRL_SET_RC2_KEY_BITS, (int)key_bin.size * 8, NULL)) {
263 *return_term = EXCP_ERROR(env, "ctrl rc2_cbc key");
264 goto err;
265 }
266 }
267 #endif
268
269 if (ivec_arg == atom_undefined || ivec_len == 0)
270 {
271 if (!EVP_CipherInit_ex(ctx_res->ctx, NULL, NULL, key_bin.data, NULL, -1)) {
272 *return_term = EXCP_ERROR(env, "Can't initialize key");
273 goto err;
274 }
275 }
276 else
277 if (!EVP_CipherInit_ex(ctx_res->ctx, NULL, NULL, key_bin.data, ivec_bin.data, -1))
278 {
279 *return_term = EXCP_ERROR(env, "Can't initialize key or iv");
280 goto err;
281 }
282
283 /* Set padding */
284 if ((padding_arg == atom_undefined) ||
285 (padding_arg == atom_none) ||
286 (padding_arg == atom_zero) ||
287 (padding_arg == atom_random) )
288 EVP_CIPHER_CTX_set_padding(ctx_res->ctx, 0);
289
290 else if (padding_arg != atom_pkcs_padding) /* pkcs_padding is default */
291 {
292 *return_term = EXCP_BADARG(env, "Bad padding flag");
293 goto err;
294 }
295
296 ctx_res->padding = padding_arg;
297
298 *return_term = atom_ok;
299
300 #if !defined(HAVE_EVP_AES_CTR)
301 success:
302 #endif
303 return 1;
304
305 err:
306 return 0;
307 }
308
309 /*************************************************************************/
310 /* Get the arguments for the EVP_CipherUpdate function, and call it. */
311 /*************************************************************************/
312
get_update_args(ErlNifEnv * env,struct evp_cipher_ctx * ctx_res,const ERL_NIF_TERM indata_arg,ERL_NIF_TERM * return_term)313 static int get_update_args(ErlNifEnv* env,
314 struct evp_cipher_ctx *ctx_res,
315 const ERL_NIF_TERM indata_arg,
316 ERL_NIF_TERM *return_term)
317 {
318 ErlNifBinary in_data_bin, out_data_bin;
319 int out_len, block_size;
320
321 if (!enif_inspect_binary(env, indata_arg, &in_data_bin) )
322 {
323 *return_term = EXCP_BADARG(env, "Bad 2:nd arg");
324 goto err0;
325 }
326
327 ASSERT(in_data_bin.size <= INT_MAX);
328
329 ctx_res->size += in_data_bin.size;
330
331 #if !defined(HAVE_EVP_AES_CTR)
332 if (ctx_res->state != atom_undefined) {
333 /* Use AES_CTR compatibility code */
334 ERL_NIF_TERM state0, newstate_and_outdata;
335 const ERL_NIF_TERM *tuple_argv;
336 int tuple_argc;
337
338 state0 = enif_make_copy(env, ctx_res->state);
339
340 if (enif_get_tuple(env, state0, &tuple_argc, &tuple_argv) && (tuple_argc == 4)) {
341 /* A compatibility state term */
342 /* encrypt and decrypt is performed by calling the same function */
343 newstate_and_outdata = aes_ctr_stream_encrypt_compat(env, state0, indata_arg);
344
345 if (enif_get_tuple(env, newstate_and_outdata, &tuple_argc, &tuple_argv) && (tuple_argc == 2)) {
346 /* newstate_and_outdata = {NewState, OutData} */
347 ctx_res->state = enif_make_copy(ctx_res->env, tuple_argv[0]);
348 /* Return the OutData (from the newstate_and_outdata tuple) only: */
349 *return_term = tuple_argv[1];
350 }
351 }
352 } else
353 #endif
354 #if defined(HAVE_UPDATE_EMPTY_DATA_BUG)
355 if (in_data_bin.size == 0)
356 {
357 if (!enif_alloc_binary(0, &out_data_bin))
358 {
359 *return_term = EXCP_ERROR(env, "Can't allocate outdata");
360 goto err;
361 }
362 *return_term = enif_make_binary(env, &out_data_bin);
363 } else
364 #endif
365 {
366 block_size = EVP_CIPHER_CTX_block_size(ctx_res->ctx);
367
368 if (!enif_alloc_binary((size_t)in_data_bin.size+block_size, &out_data_bin))
369 {
370 *return_term = EXCP_ERROR(env, "Can't allocate outdata");
371 goto err0;
372 }
373
374 if (!EVP_CipherUpdate(ctx_res->ctx, out_data_bin.data, &out_len, in_data_bin.data, in_data_bin.size))
375 {
376 *return_term = EXCP_ERROR(env, "Can't update");
377 goto err;
378 }
379
380 if (!enif_realloc_binary(&out_data_bin, (size_t)out_len))
381 {
382 *return_term = EXCP_ERROR(env, "Can't reallocate");
383 goto err;
384 }
385
386 CONSUME_REDS(env, in_data_bin);
387 /* return the result text as a binary: */
388 *return_term = enif_make_binary(env, &out_data_bin);
389 }
390
391 /* success: */
392 return 1;
393
394 err:
395 enif_release_binary(&out_data_bin);
396 err0:
397 return 0;
398 }
399
400 /*************************************************************************/
401 /* Get the arguments for the EVP_CipherFinal function, and call it. */
402 /*************************************************************************/
403
get_final_args(ErlNifEnv * env,struct evp_cipher_ctx * ctx_res,ERL_NIF_TERM * return_term)404 static int get_final_args(ErlNifEnv* env,
405 struct evp_cipher_ctx *ctx_res,
406 ERL_NIF_TERM *return_term
407 )
408 {
409 ErlNifBinary out_data_bin;
410 int block_size, pad_size;
411 int out_len, pad_offset;
412
413 #if !defined(HAVE_EVP_AES_CTR)
414 if (ctx_res->state != atom_undefined) {
415 /* Use AES_CTR compatibility code */
416 /* Padding size is always 0, because the block_size is 1 and therefore
417 always filled
418 */
419 ctx_res->padded_size = 0;
420 out_len = 0;
421
422 if (!enif_alloc_binary(out_len, &out_data_bin))
423 {
424 *return_term = EXCP_ERROR(env, "Can't allocate empty outdata");
425 goto err0;
426 }
427 } else
428 #endif
429 {
430 block_size = EVP_CIPHER_CTX_block_size(ctx_res->ctx);
431
432 pad_size = ctx_res->size % block_size;
433 if (pad_size)
434 pad_size = block_size - pad_size;
435
436 if (!enif_alloc_binary((size_t)block_size, &out_data_bin))
437 {
438 *return_term = EXCP_ERROR(env, "Can't allocate final outdata");
439 goto err0;
440 }
441
442 if (ctx_res->encflag)
443 {/* Maybe do padding */
444
445 /* First set lengths etc and do the otp_padding */
446 if (ctx_res->padding == atom_undefined)
447 {
448 ctx_res->padded_size = pad_size;
449 pad_offset = 0;
450 }
451
452 else if (ctx_res->padding == atom_none)
453 {
454 ASSERT(pad_size == 0);
455 ctx_res->padded_size = pad_size;
456 pad_offset = 0;
457 }
458
459 else if (ctx_res->padding == atom_pkcs_padding)
460 {
461 ctx_res->padded_size = pad_size ? pad_size : block_size;
462 pad_offset = 0;
463 }
464
465 else if ((ctx_res->padding == atom_zero) ||
466 (ctx_res->padding == atom_random))
467 {
468 if (pad_size)
469 {
470 unsigned char padding[EVP_MAX_BLOCK_LENGTH];
471 int i;
472 if (ctx_res->padding == atom_zero)
473 for(i=0; i<pad_size; i++) padding[i] = (unsigned char)0;
474 else
475 RAND_bytes(padding, pad_size);
476 if (!EVP_CipherUpdate(ctx_res->ctx, out_data_bin.data, &out_len, padding, pad_size))
477 {
478 *return_term = EXCP_ERROR(env, "Can't pad");
479 goto err;
480 }
481 }
482 else
483 out_len = 0;
484
485 ctx_res->padded_size = pad_size;
486 pad_offset = out_len;
487 }
488
489 else
490 {
491 *return_term = EXCP_ERROR(env, "Bad padding flg");
492 goto err;
493 }
494
495 /* Decide how many bytes that are to be returned and set out_len to that value */
496 if (ctx_res->padding == atom_undefined)
497 {
498 out_len = 0;
499 }
500
501 else
502 {
503 if (!EVP_CipherFinal_ex(ctx_res->ctx, out_data_bin.data+pad_offset, &out_len))
504 {
505 if (ctx_res->padding == atom_none)
506 *return_term = EXCP_ERROR(env, "Padding 'none' but unfilled last block");
507 else if (ctx_res->padding == atom_pkcs_padding)
508 *return_term = EXCP_ERROR(env, "Can't finalize");
509 else
510 *return_term = EXCP_ERROR(env, "Padding failed");
511 goto err;
512 }
513 else
514 out_len += pad_offset;
515 }
516
517 /* (end of encryption part) */
518 }
519 else
520 { /* decryption. */
521 /* Decide how many bytes that are to be returned and set out_len to that value */
522 if (ctx_res->padding == atom_undefined)
523 {
524 out_len = 0;
525 }
526
527 else if ((ctx_res->padding == atom_none) ||
528 (ctx_res->padding == atom_pkcs_padding) ||
529 (ctx_res->padding == atom_zero) ||
530 (ctx_res->padding == atom_random) )
531 {
532 if (!EVP_CipherFinal_ex(ctx_res->ctx, out_data_bin.data, &out_len))
533 {
534 *return_term = EXCP_ERROR(env, "Can't finalize");
535 goto err;
536 }
537 }
538 else
539 {
540 *return_term = EXCP_ERROR(env, "Bad padding flg");
541 goto err;
542 }
543 /* (end of decryption part) */
544 }
545 }
546
547 /* success: */
548 if (!enif_realloc_binary(&out_data_bin, (size_t)out_len))
549 {
550 *return_term = EXCP_ERROR(env, "Can't reallocate");
551 goto err;
552 }
553
554 *return_term = enif_make_binary(env, &out_data_bin);
555
556 return 1;
557
558 err:
559 enif_release_binary(&out_data_bin);
560 err0:
561 return 0;
562 }
563
564
565 /*************************************************************************/
566 /* Initialize the state for (de/en)cryption */
567 /*************************************************************************/
568
ng_crypto_init_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])569 ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
570 {/* (Cipher, Key, IVec, Encrypt, Padding) % if no IV for the Cipher, set IVec = <<>>
571 */
572 struct evp_cipher_ctx *ctx_res = NULL;
573 const struct cipher_type_t *cipherp;
574 ERL_NIF_TERM ret;
575
576 if (enif_is_atom(env, argv[0])) {
577 if ((ctx_res = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL)
578 return EXCP_ERROR(env, "Can't allocate resource");
579
580 if (get_init_args(env, ctx_res, argv[0], argv[1], argv[2], argv[3], argv[4],
581 &cipherp, &ret))
582 ret = enif_make_resource(env, ctx_res);
583 /* else error msg in ret */
584
585 if(ctx_res) enif_release_resource(ctx_res);
586
587 } else if (enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res)) {
588 /* Fetch the flag telling if we are going to encrypt (=true) or decrypt (=false) */
589 if (argv[3] == atom_true)
590 ctx_res->encflag = 1;
591 else if (argv[3] == atom_false)
592 ctx_res->encflag = 0;
593 else {
594 ret = EXCP_BADARG(env, "Bad enc flag");
595 goto ret;
596 }
597 if (ctx_res->ctx) {
598 /* It is *not* a ctx_res for the compatibility handling of non-EVP aes_ctr */
599 if (!EVP_CipherInit_ex(ctx_res->ctx, NULL, NULL, NULL, NULL, ctx_res->encflag)) {
600 ret = EXCP_ERROR(env, "Can't initialize encflag");
601 goto ret;
602 }
603 }
604 ret = argv[0];
605 } else {
606 ret = EXCP_BADARG(env, "Bad 1:st arg");
607 goto ret;
608 }
609
610 ret:
611 return ret;
612 }
613
614
615 /*************************************************************************/
616 /* Encrypt/decrypt */
617 /*************************************************************************/
618
ng_crypto_update(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])619 ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
620 {/* (Context, Data [, IV]) */
621 struct evp_cipher_ctx *ctx_res;
622 struct evp_cipher_ctx ctx_res_copy;
623 ERL_NIF_TERM ret;
624
625 ctx_res_copy.ctx = NULL;
626
627 if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res))
628 return EXCP_BADARG(env, "Bad 1:st arg");
629
630 if (argc == 3) {
631 /* We have an IV in this call. Make a copy of the context */
632 ErlNifBinary ivec_bin;
633
634 memcpy(&ctx_res_copy, ctx_res, sizeof ctx_res_copy);
635 #if !defined(HAVE_EVP_AES_CTR)
636 if (ctx_res_copy.state == atom_undefined)
637 /* Not going to use aes_ctr compat functions */
638 #endif
639 {
640 ctx_res_copy.ctx = EVP_CIPHER_CTX_new();
641
642 if (!EVP_CIPHER_CTX_copy(ctx_res_copy.ctx, ctx_res->ctx)) {
643 ret = EXCP_ERROR(env, "Can't copy ctx_res");
644 goto err;
645 }
646 }
647
648 if (!enif_inspect_iolist_as_binary(env, argv[2], &ivec_bin))
649 {
650 ret = EXCP_BADARG(env, "Bad iv type");
651 goto err;
652 }
653
654 if (ctx_res_copy.iv_len != ivec_bin.size)
655 {
656 ret = EXCP_BADARG(env, "Bad iv size");
657 goto err;
658 }
659
660 #if !defined(HAVE_EVP_AES_CTR)
661 if ((ctx_res_copy.state != atom_undefined) ) {
662 /* replace the iv in state with argv[2] */
663 ERL_NIF_TERM state0;
664 const ERL_NIF_TERM *tuple_argv;
665 int tuple_argc;
666 state0 = enif_make_copy(env, ctx_res_copy.state);
667 if (enif_get_tuple(env, state0, &tuple_argc, &tuple_argv) && (tuple_argc == 4)) {
668 /* A compatibility state term */
669 ctx_res_copy.state = enif_make_tuple4(env, tuple_argv[0], argv[2], tuple_argv[2], tuple_argv[3]);
670 }
671 } else
672 #endif
673 if (!EVP_CipherInit_ex(ctx_res_copy.ctx, NULL, NULL, NULL, ivec_bin.data, -1))
674 {
675 ret = EXCP_ERROR(env, "Can't set iv");
676 goto err;
677 }
678
679 get_update_args(env, &ctx_res_copy, argv[1], &ret);
680 ctx_res->size = ctx_res_copy.size;
681 } else
682 /* argc != 3, that is, argc = 2 (we don't have an IV in this call) */
683 get_update_args(env, ctx_res, argv[1], &ret);
684
685 err:
686 if (ctx_res_copy.ctx)
687 EVP_CIPHER_CTX_free(ctx_res_copy.ctx);
688
689 return ret; /* Both success and error */
690 }
691
692
ng_crypto_update_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])693 ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
694 {/* (Context, Data [, IV]) */
695 ErlNifBinary data_bin;
696
697 if (!enif_inspect_binary(env, argv[1], &data_bin))
698 return EXCP_BADARG(env, "expected binary as data");
699
700 if (data_bin.size > INT_MAX)
701 return EXCP_BADARG(env, "to long data");
702
703 /* Run long jobs on a dirty scheduler to not block the current emulator thread */
704 if (data_bin.size > MAX_BYTES_TO_NIF) {
705 return enif_schedule_nif(env, "ng_crypto_update",
706 ERL_NIF_DIRTY_JOB_CPU_BOUND,
707 ng_crypto_update, argc, argv);
708 }
709
710 return ng_crypto_update(env, argc, argv);
711 }
712
713 /*************************************************************************/
714 /* Final */
715 /*************************************************************************/
716
ng_crypto_final_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])717 ERL_NIF_TERM ng_crypto_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
718 {/* (Context) */
719 /* No need for enif_schedule_nif since maximum BlockSize-1 bytes are handled */
720 struct evp_cipher_ctx *ctx_res;
721 ERL_NIF_TERM ret;
722
723 if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res))
724 return EXCP_BADARG(env, "Bad arg");
725
726 get_final_args(env, ctx_res, &ret);
727
728 return ret;
729 }
730
731 /*************************************************************************/
732 /* One shot */
733 /*************************************************************************/
734
ng_crypto_one_time(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])735 ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
736 {/* (Cipher, Key, IVec, Data, Encrypt, PaddingType) */
737 struct evp_cipher_ctx ctx_res;
738 const struct cipher_type_t *cipherp;
739 ERL_NIF_TERM ret;
740 ErlNifBinary out_data_bin, final_data_bin;
741 unsigned char *append_buf;
742
743 ctx_res.ctx = NULL;
744 #if !defined(HAVE_EVP_AES_CTR)
745 ctx_res.env = NULL;
746 #endif
747
748 /* EVP_CipherInit */
749 if (!get_init_args(env, &ctx_res, argv[0], argv[1], argv[2], argv[4], argv[5], &cipherp, &ret))
750 goto out;
751
752 /* out_data = EVP_CipherUpdate */
753 if (!get_update_args(env, &ctx_res, argv[3], &ret))
754 /* Got an exception as result in &ret */
755 goto out;
756
757 if (!enif_inspect_binary(env, ret, &out_data_bin) )
758 {
759 ret = EXCP_ERROR(env, "Can't inspect first");
760 goto out;
761 }
762
763 /* final_data = EVP_CipherFinal_ex */
764 if (!get_final_args(env, &ctx_res, &ret))
765 /* Got an exception as result in &ret */
766 goto out;
767
768 if (!enif_inspect_binary(env, ret, &final_data_bin) )
769 {
770 ret = EXCP_ERROR(env, "Can't inspect final");
771 goto out;
772 }
773
774 /* Concatenate out_data and final_date into a new binary kept in the variable ret. */
775 append_buf = enif_make_new_binary(env, out_data_bin.size + final_data_bin.size, &ret);
776 if (!append_buf)
777 {
778 ret = EXCP_ERROR(env, "Can't append");
779 goto out;
780 }
781
782 memcpy(append_buf, out_data_bin.data, out_data_bin.size);
783 memcpy(append_buf+out_data_bin.size, final_data_bin.data, final_data_bin.size);
784
785 out:
786 if (ctx_res.ctx)
787 EVP_CIPHER_CTX_free(ctx_res.ctx);
788
789 #if !defined(HAVE_EVP_AES_CTR)
790 if (ctx_res.env)
791 enif_free_env(ctx_res.env);
792 #endif
793
794 return ret;
795 }
796
797
ng_crypto_one_time_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])798 ERL_NIF_TERM ng_crypto_one_time_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
799 {/* (Cipher, Key, IVec, Data, Encrypt, Padding) % if no IV for the Cipher, set IVec = <<>>
800 */
801 ErlNifBinary data_bin;
802
803 if (!enif_inspect_binary(env, argv[3], &data_bin))
804 return EXCP_BADARG(env, "expected binary as data");
805
806 if (data_bin.size > INT_MAX)
807 return EXCP_BADARG(env, "to long data");
808
809 /* Run long jobs on a dirty scheduler to not block the current emulator thread */
810 if (data_bin.size > MAX_BYTES_TO_NIF) {
811 return enif_schedule_nif(env, "ng_crypto_one_time",
812 ERL_NIF_DIRTY_JOB_CPU_BOUND,
813 ng_crypto_one_time, argc, argv);
814 }
815
816 return ng_crypto_one_time(env, argc, argv);
817 }
818
819
820 /*************************************************************************/
821 /* Get data from the cipher resource */
822 /*************************************************************************/
823
ng_crypto_get_data_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])824 ERL_NIF_TERM ng_crypto_get_data_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
825 {/* (Context) -> map */
826 struct evp_cipher_ctx *ctx_res;
827 ERL_NIF_TERM ret;
828
829 if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res))
830 return EXCP_BADARG(env, "Bad arg");
831
832 ret = enif_make_new_map(env);
833
834 enif_make_map_put(env, ret, atom_size,
835 enif_make_int(env, ctx_res->size),
836 &ret);
837
838 enif_make_map_put(env, ret, atom_padding_size,
839 enif_make_int(env, ctx_res->padded_size),
840 &ret);
841
842 enif_make_map_put(env, ret, atom_padding_type,
843 ctx_res->padding,
844 &ret);
845
846 enif_make_map_put(env, ret, atom_encrypt,
847 (ctx_res->encflag) ? atom_true : atom_false,
848 &ret);
849
850 return ret;
851 }
852