diff options
Diffstat (limited to 'lib/crypto/c_src/api_ng.c')
-rw-r--r-- | lib/crypto/c_src/api_ng.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c new file mode 100644 index 0000000000..c4114d1626 --- /dev/null +++ b/lib/crypto/c_src/api_ng.c @@ -0,0 +1,223 @@ +/* + * %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<argc; i++) new_argv[i] = argv[i]; + new_argv[1] = enif_make_binary(env, &data_bin); + + /* Run long jobs on a dirty scheduler to not block the current emulator thread */ + if (data_bin.size > 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); +} + |