/* * %CopyrightBegin% * * Copyright Ericsson AB 2010-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * %CopyrightEnd% */ #include "api_ng.h" #include "aes.h" #include "cipher.h" /* * A unified set of functions for encryption/decryption. * * EXPERIMENTAL!! * */ ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); /* Try better error messages in new functions */ #define ERROR_Term(Env, ReasonTerm) enif_make_tuple2((Env), atom_error, (ReasonTerm)) #define ERROR_Str(Env, ReasonString) ERROR_Term((Env), enif_make_string((Env),(ReasonString),(ERL_NIF_LATIN1))) /* Initializes state for (de)encryption */ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Cipher, Key, IVec, Encrypt) % if no IV for the Cipher, set IVec = <<>> */ ErlNifBinary key_bin, ivec_bin; unsigned char *iv = NULL; struct evp_cipher_ctx *ctx; const struct cipher_type_t *cipherp; const EVP_CIPHER *cipher; ERL_NIF_TERM enc_flg_arg, ret; int enc; unsigned iv_len; enc_flg_arg = argv[argc-1]; if (enc_flg_arg == atom_true) enc = 1; else if (enc_flg_arg == atom_false) enc = 0; else if (enc_flg_arg == atom_undefined) /* For compat funcs in crypto.erl */ enc = -1; else return ERROR_Str(env, "Bad enc flag"); if (!enif_inspect_binary(env, argv[1], &key_bin)) return ERROR_Str(env, "Bad key"); if (!(cipherp = get_cipher_type(argv[0], key_bin.size))) return ERROR_Str(env, "Unknown cipher or bad key size"); if (FORBIDDEN_IN_FIPS(cipherp)) return enif_raise_exception(env, atom_notsup); if (enc == -1) return atom_undefined; if (!(cipher = cipherp->cipher.p)) { #if !defined(HAVE_EVP_AES_CTR) if (cipherp->flags & AES_CTR_COMPAT) return aes_ctr_stream_init_compat(env, argv[1], argv[2]); else #endif return enif_raise_exception(env, atom_notsup); } #ifdef HAVE_ECB_IVEC_BUG if (cipherp->flags & ECB_BUG_0_9_8L) iv_len = 0; /* <= 0.9.8l returns faulty ivec length */ else #endif iv_len = EVP_CIPHER_iv_length(cipher); if (iv_len) { if (!enif_inspect_binary(env, argv[2], &ivec_bin)) return ERROR_Str(env, "Bad iv type"); if (iv_len != ivec_bin.size) return ERROR_Str(env, "Bad iv size"); iv = ivec_bin.data; } if ((ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL) return ERROR_Str(env, "Can't allocate resource"); ctx->ctx = EVP_CIPHER_CTX_new(); if (! ctx->ctx) return ERROR_Str(env, "Can't allocate context"); if (!EVP_CipherInit_ex(ctx->ctx, cipher, NULL, NULL, NULL, enc)) { enif_release_resource(ctx); return ERROR_Str(env, "Can't initialize context, step 1"); } if (!EVP_CIPHER_CTX_set_key_length(ctx->ctx, (int)key_bin.size)) { enif_release_resource(ctx); return ERROR_Str(env, "Can't initialize context, key_length"); } if (EVP_CIPHER_type(cipher) == NID_rc2_cbc) { if (key_bin.size > INT_MAX / 8) { enif_release_resource(ctx); return ERROR_Str(env, "To large rc2_cbc key"); } if (!EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_SET_RC2_KEY_BITS, (int)key_bin.size * 8, NULL)) { enif_release_resource(ctx); return ERROR_Str(env, "ctrl rc2_cbc key"); } } if (!EVP_CipherInit_ex(ctx->ctx, NULL, NULL, key_bin.data, iv, enc)) { enif_release_resource(ctx); return ERROR_Str(env, "Can't initialize key and/or iv"); } EVP_CIPHER_CTX_set_padding(ctx->ctx, 0); ret = enif_make_resource(env, ctx); enif_release_resource(ctx); return ret; } ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Context, Data) (Context, Data, IV) */ struct evp_cipher_ctx *ctx; ErlNifBinary in_data_bin, ivec_bin, out_data_bin; int out_len, block_size; #if !defined(HAVE_EVP_AES_CTR) const ERL_NIF_TERM *state_term; int state_arity; if (enif_get_tuple(env, argv[0], &state_arity, &state_term) && (state_arity == 4)) { return aes_ctr_stream_encrypt_compat(env, argv[0], argv[1]); } #endif if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx)) return ERROR_Str(env, "Bad 1:st arg"); if (!enif_inspect_binary(env, argv[1], &in_data_bin) ) return ERROR_Str(env, "Bad 2:nd arg"); /* arg[1] was checked by the caller */ ASSERT(in_data_bin.size =< INT_MAX); block_size = EVP_CIPHER_CTX_block_size(ctx->ctx); if (in_data_bin.size % (size_t)block_size != 0) return ERROR_Str(env, "Data not a multiple of block size"); if (argc==3) { if (!enif_inspect_iolist_as_binary(env, argv[2], &ivec_bin)) return ERROR_Str(env, "Not binary IV"); if (ivec_bin.size > INT_MAX) return ERROR_Str(env, "Too big IV"); if (!EVP_CipherInit_ex(ctx->ctx, NULL, NULL, NULL, ivec_bin.data, -1)) return ERROR_Str(env, "Can't set IV"); } if (!enif_alloc_binary((size_t)in_data_bin.size+block_size, &out_data_bin)) return ERROR_Str(env, "Can't allocate outdata"); if (!EVP_CipherUpdate(ctx->ctx, out_data_bin.data, &out_len, in_data_bin.data, in_data_bin.size)) return ERROR_Str(env, "Can't update"); if (!enif_realloc_binary(&out_data_bin, (size_t)out_len)) return ERROR_Str(env, "Can't reallocate"); CONSUME_REDS(env, in_data_bin); return enif_make_binary(env, &out_data_bin); } ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Context, Data) (Context, Data, IV) */ int i; ErlNifBinary data_bin; ERL_NIF_TERM new_argv[3]; ASSERT(argc =< 3); if (!enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) return ERROR_Str(env, "iodata expected as data"); if (data_bin.size > INT_MAX) return ERROR_Str(env, "to long data"); for (i=0; i MAX_BYTES_TO_NIF) { return enif_schedule_nif(env, "ng_crypto_update", ERL_NIF_DIRTY_JOB_CPU_BOUND, ng_crypto_update, argc, new_argv); } return ng_crypto_update(env, argc, new_argv); }