aboutsummaryrefslogtreecommitdiffstats
path: root/lib/crypto/c_src/api_ng.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/crypto/c_src/api_ng.c')
-rw-r--r--lib/crypto/c_src/api_ng.c223
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);
+}
+