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