diff options
Diffstat (limited to 'lib/crypto')
78 files changed, 12107 insertions, 6662 deletions
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index 31124ba477..e1e7f71538 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -19,7 +19,6 @@ # include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk -include $(ERL_TOP)/make/$(TARGET)/otp_ded.mk # ---------------------------------------------------- # Application version @@ -31,23 +30,20 @@ VSN=$(CRYPTO_VSN) # The following variables differ between systems. # Set by configure. # ---------------------------------------------------- -CC = $(DED_CC) -LD = $(DED_LD) +CC = @DED_CC@ +LD = @DED_LD@ SHELL = /bin/sh -LIBS = $(DED_LIBS) -LDFLAGS += $(DED_LDFLAGS) -CFLAGS = $(DED_CFLAGS) +LIBS = @DED_LIBS@ +LDFLAGS += @DED_LDFLAGS@ +CFLAGS = @DED_CFLAGS@ @SSL_FLAGS@ # From erts/configure SSL_LIBDIR = @SSL_LIBDIR@ SSL_INCLUDE = @SSL_INCLUDE@ SSL_CRYPTO_LIBNAME = @SSL_CRYPTO_LIBNAME@ SSL_SSL_LIBNAME = @SSL_SSL_LIBNAME@ -SSL_FLAGS = @SSL_FLAGS@ - -INCLUDES = $(SSL_INCLUDE) $(DED_INCLUDES) -CFLAGS += $(SSL_FLAGS) +INCLUDES = $(SSL_INCLUDE) @DED_INCLUDE@ ifeq ($(TYPE),debug) TYPEMARKER = .debug @@ -70,7 +66,41 @@ RELSYSDIR = $(RELEASE_PATH)/lib/crypto-$(VSN) # ---------------------------------------------------- # Misc Macros # ---------------------------------------------------- -CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o + +PRIVDIR = ../priv +OBJDIR = $(PRIVDIR)/obj/$(TARGET) +LIBDIR = $(PRIVDIR)/lib/$(TARGET) + +CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ + $(OBJDIR)/aead$(TYPEMARKER).o \ + $(OBJDIR)/aes$(TYPEMARKER).o \ + $(OBJDIR)/algorithms$(TYPEMARKER).o \ + $(OBJDIR)/api_ng$(TYPEMARKER).o \ + $(OBJDIR)/atoms$(TYPEMARKER).o \ + $(OBJDIR)/block$(TYPEMARKER).o \ + $(OBJDIR)/bn$(TYPEMARKER).o \ + $(OBJDIR)/chacha20$(TYPEMARKER).o \ + $(OBJDIR)/cipher$(TYPEMARKER).o \ + $(OBJDIR)/cmac$(TYPEMARKER).o \ + $(OBJDIR)/dh$(TYPEMARKER).o \ + $(OBJDIR)/digest$(TYPEMARKER).o \ + $(OBJDIR)/dss$(TYPEMARKER).o \ + $(OBJDIR)/ec$(TYPEMARKER).o \ + $(OBJDIR)/ecdh$(TYPEMARKER).o \ + $(OBJDIR)/eddsa$(TYPEMARKER).o \ + $(OBJDIR)/engine$(TYPEMARKER).o \ + $(OBJDIR)/evp$(TYPEMARKER).o \ + $(OBJDIR)/fips$(TYPEMARKER).o \ + $(OBJDIR)/hash$(TYPEMARKER).o \ + $(OBJDIR)/hmac$(TYPEMARKER).o \ + $(OBJDIR)/info$(TYPEMARKER).o \ + $(OBJDIR)/math$(TYPEMARKER).o \ + $(OBJDIR)/pkey$(TYPEMARKER).o \ + $(OBJDIR)/poly1305$(TYPEMARKER).o \ + $(OBJDIR)/rand$(TYPEMARKER).o \ + $(OBJDIR)/rc4$(TYPEMARKER).o \ + $(OBJDIR)/rsa$(TYPEMARKER).o \ + $(OBJDIR)/srp$(TYPEMARKER).o CALLBACK_OBJS = $(OBJDIR)/crypto_callback$(TYPEMARKER).o NIF_MAKEFILE = $(PRIVDIR)/Makefile CRYPTO_STATIC_OBJS = $(OBJDIR)/crypto_static$(TYPEMARKER).o\ @@ -80,19 +110,10 @@ NIF_ARCHIVE = $(LIBDIR)/crypto$(TYPEMARKER).a TEST_ENGINE_OBJS = $(OBJDIR)/otp_test_engine$(TYPEMARKER).o -ifeq ($(findstring win32,$(TARGET)), win32) -NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).dll -CALLBACK_LIB = $(LIBDIR)/crypto_callback$(TYPEMARKER).dll -TEST_ENGINE_LIB = $(LIBDIR)/otp_test_engine$(TYPEMARKER).dll -else -NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).so -CALLBACK_LIB = $(LIBDIR)/crypto_callback$(TYPEMARKER).so -TEST_ENGINE_LIB = $(LIBDIR)/otp_test_engine$(TYPEMARKER).so -endif +NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).@DED_EXT@ +CALLBACK_LIB = $(LIBDIR)/crypto_callback$(TYPEMARKER).@DED_EXT@ +TEST_ENGINE_LIB = $(LIBDIR)/otp_test_engine$(TYPEMARKER).@DED_EXT@ -ifeq ($(HOST_OS),) -HOST_OS := $(shell $(ERL_TOP)/erts/autoconf/config.guess) -endif DYNAMIC_CRYPTO_LIB=@SSL_DYNAMIC_ONLY@ ifeq ($(DYNAMIC_CRYPTO_LIB),yes) @@ -125,7 +146,7 @@ RANLIB=true endif ALL_CFLAGS = $(TYPE_FLAGS) $(EXTRA_FLAGS) $(INCLUDES) -ALL_STATIC_CFLAGS = $(DED_STATIC_CFLAGS) $(INCLUDES) +ALL_STATIC_CFLAGS = @DED_STATIC_CFLAGS@ $(INCLUDES) # ---------------------------------------------------- # Targets @@ -180,32 +201,21 @@ $(LIBDIR)/crypto_callback$(TYPEMARKER).dll: $(CALLBACK_OBJS) endif -clean: -ifeq ($(findstring win32,$(TARGET)), win32) - rm -f $(LIBDIR)/crypto.dll - rm -f $(LIBDIR)/crypto.debug.dll - rm -f $(LIBDIR)/crypto_callback.dll - rm -f $(LIBDIR)/crypto_callback.debug.dll - rm -f $(LIBDIR)/otp_test_engine.dll -else - rm -f $(LIBDIR)/crypto.so - rm -f $(LIBDIR)/crypto.debug.so - rm -f $(LIBDIR)/crypto.valgrind.so - rm -f $(LIBDIR)/crypto_callback.so - rm -f $(LIBDIR)/crypto_callback.debug.so - rm -f $(LIBDIR)/crypto_callback.valgrind.so - rm -f $(LIBDIR)/otp_test_engine.so -endif - rm -f $(OBJDIR)/crypto.o - rm -f $(OBJDIR)/crypto_static.o - rm -f $(OBJDIR)/crypto.debug.o - rm -f $(OBJDIR)/crypto_static.debug.o - rm -f $(OBJDIR)/crypto.valgrind.o - rm -f $(OBJDIR)/crypto_static.valgrind.o - rm -f $(OBJDIR)/crypto_callback.o - rm -f $(OBJDIR)/crypto_callback.debug.o - rm -f $(OBJDIR)/crypto_callback.valgrind.o - rm -f $(OBJDIR)/otp_test_engine.o +CLEAN_OBJS_RAW = $(CRYPTO_OBJS) $(CALLBACK_OBJS) $(CRYPTO_STATIC_OBJS) $(TEST_ENGINE_OBJS) +CLEAN_OBJS_O = $(patsubst %.debug.o,%.o,$(CLEAN_OBJS_RAW:.valgrind.o=.o)) + +CLEAN_LIBS_RAW = $(NIF_LIB) $(CALLBACK_LIB) $(TEST_ENGINE_LIB) +CLEAN_LIBS_SO = $(patsubst %.debug.@DED_EXT@,%.@DED_EXT@,$(CLEAN_LIBS_RAW:.valgrind.@DED_EXT@=.@DED_EXT@)) + +clean_dynamic_libs: + rm -f $(CLEAN_LIBS_SO) + rm -f $(foreach T,.valgrind.@DED_EXT@ .debug.@DED_EXT@,$(CLEAN_LIBS_SO:.@DED_EXT@=$T)) + +clean_objs: + rm -f $(CLEAN_OBJS_O) + rm -f $(foreach T,.valgrind.o .debug.o,$(CLEAN_OBJS_O:.o=$T)) + +clean: clean_objs clean_dynamic_libs rm -f core *~ docs: diff --git a/lib/crypto/c_src/aead.c b/lib/crypto/c_src/aead.c new file mode 100644 index 0000000000..3ee04f1be9 --- /dev/null +++ b/lib/crypto/c_src/aead.c @@ -0,0 +1,243 @@ +/* + * %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 "aead.h" +#include "aes.h" +#include "cipher.h" + +ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type,Key,Iv,AAD,In) */ +#if defined(HAVE_AEAD) + const struct cipher_type_t *cipherp; + EVP_CIPHER_CTX *ctx = NULL; + const EVP_CIPHER *cipher = NULL; + ErlNifBinary key, iv, aad, in; + unsigned int tag_len; + unsigned char *outp, *tagp; + ERL_NIF_TERM type, out, out_tag, ret; + int len, ctx_ctrl_set_ivlen, ctx_ctrl_get_tag, ctx_ctrl_set_tag; + + type = argv[0]; + + ASSERT(argc == 6); + + if (!enif_is_atom(env, type)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) + goto bad_arg; + if (!enif_inspect_binary(env, argv[2], &iv)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[3], &aad)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[4], &in)) + goto bad_arg; + if (!enif_get_uint(env, argv[5], &tag_len)) + goto bad_arg; + + if (tag_len > INT_MAX + || iv.size > INT_MAX + || in.size > INT_MAX + || aad.size > INT_MAX) + goto bad_arg; + + if ((cipherp = get_cipher_type(type, key.size)) == NULL) + goto bad_arg; + if (cipherp->flags & NON_EVP_CIPHER) + goto bad_arg; + if (! (cipherp->flags & AEAD_CIPHER) ) + goto bad_arg; + if ((cipher = cipherp->cipher.p) == NULL) + return enif_raise_exception(env, atom_notsup); + + ctx_ctrl_set_ivlen = cipherp->extra.aead.ctx_ctrl_set_ivlen; + ctx_ctrl_get_tag = cipherp->extra.aead.ctx_ctrl_get_tag; + ctx_ctrl_set_tag = cipherp->extra.aead.ctx_ctrl_set_tag; + + if ((ctx = EVP_CIPHER_CTX_new()) == NULL) + goto err; + + if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) + goto err; + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1) + goto err; + +#if defined(HAVE_CCM) + if (type == atom_aes_ccm) { + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag_len, NULL) != 1) + goto err; + if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) + goto err; + if (EVP_EncryptUpdate(ctx, NULL, &len, NULL, (int)in.size) != 1) + goto err; + } else +#endif + { + if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) + goto err; + } + + if (EVP_EncryptUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1) + goto err; + + if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL) + goto err; + + if (EVP_EncryptUpdate(ctx, outp, &len, in.data, (int)in.size) != 1) + goto err; + if (EVP_EncryptFinal_ex(ctx, outp/*+len*/, &len) != 1) + goto err; + + if ((tagp = enif_make_new_binary(env, tag_len, &out_tag)) == NULL) + goto err; + + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_get_tag, (int)tag_len, tagp) != 1) + goto err; + + CONSUME_REDS(env, in); + ret = enif_make_tuple2(env, out, out_tag); + goto done; + + bad_arg: + ret = enif_make_badarg(env); + goto done; + + err: + ret = atom_error; + + done: + if (ctx) + EVP_CIPHER_CTX_free(ctx); + return ret; + +#else + return enif_raise_exception(env, atom_notsup); +#endif +} + +ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type,Key,Iv,AAD,In,Tag) */ +#if defined(HAVE_AEAD) + const struct cipher_type_t *cipherp; + EVP_CIPHER_CTX *ctx = NULL; + const EVP_CIPHER *cipher = NULL; + ErlNifBinary key, iv, aad, in, tag; + unsigned char *outp; + ERL_NIF_TERM type, out, ret; + int len, ctx_ctrl_set_ivlen, ctx_ctrl_set_tag; + + ASSERT(argc == 6); + + type = argv[0]; +#if defined(HAVE_GCM_EVP_DECRYPT_BUG) + if (type == atom_aes_gcm) + return aes_gcm_decrypt_NO_EVP(env, argc, argv); +#endif + + if (!enif_is_atom(env, type)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) + goto bad_arg; + if (!enif_inspect_binary(env, argv[2], &iv)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[3], &aad)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[4], &in)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[5], &tag)) + goto bad_arg; + + if (tag.size > INT_MAX + || key.size > INT_MAX + || iv.size > INT_MAX + || in.size > INT_MAX + || aad.size > INT_MAX) + goto bad_arg; + + if ((cipherp = get_cipher_type(type, key.size)) == NULL) + goto bad_arg; + if (cipherp->flags & NON_EVP_CIPHER) + goto bad_arg; + if ( !(cipherp->flags & AEAD_CIPHER) ) + goto bad_arg; + if ((cipher = cipherp->cipher.p) == NULL) + return enif_raise_exception(env, atom_notsup); + + ctx_ctrl_set_ivlen = cipherp->extra.aead.ctx_ctrl_set_ivlen; + ctx_ctrl_set_tag = cipherp->extra.aead.ctx_ctrl_set_tag; + + if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL) + goto err; + + if ((ctx = EVP_CIPHER_CTX_new()) == NULL) + goto err; + if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) + goto err; + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1) + goto err; + +#if defined(HAVE_CCM) + if (type == atom_aes_ccm) { + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag.size, tag.data) != 1) + goto err; + if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) + goto err; + if (EVP_DecryptUpdate(ctx, NULL, &len, NULL, (int)in.size) != 1) + goto err; + } + else +#endif + { + if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) + goto err; + } + + if (EVP_DecryptUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1) + goto err; + if (EVP_DecryptUpdate(ctx, outp, &len, in.data, (int)in.size) != 1) + goto err; + +#if defined(HAVE_GCM) + if (type == atom_aes_gcm) { + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag.size, tag.data) != 1) + goto err; + if (EVP_DecryptFinal_ex(ctx, outp+len, &len) != 1) + goto err; + } +#endif + CONSUME_REDS(env, in); + ret = out; + goto done; + + bad_arg: + ret = enif_make_badarg(env); + goto done; + + err: + ret = atom_error; + + done: + if (ctx) + EVP_CIPHER_CTX_free(ctx); + return ret; + +#else + return enif_raise_exception(env, atom_notsup); +#endif +} diff --git a/lib/crypto/c_src/aead.h b/lib/crypto/c_src/aead.h new file mode 100644 index 0000000000..54c0711535 --- /dev/null +++ b/lib/crypto/c_src/aead.h @@ -0,0 +1,29 @@ +/* + * %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% + */ + +#ifndef E_AEAD_H__ +#define E_AEAD_H__ 1 + +#include "common.h" + +ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_AEAD_H__ */ diff --git a/lib/crypto/c_src/aes.c b/lib/crypto/c_src/aes.c new file mode 100644 index 0000000000..ee2bb70fb7 --- /dev/null +++ b/lib/crypto/c_src/aes.c @@ -0,0 +1,451 @@ +/* + * %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 "aes.h" +#include "cipher.h" + +ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec, Data, IsEncrypt) */ + ErlNifBinary key, ivec, text; + AES_KEY aes_key; + unsigned char ivec_clone[16]; /* writable copy */ + int new_ivlen = 0; + ERL_NIF_TERM ret; + unsigned char *outp; + + CHECK_NO_FIPS_MODE(); + + ASSERT(argc == 4); + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key)) + goto bad_arg; + if (key.size != 16 && key.size != 24 && key.size != 32) + goto bad_arg; + if (!enif_inspect_binary(env, argv[1], &ivec)) + goto bad_arg; + if (ivec.size != 16) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[2], &text)) + goto bad_arg; + + memcpy(ivec_clone, ivec.data, 16); + + /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ + if (AES_set_encrypt_key(key.data, (int)key.size * 8, &aes_key) != 0) + goto err; + if ((outp = enif_make_new_binary(env, text.size, &ret)) == NULL) + goto err; + AES_cfb8_encrypt((unsigned char *) text.data, + outp, + text.size, &aes_key, ivec_clone, &new_ivlen, + (argv[3] == atom_true)); + CONSUME_REDS(env,text); + return ret; + + bad_arg: + err: + return enif_make_badarg(env); +} + +ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec, Data, IsEncrypt) */ + ErlNifBinary key, ivec, text; + AES_KEY aes_key; + unsigned char ivec_clone[16]; /* writable copy */ + int new_ivlen = 0; + ERL_NIF_TERM ret; + unsigned char *outp; + + ASSERT(argc == 4); + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key)) + goto bad_arg; + if (key.size != 16 && key.size != 24 && key.size != 32) + goto bad_arg; + if (!enif_inspect_binary(env, argv[1], &ivec)) + goto bad_arg; + if (ivec.size != 16) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[2], &text)) + goto bad_arg; + + memcpy(ivec_clone, ivec.data, 16); + + /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ + if (AES_set_encrypt_key(key.data, (int)key.size * 8, &aes_key) != 0) + goto err; + + if ((outp = enif_make_new_binary(env, text.size, &ret)) == NULL) + goto err; + AES_cfb128_encrypt((unsigned char *) text.data, + outp, + text.size, &aes_key, ivec_clone, &new_ivlen, + (argv[3] == atom_true)); + CONSUME_REDS(env,text); + return ret; + + bad_arg: + err: + return enif_make_badarg(env); +} + +ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec, Data, IsEncrypt) */ +#ifdef HAVE_AES_IGE + ErlNifBinary key_bin, ivec_bin, data_bin; + AES_KEY aes_key; + unsigned char ivec[32]; + int type; + unsigned char* ret_ptr; + ERL_NIF_TERM ret; + + CHECK_NO_FIPS_MODE(); + + ASSERT(argc == 4); + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)) + goto bad_arg; + if (key_bin.size != 16 && key_bin.size != 32) + goto bad_arg; + if (!enif_inspect_binary(env, argv[1], &ivec_bin)) + goto bad_arg; + if (ivec_bin.size != 32) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[2], &data_bin)) + goto bad_arg; + if (data_bin.size % 16 != 0) + goto bad_arg; + + if (argv[3] == atom_true) { + type = AES_ENCRYPT; + /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ + if (AES_set_encrypt_key(key_bin.data, (int)key_bin.size * 8, &aes_key) != 0) + goto err; + } + else { + type = AES_DECRYPT; + /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ + if (AES_set_decrypt_key(key_bin.data, (int)key_bin.size * 8, &aes_key) != 0) + goto err; + } + + if ((ret_ptr = enif_make_new_binary(env, data_bin.size, &ret)) == NULL) + goto err; + + memcpy(ivec, ivec_bin.data, 32); /* writable copy */ + + AES_ige_encrypt(data_bin.data, ret_ptr, data_bin.size, &aes_key, ivec, type); + + CONSUME_REDS(env,data_bin); + return ret; + + bad_arg: + err: + return enif_make_badarg(env); + +#else + return atom_notsup; +#endif +} + + +#ifdef HAVE_EVP_AES_CTR +ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec) */ + ErlNifBinary key_bin, ivec_bin; + struct evp_cipher_ctx *ctx = NULL; + const EVP_CIPHER *cipher; + ERL_NIF_TERM ret; + + ASSERT(argc == 2); + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)) + goto bad_arg; + if (!enif_inspect_binary(env, argv[1], &ivec_bin)) + goto bad_arg; + if (ivec_bin.size != 16) + goto bad_arg; + + switch (key_bin.size) + { + case 16: + cipher = EVP_aes_128_ctr(); + break; + case 24: + cipher = EVP_aes_192_ctr(); + break; + case 32: + cipher = EVP_aes_256_ctr(); + break; + default: + goto bad_arg; + } + + if ((ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL) + goto err; + if ((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL) + goto err; + + if (EVP_CipherInit_ex(ctx->ctx, cipher, NULL, + key_bin.data, ivec_bin.data, 1) != 1) + goto err; + + if (EVP_CIPHER_CTX_set_padding(ctx->ctx, 0) != 1) + goto err; + + ret = enif_make_resource(env, ctx); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = enif_make_badarg(env); + + done: + if (ctx) + enif_release_resource(ctx); + return ret; +} + +ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + struct evp_cipher_ctx *ctx = NULL, *new_ctx = NULL; + ErlNifBinary data_bin; + ERL_NIF_TERM ret, cipher_term; + unsigned char *out; + int outl = 0; + + ASSERT(argc == 2); + + if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) + goto bad_arg; + if (data_bin.size > INT_MAX) + goto bad_arg; + + if ((new_ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL) + goto err; + if ((new_ctx->ctx = EVP_CIPHER_CTX_new()) == NULL) + goto err; + + if (EVP_CIPHER_CTX_copy(new_ctx->ctx, ctx->ctx) != 1) + goto err; + + if ((out = enif_make_new_binary(env, data_bin.size, &cipher_term)) == NULL) + goto err; + + if (EVP_CipherUpdate(new_ctx->ctx, out, &outl, data_bin.data, (int)data_bin.size) != 1) + goto err; + ASSERT(outl >= 0 && (size_t)outl == data_bin.size); + + ret = enif_make_tuple2(env, enif_make_resource(env, new_ctx), cipher_term); + CONSUME_REDS(env,data_bin); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = enif_make_badarg(env); + + done: + if (new_ctx) + enif_release_resource(new_ctx); + return ret; +} + +#else /* if not HAVE_EVP_AES_CTR */ + +ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec) */ + ASSERT(argc == 2); + + return aes_ctr_stream_init_compat(env, argv[0], argv[1]); +} + + +ERL_NIF_TERM aes_ctr_stream_init_compat(ErlNifEnv* env, const ERL_NIF_TERM key_term, const ERL_NIF_TERM iv_term) +{ + ErlNifBinary key_bin, ivec_bin; + ERL_NIF_TERM ecount_bin; + unsigned char *outp; + + if (!enif_inspect_iolist_as_binary(env, key_term, &key_bin)) + goto bad_arg; + if (key_bin.size != 16 && key_bin.size != 24 && key_bin.size != 32) + goto bad_arg; + if (!enif_inspect_binary(env, iv_term, &ivec_bin)) + goto bad_arg; + if (ivec_bin.size != 16) + goto bad_arg; + if ((outp = enif_make_new_binary(env, AES_BLOCK_SIZE, &ecount_bin)) == NULL) + goto err; + memset(outp, 0, AES_BLOCK_SIZE); + + return enif_make_tuple4(env, key_term, iv_term, ecount_bin, enif_make_int(env, 0)); + + bad_arg: + err: + return enif_make_badarg(env); +} + +ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ASSERT(argc == 2); + + return aes_ctr_stream_encrypt_compat(env, argv[0], argv[1]); +} + + +ERL_NIF_TERM aes_ctr_stream_encrypt_compat(ErlNifEnv* env, const ERL_NIF_TERM state_arg, const ERL_NIF_TERM data_arg) +{/* ({Key, IVec, ECount, Num}, Data) */ + ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin; + AES_KEY aes_key; + unsigned int num; + ERL_NIF_TERM ret, num2_term, cipher_term, ivec2_term, ecount2_term, new_state_term; + int state_arity; + const ERL_NIF_TERM *state_term; + unsigned char * ivec2_buf; + unsigned char * ecount2_buf; + unsigned char *outp; + + if (!enif_get_tuple(env, state_arg, &state_arity, &state_term)) + goto bad_arg; + if (state_arity != 4) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, state_term[0], &key_bin)) + goto bad_arg; + if (key_bin.size > INT_MAX / 8) + goto bad_arg; + if (!enif_inspect_binary(env, state_term[1], &ivec_bin)) + goto bad_arg; + if (ivec_bin.size != 16) + goto bad_arg; + if (!enif_inspect_binary(env, state_term[2], &ecount_bin)) + goto bad_arg; + if (ecount_bin.size != AES_BLOCK_SIZE) + goto bad_arg; + if (!enif_get_uint(env, state_term[3], &num)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, data_arg, &text_bin)) + goto bad_arg; + + /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ + if (AES_set_encrypt_key(key_bin.data, (int)key_bin.size * 8, &aes_key) != 0) + goto bad_arg; + + if ((ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term)) == NULL) + goto err; + if ((ecount2_buf = enif_make_new_binary(env, ecount_bin.size, &ecount2_term)) == NULL) + goto err; + + memcpy(ivec2_buf, ivec_bin.data, 16); + memcpy(ecount2_buf, ecount_bin.data, ecount_bin.size); + + if ((outp = enif_make_new_binary(env, text_bin.size, &cipher_term)) == NULL) + goto err; + + AES_ctr128_encrypt((unsigned char *) text_bin.data, + outp, + text_bin.size, &aes_key, ivec2_buf, ecount2_buf, &num); + + num2_term = enif_make_uint(env, num); + new_state_term = enif_make_tuple4(env, state_term[0], ivec2_term, ecount2_term, num2_term); + ret = enif_make_tuple2(env, new_state_term, cipher_term); + CONSUME_REDS(env,text_bin); + return ret; + + bad_arg: + err: + return enif_make_badarg(env); +} +#endif /* !HAVE_EVP_AES_CTR */ + +#ifdef HAVE_GCM_EVP_DECRYPT_BUG +ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type,Key,Iv,AAD,In,Tag) */ + GCM128_CONTEXT *ctx = NULL; + ErlNifBinary key, iv, aad, in, tag; + AES_KEY aes_key; + unsigned char *outp; + ERL_NIF_TERM out, ret; + + ASSERT(argc == 6); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) + goto bad_arg; + if (key.size > INT_MAX / 8) + goto bad_arg; + if (!enif_inspect_binary(env, argv[2], &iv)) + goto bad_arg; + if (iv.size == 0) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[3], &aad)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[4], &in)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[5], &tag)) + goto bad_arg; + + /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ + if (AES_set_encrypt_key(key.data, (int)key.size * 8, &aes_key) != 0) + goto bad_arg; + + if ((ctx = CRYPTO_gcm128_new(&aes_key, (block128_f)AES_encrypt)) == NULL) + goto err; + + CRYPTO_gcm128_setiv(ctx, iv.data, iv.size); + + /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ + if (CRYPTO_gcm128_aad(ctx, aad.data, aad.size) != 0) + goto err; + + if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL) + goto err; + + /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ + if (CRYPTO_gcm128_decrypt(ctx, in.data, outp, in.size) != 0) + goto err; + + /* calculate and check the tag */ + /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ + if (CRYPTO_gcm128_finish(ctx, tag.data, tag.size) != 0) + goto err; + + CONSUME_REDS(env, in); + ret = out; + goto done; + + bad_arg: + ret = enif_make_badarg(env); + goto done; + + err: + ret = atom_error; + + done: + if (ctx) + CRYPTO_gcm128_release(ctx); + return ret; +} +#endif /* HAVE_GCM_EVP_DECRYPT_BUG */ + diff --git a/lib/crypto/c_src/aes.h b/lib/crypto/c_src/aes.h new file mode 100644 index 0000000000..527d041410 --- /dev/null +++ b/lib/crypto/c_src/aes.h @@ -0,0 +1,41 @@ +/* + * %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% + */ + +#ifndef E_AES_H__ +#define E_AES_H__ 1 + +#include "common.h" + +ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#if !defined(HAVE_EVP_AES_CTR) +ERL_NIF_TERM aes_ctr_stream_init_compat(ErlNifEnv* env, const ERL_NIF_TERM key_term, const ERL_NIF_TERM iv_term); +ERL_NIF_TERM aes_ctr_stream_encrypt_compat(ErlNifEnv* env, const ERL_NIF_TERM state_arg, const ERL_NIF_TERM data_arg); +#endif + +#ifdef HAVE_GCM_EVP_DECRYPT_BUG +ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#endif + +#endif /* E_AES_H__ */ diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c new file mode 100644 index 0000000000..06cd109fc1 --- /dev/null +++ b/lib/crypto/c_src/algorithms.c @@ -0,0 +1,273 @@ +/* + * %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 "algorithms.h" +#include "cipher.h" + +static unsigned int algo_hash_cnt, algo_hash_fips_cnt; +static ERL_NIF_TERM algo_hash[14]; /* increase when extending the list */ +static unsigned int algo_pubkey_cnt, algo_pubkey_fips_cnt; +static ERL_NIF_TERM algo_pubkey[12]; /* increase when extending the list */ +static unsigned int algo_mac_cnt, algo_mac_fips_cnt; +static ERL_NIF_TERM algo_mac[3]; /* increase when extending the list */ +static unsigned int algo_curve_cnt, algo_curve_fips_cnt; +static ERL_NIF_TERM algo_curve[89]; /* increase when extending the list */ +static unsigned int algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; +static ERL_NIF_TERM algo_rsa_opts[11]; /* increase when extending the list */ + +void init_algorithms_types(ErlNifEnv* env) +{ + // Validated algorithms first + algo_hash_cnt = 0; + algo_hash[algo_hash_cnt++] = atom_sha; +#ifdef HAVE_SHA224 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha224"); +#endif +#ifdef HAVE_SHA256 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha256"); +#endif +#ifdef HAVE_SHA384 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha384"); +#endif +#ifdef HAVE_SHA512 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha512"); +#endif +#ifdef HAVE_SHA3_224 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_224"); +#endif +#ifdef HAVE_SHA3_256 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_256"); +#endif +#ifdef HAVE_SHA3_384 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_384"); +#endif +#ifdef HAVE_SHA3_512 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_512"); +#endif +#ifdef HAVE_BLAKE2 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "blake2b"); + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "blake2s"); +#endif + + // Non-validated algorithms follow + algo_hash_fips_cnt = algo_hash_cnt; + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md4"); + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md5"); + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "ripemd160"); + + algo_pubkey_cnt = 0; + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "rsa"); + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dss"); + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dh"); +#if defined(HAVE_EC) +#if !defined(OPENSSL_NO_EC2M) + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ec_gf2m"); +#endif + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdsa"); + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdh"); +#endif + // Non-validated algorithms follow + algo_pubkey_fips_cnt = algo_pubkey_cnt; + // Don't know if Edward curves are fips validated +#if defined(HAVE_EDDSA) + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "eddsa"); +#endif + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp"); + + + // Validated algorithms first + algo_mac_cnt = 0; + algo_mac[algo_mac_cnt++] = enif_make_atom(env,"hmac"); +#ifdef HAVE_CMAC + algo_mac[algo_mac_cnt++] = enif_make_atom(env,"cmac"); +#endif +#ifdef HAVE_POLY1305 + algo_mac[algo_mac_cnt++] = enif_make_atom(env,"poly1305"); +#endif + // Non-validated algorithms follow + algo_mac_fips_cnt = algo_mac_cnt; + + // Validated algorithms first + algo_curve_cnt = 0; +#if defined(HAVE_EC) + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp192r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp192k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp224k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp224r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp256k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp256r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp384r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp521r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime256v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls7"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls9"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls12"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP160r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP160t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP192r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP192t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP224r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP224t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP256r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP256t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP320r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP320t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP384r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP384t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP512r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP512t1"); +#if !defined(OPENSSL_NO_EC2M) + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect193r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect193r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect233k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect233r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect239k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect283k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect283r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect409k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect409r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect571k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect571r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb176v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb208w1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb272w1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb304w1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb359v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb368w1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb431r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls5"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls10"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls11"); +#endif +#endif + // Non-validated algorithms follow + algo_curve_fips_cnt = algo_curve_cnt; +#if defined(HAVE_EC) + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp112r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp112r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp128r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp128r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls6"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls8"); +#if !defined(OPENSSL_NO_EC2M) + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect113r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect113r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect131r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect131r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls4"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ipsec3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ipsec4"); +#endif +#endif + //-- +#ifdef HAVE_EDDSA + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ed25519"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ed448"); +#endif +#ifdef HAVE_ED_CURVE_DH + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"x25519"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"x448"); +#endif + + // Validated algorithms first + algo_rsa_opts_cnt = 0; +#ifdef HAS_EVP_PKEY_CTX +# ifdef HAVE_RSA_PKCS1_PSS_PADDING + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_pss_padding"); + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pss_saltlen"); +# endif +# ifdef HAVE_RSA_MGF1_MD + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_mgf1_md"); +# endif +# ifdef HAVE_RSA_OAEP_PADDING + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_oaep_padding"); +# endif +# ifdef HAVE_RSA_OAEP_MD + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_oaep_label"); + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_oaep_md"); +# endif + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"signature_md"); +#endif + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_padding"); + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_x931_padding"); +#ifdef HAVE_RSA_SSLV23_PADDING + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_sslv23_padding"); +#endif + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_no_padding"); + algo_rsa_opts_fips_cnt = algo_rsa_opts_cnt; + + + // Check that the max number of algos is updated + ASSERT(algo_hash_cnt <= sizeof(algo_hash)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_mac_cnt <= sizeof(algo_mac)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_curve_cnt <= sizeof(algo_curve)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(ERL_NIF_TERM)); +} + +ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef FIPS_SUPPORT + int fips_mode = FIPS_mode(); + + unsigned int hash_cnt = fips_mode ? algo_hash_fips_cnt : algo_hash_cnt; + unsigned int pubkey_cnt = fips_mode ? algo_pubkey_fips_cnt : algo_pubkey_cnt; + unsigned int mac_cnt = fips_mode ? algo_mac_fips_cnt : algo_mac_cnt; + unsigned int curve_cnt = fips_mode ? algo_curve_fips_cnt : algo_curve_cnt; + unsigned int rsa_opts_cnt = fips_mode ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt; +#else + unsigned int hash_cnt = algo_hash_cnt; + unsigned int pubkey_cnt = algo_pubkey_cnt; + unsigned int mac_cnt = algo_mac_cnt; + unsigned int curve_cnt = algo_curve_cnt; + unsigned int rsa_opts_cnt = algo_rsa_opts_cnt; +#endif + return enif_make_tuple6(env, + enif_make_list_from_array(env, algo_hash, hash_cnt), + enif_make_list_from_array(env, algo_pubkey, pubkey_cnt), + cipher_types_as_list(env), + enif_make_list_from_array(env, algo_mac, mac_cnt), + enif_make_list_from_array(env, algo_curve, curve_cnt), + enif_make_list_from_array(env, algo_rsa_opts, rsa_opts_cnt) + ); +} diff --git a/lib/crypto/c_src/algorithms.h b/lib/crypto/c_src/algorithms.h new file mode 100644 index 0000000000..068fb661ec --- /dev/null +++ b/lib/crypto/c_src/algorithms.h @@ -0,0 +1,30 @@ +/* + * %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% + */ + +#ifndef E_ALGORITHMS_H__ +#define E_ALGORITHMS_H__ 1 + +#include "common.h" + +void init_algorithms_types(ErlNifEnv* env); + +ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_ALGORITHMS_H__ */ 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); +} + diff --git a/lib/crypto/c_src/api_ng.h b/lib/crypto/c_src/api_ng.h new file mode 100644 index 0000000000..a3b40fe7fc --- /dev/null +++ b/lib/crypto/c_src/api_ng.h @@ -0,0 +1,29 @@ +/* + * %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% + */ + +#ifndef E_API_NG_H__ +#define E_API_NG_H__ 1 + +#include "common.h" + +ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_AES_H__ */ diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c new file mode 100644 index 0000000000..2e417da7f4 --- /dev/null +++ b/lib/crypto/c_src/atoms.c @@ -0,0 +1,212 @@ +/* + * %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 "atoms.h" + +ERL_NIF_TERM atom_true; +ERL_NIF_TERM atom_false; +ERL_NIF_TERM atom_sha; +ERL_NIF_TERM atom_error; +ERL_NIF_TERM atom_rsa_pkcs1_padding; +ERL_NIF_TERM atom_rsa_pkcs1_oaep_padding; +ERL_NIF_TERM atom_rsa_no_padding; +ERL_NIF_TERM atom_signature_md; +ERL_NIF_TERM atom_undefined; + +ERL_NIF_TERM atom_ok; +ERL_NIF_TERM atom_none; +ERL_NIF_TERM atom_notsup; +ERL_NIF_TERM atom_digest; +#ifdef FIPS_SUPPORT +ERL_NIF_TERM atom_enabled; +ERL_NIF_TERM atom_not_enabled; +#else +ERL_NIF_TERM atom_not_supported; +#endif + +#if defined(HAVE_EC) +ERL_NIF_TERM atom_prime_field; +ERL_NIF_TERM atom_characteristic_two_field; +ERL_NIF_TERM atom_tpbasis; +ERL_NIF_TERM atom_ppbasis; +ERL_NIF_TERM atom_onbasis; +#endif + +ERL_NIF_TERM atom_aes_cfb8; +ERL_NIF_TERM atom_aes_cfb128; +#ifdef HAVE_GCM +ERL_NIF_TERM atom_aes_gcm; +#endif +#ifdef HAVE_CCM +ERL_NIF_TERM atom_aes_ccm; +#endif + +ERL_NIF_TERM atom_rsa; +ERL_NIF_TERM atom_dss; +ERL_NIF_TERM atom_ecdsa; + +#ifdef HAVE_ED_CURVE_DH +ERL_NIF_TERM atom_x25519; +ERL_NIF_TERM atom_x448; +#endif + +ERL_NIF_TERM atom_eddsa; +#ifdef HAVE_EDDSA +ERL_NIF_TERM atom_ed25519; +ERL_NIF_TERM atom_ed448; +#endif + +ERL_NIF_TERM atom_rsa_mgf1_md; +ERL_NIF_TERM atom_rsa_oaep_label; +ERL_NIF_TERM atom_rsa_oaep_md; +ERL_NIF_TERM atom_rsa_pad; /* backwards compatibility */ +ERL_NIF_TERM atom_rsa_padding; +ERL_NIF_TERM atom_rsa_pkcs1_pss_padding; +#ifdef HAVE_RSA_SSLV23_PADDING +ERL_NIF_TERM atom_rsa_sslv23_padding; +#endif +ERL_NIF_TERM atom_rsa_x931_padding; +ERL_NIF_TERM atom_rsa_pss_saltlen; + +#ifdef HAVE_BLAKE2 +ERL_NIF_TERM atom_blake2b; +ERL_NIF_TERM atom_blake2s; +#endif + +#ifdef HAS_ENGINE_SUPPORT + +ERL_NIF_TERM atom_engine_method_rsa; +ERL_NIF_TERM atom_engine_method_dsa; +ERL_NIF_TERM atom_engine_method_dh; +ERL_NIF_TERM atom_engine_method_rand; +ERL_NIF_TERM atom_engine_method_ecdh; +ERL_NIF_TERM atom_engine_method_ecdsa; +ERL_NIF_TERM atom_engine_method_ciphers; +ERL_NIF_TERM atom_engine_method_digests; +ERL_NIF_TERM atom_engine_method_store; +ERL_NIF_TERM atom_engine_method_pkey_meths; +ERL_NIF_TERM atom_engine_method_pkey_asn1_meths; +ERL_NIF_TERM atom_engine_method_ec; + +ERL_NIF_TERM atom_engine; +ERL_NIF_TERM atom_key_id; +ERL_NIF_TERM atom_password; +#endif + +int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM load_info) { + atom_true = enif_make_atom(env,"true"); + atom_false = enif_make_atom(env,"false"); + /* Enter FIPS mode */ + if (fips_mode == atom_true) { +#ifdef FIPS_SUPPORT + if (!FIPS_mode_set(1)) { +#else + { +#endif + PRINTF_ERR0("CRYPTO: Could not setup FIPS mode"); + return 0; + } + } else if (fips_mode != atom_false) { + PRINTF_ERR1("CRYPTO: Invalid load_info '%T'", load_info); + return 0; + } + + atom_sha = enif_make_atom(env,"sha"); + atom_error = enif_make_atom(env,"error"); + atom_rsa_pkcs1_padding = enif_make_atom(env,"rsa_pkcs1_padding"); + atom_rsa_pkcs1_oaep_padding = enif_make_atom(env,"rsa_pkcs1_oaep_padding"); + atom_rsa_no_padding = enif_make_atom(env,"rsa_no_padding"); + atom_signature_md = enif_make_atom(env,"signature_md"); + atom_undefined = enif_make_atom(env,"undefined"); + atom_ok = enif_make_atom(env,"ok"); + atom_none = enif_make_atom(env,"none"); + atom_notsup = enif_make_atom(env,"notsup"); + atom_digest = enif_make_atom(env,"digest"); + +#if defined(HAVE_EC) + atom_prime_field = enif_make_atom(env,"prime_field"); + atom_characteristic_two_field = enif_make_atom(env,"characteristic_two_field"); + atom_tpbasis = enif_make_atom(env,"tpbasis"); + atom_ppbasis = enif_make_atom(env,"ppbasis"); + atom_onbasis = enif_make_atom(env,"onbasis"); +#endif + + atom_aes_cfb8 = enif_make_atom(env, "aes_cfb8"); + atom_aes_cfb128 = enif_make_atom(env, "aes_cfb128"); +#ifdef HAVE_GCM + atom_aes_gcm = enif_make_atom(env, "aes_gcm"); +#endif +#ifdef HAVE_CCM + atom_aes_ccm = enif_make_atom(env, "aes_ccm"); +#endif + +#ifdef FIPS_SUPPORT + atom_enabled = enif_make_atom(env,"enabled"); + atom_not_enabled = enif_make_atom(env,"not_enabled"); +#else + atom_not_supported = enif_make_atom(env,"not_supported"); +#endif + atom_rsa = enif_make_atom(env,"rsa"); + atom_dss = enif_make_atom(env,"dss"); + atom_ecdsa = enif_make_atom(env,"ecdsa"); + +#ifdef HAVE_ED_CURVE_DH + atom_x25519 = enif_make_atom(env,"x25519"); + atom_x448 = enif_make_atom(env,"x448"); +#endif + atom_eddsa = enif_make_atom(env,"eddsa"); +#ifdef HAVE_EDDSA + atom_ed25519 = enif_make_atom(env,"ed25519"); + atom_ed448 = enif_make_atom(env,"ed448"); +#endif + atom_rsa_mgf1_md = enif_make_atom(env,"rsa_mgf1_md"); + atom_rsa_oaep_label = enif_make_atom(env,"rsa_oaep_label"); + atom_rsa_oaep_md = enif_make_atom(env,"rsa_oaep_md"); + atom_rsa_pad = enif_make_atom(env,"rsa_pad"); /* backwards compatibility */ + atom_rsa_padding = enif_make_atom(env,"rsa_padding"); + atom_rsa_pkcs1_pss_padding = enif_make_atom(env,"rsa_pkcs1_pss_padding"); +#ifdef HAVE_RSA_SSLV23_PADDING + atom_rsa_sslv23_padding = enif_make_atom(env,"rsa_sslv23_padding"); +#endif + atom_rsa_x931_padding = enif_make_atom(env,"rsa_x931_padding"); + atom_rsa_pss_saltlen = enif_make_atom(env,"rsa_pss_saltlen"); + +#ifdef HAS_ENGINE_SUPPORT + + atom_engine_method_rsa = enif_make_atom(env,"engine_method_rsa"); + atom_engine_method_dsa = enif_make_atom(env,"engine_method_dsa"); + atom_engine_method_dh = enif_make_atom(env,"engine_method_dh"); + atom_engine_method_rand = enif_make_atom(env,"engine_method_rand"); + atom_engine_method_ecdh = enif_make_atom(env,"engine_method_ecdh"); + atom_engine_method_ecdsa = enif_make_atom(env,"engine_method_ecdsa"); + atom_engine_method_store = enif_make_atom(env,"engine_method_store"); + atom_engine_method_ciphers = enif_make_atom(env,"engine_method_ciphers"); + atom_engine_method_digests = enif_make_atom(env,"engine_method_digests"); + atom_engine_method_pkey_meths = enif_make_atom(env,"engine_method_pkey_meths"); + atom_engine_method_pkey_asn1_meths = enif_make_atom(env,"engine_method_pkey_asn1_meths"); + atom_engine_method_ec = enif_make_atom(env,"engine_method_ec"); + + atom_engine = enif_make_atom(env,"engine"); + atom_key_id = enif_make_atom(env,"key_id"); + atom_password = enif_make_atom(env,"password"); +#endif + + return 1; +} diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h new file mode 100644 index 0000000000..f15523d865 --- /dev/null +++ b/lib/crypto/c_src/atoms.h @@ -0,0 +1,114 @@ +/* + * %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% + */ + +#ifndef E_ATOMS_H__ +#define E_ATOMS_H__ 1 + +#include <erl_nif.h> +#include "openssl_config.h" + +extern ERL_NIF_TERM atom_true; +extern ERL_NIF_TERM atom_false; +extern ERL_NIF_TERM atom_sha; +extern ERL_NIF_TERM atom_error; +extern ERL_NIF_TERM atom_rsa_pkcs1_padding; +extern ERL_NIF_TERM atom_rsa_pkcs1_oaep_padding; +extern ERL_NIF_TERM atom_rsa_no_padding; +extern ERL_NIF_TERM atom_signature_md; +extern ERL_NIF_TERM atom_undefined; + +extern ERL_NIF_TERM atom_ok; +extern ERL_NIF_TERM atom_none; +extern ERL_NIF_TERM atom_notsup; +extern ERL_NIF_TERM atom_digest; +#ifdef FIPS_SUPPORT +extern ERL_NIF_TERM atom_enabled; +extern ERL_NIF_TERM atom_not_enabled; +#else +extern ERL_NIF_TERM atom_not_supported; +#endif + +#if defined(HAVE_EC) +extern ERL_NIF_TERM atom_prime_field; +extern ERL_NIF_TERM atom_characteristic_two_field; +extern ERL_NIF_TERM atom_tpbasis; +extern ERL_NIF_TERM atom_ppbasis; +extern ERL_NIF_TERM atom_onbasis; +#endif + +extern ERL_NIF_TERM atom_aes_cfb8; +extern ERL_NIF_TERM atom_aes_cfb128; +#ifdef HAVE_GCM +extern ERL_NIF_TERM atom_aes_gcm; +#endif +#ifdef HAVE_CCM +extern ERL_NIF_TERM atom_aes_ccm; +#endif + +extern ERL_NIF_TERM atom_rsa; +extern ERL_NIF_TERM atom_dss; +extern ERL_NIF_TERM atom_ecdsa; + +#ifdef HAVE_ED_CURVE_DH +extern ERL_NIF_TERM atom_x25519; +extern ERL_NIF_TERM atom_x448; +#endif + +extern ERL_NIF_TERM atom_eddsa; +#ifdef HAVE_EDDSA +extern ERL_NIF_TERM atom_ed25519; +extern ERL_NIF_TERM atom_ed448; +#endif + +extern ERL_NIF_TERM atom_rsa_mgf1_md; +extern ERL_NIF_TERM atom_rsa_oaep_label; +extern ERL_NIF_TERM atom_rsa_oaep_md; +extern ERL_NIF_TERM atom_rsa_pad; /* backwards compatibility */ +extern ERL_NIF_TERM atom_rsa_padding; +extern ERL_NIF_TERM atom_rsa_pkcs1_pss_padding; +#ifdef HAVE_RSA_SSLV23_PADDING +extern ERL_NIF_TERM atom_rsa_sslv23_padding; +#endif +extern ERL_NIF_TERM atom_rsa_x931_padding; +extern ERL_NIF_TERM atom_rsa_pss_saltlen; + +#ifdef HAS_ENGINE_SUPPORT + +extern ERL_NIF_TERM atom_engine_method_rsa; +extern ERL_NIF_TERM atom_engine_method_dsa; +extern ERL_NIF_TERM atom_engine_method_dh; +extern ERL_NIF_TERM atom_engine_method_rand; +extern ERL_NIF_TERM atom_engine_method_ecdh; +extern ERL_NIF_TERM atom_engine_method_ecdsa; +extern ERL_NIF_TERM atom_engine_method_ciphers; +extern ERL_NIF_TERM atom_engine_method_digests; +extern ERL_NIF_TERM atom_engine_method_store; +extern ERL_NIF_TERM atom_engine_method_pkey_meths; +extern ERL_NIF_TERM atom_engine_method_pkey_asn1_meths; +extern ERL_NIF_TERM atom_engine_method_ec; + +extern ERL_NIF_TERM atom_engine; +extern ERL_NIF_TERM atom_key_id; +extern ERL_NIF_TERM atom_password; +#endif + +int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM load_info); + +#endif /* E_ATOMS_H__ */ diff --git a/lib/crypto/c_src/block.c b/lib/crypto/c_src/block.c new file mode 100644 index 0000000000..0a4fd72623 --- /dev/null +++ b/lib/crypto/c_src/block.c @@ -0,0 +1,149 @@ +/* + * %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 "block.h" +#include "aes.h" +#include "cipher.h" + +ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Key, Ivec, Text, IsEncrypt) or (Type, Key, Text, IsEncrypt) */ + const struct cipher_type_t *cipherp; + const EVP_CIPHER *cipher; + ErlNifBinary key, ivec, text; + EVP_CIPHER_CTX *ctx = NULL; + ERL_NIF_TERM ret; + unsigned char *out; + int ivec_size, out_size = 0; + int cipher_len; + + ASSERT(argc == 4 || argc == 5); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) + goto bad_arg; + if (key.size > INT_MAX) + goto bad_arg; + if ((cipherp = get_cipher_type(argv[0], key.size)) == NULL) + goto bad_arg; + if (cipherp->flags & (NON_EVP_CIPHER | AEAD_CIPHER)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[argc - 2], &text)) + goto bad_arg; + if (text.size > INT_MAX) + goto bad_arg; + + if (FORBIDDEN_IN_FIPS(cipherp)) + return enif_raise_exception(env, atom_notsup); + if ((cipher = cipherp->cipher.p) == NULL) + return enif_raise_exception(env, atom_notsup); + + if (cipherp->flags & AES_CFBx) { + if (argv[0] == atom_aes_cfb8 + && (key.size == 24 || key.size == 32)) { + /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? + * Fall back on low level API + */ + return aes_cfb_8_crypt(env, argc-1, argv+1); + } + else if (argv[0] == atom_aes_cfb128 + && (key.size == 24 || key.size == 32)) { + /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? + * Fall back on low level API + */ + return aes_cfb_128_crypt_nif(env, argc-1, argv+1); + } + } + + ivec_size = EVP_CIPHER_iv_length(cipher); + +#ifdef HAVE_ECB_IVEC_BUG + if (cipherp->flags & ECB_BUG_0_9_8L) + ivec_size = 0; /* 0.9.8l returns faulty ivec_size */ +#endif + + if (ivec_size < 0) + goto bad_arg; + + if ((cipher_len = EVP_CIPHER_block_size(cipher)) < 0) + goto bad_arg; + if (text.size % (size_t)cipher_len != 0) + goto bad_arg; + + if (ivec_size == 0) { + if (argc != 4) + goto bad_arg; + } else { + if (argc != 5) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[2], &ivec)) + goto bad_arg; + if (ivec.size != (size_t)ivec_size) + goto bad_arg; + } + + if ((out = enif_make_new_binary(env, text.size, &ret)) == NULL) + goto err; + if ((ctx = EVP_CIPHER_CTX_new()) == NULL) + goto err; + + if (!EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, + (argv[argc - 1] == atom_true))) + goto err; + if (!EVP_CIPHER_CTX_set_key_length(ctx, (int)key.size)) + goto err; + + if (EVP_CIPHER_type(cipher) == NID_rc2_cbc) { + if (key.size > INT_MAX / 8) + goto err; + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, (int)key.size * 8, NULL)) + goto err; + } + + if (!EVP_CipherInit_ex(ctx, NULL, NULL, key.data, + ivec_size ? ivec.data : NULL, -1)) + goto err; + if (!EVP_CIPHER_CTX_set_padding(ctx, 0)) + goto err; + + /* OpenSSL 0.9.8h asserts text.size > 0 */ + if (text.size > 0) { + if (!EVP_CipherUpdate(ctx, out, &out_size, text.data, (int)text.size)) + goto err; + if (ASSERT(out_size == text.size), 0) + goto err; + if (!EVP_CipherFinal_ex(ctx, out + out_size, &out_size)) + goto err; + } + + ASSERT(out_size == 0); + CONSUME_REDS(env, text); + goto done; + + bad_arg: + ret = enif_make_badarg(env); + goto done; + + err: + ret = enif_raise_exception(env, atom_notsup); + + done: + if (ctx) + EVP_CIPHER_CTX_free(ctx); + return ret; +} diff --git a/lib/crypto/c_src/block.h b/lib/crypto/c_src/block.h new file mode 100644 index 0000000000..cc5e78ce12 --- /dev/null +++ b/lib/crypto/c_src/block.h @@ -0,0 +1,28 @@ +/* + * %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% + */ + +#ifndef E_BLOCK_H__ +#define E_BLOCK_H__ 1 + +#include "common.h" + +ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_BLOCK_H__ */ diff --git a/lib/crypto/c_src/bn.c b/lib/crypto/c_src/bn.c new file mode 100644 index 0000000000..34ed4f7ebc --- /dev/null +++ b/lib/crypto/c_src/bn.c @@ -0,0 +1,186 @@ +/* + * %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 "bn.h" + + +int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) +{ + BIGNUM *ret; + ErlNifBinary bin; + int sz; + + if (!enif_inspect_binary(env, term, &bin)) + goto err; + if (bin.size > INT_MAX - 4) + goto err; + + ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); + + if (bin.size < 4) + goto err; + sz = (int)bin.size - 4; + if (get_int32(bin.data) != sz) + goto err; + + if ((ret = BN_bin2bn(bin.data+4, sz, NULL)) == NULL) + goto err; + + *bnp = ret; + return 1; + + err: + return 0; +} + +int get_bn_from_bin(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) +{ + BIGNUM *ret; + ErlNifBinary bin; + + if (!enif_inspect_binary(env, term, &bin)) + goto err; + if (bin.size > INT_MAX) + goto err; + + ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); + + if ((ret = BN_bin2bn(bin.data, (int)bin.size, NULL)) == NULL) + goto err; + + *bnp = ret; + return 1; + + err: + return 0; +} + +ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn) +{ + int bn_len; + unsigned char *bin_ptr; + ERL_NIF_TERM term; + + /* Copy the bignum into an erlang binary. */ + if ((bn_len = BN_num_bytes(bn)) < 0) + goto err; + if ((bin_ptr = enif_make_new_binary(env, (size_t)bn_len, &term)) == NULL) + goto err; + + if (BN_bn2bin(bn, bin_ptr) < 0) + goto err; + + return term; + + err: + return atom_error; +} + +ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Base,Exponent,Modulo,bin_hdr) */ + BIGNUM *bn_base = NULL, *bn_exponent = NULL, *bn_modulo = NULL, *bn_result = NULL; + BN_CTX *bn_ctx = NULL; + unsigned char* ptr; + int dlen; + unsigned bin_hdr; /* return type: 0=plain binary, 4: mpint */ + unsigned extra_byte; + ERL_NIF_TERM ret; + + ASSERT(argc == 4); + + if (!get_bn_from_bin(env, argv[0], &bn_base)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[1], &bn_exponent)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[2], &bn_modulo)) + goto bad_arg; + if (!enif_get_uint(env, argv[3], &bin_hdr)) + goto bad_arg; + if (bin_hdr != 0 && bin_hdr != 4) + goto bad_arg; + + if ((bn_result = BN_new()) == NULL) + goto err; + if ((bn_ctx = BN_CTX_new()) == NULL) + goto err; + + if (!BN_mod_exp(bn_result, bn_base, bn_exponent, bn_modulo, bn_ctx)) + goto err; + + dlen = BN_num_bytes(bn_result); + if (dlen < 0 || dlen > INT_MAX / 8) + goto bad_arg; + extra_byte = bin_hdr && BN_is_bit_set(bn_result, dlen * 8 - 1); + + if ((ptr = enif_make_new_binary(env, bin_hdr + extra_byte + (unsigned int)dlen, &ret)) == NULL) + goto err; + + if (bin_hdr) { + put_uint32(ptr, extra_byte + (unsigned int)dlen); + ptr[4] = 0; /* extra zeroed byte to ensure a positive mpint */ + ptr += bin_hdr + extra_byte; + } + + BN_bn2bin(bn_result, ptr); + goto done; + + bad_arg: + err: + ret = enif_make_badarg(env); + + done: + if (bn_base) + BN_free(bn_base); + if (bn_exponent) + BN_free(bn_exponent); + if (bn_modulo) + BN_free(bn_modulo); + if (bn_result) + BN_free(bn_result); + if (bn_ctx) + BN_CTX_free(bn_ctx); + return ret; +} + +#ifdef HAVE_EC +ERL_NIF_TERM bn2term(ErlNifEnv* env, const BIGNUM *bn) +{ + int dlen; + unsigned char* ptr; + ERL_NIF_TERM ret; + + if (bn == NULL) + return atom_undefined; + + dlen = BN_num_bytes(bn); + if (dlen < 0) + goto err; + if ((ptr = enif_make_new_binary(env, (size_t)dlen, &ret)) == NULL) + goto err; + + BN_bn2bin(bn, ptr); + + ERL_VALGRIND_MAKE_MEM_DEFINED(ptr, dlen); + return ret; + + err: + return enif_make_badarg(env); +} +#endif diff --git a/lib/crypto/c_src/bn.h b/lib/crypto/c_src/bn.h new file mode 100644 index 0000000000..332b06e79d --- /dev/null +++ b/lib/crypto/c_src/bn.h @@ -0,0 +1,36 @@ +/* + * %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% + */ + +#ifndef E_BN_H__ +#define E_BN_H__ 1 + +#include "common.h" + +ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn); +ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#ifdef HAVE_EC +ERL_NIF_TERM bn2term(ErlNifEnv* env, const BIGNUM *bn); +#endif + +int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp); +int get_bn_from_bin(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp); + +#endif /* E_BN_H__ */ diff --git a/lib/crypto/c_src/chacha20.c b/lib/crypto/c_src/chacha20.c new file mode 100644 index 0000000000..cfcc395dca --- /dev/null +++ b/lib/crypto/c_src/chacha20.c @@ -0,0 +1,124 @@ +/* + * %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 "chacha20.h" +#include "cipher.h" + +ERL_NIF_TERM chacha20_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IV) */ +#if defined(HAVE_CHACHA20) + ErlNifBinary key_bin, ivec_bin; + struct evp_cipher_ctx *ctx = NULL; + const EVP_CIPHER *cipher; + ERL_NIF_TERM ret; + + ASSERT(argc == 2); + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)) + goto bad_arg; + if (key_bin.size != 32) + goto bad_arg; + if (!enif_inspect_binary(env, argv[1], &ivec_bin)) + goto bad_arg; + if (ivec_bin.size != 16) + goto bad_arg; + + cipher = EVP_chacha20(); + + if ((ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL) + goto err; + if ((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL) + goto err; + + if (EVP_CipherInit_ex(ctx->ctx, cipher, NULL, + key_bin.data, ivec_bin.data, 1) != 1) + goto err; + if (EVP_CIPHER_CTX_set_padding(ctx->ctx, 0) != 1) + goto err; + + ret = enif_make_resource(env, ctx); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = enif_make_badarg(env); + + done: + if (ctx) + enif_release_resource(ctx); + return ret; + +#else + return enif_raise_exception(env, atom_notsup); +#endif +} + +ERL_NIF_TERM chacha20_stream_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (State, Data) */ +#if defined(HAVE_CHACHA20) + struct evp_cipher_ctx *ctx = NULL, *new_ctx = NULL; + ErlNifBinary data_bin; + ERL_NIF_TERM ret, cipher_term; + unsigned char *out; + int outl = 0; + + ASSERT(argc == 2); + + if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) + goto bad_arg; + if (data_bin.size > INT_MAX) + goto bad_arg; + + if ((new_ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL) + goto err; + if ((new_ctx->ctx = EVP_CIPHER_CTX_new()) == NULL) + goto err; + + if (EVP_CIPHER_CTX_copy(new_ctx->ctx, ctx->ctx) != 1) + goto err; + if ((out = enif_make_new_binary(env, data_bin.size, &cipher_term)) == NULL) + goto err; + if (EVP_CipherUpdate(new_ctx->ctx, out, &outl, data_bin.data, (int)data_bin.size) != 1) + goto err; + ASSERT(outl >= 0 && (size_t)outl == data_bin.size); + + ret = enif_make_tuple2(env, enif_make_resource(env, new_ctx), cipher_term); + CONSUME_REDS(env, data_bin); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = enif_make_badarg(env); + + done: + if (new_ctx) + enif_release_resource(new_ctx); + return ret; + +#else + return enif_raise_exception(env, atom_notsup); +#endif +} diff --git a/lib/crypto/c_src/chacha20.h b/lib/crypto/c_src/chacha20.h new file mode 100644 index 0000000000..7e2ccae2bb --- /dev/null +++ b/lib/crypto/c_src/chacha20.h @@ -0,0 +1,29 @@ +/* + * %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% + */ + +#ifndef E_CHACHA20_H__ +#define E_CHACHA20_H__ 1 + +#include "common.h" + +ERL_NIF_TERM chacha20_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM chacha20_stream_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_CHACHA20_H__ */ diff --git a/lib/crypto/c_src/check_erlang.cocci b/lib/crypto/c_src/check_erlang.cocci new file mode 100644 index 0000000000..b2a981f2ac --- /dev/null +++ b/lib/crypto/c_src/check_erlang.cocci @@ -0,0 +1,196 @@ +// %CopyrightBegin% +// +// Copyright Doug Hogan 2019. 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% + +// Coccinelle script to help verify Erlang calls. +// http://coccinelle.lip6.fr +// https://github.com/coccinelle/coccinelle +// +// These work with the Erlang code because it has a rigid coding pattern. +// $ spatch.opt --all-includes -sp_file check_erlang.cocci -dir . + +// Make sure resources are cleaned up properly in all paths. +// Need 'strict' so it's also checked in error handling paths. +@enif_alloc_resource@ +type T; +identifier CTX, L; +identifier virtual.enif_alloc_resource, virtual.enif_release_resource; +position p, pr; +@@ + + T *CTX = NULL; + + ... + if ((CTX = enif_alloc_resource(...)@p) == NULL) + goto L; + + ... when strict, forall + if (CTX) + enif_release_resource(CTX)@pr; + + +// After calling enif_alloc_binary(), you must either release it with +// enif_release_binary() or transfer ownership to Erlang via enif_make_binary(). +@enif_alloc_binary@ +expression SZ; +identifier BIN, RET, ENV, X, L; +identifier TUPLE =~ "^enif_make_tuple[0-9]+$"; +identifier virtual.enif_alloc_binary, virtual.enif_make_binary; +identifier virtual.enif_release_binary; +position pa, pm, pr; +@@ + +// This construct is used in engine.c +( + if (!enif_alloc_binary(SZ, &BIN)@pa) + goto L; + + ... when strict, forall + return +( + enif_make_binary(ENV, &BIN)@pm +| + TUPLE(..., enif_make_binary(ENV, &BIN)@pm)@pm +); + +| +// This is the typical way we allocate and use binaries. + int X = 0; + + ... + if (!enif_alloc_binary(SZ, &BIN)@pa) + goto L; + X = 1; + + ... when strict, forall +( + RET = enif_make_binary(ENV, &BIN)@pm; + X = 0; +| + if (X) + enif_release_binary(&BIN)@pr; +| + return enif_make_binary(ENV, &BIN)@pm; +) +) + +// TODO: These don't have single checks that handle all cases. +// +// enif_consume_timeslice returns 1 if exhausted or else 0 +// enif_has_pending_exception returns true if exception pending + +@erlang_check_void@ +identifier FUNCVOID =~ "^(enif_mutex_destroy|enif_mutex_lock|enif_mutex_unlock|enif_rwlock_destroy|enif_rwlock_rlock|enif_rwlock_runlock|enif_rwlock_rwlock|enif_rwlock_rwunlock|enif_system_info)$"; +position p; +@@ + + FUNCVOID(...)@p; + + +@erlang_check_null@ +expression X; +identifier L; +identifier FUNCNULL =~ "^(enif_alloc|enif_alloc_resource|enif_dlopen|enif_dlsym|enif_make_new_binary|enif_mutex_create|enif_open_resource_type|enif_realloc|enif_rwlock_create)$"; +position p; +@@ + +( + if ((X = FUNCNULL(...)@p) == NULL) + goto L; +| + X = FUNCNULL(...)@p; + if (X == NULL) + goto L; +| + return FUNCNULL(...)@p; +) + + +@erlang_check_not@ +identifier L; +identifier FUNCNOT =~ "^(enif_alloc_binary|enif_get_int|enif_get_list_cell|enif_get_list_length|enif_get_long|enif_get_map_value|enif_get_resource|enif_get_tuple|enif_get_uint|enif_get_ulong|enif_inspect_binary|enif_inspect_iolist_as_binary|enif_is_atom|enif_is_binary|enif_is_current_process_alive|enif_is_empty_list|enif_is_list|enif_is_map|enif_is_tuple|enif_realloc_binary)$"; +position p; +@@ + +( + if (!FUNCNOT(...)@p) + goto L; +| + return FUNCNOT(...)@p; +) + + +@erlang_check_null_free@ +expression X; +identifier FUNCFREE =~ "^(enif_free|enif_free_env|enif_free_iovec|enif_release_binary|enif_release_resource)$"; +position p; +@@ + + if ( +( + X +| + X != NULL +) + ) + FUNCFREE(X)@p; + + +@erlang_check_new@ +expression RET; +identifier FUNCNEW =~ "^(enif_make_atom|enif_make_badarg|enif_make_binary|enif_make_int|enif_make_list|enif_make_list_from_array|enif_make_resource|enif_make_tuple|enif_raise_exception|enif_schedule_nif|enif_thread_self)$"; +position p; +@@ + +( + RET = FUNCNEW(...)@p; +| + return FUNCNEW(...)@p; +) + + +// Flag any calls that aren't part of the above pattern. +@enif_alloc_not_free@ + +identifier FUNCVOID =~ "^(enif_mutex_destroy|enif_mutex_lock|enif_mutex_unlock|enif_rwlock_destroy|enif_rwlock_rlock|enif_rwlock_runlock|enif_rwlock_rwlock|enif_rwlock_rwunlock|enif_system_info)$"; +position pvoid != {erlang_check_void.p,enif_alloc_binary.pr}; + +identifier FUNCNULL =~ "^(enif_alloc|enif_alloc_resource|enif_dlopen|enif_dlsym|enif_make_new_binary|enif_mutex_create|enif_open_resource_type|enif_realloc|enif_rwlock_create)$"; +position pnull != {erlang_check_null.p,enif_alloc_resource.p}; + +identifier FUNCNOT =~ "^(enif_alloc_binary|enif_get_int|enif_get_list_cell|enif_get_list_length|enif_get_long|enif_get_map_value|enif_get_resource|enif_get_tuple|enif_get_uint|enif_get_ulong|enif_inspect_binary|enif_inspect_iolist_as_binary|enif_is_atom|enif_is_binary|enif_is_current_process_alive|enif_is_empty_list|enif_is_list|enif_is_map|enif_is_tuple|enif_realloc_binary)$"; +position pnot != {erlang_check_not.p,enif_alloc_binary.pa}; + +identifier FUNCNEW =~ "^(enif_make_atom|enif_make_badarg|enif_make_binary|enif_make_int|enif_make_list|enif_make_list_from_array|enif_make_resource|enif_make_tuple|enif_raise_exception|enif_schedule_nif|enif_thread_self)$"; +position pnew != {erlang_check_new.p,enif_alloc_binary.pm}; + +identifier FUNCFREE =~ "^(enif_free|enif_free_env|enif_free_iovec|enif_release_binary|enif_release_resource)$"; +position pfree != {enif_alloc_resource.pr,enif_alloc_binary.pr,erlang_check_null_free.p}; + +@@ + +( +* FUNCVOID(...)@pvoid +| +* FUNCNULL(...)@pnull +| +* FUNCNOT(...)@pnot +| +* FUNCNEW(...)@pnew +| +* FUNCFREE(...)@pfree +) diff --git a/lib/crypto/c_src/check_openssl.cocci b/lib/crypto/c_src/check_openssl.cocci new file mode 100644 index 0000000000..75d1a6e44b --- /dev/null +++ b/lib/crypto/c_src/check_openssl.cocci @@ -0,0 +1,281 @@ +// %CopyrightBegin% +// +// Copyright Doug Hogan 2019. 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% + +// Coccinelle script to help verify the subset of OpenSSL calls used by Erlang. +// http://coccinelle.lip6.fr +// https://github.com/coccinelle/coccinelle +// +// These work with the Erlang code because it has a rigid coding pattern. +// $ spatch.opt --all-includes -sp_file check_openssl.cocci -dir . + +// TODO: These APIs may not have a single check that covers all cases +// or may not be necessary to check. +// +// BN_GENCB_get_arg +// BN_bn2bin +// BN_cmp +// BN_is_bit_set +// BN_is_negative +// BN_is_zero +// BN_num_bits +// DH_get0_key +// DH_size +// EC_GROUP_get_degree +// EC_KEY_get0_group +// EC_KEY_get0_private_key +// EC_KEY_get0_public_key +// EC_KEY_get_conv_form +// EVP_CIPHER_block_size +// EVP_CIPHER_iv_length +// EVP_CIPHER_type +// EVP_MD_CTX_md +// EVP_MD_size +// EVP_aes_128_cbc +// EVP_aes_128_ccm +// EVP_aes_128_cfb128 +// EVP_aes_128_cfb8 +// EVP_aes_128_ctr +// EVP_aes_128_ecb +// EVP_aes_128_gcm +// EVP_aes_192_cbc +// EVP_aes_192_ccm +// EVP_aes_192_ctr +// EVP_aes_192_ecb +// EVP_aes_192_gcm +// EVP_aes_256_cbc +// EVP_aes_256_ccm +// EVP_aes_256_ctr +// EVP_aes_256_ecb +// EVP_aes_256_gcm +// EVP_bf_cbc +// EVP_bf_cfb64 +// EVP_bf_ecb +// EVP_bf_ofb +// EVP_chacha20 +// EVP_chacha20_poly1305 +// EVP_des_cbc +// EVP_des_cfb8 +// EVP_des_ecb +// EVP_des_ede3_cbc +// EVP_des_ede3_cfb8 +// EVP_md4 +// EVP_md5 +// EVP_rc2_cbc +// EVP_ripemd160 +// EVP_sha1 +// EVP_sha224 +// EVP_sha256 +// EVP_sha384 +// EVP_sha3_224 +// EVP_sha3_256 +// EVP_sha3_384 +// EVP_sha3_512 +// EVP_sha512 +// OpenSSL_version +// OpenSSL_version_num +// PEM_read_PrivateKey +// PEM_read_PUBKEY +// RSA_size + +// Unusual API for OpenSSL: 0 or positive on success and negative value(s) on error. +@openssl_check_negative@ +identifier FUNCNEG =~ "^(DH_compute_key|RSA_padding_check_SSLv23)$"; +expression X; +identifier L; +position p; +@@ + + if ( +( + FUNCNEG(...)@p < 0 +| + (X = FUNCNEG(...)@p) < 0 +) + ) + goto L; + +// Unusual API for OpenSSL: positive on success or else error +@openssl_check_positive@ +identifier FUNCPOS =~ "^(ECDH_compute_key|EVP_CIPHER_asn1_to_param|EVP_CIPHER_param_to_asn1|EVP_PKEY_CTX_ctrl|RSA_pkey_ctx_ctrl)$"; +identifier L; +expression X; +position p; +@@ + + if ( +( + FUNCPOS(...)@p < 1 +| + (X = FUNCPOS(...)@p) < 1 +) + ) + goto L; + +// Unusual API for OpenSSL: 0=success. +@openssl_check_0@ +identifier L; +expression X; +identifier FUNC0 =~ "^(AES_set_decrypt_key|AES_set_encrypt_key|CRYPTO_gcm128_aad|CRYPTO_gcm128_decrypt|CRYPTO_gcm128_finish)$"; +position p; +@@ + + if ( +( + FUNC0(...)@p != 0 +| + (X = FUNC0(...)@p) != 0 +) + ) + goto L; + +// These do not necessarily allocate resources but they may return NULL. +@openssl_check_null@ +expression X; +identifier L; +identifier FUNCNULL =~ "^(BN_CTX_new|BN_GENCB_new|BN_MONT_CTX_new|BN_bin2bn|BN_dup|BN_generate_prime|BN_new|CMAC_CTX_new|CRYPTO_clear_realloc|CRYPTO_gcm128_new|CRYPTO_malloc|CRYPTO_realloc|CRYPTO_zalloc|DH_generate_parameters|DH_new|DSA_new|EC_GROUP_dup|EC_GROUP_get0_generator|EC_GROUP_method_of|EC_GROUP_new_curve_GFm|EC_GROUP_new_curve_GFp|EC_KEY_copy|EC_KEY_dup|EC_KEY_get0_engine|EC_KEY_new|EC_KEY_new_by_curve_name|EC_POINT_bn2point|EC_POINT_dup|EC_POINT_new|EC_POINT_point2bn|ENGINE_by_id|ENGINE_get_cipher_engine|ENGINE_get_default_DH|ENGINE_get_default_DSA|ENGINE_get_default_RAND|ENGINE_get_default_RSA|ENGINE_get_digest_engine|ENGINE_get_first|ENGINE_get_id|ENGINE_get_last|ENGINE_get_name|ENGINE_get_next|ENGINE_get_prev|ENGINE_load_private_key|ENGINE_load_public_key|ENGINE_new|EVP_CIPHER_CTX_new|EVP_MAC_CTX_new|EVP_MAC_CTX_new_id|EVP_MD_CTX_new|EVP_MD_meth_new|EVP_PKEY_CTX_new|EVP_PKEY_CTX_new_id|EVP_PKEY_get1_DH|EVP_PKEY_get1_DSA|EVP_PKEY_get1_EC_KEY|EVP_PKEY_get1_RSA|EVP_PKEY_new|EVP_PKEY_new_raw_private_key|EVP_PKEY_new_raw_public_key|EVP_get_cipherbyname|EVP_get_cipherbynid|EVP_get_cipherbyobj|EVP_get_macbyname|EVP_get_macbynid|EVP_get_macbyobj|HMAC|HMAC_CTX_new|OPENSSL_buf2hexstr|OPENSSL_clear_realloc|OPENSSL_hexstr2buf|OPENSSL_malloc|OPENSSL_realloc|OPENSSL_strdup|OPENSSL_strndup|OPENSSL_zalloc|RSA_meth_dup|RSA_meth_new|RSA_new)$"; +position p; +@@ + +( + if ((X = FUNCNULL(...)@p) == NULL) + goto L; +| + X = FUNCNULL(...)@p; + if (X == NULL) + goto L; +) + +// non-zero=success, 0=failure. These can be safely used with ! +@openssl_check_not@ +expression X; +identifier L; +identifier FUNCNOT =~ "^(BN_add|BN_div|BN_exp|BN_from_montgomery|BN_gcd|BN_generate_prime_ex|BN_mod|BN_mod_add|BN_mod_exp|BN_mod_mul|BN_mod_mul_montgomery|BN_mod_sqr|BN_mod_sub|BN_mul|BN_nnmod|BN_priv_rand|BN_priv_rand_range|BN_pseudo_rand|BN_pseudo_rand_range|BN_rand|BN_rand_range|BN_set_bit|BN_set_word|BN_sqr|BN_sub|BN_to_montgomery|CMAC_Final|CMAC_Init|CMAC_Update|CRYPTO_set_mem_debug|CRYPTO_set_mem_functions|DH_check|DH_check_ex|DH_check_params|DH_check_pub_key_ex|DH_generate_key|DH_generate_parameters_ex|DH_set0_key|DH_set0_pqg|DH_set_length|DSA_set0_key|DSA_set0_pqg|EC_GROUP_check|EC_GROUP_check_discriminant|EC_GROUP_copy|EC_GROUP_get_curve_name|EC_GROUP_get_pentanomial_basis|EC_GROUP_get_trinomial_basis|EC_GROUP_precompute_mult|EC_GROUP_set_generator|EC_GROUP_set_seed|EC_KEY_check_key|EC_KEY_generate_key|EC_KEY_key2buf|EC_KEY_oct2key|EC_KEY_oct2priv|EC_KEY_precompute_mult|EC_KEY_priv2buf|EC_KEY_priv2oct|EC_KEY_set_group|EC_KEY_set_private_key|EC_KEY_set_public_key|EC_KEY_set_public_key_affine_coordinates|EC_KEY_up_ref|EC_POINT_add|EC_POINT_copy|EC_POINT_dbl|EC_POINT_get_Jprojective_coordinates_GFp|EC_POINT_get_affine_coordinates_GF2m|EC_POINT_get_affine_coordinates_GFp|EC_POINT_invert|EC_POINT_make_affine|EC_POINT_mul|EC_POINT_oct2point|EC_POINT_point2oct|EC_POINT_set_Jprojective_coordinates_GFp|EC_POINT_set_affine_coordinates_GF2m|EC_POINT_set_affine_coordinates_GFp|EC_POINT_set_compressed_coordinates_GF2m|EC_POINT_set_compressed_coordinates_GFp|EC_POINT_set_to_infinity|EC_POINTs_make_affine|EC_POINTs_mul|ENGINE_add|ENGINE_ctrl_cmd|ENGINE_ctrl_cmd_string|ENGINE_finish|ENGINE_free|ENGINE_init|ENGINE_register_DH|ENGINE_register_DSA|ENGINE_register_EC|ENGINE_register_RAND|ENGINE_register_RSA|ENGINE_register_all_complete|ENGINE_register_ciphers|ENGINE_register_complete|ENGINE_register_digests|ENGINE_register_pkey_asn1_meths|ENGINE_register_pkey_meths|ENGINE_remove|ENGINE_set_RSA|ENGINE_set_default|ENGINE_set_default_DH|ENGINE_set_default_DSA|ENGINE_set_default_EC|ENGINE_set_default_RAND|ENGINE_set_default_RSA|ENGINE_set_digests|ENGINE_set_id|ENGINE_set_init_function|ENGINE_set_load_privkey_function|ENGINE_set_load_pubkey_function|ENGINE_set_name|ENGINE_up_ref|HMAC_CTX_copy|HMAC_CTX_reset|HMAC_Final|HMAC_Init_ex|HMAC_Update|MD2_Init|MD2_Update|MD2_Final|MD4_Init|MD4_Update|MD4_Final|MD5_Init|MD5_Update|MD5_Final|OPENSSL_init_crypto|OPENSSL_mem_debug_pop|OPENSSL_mem_debug_push|RSA_generate_key_ex|RSA_generate_multi_prime_key|RSA_meth_set_finish|RSA_meth_set_sign|RSA_meth_set_verify|RSA_padding_add_SSLv23|RSA_set0_crt_params|RSA_set0_factors|RSA_set0_key|RSA_set0_multi_prime_params)$"; +position p; +@@ + + if ( +( + !FUNCNOT(...)@p +| + !(X = FUNCNOT)@p +) + ) + goto L; + +// 1=success. These may have == 0 or <= 0 or non-one failure so we explicitly check for success. +// Since some EVP_* functions use failure == 0 and others use <= 0, we consolidate all +// EVP_* calls into here so it's less error prone. In such cases, they all use 1 for success. +@openssl_check_1@ +expression X; +identifier L; +identifier FUNC1 =~ "^(EVP_CIPHER_CTX_copy|EVP_CIPHER_CTX_ctrl|EVP_CIPHER_CTX_rand_key|EVP_CIPHER_CTX_reset|EVP_CIPHER_CTX_set_key_length|EVP_CIPHER_CTX_set_padding|EVP_CipherFinal_ex|EVP_CipherInit_ex|EVP_CipherUpdate|EVP_DecryptFinal_ex|EVP_DecryptInit_ex|EVP_DecryptUpdate|EVP_Digest|EVP_DigestFinal|EVP_DigestFinal_ex|EVP_DigestInit|EVP_DigestInit_ex|EVP_DigestSign|EVP_DigestSignInit|EVP_DigestSignUpdate|EVP_DigestSignaFinal|EVP_DigestUpdate|EVP_DigestVerify|EVP_DigestVerifyInit|EVP_EncryptFinal_ex|EVP_EncryptInit_ex|EVP_EncryptUpdate|EVP_MAC_CTX_copy|EVP_MAC_ctrl|EVP_MAC_ctrl_str|EVP_MAC_hex2ctrl|EVP_MAC_init|EVP_MAC_reset|EVP_MAC_str2ctrl|EVP_MAC_update|EVP_MD_CTX_copy|EVP_MD_CTX_copy_ex|EVP_MD_CTX_ctrl|EVP_MD_meth_set_app_datasize|EVP_MD_meth_set_cleanup|EVP_MD_meth_set_copy|EVP_MD_meth_set_ctrl|EVP_MD_meth_set_final|EVP_MD_meth_set_flags|EVP_MD_meth_set_init|EVP_MD_meth_set_input_blocksize|EVP_MD_meth_set_result_size|EVP_MD_meth_set_update|EVP_PKEY_CTX_set_rsa_mgf1_md|EVP_PKEY_CTX_set_rsa_padding|EVP_PKEY_CTX_set_rsa_pss_saltlen|EVP_PKEY_CTX_set_signature|EVP_PKEY_assign|EVP_PKEY_assign_DSA|EVP_PKEY_assign_EC_KEY|EVP_PKEY_assign_RSA|EVP_PKEY_decrypt|EVP_PKEY_decrypt_init|EVP_PKEY_derive|EVP_PKEY_derive_init|EVP_PKEY_derive_set_peer|EVP_PKEY_encrypt|EVP_PKEY_encrypt_init|EVP_PKEY_get1_DH|EVP_PKEY_get_raw_private_key|EVP_PKEY_get_raw_public_key|EVP_PKEY_keygen|EVP_PKEY_keygen_init|EVP_PKEY_set1_DH|EVP_PKEY_sign|EVP_PKEY_sign_init|EVP_PKEY_verify|EVP_PKEY_verify_init|EVP_PKEY_verify_recover|EVP_PKEY_verify_recover_init|EVP_add_mac|RAND_bytes|RAND_priv_bytes)$"; +position p; +@@ + + if ( +( + FUNC1(...)@p != 1 +| + (X = FUNC1(...)@p) != 1 +) + ) + goto L; + + +// These are void but here for completeness +@openssl_void@ +identifier FUNCVOID =~ "^(AES_cfb128_encrypt|AES_cfb8_encrypt|AES_ige_encrypt|BN_GENCB_set|DSA_get0_key|DSA_get0_pqg|EC_GROUP_set_asn1_flag|EC_GROUP_set_point_conversion_form|ENGINE_get_static_state|ENGINE_unregister_DH|ENGINE_unregister_DSA|ENGINE_unregister_EC|ENGINE_unregister_RAND|ENGINE_unregister_RSA|ENGINE_unregister_ciphers|ENGINE_unregister_digests|ENGINE_unregister_pkey_asn1_meths|ENGINE_unregister_pkey_meths|OpenSSL_add_all_ciphers|OpenSSL_add_all_digests|RAND_seed|RC4|RC4_set_key|RSA_get0_crt_params|RSA_get0_factors|RSA_get0_key)$"; +position p; +@@ + + FUNCVOID(...)@p; + + +// Traditionally, OpenSSL didn't adhere to the semantics of free() calls +// allowing for NULL. However, they have been changing it over time. +// Since Erlang allows for unmaintained versions of OpenSSL, be conservative +// and assume the worst. +@openssl_free@ +expression X; +identifier FUNCFREE =~ "^(BN_CTX_free|BN_GENCB_free|BN_clear_free|BN_free|CMAC_CTX_free|CRYPTO_free|DH_free|DSA_free|EC_GROUP_free|EC_KEY_free|EC_POINT_free|EVP_CIPHER_CTX_free|EVP_MD_CTX_free|EVP_PKEY_CTX_free|EVP_PKEY_free|HMAC_CTX_free|RSA_free|RSA_meth_free)$"; +position p; +@@ + + if ( +( + X +| + X != NULL +) + ) + FUNCFREE(X)@p; + + +// NOTE: Keep these in sync with the above definitions! +// +// Find all of the cases that we haven't marked safe positions of. +// +// This will flag a few false positives because the code isn't using the +// standard pattern. +// +// NOTE: You have to copy the regexps because there doesn't appear to be a way in +// coccinelle to reference a regexp identifier from another rule properly. +@openssl_check_NOT_SAFE@ + +identifier FUNCNEG =~ "^(DH_compute_key|RSA_padding_check_SSLv23)$"; +position pneg != openssl_check_negative.p; + +identifier FUNCPOS =~ "^(ECDH_compute_key|EVP_CIPHER_asn1_to_param|EVP_CIPHER_param_to_asn1|EVP_PKEY_CTX_ctrl|RSA_pkey_ctx_ctrl)$"; +position ppos != openssl_check_positive.p; + +identifier FUNC0 =~ "^(AES_set_decrypt_key|AES_set_encrypt_key|CRYPTO_gcm128_aad|CRYPTO_gcm128_decrypt|CRYPTO_gcm128_finish)$"; +position p0 != openssl_check_0.p; + +identifier FUNCNULL =~ "^(BN_CTX_new|BN_GENCB_new|BN_MONT_CTX_new|BN_bin2bn|BN_dup|BN_generate_prime|BN_new|CMAC_CTX_new|CRYPTO_clear_realloc|CRYPTO_gcm128_new|CRYPTO_malloc|CRYPTO_realloc|CRYPTO_zalloc|DH_generate_parameters|DH_new|DSA_new|EC_GROUP_dup|EC_GROUP_get0_generator|EC_GROUP_method_of|EC_GROUP_new_curve_GFm|EC_GROUP_new_curve_GFp|EC_KEY_copy|EC_KEY_dup|EC_KEY_get0_engine|EC_KEY_new|EC_KEY_new_by_curve_name|EC_POINT_bn2point|EC_POINT_dup|EC_POINT_new|EC_POINT_point2bn|ENGINE_by_id|ENGINE_get_cipher_engine|ENGINE_get_default_DH|ENGINE_get_default_DSA|ENGINE_get_default_RAND|ENGINE_get_default_RSA|ENGINE_get_digest_engine|ENGINE_get_first|ENGINE_get_id|ENGINE_get_last|ENGINE_get_name|ENGINE_get_next|ENGINE_get_prev|ENGINE_load_private_key|ENGINE_load_public_key|ENGINE_new|EVP_CIPHER_CTX_new|EVP_MAC_CTX_new|EVP_MAC_CTX_new_id|EVP_MD_CTX_new|EVP_MD_meth_new|EVP_PKEY_CTX_new|EVP_PKEY_CTX_new_id|EVP_PKEY_get1_DH|EVP_PKEY_get1_DSA|EVP_PKEY_get1_EC_KEY|EVP_PKEY_get1_RSA|EVP_PKEY_new|EVP_PKEY_new_raw_private_key|EVP_PKEY_new_raw_public_key|EVP_get_cipherbyname|EVP_get_cipherbynid|EVP_get_cipherbyobj|EVP_get_macbyname|EVP_get_macbynid|EVP_get_macbyobj|HMAC|HMAC_CTX_new|OPENSSL_buf2hexstr|OPENSSL_clear_realloc|OPENSSL_hexstr2buf|OPENSSL_malloc|OPENSSL_realloc|OPENSSL_strdup|OPENSSL_strndup|OPENSSL_zalloc|RSA_meth_dup|RSA_meth_new|RSA_new)$"; +position pnull != openssl_check_null.p; + +identifier FUNCNOT =~ "^(BN_add|BN_div|BN_exp|BN_from_montgomery|BN_gcd|BN_generate_prime_ex|BN_mod|BN_mod_add|BN_mod_exp|BN_mod_mul|BN_mod_mul_montgomery|BN_mod_sqr|BN_mod_sub|BN_mul|BN_nnmod|BN_priv_rand|BN_priv_rand_range|BN_pseudo_rand|BN_pseudo_rand_range|BN_rand|BN_rand_range|BN_set_bit|BN_set_word|BN_sqr|BN_sub|BN_to_montgomery|CMAC_Final|CMAC_Init|CMAC_Update|CRYPTO_set_mem_debug|CRYPTO_set_mem_functions|DH_check|DH_check_ex|DH_check_params|DH_check_pub_key_ex|DH_generate_key|DH_generate_parameters_ex|DH_set0_key|DH_set0_pqg|DH_set_length|DSA_set0_key|DSA_set0_pqg|EC_GROUP_check|EC_GROUP_check_discriminant|EC_GROUP_copy|EC_GROUP_get_curve_name|EC_GROUP_get_pentanomial_basis|EC_GROUP_get_trinomial_basis|EC_GROUP_precompute_mult|EC_GROUP_set_generator|EC_GROUP_set_seed|EC_KEY_check_key|EC_KEY_generate_key|EC_KEY_key2buf|EC_KEY_oct2key|EC_KEY_oct2priv|EC_KEY_precompute_mult|EC_KEY_priv2buf|EC_KEY_priv2oct|EC_KEY_set_group|EC_KEY_set_private_key|EC_KEY_set_public_key|EC_KEY_set_public_key_affine_coordinates|EC_KEY_up_ref|EC_POINT_add|EC_POINT_copy|EC_POINT_dbl|EC_POINT_get_Jprojective_coordinates_GFp|EC_POINT_get_affine_coordinates_GF2m|EC_POINT_get_affine_coordinates_GFp|EC_POINT_invert|EC_POINT_make_affine|EC_POINT_mul|EC_POINT_oct2point|EC_POINT_point2oct|EC_POINT_set_Jprojective_coordinates_GFp|EC_POINT_set_affine_coordinates_GF2m|EC_POINT_set_affine_coordinates_GFp|EC_POINT_set_compressed_coordinates_GF2m|EC_POINT_set_compressed_coordinates_GFp|EC_POINT_set_to_infinity|EC_POINTs_make_affine|EC_POINTs_mul|ENGINE_add|ENGINE_ctrl_cmd|ENGINE_ctrl_cmd_string|ENGINE_finish|ENGINE_free|ENGINE_init|ENGINE_register_DH|ENGINE_register_DSA|ENGINE_register_EC|ENGINE_register_RAND|ENGINE_register_RSA|ENGINE_register_all_complete|ENGINE_register_ciphers|ENGINE_register_complete|ENGINE_register_digests|ENGINE_register_pkey_asn1_meths|ENGINE_register_pkey_meths|ENGINE_remove|ENGINE_set_RSA|ENGINE_set_default|ENGINE_set_default_DH|ENGINE_set_default_DSA|ENGINE_set_default_EC|ENGINE_set_default_RAND|ENGINE_set_default_RSA|ENGINE_set_digests|ENGINE_set_id|ENGINE_set_init_function|ENGINE_set_load_privkey_function|ENGINE_set_load_pubkey_function|ENGINE_set_name|ENGINE_up_ref|HMAC_CTX_copy|HMAC_CTX_reset|HMAC_Final|HMAC_Init_ex|HMAC_Update|MD2_Init|MD2_Update|MD2_Final|MD4_Init|MD4_Update|MD4_Final|MD5_Init|MD5_Update|MD5_Final|OPENSSL_init_crypto|OPENSSL_mem_debug_pop|OPENSSL_mem_debug_push|RSA_generate_key_ex|RSA_generate_multi_prime_key|RSA_meth_set_finish|RSA_meth_set_sign|RSA_meth_set_verify|RSA_padding_add_SSLv23|RSA_set0_crt_params|RSA_set0_factors|RSA_set0_key|RSA_set0_multi_prime_params)$"; +position pnot != openssl_check_not.p; + +identifier FUNC1 =~ "^(EVP_CIPHER_CTX_copy|EVP_CIPHER_CTX_ctrl|EVP_CIPHER_CTX_rand_key|EVP_CIPHER_CTX_reset|EVP_CIPHER_CTX_set_key_length|EVP_CIPHER_CTX_set_padding|EVP_CipherFinal_ex|EVP_CipherInit_ex|EVP_CipherUpdate|EVP_DecryptFinal_ex|EVP_DecryptInit_ex|EVP_DecryptUpdate|EVP_Digest|EVP_DigestFinal|EVP_DigestFinal_ex|EVP_DigestInit|EVP_DigestInit_ex|EVP_DigestSign|EVP_DigestSignInit|EVP_DigestSignUpdate|EVP_DigestSignaFinal|EVP_DigestUpdate|EVP_DigestVerify|EVP_DigestVerifyInit|EVP_EncryptFinal_ex|EVP_EncryptInit_ex|EVP_EncryptUpdate|EVP_MAC_CTX_copy|EVP_MAC_ctrl|EVP_MAC_ctrl_str|EVP_MAC_hex2ctrl|EVP_MAC_init|EVP_MAC_reset|EVP_MAC_str2ctrl|EVP_MAC_update|EVP_MD_CTX_copy|EVP_MD_CTX_copy_ex|EVP_MD_CTX_ctrl|EVP_MD_meth_set_app_datasize|EVP_MD_meth_set_cleanup|EVP_MD_meth_set_copy|EVP_MD_meth_set_ctrl|EVP_MD_meth_set_final|EVP_MD_meth_set_flags|EVP_MD_meth_set_init|EVP_MD_meth_set_input_blocksize|EVP_MD_meth_set_result_size|EVP_MD_meth_set_update|EVP_PKEY_CTX_set_rsa_mgf1_md|EVP_PKEY_CTX_set_rsa_padding|EVP_PKEY_CTX_set_rsa_pss_saltlen|EVP_PKEY_CTX_set_signature|EVP_PKEY_assign|EVP_PKEY_assign_DSA|EVP_PKEY_assign_EC_KEY|EVP_PKEY_assign_RSA|EVP_PKEY_decrypt|EVP_PKEY_decrypt_init|EVP_PKEY_derive|EVP_PKEY_derive_init|EVP_PKEY_derive_set_peer|EVP_PKEY_encrypt|EVP_PKEY_encrypt_init|EVP_PKEY_get1_DH|EVP_PKEY_get_raw_private_key|EVP_PKEY_get_raw_public_key|EVP_PKEY_keygen|EVP_PKEY_keygen_init|EVP_PKEY_set1_DH|EVP_PKEY_sign|EVP_PKEY_sign_init|EVP_PKEY_verify|EVP_PKEY_verify_init|EVP_PKEY_verify_recover|EVP_PKEY_verify_recover_init|EVP_add_mac|RAND_bytes|RAND_priv_bytes)$"; +position p1 != openssl_check_1.p; + +identifier FUNCVOID =~ "^(AES_cfb128_encrypt|AES_cfb8_encrypt|AES_ige_encrypt|BN_GENCB_set|DSA_get0_key|DSA_get0_pqg|EC_GROUP_set_asn1_flag|EC_GROUP_set_point_conversion_form|ENGINE_get_static_state|ENGINE_unregister_DH|ENGINE_unregister_DSA|ENGINE_unregister_EC|ENGINE_unregister_RAND|ENGINE_unregister_RSA|ENGINE_unregister_ciphers|ENGINE_unregister_digests|ENGINE_unregister_pkey_asn1_meths|ENGINE_unregister_pkey_meths|OpenSSL_add_all_ciphers|OpenSSL_add_all_digests|RAND_seed|RC4|RC4_set_key|RSA_get0_crt_params|RSA_get0_factors|RSA_get0_key)$"; +position pvoid != openssl_void.p; + +identifier FUNCFREE =~ "^(BN_CTX_free|BN_GENCB_free|BN_clear_free|BN_free|CMAC_CTX_free|CRYPTO_free|DH_free|DSA_free|EC_GROUP_free|EC_KEY_free|EC_POINT_free|EVP_CIPHER_CTX_free|EVP_MD_CTX_free|EVP_PKEY_CTX_free|EVP_PKEY_free|HMAC_CTX_free|RSA_free|RSA_meth_free)$"; +position pfree != openssl_free.p; +@@ + +( +* FUNCNEG(...)@pneg +| +* FUNCPOS(...)@ppos +| +* FUNCNULL(...)@pnull +| +* FUNC0(...)@p0 +| +* FUNC1(...)@p1 +| +* FUNCNOT(...)@pnot +| +* FUNCVOID(...)@pvoid +| +* FUNCFREE(...)@pfree +) diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c new file mode 100644 index 0000000000..f8e44b228a --- /dev/null +++ b/lib/crypto/c_src/cipher.c @@ -0,0 +1,229 @@ +/* + * %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 "cipher.h" + +#ifdef OPENSSL_NO_DES +#define COND_NO_DES_PTR(Ptr) (NULL) +#else +#define COND_NO_DES_PTR(Ptr) (Ptr) +#endif + +static struct cipher_type_t cipher_types[] = +{ +#ifndef OPENSSL_NO_RC2 + {{"rc2_cbc"}, {&EVP_rc2_cbc}, 0, NO_FIPS_CIPHER}, +#else + {{"rc2_cbc"}, {NULL}, 0, NO_FIPS_CIPHER}, +#endif +#ifndef OPENSSL_NO_RC4 + {{"rc4"}, {&EVP_rc4}, 0, NO_FIPS_CIPHER}, +#else + {{"rc4"}, {NULL}, 0, NO_FIPS_CIPHER}, +#endif + {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}, 0, NO_FIPS_CIPHER}, + {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}, 0, NO_FIPS_CIPHER}, + {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}, 0, NO_FIPS_CIPHER | ECB_BUG_0_9_8L}, + + {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}, 0, 0}, + +#ifdef HAVE_DES_ede3_cfb_encrypt + {{"des_ede3_cfb"}, {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)}, 0, 0}, +#else + {{"des_ede3_cfb"}, {NULL}, 0, 0}, +#endif + + {{"blowfish_cbc"}, {&EVP_bf_cbc}, 0, NO_FIPS_CIPHER}, + {{"blowfish_cfb64"}, {&EVP_bf_cfb64}, 0, NO_FIPS_CIPHER}, + {{"blowfish_ofb64"}, {&EVP_bf_ofb}, 0, NO_FIPS_CIPHER}, + {{"blowfish_ecb"}, {&EVP_bf_ecb}, 0, NO_FIPS_CIPHER | ECB_BUG_0_9_8L}, + + {{"aes_cbc"}, {&EVP_aes_128_cbc}, 16, 0}, + {{"aes_cbc"}, {&EVP_aes_192_cbc}, 24, 0}, + {{"aes_cbc"}, {&EVP_aes_256_cbc}, 32, 0}, + + {{"aes_128_cbc"}, {&EVP_aes_128_cbc}, 16, 0}, + {{"aes_192_cbc"}, {&EVP_aes_192_cbc}, 24, 0}, + {{"aes_256_cbc"}, {&EVP_aes_256_cbc}, 32, 0}, + + {{"aes_cfb8"}, {&EVP_aes_128_cfb8}, 16, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb8"}, {&EVP_aes_192_cfb8}, 24, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb8"}, {&EVP_aes_256_cfb8}, 32, NO_FIPS_CIPHER | AES_CFBx}, + + {{"aes_cfb128"}, {&EVP_aes_128_cfb128}, 16, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb128"}, {&EVP_aes_192_cfb128}, 24, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb128"}, {&EVP_aes_256_cfb128}, 32, NO_FIPS_CIPHER | AES_CFBx}, + + {{"aes_ecb"}, {&EVP_aes_128_ecb}, 16, ECB_BUG_0_9_8L}, + {{"aes_ecb"}, {&EVP_aes_192_ecb}, 24, ECB_BUG_0_9_8L}, + {{"aes_ecb"}, {&EVP_aes_256_ecb}, 32, ECB_BUG_0_9_8L}, + +#if defined(HAVE_EVP_AES_CTR) + {{"aes_128_ctr"}, {&EVP_aes_128_ctr}, 16, 0}, + {{"aes_192_ctr"}, {&EVP_aes_192_ctr}, 24, 0}, + {{"aes_256_ctr"}, {&EVP_aes_256_ctr}, 32, 0}, + {{"aes_ctr"}, {&EVP_aes_128_ctr}, 16, 0}, + {{"aes_ctr"}, {&EVP_aes_192_ctr}, 24, 0}, + {{"aes_ctr"}, {&EVP_aes_256_ctr}, 32, 0}, +#else + {{"aes_128_ctr"}, {NULL}, 16, AES_CTR_COMPAT}, + {{"aes_192_ctr"}, {NULL}, 24, AES_CTR_COMPAT}, + {{"aes_256_ctr"}, {NULL}, 32, AES_CTR_COMPAT}, + {{"aes_ctr"}, {NULL}, 0, AES_CTR_COMPAT}, +#endif + +#if defined(HAVE_CHACHA20) + {{"chacha20"}, {&EVP_chacha20}, 32, NO_FIPS_CIPHER}, +#else + {{"chacha20"}, {NULL}, 0, NO_FIPS_CIPHER}, +#endif + + /*==== AEAD ciphers ====*/ +#if defined(HAVE_CHACHA20_POLY1305) + {{"chacha20_poly1305"}, {&EVP_chacha20_poly1305}, 0, NO_FIPS_CIPHER | AEAD_CIPHER, {{EVP_CTRL_AEAD_SET_IVLEN,EVP_CTRL_AEAD_GET_TAG,EVP_CTRL_AEAD_SET_TAG}}}, +#else + {{"chacha20_poly1305"}, {NULL}, 0, NO_FIPS_CIPHER | AEAD_CIPHER, {{0,0,0}}}, +#endif + +#if defined(HAVE_GCM) + {{"aes_gcm"}, {&EVP_aes_128_gcm}, 16, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_gcm"}, {&EVP_aes_192_gcm}, 24, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_gcm"}, {&EVP_aes_256_gcm}, 32, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_128_gcm"}, {&EVP_aes_128_gcm}, 16, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_192_gcm"}, {&EVP_aes_192_gcm}, 24, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_256_gcm"}, {&EVP_aes_256_gcm}, 32, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, +#else + {{"aes_gcm"}, {NULL}, 0, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_128_gcm"}, {NULL}, 16, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_192_gcm"}, {NULL}, 24, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_256_gcm"}, {NULL}, 32, AEAD_CIPHER, {{0,0,0}}}, +#endif + +#if defined(HAVE_CCM) + {{"aes_ccm"}, {&EVP_aes_128_ccm}, 16, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_ccm"}, {&EVP_aes_192_ccm}, 24, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_ccm"}, {&EVP_aes_256_ccm}, 32, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_128_ccm"}, {&EVP_aes_128_ccm}, 16, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_192_ccm"}, {&EVP_aes_192_ccm}, 24, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_256_ccm"}, {&EVP_aes_256_ccm}, 32, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, +#else + {{"aes_ccm"}, {NULL}, 0, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_128_ccm"}, {NULL}, 16, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_192_ccm"}, {NULL}, 24, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_256_ccm"}, {NULL}, 32, AEAD_CIPHER, {{0,0,0}}}, +#endif + + /*==== Specialy handled ciphers, only for inclusion in algorithm's list ====*/ +#ifdef HAVE_AES_IGE + {{"aes_ige256"}, {NULL}, 0, NO_FIPS_CIPHER | NON_EVP_CIPHER}, +#endif + + /*==== End of list ==== */ + + {{NULL},{NULL},0,0} +}; + +ErlNifResourceType* evp_cipher_ctx_rtype; + +static size_t num_cipher_types = 0; + +static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) { + if (ctx == NULL) + return; + + if (ctx->ctx) + EVP_CIPHER_CTX_free(ctx->ctx); +} + +int init_cipher_ctx(ErlNifEnv *env) { + evp_cipher_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_CIPHER_CTX", + (ErlNifResourceDtor*) evp_cipher_ctx_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (evp_cipher_ctx_rtype == NULL) + goto err; + + return 1; + + err: + PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_CIPHER_CTX'"); + return 0; +} + +void init_cipher_types(ErlNifEnv* env) +{ + struct cipher_type_t* p = cipher_types; + + num_cipher_types = 0; + for (p = cipher_types; p->type.str; p++) { + num_cipher_types++; + p->type.atom = enif_make_atom(env, p->type.str); + if (p->cipher.funcp) + p->cipher.p = p->cipher.funcp(); + } + p->type.atom = atom_false; /* end marker */ + + qsort(cipher_types, num_cipher_types, sizeof(cipher_types[0]), cmp_cipher_types); +} + +const struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len) +{ + struct cipher_type_t key; + + key.type.atom = type; + key.key_len = key_len; + + return bsearch(&key, cipher_types, num_cipher_types, sizeof(cipher_types[0]), cmp_cipher_types); +} + + +int cmp_cipher_types(const void *keyp, const void *elemp) { + const struct cipher_type_t *key = keyp; + const struct cipher_type_t *elem = elemp; + + if (key->type.atom < elem->type.atom) return -1; + else if (key->type.atom > elem->type.atom) return 1; + else /* key->type.atom == elem->type.atom */ + if (!elem->key_len || key->key_len == elem->key_len) return 0; + else if (key->key_len < elem->key_len) return -1; + else return 1; +} + + +ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env) +{ + struct cipher_type_t* p; + ERL_NIF_TERM prev, hd; + + hd = enif_make_list(env, 0); + prev = atom_undefined; + + for (p = cipher_types; (p->type.atom & (p->type.atom != atom_false)); p++) { + if ((prev != p->type.atom) && + ((p->cipher.p != NULL) || + (p->flags & (NON_EVP_CIPHER|AES_CTR_COMPAT)) ) && /* Special handling. Bad indeed... */ + ! FORBIDDEN_IN_FIPS(p) + ) + hd = enif_make_list_cell(env, p->type.atom, hd); + prev = p->type.atom; + } + + return hd; +} diff --git a/lib/crypto/c_src/cipher.h b/lib/crypto/c_src/cipher.h new file mode 100644 index 0000000000..6b43afea99 --- /dev/null +++ b/lib/crypto/c_src/cipher.h @@ -0,0 +1,73 @@ +/* + * %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% + */ + +#ifndef E_CIPHER_H__ +#define E_CIPHER_H__ 1 + +#include "common.h" + +struct cipher_type_t { + union { + const char* str; /* before init */ + ERL_NIF_TERM atom; /* after init */ + }type; + union { + const EVP_CIPHER* (*funcp)(void); /* before init, NULL if notsup */ + const EVP_CIPHER* p; /* after init, NULL if notsup */ + }cipher; + size_t key_len; /* != 0 to also match on key_len */ + unsigned flags; + union { + struct aead_ctrl {int ctx_ctrl_set_ivlen, ctx_ctrl_get_tag, ctx_ctrl_set_tag;} aead; + } extra; +}; + +/* masks in the flags field if cipher_type_t */ +#define NO_FIPS_CIPHER 1 +#define AES_CFBx 2 +#define ECB_BUG_0_9_8L 4 +#define AEAD_CIPHER 8 +#define NON_EVP_CIPHER 16 +#define AES_CTR_COMPAT 32 + + +#ifdef FIPS_SUPPORT +/* May have FIPS support, must check dynamically if it is enabled */ +# define FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_CIPHER) && FIPS_mode()) +#else +/* No FIPS support since the symbol FIPS_SUPPORT is undefined */ +# define FORBIDDEN_IN_FIPS(P) 0 +#endif + +extern ErlNifResourceType* evp_cipher_ctx_rtype; +struct evp_cipher_ctx { + EVP_CIPHER_CTX* ctx; +}; + +int init_cipher_ctx(ErlNifEnv *env); + +void init_cipher_types(ErlNifEnv* env); +const struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len); + +int cmp_cipher_types(const void *keyp, const void *elemp); + +ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env); + +#endif /* E_CIPHER_H__ */ diff --git a/lib/crypto/c_src/cmac.c b/lib/crypto/c_src/cmac.c new file mode 100644 index 0000000000..49e67ccf29 --- /dev/null +++ b/lib/crypto/c_src/cmac.c @@ -0,0 +1,88 @@ +/* + * %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 "cmac.h" +#include "cipher.h" + +ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Key, Data) */ +#if defined(HAVE_CMAC) + const struct cipher_type_t *cipherp; + const EVP_CIPHER *cipher; + CMAC_CTX *ctx = NULL; + ErlNifBinary key; + ErlNifBinary data; + ERL_NIF_TERM ret; + size_t ret_size; + unsigned char *outp; + int cipher_len; + + ASSERT(argc == 3); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) + goto bad_arg; + if ((cipherp = get_cipher_type(argv[0], key.size)) == NULL) + goto bad_arg; + if (cipherp->flags & (NON_EVP_CIPHER | AEAD_CIPHER)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[2], &data)) + goto bad_arg; + + if (FORBIDDEN_IN_FIPS(cipherp)) + return enif_raise_exception(env, atom_notsup); + if ((cipher = cipherp->cipher.p) == NULL) + return enif_raise_exception(env, atom_notsup); + + if ((ctx = CMAC_CTX_new()) == NULL) + goto err; + if (!CMAC_Init(ctx, key.data, key.size, cipher, NULL)) + goto err; + if (!CMAC_Update(ctx, data.data, data.size)) + goto err; + if ((cipher_len = EVP_CIPHER_block_size(cipher)) < 0) + goto err; + if ((outp = enif_make_new_binary(env, (size_t)cipher_len, &ret)) == NULL) + goto err; + if (!CMAC_Final(ctx, outp, &ret_size)) + goto err; + + ASSERT(ret_size == (unsigned)EVP_CIPHER_block_size(cipher)); + CONSUME_REDS(env, data); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = atom_notsup; + + done: + if (ctx) + CMAC_CTX_free(ctx); + return ret; + +#else + /* The CMAC functionality was introduced in OpenSSL 1.0.1 + * Although OTP requires at least version 0.9.8, the versions 0.9.8 and 1.0.0 are + * no longer maintained. */ + return atom_notsup; +#endif +} + diff --git a/lib/crypto/c_src/cmac.h b/lib/crypto/c_src/cmac.h new file mode 100644 index 0000000000..14488def58 --- /dev/null +++ b/lib/crypto/c_src/cmac.h @@ -0,0 +1,28 @@ +/* + * %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% + */ + +#ifndef E_CMAC_H__ +#define E_CMAC_H__ 1 + +#include "common.h" + +ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_CMAC_H__ */ diff --git a/lib/crypto/c_src/common.h b/lib/crypto/c_src/common.h new file mode 100644 index 0000000000..2bc8bdd73c --- /dev/null +++ b/lib/crypto/c_src/common.h @@ -0,0 +1,38 @@ +/* + * %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% + */ + +#ifndef E_COMMON_H__ +#define E_COMMON_H__ 1 + +#ifdef __WIN32__ +# include <windows.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <stdint.h> + +#include <erl_nif.h> +#include "openssl_config.h" +#include "atoms.h" + +#endif /* E_COMMON_H__ */ diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index df607732bf..06439c34b2 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -23,982 +23,123 @@ * Based on OpenSSL. */ -#ifdef __WIN32__ - #include <windows.h> -#endif - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include <erl_nif.h> - -#define OPENSSL_THREAD_DEFINES -#include <openssl/opensslconf.h> - -#include <openssl/crypto.h> -#ifndef OPENSSL_NO_DES -#include <openssl/des.h> -#endif /* #ifndef OPENSSL_NO_DES */ -/* #include <openssl/idea.h> This is not supported on the openssl OTP requires */ -#include <openssl/dsa.h> -#include <openssl/rsa.h> -#include <openssl/aes.h> -#include <openssl/md5.h> -#include <openssl/md4.h> -#include <openssl/sha.h> -#include <openssl/ripemd.h> -#include <openssl/bn.h> -#include <openssl/objects.h> -#ifndef OPENSSL_NO_RC4 - #include <openssl/rc4.h> -#endif /* OPENSSL_NO_RC4 */ -#ifndef OPENSSL_NO_RC2 - #include <openssl/rc2.h> -#endif -#include <openssl/blowfish.h> -#include <openssl/rand.h> -#include <openssl/evp.h> -#include <openssl/hmac.h> -#include <openssl/err.h> - -/* Helper macro to construct a OPENSSL_VERSION_NUMBER. - * See openssl/opensslv.h - */ -#define PACKED_OPENSSL_VERSION(MAJ, MIN, FIX, P) \ - ((((((((MAJ << 8) | MIN) << 8 ) | FIX) << 8) | (P-'a'+1)) << 4) | 0xf) - -#define PACKED_OPENSSL_VERSION_PLAIN(MAJ, MIN, FIX) \ - PACKED_OPENSSL_VERSION(MAJ,MIN,FIX,('a'-1)) - - -/* LibreSSL was cloned from OpenSSL 1.0.1g and claims to be API and BPI compatible - * with 1.0.1. - * - * LibreSSL has the same names on include files and symbols as OpenSSL, but defines - * the OPENSSL_VERSION_NUMBER to be >= 2.0.0 - * - * Therefor works tests like this as intendend: - * OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) - * (The test is for example "2.4.2" >= "1.0.0" although the test - * with the cloned OpenSSL test would be "1.0.1" >= "1.0.0") - * - * But tests like this gives wrong result: - * OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) - * (The test is false since "2.4.2" < "1.1.0". It should have been - * true because the LibreSSL API version is "1.0.1") - * - */ - -#ifdef LIBRESSL_VERSION_NUMBER -/* A macro to test on in this file */ -#define HAS_LIBRESSL -#endif - -#ifdef HAS_LIBRESSL -/* LibreSSL dislikes FIPS */ -# ifdef FIPS_SUPPORT -# undef FIPS_SUPPORT -# endif - -# if LIBRESSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(2,7,0) -/* LibreSSL wants the 1.0.1 API */ -# define NEED_EVP_COMPATIBILITY_FUNCTIONS -# endif -#endif - - -#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) -# define NEED_EVP_COMPATIBILITY_FUNCTIONS -#endif - - -#ifndef HAS_LIBRESSL -# if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) -# define HAS_EVP_PKEY_CTX -# endif -#endif - - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) -#include <openssl/modes.h> -#endif - -#include "crypto_callback.h" - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ - && !defined(OPENSSL_NO_SHA224) && defined(NID_sha224) \ - && !defined(OPENSSL_NO_SHA256) /* disabled like this in my sha.h (?) */ -# define HAVE_SHA224 -#endif -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ - && !defined(OPENSSL_NO_SHA256) && defined(NID_sha256) -# define HAVE_SHA256 -#endif -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ - && !defined(OPENSSL_NO_SHA384) && defined(NID_sha384)\ - && !defined(OPENSSL_NO_SHA512) /* disabled like this in my sha.h (?) */ -# define HAVE_SHA384 -#endif -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ - && !defined(OPENSSL_NO_SHA512) && defined(NID_sha512) -# define HAVE_SHA512 -#endif -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,7,'e') -# define HAVE_DES_ede3_cfb_encrypt -#endif - -// SHA3: -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,1) -// An error in beta releases of 1.1.1 fixed in production release -# ifdef NID_sha3_224 -# define HAVE_SHA3_224 -# endif -# ifdef NID_sha3_256 -# define HAVE_SHA3_256 -# endif -#endif -# ifdef NID_sha3_384 -# define HAVE_SHA3_384 -# endif -# ifdef NID_sha3_512 -# define HAVE_SHA3_512 -# endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'o') \ - && !defined(OPENSSL_NO_EC) \ - && !defined(OPENSSL_NO_ECDH) \ - && !defined(OPENSSL_NO_ECDSA) -# define HAVE_EC -#endif - -// (test for >= 1.1.1pre8) -#if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1) -7) \ - && !defined(HAS_LIBRESSL) \ - && defined(HAVE_EC) -# define HAVE_ED_CURVE_DH -# if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1)) -# define HAVE_EDDSA -# endif -#endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'c') -# define HAVE_AES_IGE -#endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,1) -# define HAVE_EVP_AES_CTR -# define HAVE_AEAD -# define HAVE_GCM -# define HAVE_CCM -# define HAVE_CMAC -# if defined(RSA_PKCS1_OAEP_PADDING) -# define HAVE_RSA_OAEP_PADDING -# endif -# define HAVE_RSA_MGF1_MD -# if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION(1,0,1,'d') -# define HAVE_GCM_EVP_DECRYPT_BUG -# endif -#endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,0) -# ifndef HAS_LIBRESSL -# define HAVE_CHACHA20 -# define HAVE_CHACHA20_POLY1305 -# define HAVE_RSA_OAEP_MD -# endif -#endif - -// OPENSSL_VERSION_NUMBER >= 1.1.1-pre8 -#if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1)-7) -# ifndef HAS_LIBRESSL -# define HAVE_POLY1305 -# endif -#endif - -#if OPENSSL_VERSION_NUMBER <= PACKED_OPENSSL_VERSION(0,9,8,'l') -# define HAVE_ECB_IVEC_BUG -#endif - -#ifndef HAS_LIBRESSL -# ifdef RSA_SSLV23_PADDING -# define HAVE_RSA_SSLV23_PADDING -# endif -#endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) -# ifdef RSA_PKCS1_PSS_PADDING -# define HAVE_RSA_PKCS1_PSS_PADDING -# endif -#endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'h') \ - && defined(HAVE_EC) -/* If OPENSSL_NO_EC is set, there will be an error in ec.h included from engine.h - So if EC is disabled, you can't use Engine either.... -*/ -# define HAS_ENGINE_SUPPORT -#endif - - -#if defined(HAS_ENGINE_SUPPORT) -# include <openssl/engine.h> -#endif - -#if defined(HAVE_CMAC) -#include <openssl/cmac.h> -#endif - -#if defined(HAVE_EC) -#include <openssl/ec.h> -#include <openssl/ecdh.h> -#include <openssl/ecdsa.h> -#endif - -#ifdef VALGRIND - # include <valgrind/memcheck.h> - -/* libcrypto mixes supplied buffer contents into its entropy pool, - which makes valgrind complain about the use of uninitialized data. - We use this valgrind "request" to make sure that no such seemingly - undefined data is returned. -*/ - # define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size) \ - VALGRIND_MAKE_MEM_DEFINED(ptr,size) - - # define ERL_VALGRIND_ASSERT_MEM_DEFINED(Ptr,Size) \ - do { \ - int __erl_valgrind_mem_defined = VALGRIND_CHECK_MEM_IS_DEFINED((Ptr),(Size)); \ - if (__erl_valgrind_mem_defined != 0) { \ - fprintf(stderr,"\r\n####### VALGRIND_ASSSERT(%p,%ld) failed at %s:%d\r\n", \ - (Ptr),(long)(Size), __FILE__, __LINE__); \ - abort(); \ - } \ - } while (0) - -#else - # define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size) - # define ERL_VALGRIND_ASSERT_MEM_DEFINED(ptr,size) -#endif - -#ifdef DEBUG - # define ASSERT(e) \ - ((void) ((e) ? 1 : (fprintf(stderr,"Assert '%s' failed at %s:%d\n",\ - #e, __FILE__, __LINE__), abort(), 0))) -#else - # define ASSERT(e) ((void) 1) -#endif - -#ifdef __GNUC__ - # define INLINE __inline__ -#elif defined(__WIN32__) - # define INLINE __forceinline -#else - # define INLINE -#endif - - -#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \ - (((unsigned char*) (s))[1] << 16) | \ - (((unsigned char*) (s))[2] << 8) | \ - (((unsigned char*) (s))[3])) - -#define put_int32(s,i) \ -{ (s)[0] = (char)(((i) >> 24) & 0xff);\ - (s)[1] = (char)(((i) >> 16) & 0xff);\ - (s)[2] = (char)(((i) >> 8) & 0xff);\ - (s)[3] = (char)((i) & 0xff);\ -} - -/* This shall correspond to the similar macro in crypto.erl */ -/* Current value is: erlang:system_info(context_reductions) * 10 */ -#define MAX_BYTES_TO_NIF 20000 - -#define CONSUME_REDS(NifEnv, Ibin) \ -do { \ - int _cost = ((Ibin).size * 100) / MAX_BYTES_TO_NIF;\ - if (_cost) { \ - (void) enif_consume_timeslice((NifEnv), \ - (_cost > 100) ? 100 : _cost); \ - } \ - } while (0) - - -#ifdef NEED_EVP_COMPATIBILITY_FUNCTIONS -/* - * In OpenSSL 1.1.0, most structs are opaque. That means that - * the structs cannot be allocated as automatic variables on the - * C stack (because the size is unknown) and that it is necessary - * to use access functions. - * - * For backward compatibility to previous versions of OpenSSL, define - * on our versions of the new functions defined in 1.1.0 here, so that - * we don't have to sprinkle ifdefs throughout the code. - */ - -static HMAC_CTX *HMAC_CTX_new(void); -static void HMAC_CTX_free(HMAC_CTX *ctx); - -static HMAC_CTX *HMAC_CTX_new() -{ - HMAC_CTX *ctx = CRYPTO_malloc(sizeof(HMAC_CTX), __FILE__, __LINE__); - HMAC_CTX_init(ctx); - return ctx; -} - -static void HMAC_CTX_free(HMAC_CTX *ctx) -{ - HMAC_CTX_cleanup(ctx); - CRYPTO_free(ctx); -} - -#define EVP_MD_CTX_new() EVP_MD_CTX_create() -#define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy(ctx) - -static INLINE void *BN_GENCB_get_arg(BN_GENCB *cb); - -static INLINE void *BN_GENCB_get_arg(BN_GENCB *cb) -{ - return cb->arg; -} - -static INLINE int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); -static INLINE void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d); -static INLINE int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q); -static INLINE void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); -static INLINE int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp); -static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp); - -static INLINE int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) -{ - r->n = n; - r->e = e; - r->d = d; - return 1; -} - -static INLINE void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) -{ - *n = r->n; - *e = r->e; - *d = r->d; -} - -static INLINE int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) -{ - r->p = p; - r->q = q; - return 1; -} - -static INLINE void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) -{ - *p = r->p; - *q = r->q; -} - -static INLINE int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) -{ - r->dmp1 = dmp1; - r->dmq1 = dmq1; - r->iqmp = iqmp; - return 1; -} - -static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp) -{ - *dmp1 = r->dmp1; - *dmq1 = r->dmq1; - *iqmp = r->iqmp; -} - -static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); -static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); -static INLINE void DSA_get0_pqg(const DSA *dsa, - const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); -static INLINE void DSA_get0_key(const DSA *dsa, - const BIGNUM **pub_key, const BIGNUM **priv_key); - -static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) -{ - d->pub_key = pub_key; - d->priv_key = priv_key; - return 1; -} - -static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - d->p = p; - d->q = q; - d->g = g; - return 1; -} - -static INLINE void -DSA_get0_pqg(const DSA *dsa, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -{ - *p = dsa->p; - *q = dsa->q; - *g = dsa->g; -} - -static INLINE void -DSA_get0_key(const DSA *dsa, const BIGNUM **pub_key, const BIGNUM **priv_key) -{ - if (pub_key) *pub_key = dsa->pub_key; - if (priv_key) *priv_key = dsa->priv_key; -} - - - -static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key); -static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); -static INLINE int DH_set_length(DH *dh, long length); -static INLINE void DH_get0_pqg(const DH *dh, - const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); -static INLINE void DH_get0_key(const DH *dh, - const BIGNUM **pub_key, const BIGNUM **priv_key); - -static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) -{ - dh->pub_key = pub_key; - dh->priv_key = priv_key; - return 1; -} - -static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - dh->p = p; - dh->q = q; - dh->g = g; - return 1; -} - -static INLINE int DH_set_length(DH *dh, long length) -{ - dh->length = length; - return 1; -} - - - -static INLINE void -DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -{ - *p = dh->p; - *q = dh->q; - *g = dh->g; -} - -static INLINE void -DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) -{ - if (pub_key) *pub_key = dh->pub_key; - if (priv_key) *priv_key = dh->priv_key; -} - -#else /* End of compatibility definitions. */ - -#define HAVE_OPAQUE_BN_GENCB - -#endif +#include "common.h" + +#include "aead.h" +#include "aes.h" +#include "algorithms.h" +#include "api_ng.h" +#include "block.h" +#include "bn.h" +#include "chacha20.h" +#include "cipher.h" +#include "cmac.h" +#include "dh.h" +#include "digest.h" +#include "dss.h" +#include "ec.h" +#include "ecdh.h" +#include "eddsa.h" +#include "engine.h" +#include "evp.h" +#include "fips.h" +#include "hash.h" +#include "hmac.h" +#include "info.h" +#include "math.h" +#include "pkey.h" +#include "poly1305.h" +#include "rand.h" +#include "rc4.h" +#include "rsa.h" +#include "srp.h" /* NIF interface declarations */ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info); static void unload(ErlNifEnv* env, void* priv_data); -/* The NIFs: */ -static ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM strong_rand_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM pkey_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM srp_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM evp_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -#ifdef HAVE_GCM_EVP_DECRYPT_BUG -static ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -#endif - -static ERL_NIF_TERM chacha20_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM chacha20_stream_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_get_name_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -/* helpers */ -static void init_algorithms_types(ErlNifEnv*); -static void init_digest_types(ErlNifEnv* env); -static void init_cipher_types(ErlNifEnv* env); -#ifdef HAVE_EC -static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg); -static int term2point(ErlNifEnv* env, ERL_NIF_TERM term, - EC_GROUP *group, EC_POINT **pptr); -#endif -static ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn); - -#ifdef HAS_ENGINE_SUPPORT -static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i); -static int zero_terminate(ErlNifBinary bin, char **buf); -#endif - static int library_refc = 0; /* number of users of this dynamic library */ static int library_initialized = 0; static ErlNifFunc nif_funcs[] = { - {"info_lib", 0, info_lib}, - {"info_fips", 0, info_fips}, - {"enable_fips_mode", 1, enable_fips_mode}, - {"algorithms", 0, algorithms}, - {"hash_nif", 2, hash_nif}, - {"hash_init_nif", 1, hash_init_nif}, - {"hash_update_nif", 2, hash_update_nif}, - {"hash_final_nif", 1, hash_final_nif}, - {"hmac_nif", 3, hmac_nif}, - {"hmac_nif", 4, hmac_nif}, - {"hmac_init_nif", 2, hmac_init_nif}, - {"hmac_update_nif", 2, hmac_update_nif}, - {"hmac_final_nif", 1, hmac_final_nif}, - {"hmac_final_nif", 2, hmac_final_nif}, - {"cmac_nif", 3, cmac_nif}, - {"block_crypt_nif", 5, block_crypt_nif}, - {"block_crypt_nif", 4, block_crypt_nif}, - {"aes_ige_crypt_nif", 4, aes_ige_crypt_nif}, - {"aes_ctr_stream_init", 2, aes_ctr_stream_init}, - {"aes_ctr_stream_encrypt", 2, aes_ctr_stream_encrypt}, - {"aes_ctr_stream_decrypt", 2, aes_ctr_stream_encrypt}, - {"strong_rand_bytes_nif", 1, strong_rand_bytes_nif}, - {"strong_rand_range_nif", 1, strong_rand_range_nif}, - {"rand_uniform_nif", 2, rand_uniform_nif}, - {"mod_exp_nif", 4, mod_exp_nif}, - {"do_exor", 2, do_exor}, - {"rc4_set_key", 1, rc4_set_key}, - {"rc4_encrypt_with_state", 2, rc4_encrypt_with_state}, - {"pkey_sign_nif", 5, pkey_sign_nif}, - {"pkey_verify_nif", 6, pkey_verify_nif}, - {"pkey_crypt_nif", 6, pkey_crypt_nif}, - {"rsa_generate_key_nif", 2, rsa_generate_key_nif}, - {"dh_generate_key_nif", 4, dh_generate_key_nif}, - {"dh_compute_key_nif", 3, dh_compute_key_nif}, - {"evp_compute_key_nif", 3, evp_compute_key_nif}, - {"evp_generate_key_nif", 1, evp_generate_key_nif}, - {"privkey_to_pubkey_nif", 2, privkey_to_pubkey_nif}, - {"srp_value_B_nif", 5, srp_value_B_nif}, - {"srp_user_secret_nif", 7, srp_user_secret_nif}, - {"srp_host_secret_nif", 5, srp_host_secret_nif}, - - {"ec_key_generate", 2, ec_key_generate}, - {"ecdh_compute_key_nif", 3, ecdh_compute_key_nif}, - - {"rand_seed_nif", 1, rand_seed_nif}, - - {"aead_encrypt", 6, aead_encrypt}, - {"aead_decrypt", 6, aead_decrypt}, - - {"chacha20_stream_init", 2, chacha20_stream_init}, - {"chacha20_stream_encrypt", 2, chacha20_stream_crypt}, - {"chacha20_stream_decrypt", 2, chacha20_stream_crypt}, - - {"poly1305_nif", 2, poly1305_nif}, - - {"engine_by_id_nif", 1, engine_by_id_nif}, - {"engine_init_nif", 1, engine_init_nif}, - {"engine_finish_nif", 1, engine_finish_nif}, - {"engine_free_nif", 1, engine_free_nif}, - {"engine_load_dynamic_nif", 0, engine_load_dynamic_nif}, - {"engine_ctrl_cmd_strings_nif", 3, engine_ctrl_cmd_strings_nif}, - {"engine_register_nif", 2, engine_register_nif}, - {"engine_unregister_nif", 2, engine_unregister_nif}, - {"engine_add_nif", 1, engine_add_nif}, - {"engine_remove_nif", 1, engine_remove_nif}, - {"engine_get_first_nif", 0, engine_get_first_nif}, - {"engine_get_next_nif", 1, engine_get_next_nif}, - {"engine_get_id_nif", 1, engine_get_id_nif}, - {"engine_get_name_nif", 1, engine_get_name_nif}, - {"engine_get_all_methods_nif", 0, engine_get_all_methods_nif} - + {"info_lib", 0, info_lib, 0}, + {"info_fips", 0, info_fips, 0}, + {"enable_fips_mode", 1, enable_fips_mode, 0}, + {"algorithms", 0, algorithms, 0}, + {"hash_nif", 2, hash_nif, 0}, + {"hash_init_nif", 1, hash_init_nif, 0}, + {"hash_update_nif", 2, hash_update_nif, 0}, + {"hash_final_nif", 1, hash_final_nif, 0}, + {"hmac_nif", 3, hmac_nif, 0}, + {"hmac_nif", 4, hmac_nif, 0}, + {"hmac_init_nif", 2, hmac_init_nif, 0}, + {"hmac_update_nif", 2, hmac_update_nif, 0}, + {"hmac_final_nif", 1, hmac_final_nif, 0}, + {"hmac_final_nif", 2, hmac_final_nif, 0}, + {"cmac_nif", 3, cmac_nif, 0}, + {"block_crypt_nif", 5, block_crypt_nif, 0}, + {"block_crypt_nif", 4, block_crypt_nif, 0}, + {"aes_ige_crypt_nif", 4, aes_ige_crypt_nif, 0}, + {"aes_ctr_stream_init", 2, aes_ctr_stream_init, 0}, + {"aes_ctr_stream_encrypt", 2, aes_ctr_stream_encrypt, 0}, + {"aes_ctr_stream_decrypt", 2, aes_ctr_stream_encrypt, 0}, + {"ng_crypto_init_nif", 4, ng_crypto_init_nif, 0}, + {"ng_crypto_update_nif", 2, ng_crypto_update_nif, 0}, + {"ng_crypto_update_nif", 3, ng_crypto_update_nif, 0}, + {"strong_rand_bytes_nif", 1, strong_rand_bytes_nif, 0}, + {"strong_rand_range_nif", 1, strong_rand_range_nif, 0}, + {"rand_uniform_nif", 2, rand_uniform_nif, 0}, + {"mod_exp_nif", 4, mod_exp_nif, 0}, + {"do_exor", 2, do_exor, 0}, + {"rc4_set_key", 1, rc4_set_key, 0}, + {"rc4_encrypt_with_state", 2, rc4_encrypt_with_state, 0}, + {"pkey_sign_nif", 5, pkey_sign_nif, 0}, + {"pkey_verify_nif", 6, pkey_verify_nif, 0}, + {"pkey_crypt_nif", 6, pkey_crypt_nif, 0}, + {"rsa_generate_key_nif", 2, rsa_generate_key_nif, 0}, + {"dh_generate_key_nif", 4, dh_generate_key_nif, 0}, + {"dh_compute_key_nif", 3, dh_compute_key_nif, 0}, + {"evp_compute_key_nif", 3, evp_compute_key_nif, 0}, + {"evp_generate_key_nif", 1, evp_generate_key_nif, 0}, + {"privkey_to_pubkey_nif", 2, privkey_to_pubkey_nif, 0}, + {"srp_value_B_nif", 5, srp_value_B_nif, 0}, + {"srp_user_secret_nif", 7, srp_user_secret_nif, 0}, + {"srp_host_secret_nif", 5, srp_host_secret_nif, 0}, + + {"ec_key_generate", 2, ec_key_generate, 0}, + {"ecdh_compute_key_nif", 3, ecdh_compute_key_nif, 0}, + + {"rand_seed_nif", 1, rand_seed_nif, 0}, + + {"aead_encrypt", 6, aead_encrypt, 0}, + {"aead_decrypt", 6, aead_decrypt, 0}, + + {"chacha20_stream_init", 2, chacha20_stream_init, 0}, + {"chacha20_stream_encrypt", 2, chacha20_stream_crypt, 0}, + {"chacha20_stream_decrypt", 2, chacha20_stream_crypt, 0}, + + {"poly1305_nif", 2, poly1305_nif, 0}, + + {"engine_by_id_nif", 1, engine_by_id_nif, 0}, + {"engine_init_nif", 1, engine_init_nif, 0}, + {"engine_finish_nif", 1, engine_finish_nif, 0}, + {"engine_free_nif", 1, engine_free_nif, 0}, + {"engine_load_dynamic_nif", 0, engine_load_dynamic_nif, 0}, + {"engine_ctrl_cmd_strings_nif", 3, engine_ctrl_cmd_strings_nif, 0}, + {"engine_register_nif", 2, engine_register_nif, 0}, + {"engine_unregister_nif", 2, engine_unregister_nif, 0}, + {"engine_add_nif", 1, engine_add_nif, 0}, + {"engine_remove_nif", 1, engine_remove_nif, 0}, + {"engine_get_first_nif", 0, engine_get_first_nif, 0}, + {"engine_get_next_nif", 1, engine_get_next_nif, 0}, + {"engine_get_id_nif", 1, engine_get_id_nif, 0}, + {"engine_get_name_nif", 1, engine_get_name_nif, 0}, + {"engine_get_all_methods_nif", 0, engine_get_all_methods_nif, 0} }; ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload) -#define MD5_CTX_LEN (sizeof(MD5_CTX)) -#define MD4_CTX_LEN (sizeof(MD4_CTX)) -#define RIPEMD160_CTX_LEN (sizeof(RIPEMD160_CTX)) - - -static ERL_NIF_TERM atom_true; -static ERL_NIF_TERM atom_false; -static ERL_NIF_TERM atom_sha; -static ERL_NIF_TERM atom_error; -static ERL_NIF_TERM atom_rsa_pkcs1_padding; -static ERL_NIF_TERM atom_rsa_pkcs1_oaep_padding; -static ERL_NIF_TERM atom_rsa_no_padding; -static ERL_NIF_TERM atom_signature_md; -static ERL_NIF_TERM atom_undefined; - -static ERL_NIF_TERM atom_ok; -static ERL_NIF_TERM atom_not_prime; -static ERL_NIF_TERM atom_not_strong_prime; -static ERL_NIF_TERM atom_unable_to_check_generator; -static ERL_NIF_TERM atom_not_suitable_generator; -static ERL_NIF_TERM atom_check_failed; -static ERL_NIF_TERM atom_unknown; -static ERL_NIF_TERM atom_none; -static ERL_NIF_TERM atom_notsup; -static ERL_NIF_TERM atom_digest; -#ifdef FIPS_SUPPORT -static ERL_NIF_TERM atom_enabled; -static ERL_NIF_TERM atom_not_enabled; -#else -static ERL_NIF_TERM atom_not_supported; -#endif - -#if defined(HAVE_EC) -static ERL_NIF_TERM atom_ec; -static ERL_NIF_TERM atom_prime_field; -static ERL_NIF_TERM atom_characteristic_two_field; -static ERL_NIF_TERM atom_tpbasis; -static ERL_NIF_TERM atom_ppbasis; -static ERL_NIF_TERM atom_onbasis; -#endif - -static ERL_NIF_TERM atom_aes_cfb8; -static ERL_NIF_TERM atom_aes_cfb128; -#ifdef HAVE_GCM -static ERL_NIF_TERM atom_aes_gcm; -#endif -#ifdef HAVE_CCM -static ERL_NIF_TERM atom_aes_ccm; -#endif -#ifdef HAVE_CHACHA20_POLY1305 -static ERL_NIF_TERM atom_chacha20_poly1305; -#endif -#ifdef HAVE_ECB_IVEC_BUG -static ERL_NIF_TERM atom_aes_ecb; -static ERL_NIF_TERM atom_des_ecb; -static ERL_NIF_TERM atom_blowfish_ecb; -#endif - -static ERL_NIF_TERM atom_rsa; -static ERL_NIF_TERM atom_dss; -static ERL_NIF_TERM atom_ecdsa; - -#ifdef HAVE_ED_CURVE_DH -static ERL_NIF_TERM atom_x25519; -static ERL_NIF_TERM atom_x448; -#endif - -static ERL_NIF_TERM atom_eddsa; -#ifdef HAVE_EDDSA -static ERL_NIF_TERM atom_ed25519; -static ERL_NIF_TERM atom_ed448; -#endif - -static ERL_NIF_TERM atom_rsa_mgf1_md; -static ERL_NIF_TERM atom_rsa_oaep_label; -static ERL_NIF_TERM atom_rsa_oaep_md; -static ERL_NIF_TERM atom_rsa_pad; /* backwards compatibility */ -static ERL_NIF_TERM atom_rsa_padding; -static ERL_NIF_TERM atom_rsa_pkcs1_pss_padding; -#ifdef HAVE_RSA_SSLV23_PADDING -static ERL_NIF_TERM atom_rsa_sslv23_padding; -#endif -static ERL_NIF_TERM atom_rsa_x931_padding; -static ERL_NIF_TERM atom_rsa_pss_saltlen; -static ERL_NIF_TERM atom_sha224; -static ERL_NIF_TERM atom_sha256; -static ERL_NIF_TERM atom_sha384; -static ERL_NIF_TERM atom_sha512; -static ERL_NIF_TERM atom_sha3_224; -static ERL_NIF_TERM atom_sha3_256; -static ERL_NIF_TERM atom_sha3_384; -static ERL_NIF_TERM atom_sha3_512; -static ERL_NIF_TERM atom_md5; -static ERL_NIF_TERM atom_ripemd160; - -#ifdef HAS_ENGINE_SUPPORT -static ERL_NIF_TERM atom_bad_engine_method; -static ERL_NIF_TERM atom_bad_engine_id; -static ERL_NIF_TERM atom_ctrl_cmd_failed; -static ERL_NIF_TERM atom_engine_init_failed; -static ERL_NIF_TERM atom_register_engine_failed; -static ERL_NIF_TERM atom_add_engine_failed; -static ERL_NIF_TERM atom_remove_engine_failed; -static ERL_NIF_TERM atom_engine_method_not_supported; - -static ERL_NIF_TERM atom_engine_method_rsa; -static ERL_NIF_TERM atom_engine_method_dsa; -static ERL_NIF_TERM atom_engine_method_dh; -static ERL_NIF_TERM atom_engine_method_rand; -static ERL_NIF_TERM atom_engine_method_ecdh; -static ERL_NIF_TERM atom_engine_method_ecdsa; -static ERL_NIF_TERM atom_engine_method_ciphers; -static ERL_NIF_TERM atom_engine_method_digests; -static ERL_NIF_TERM atom_engine_method_store; -static ERL_NIF_TERM atom_engine_method_pkey_meths; -static ERL_NIF_TERM atom_engine_method_pkey_asn1_meths; -static ERL_NIF_TERM atom_engine_method_ec; - -static ERL_NIF_TERM atom_engine; -static ERL_NIF_TERM atom_key_id; -static ERL_NIF_TERM atom_password; -#endif - -static ErlNifResourceType* hmac_context_rtype; -struct hmac_context -{ - ErlNifMutex* mtx; - int alive; - HMAC_CTX* ctx; -}; -static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context*); - -struct digest_type_t { - union { - const char* str; /* before init, NULL for end-of-table */ - ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ - }type; - union { - const EVP_MD* (*funcp)(void); /* before init, NULL if notsup */ - const EVP_MD* p; /* after init, NULL if notsup */ - }md; -}; - -static struct digest_type_t digest_types[] = -{ - {{"md4"}, {&EVP_md4}}, - {{"md5"}, {&EVP_md5}}, - {{"ripemd160"}, {&EVP_ripemd160}}, - {{"sha"}, {&EVP_sha1}}, - {{"sha224"}, -#ifdef HAVE_SHA224 - {&EVP_sha224} -#else - {NULL} -#endif - }, - {{"sha256"}, -#ifdef HAVE_SHA256 - {&EVP_sha256} -#else - {NULL} -#endif - }, - {{"sha384"}, -#ifdef HAVE_SHA384 - {&EVP_sha384} -#else - {NULL} -#endif - }, - {{"sha512"}, -#ifdef HAVE_SHA512 - {&EVP_sha512} -#else - {NULL} -#endif - }, - {{"sha3_224"}, -#ifdef HAVE_SHA3_224 - {&EVP_sha3_224} -#else - {NULL} -#endif - }, - {{"sha3_256"}, -#ifdef HAVE_SHA3_256 - {&EVP_sha3_256} -#else - {NULL} -#endif - }, - {{"sha3_384"}, -#ifdef HAVE_SHA3_384 - {&EVP_sha3_384} -#else - {NULL} -#endif - }, - {{"sha3_512"}, -#ifdef HAVE_SHA3_512 - {&EVP_sha3_512} -#else - {NULL} -#endif - }, - - {{NULL}} -}; - -static struct digest_type_t* get_digest_type(ERL_NIF_TERM type); - -struct cipher_type_t { - union { - const char* str; /* before init */ - ERL_NIF_TERM atom; /* after init */ - }type; - union { - const EVP_CIPHER* (*funcp)(void); /* before init, NULL if notsup */ - const EVP_CIPHER* p; /* after init, NULL if notsup */ - }cipher; - const size_t key_len; /* != 0 to also match on key_len */ -}; - -#ifdef OPENSSL_NO_DES -#define COND_NO_DES_PTR(Ptr) (NULL) -#else -#define COND_NO_DES_PTR(Ptr) (Ptr) -#endif - -static struct cipher_type_t cipher_types[] = -{ - {{"rc2_cbc"}, -#ifndef OPENSSL_NO_RC2 - {&EVP_rc2_cbc} -#else - {NULL} -#endif - }, - {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}}, - {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}}, - {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}}, - {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}}, - {{"des_ede3_cbf"}, /* Misspelled, retained */ -#ifdef HAVE_DES_ede3_cfb_encrypt - {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} -#else - {NULL} -#endif - }, - {{"des_ede3_cfb"}, -#ifdef HAVE_DES_ede3_cfb_encrypt - {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} -#else - {NULL} -#endif - }, - {{"blowfish_cbc"}, {&EVP_bf_cbc}}, - {{"blowfish_cfb64"}, {&EVP_bf_cfb64}}, - {{"blowfish_ofb64"}, {&EVP_bf_ofb}}, - {{"blowfish_ecb"}, {&EVP_bf_ecb}}, - {{"aes_cbc"}, {&EVP_aes_128_cbc}, 16}, - {{"aes_cbc"}, {&EVP_aes_192_cbc}, 24}, - {{"aes_cbc"}, {&EVP_aes_256_cbc}, 32}, - {{"aes_cbc128"}, {&EVP_aes_128_cbc}}, - {{"aes_cbc256"}, {&EVP_aes_256_cbc}}, - {{"aes_cfb8"}, {&EVP_aes_128_cfb8}}, - {{"aes_cfb128"}, {&EVP_aes_128_cfb128}}, - {{"aes_ecb"}, {&EVP_aes_128_ecb}, 16}, - {{"aes_ecb"}, {&EVP_aes_192_ecb}, 24}, - {{"aes_ecb"}, {&EVP_aes_256_ecb}, 32}, - {{NULL}} -}; - -static struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len); - - -/* -#define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n") -#define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1) -#define PRINTF_ERR2(FMT, A1, A2) enif_fprintf(stderr, FMT "\n", A1, A2) -*/ - -#define PRINTF_ERR0(FMT) -#define PRINTF_ERR1(FMT,A1) -#define PRINTF_ERR2(FMT,A1,A2) - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) -/* Define resource types for OpenSSL context structures. */ -static ErlNifResourceType* evp_md_ctx_rtype; -struct evp_md_ctx { - EVP_MD_CTX* ctx; -}; -static void evp_md_ctx_dtor(ErlNifEnv* env, struct evp_md_ctx *ctx) { - EVP_MD_CTX_free(ctx->ctx); -} -#endif - -#ifdef HAVE_EVP_AES_CTR -static ErlNifResourceType* evp_cipher_ctx_rtype; -struct evp_cipher_ctx { - EVP_CIPHER_CTX* ctx; -}; -static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) { - EVP_CIPHER_CTX_free(ctx->ctx); -} -#endif - -// Engine -#ifdef HAS_ENGINE_SUPPORT -static ErlNifResourceType* engine_ctx_rtype; -struct engine_ctx { - ENGINE *engine; - char *id; -}; -static void engine_ctx_dtor(ErlNifEnv* env, struct engine_ctx* ctx) { - PRINTF_ERR0("engine_ctx_dtor"); - if(ctx->id) { - PRINTF_ERR1(" non empty ctx->id=%s", ctx->id); - enif_free(ctx->id); - } else - PRINTF_ERR0(" empty ctx->id=NULL"); -} -#endif static int verify_lib_version(void) { @@ -1015,46 +156,6 @@ static int verify_lib_version(void) return 1; } -#ifdef FIPS_SUPPORT -/* In FIPS mode non-FIPS algorithms are disabled and return badarg. */ -#define CHECK_NO_FIPS_MODE() { if (FIPS_mode()) return atom_notsup; } -#else -#define CHECK_NO_FIPS_MODE() -#endif - -#ifdef HAVE_DYNAMIC_CRYPTO_LIB - -# if defined(DEBUG) -static char crypto_callback_name[] = "crypto_callback.debug"; -# elif defined(VALGRIND) -static char crypto_callback_name[] = "crypto_callback.valgrind"; -# else -static char crypto_callback_name[] = "crypto_callback"; -# endif - -static int change_basename(ErlNifBinary* bin, char* buf, int bufsz, const char* newfile) -{ - int i; - - for (i = bin->size; i > 0; i--) { - if (bin->data[i-1] == '/') - break; - } - if (i + strlen(newfile) >= bufsz) { - PRINTF_ERR0("CRYPTO: lib name too long"); - return 0; - } - memcpy(buf, bin->data, i); - strcpy(buf+i, newfile); - return 1; -} - -static void error_handler(void* null, const char* errstr) -{ - PRINTF_ERR1("CRYPTO LOADING ERROR: '%s'", errstr); -} -#endif /* HAVE_DYNAMIC_CRYPTO_LIB */ - static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) { #ifdef OPENSSL_THREADS @@ -1068,59 +169,37 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) int vernum; ErlNifBinary lib_bin; char lib_buf[1000]; +#ifdef HAVE_DYNAMIC_CRYPTO_LIB + void *handle; +#endif if (!verify_lib_version()) return __LINE__; /* load_info: {302, <<"/full/path/of/this/library">>,true|false} */ - if (!enif_get_tuple(env, load_info, &tpl_arity, &tpl_array) - || tpl_arity != 3 - || !enif_get_int(env, tpl_array[0], &vernum) - || vernum != 302 - || !enif_inspect_binary(env, tpl_array[1], &lib_bin)) { - - PRINTF_ERR1("CRYPTO: Invalid load_info '%T'", load_info); - return __LINE__; - } + if (!enif_get_tuple(env, load_info, &tpl_arity, &tpl_array)) + return __LINE__; + if (tpl_arity != 3) + return __LINE__; + if (!enif_get_int(env, tpl_array[0], &vernum)) + return __LINE__; + if (vernum != 302) + return __LINE__; + if (!enif_inspect_binary(env, tpl_array[1], &lib_bin)) + return __LINE__; - hmac_context_rtype = enif_open_resource_type(env, NULL, "hmac_context", - (ErlNifResourceDtor*) hmac_context_dtor, - ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, - NULL); - if (!hmac_context_rtype) { - PRINTF_ERR0("CRYPTO: Could not open resource type 'hmac_context'"); + if (!init_hmac_ctx(env)) { return __LINE__; } -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) - evp_md_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_MD_CTX", - (ErlNifResourceDtor*) evp_md_ctx_dtor, - ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, - NULL); - if (!evp_md_ctx_rtype) { - PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_MD_CTX'"); + if (!init_hash_ctx(env)) { return __LINE__; } -#endif -#ifdef HAVE_EVP_AES_CTR - evp_cipher_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_CIPHER_CTX", - (ErlNifResourceDtor*) evp_cipher_ctx_dtor, - ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, - NULL); - if (!evp_cipher_ctx_rtype) { - PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_CIPHER_CTX'"); + if (!init_cipher_ctx(env)) { return __LINE__; } -#endif -#ifdef HAS_ENGINE_SUPPORT - engine_ctx_rtype = enif_open_resource_type(env, NULL, "ENGINE_CTX", - (ErlNifResourceDtor*) engine_ctx_dtor, - ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, - NULL); - if (!engine_ctx_rtype) { - PRINTF_ERR0("CRYPTO: Could not open resource type 'ENGINE_CTX'"); + if (!init_engine_ctx(env)) { return __LINE__; } -#endif if (library_initialized) { /* Repeated loading of this library (module upgrade). @@ -1129,149 +208,18 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) return 0; } - atom_true = enif_make_atom(env,"true"); - atom_false = enif_make_atom(env,"false"); - /* Enter FIPS mode */ - if (tpl_array[2] == atom_true) { -#ifdef FIPS_SUPPORT - if (!FIPS_mode_set(1)) { -#else - { -#endif - PRINTF_ERR0("CRYPTO: Could not setup FIPS mode"); - return 0; - } - } else if (tpl_array[2] != atom_false) { - PRINTF_ERR1("CRYPTO: Invalid load_info '%T'", load_info); - return 0; + if (!init_atoms(env, tpl_array[2], load_info)) { + return __LINE__; } - atom_sha = enif_make_atom(env,"sha"); - atom_error = enif_make_atom(env,"error"); - atom_rsa_pkcs1_padding = enif_make_atom(env,"rsa_pkcs1_padding"); - atom_rsa_pkcs1_oaep_padding = enif_make_atom(env,"rsa_pkcs1_oaep_padding"); - atom_rsa_no_padding = enif_make_atom(env,"rsa_no_padding"); - atom_signature_md = enif_make_atom(env,"signature_md"); - atom_undefined = enif_make_atom(env,"undefined"); - atom_ok = enif_make_atom(env,"ok"); - atom_not_prime = enif_make_atom(env,"not_prime"); - atom_not_strong_prime = enif_make_atom(env,"not_strong_prime"); - atom_unable_to_check_generator = enif_make_atom(env,"unable_to_check_generator"); - atom_not_suitable_generator = enif_make_atom(env,"not_suitable_generator"); - atom_check_failed = enif_make_atom(env,"check_failed"); - atom_unknown = enif_make_atom(env,"unknown"); - atom_none = enif_make_atom(env,"none"); - atom_notsup = enif_make_atom(env,"notsup"); - atom_digest = enif_make_atom(env,"digest"); - -#if defined(HAVE_EC) - atom_ec = enif_make_atom(env,"ec"); - atom_prime_field = enif_make_atom(env,"prime_field"); - atom_characteristic_two_field = enif_make_atom(env,"characteristic_two_field"); - atom_tpbasis = enif_make_atom(env,"tpbasis"); - atom_ppbasis = enif_make_atom(env,"ppbasis"); - atom_onbasis = enif_make_atom(env,"onbasis"); -#endif - - atom_aes_cfb8 = enif_make_atom(env, "aes_cfb8"); - atom_aes_cfb128 = enif_make_atom(env, "aes_cfb128"); -#ifdef HAVE_GCM - atom_aes_gcm = enif_make_atom(env, "aes_gcm"); -#endif -#ifdef HAVE_CCM - atom_aes_ccm = enif_make_atom(env, "aes_ccm"); -#endif -#ifdef HAVE_CHACHA20_POLY1305 - atom_chacha20_poly1305 = enif_make_atom(env,"chacha20_poly1305"); -#endif -#ifdef HAVE_ECB_IVEC_BUG - atom_aes_ecb = enif_make_atom(env, "aes_ecb"); - atom_des_ecb = enif_make_atom(env, "des_ecb"); - atom_blowfish_ecb = enif_make_atom(env, "blowfish_ecb"); -#endif - -#ifdef FIPS_SUPPORT - atom_enabled = enif_make_atom(env,"enabled"); - atom_not_enabled = enif_make_atom(env,"not_enabled"); -#else - atom_not_supported = enif_make_atom(env,"not_supported"); -#endif - atom_rsa = enif_make_atom(env,"rsa"); - atom_dss = enif_make_atom(env,"dss"); - atom_ecdsa = enif_make_atom(env,"ecdsa"); -#ifdef HAVE_ED_CURVE_DH - atom_x25519 = enif_make_atom(env,"x25519"); - atom_x448 = enif_make_atom(env,"x448"); -#endif - atom_eddsa = enif_make_atom(env,"eddsa"); -#ifdef HAVE_EDDSA - atom_ed25519 = enif_make_atom(env,"ed25519"); - atom_ed448 = enif_make_atom(env,"ed448"); -#endif - atom_rsa_mgf1_md = enif_make_atom(env,"rsa_mgf1_md"); - atom_rsa_oaep_label = enif_make_atom(env,"rsa_oaep_label"); - atom_rsa_oaep_md = enif_make_atom(env,"rsa_oaep_md"); - atom_rsa_pad = enif_make_atom(env,"rsa_pad"); /* backwards compatibility */ - atom_rsa_padding = enif_make_atom(env,"rsa_padding"); - atom_rsa_pkcs1_pss_padding = enif_make_atom(env,"rsa_pkcs1_pss_padding"); -#ifdef HAVE_RSA_SSLV23_PADDING - atom_rsa_sslv23_padding = enif_make_atom(env,"rsa_sslv23_padding"); -#endif - atom_rsa_x931_padding = enif_make_atom(env,"rsa_x931_padding"); - atom_rsa_pss_saltlen = enif_make_atom(env,"rsa_pss_saltlen"); - atom_sha224 = enif_make_atom(env,"sha224"); - atom_sha256 = enif_make_atom(env,"sha256"); - atom_sha384 = enif_make_atom(env,"sha384"); - atom_sha512 = enif_make_atom(env,"sha512"); - atom_sha3_224 = enif_make_atom(env,"sha3_224"); - atom_sha3_256 = enif_make_atom(env,"sha3_256"); - atom_sha3_384 = enif_make_atom(env,"sha3_384"); - atom_sha3_512 = enif_make_atom(env,"sha3_512"); - atom_md5 = enif_make_atom(env,"md5"); - atom_ripemd160 = enif_make_atom(env,"ripemd160"); - -#ifdef HAS_ENGINE_SUPPORT - atom_bad_engine_method = enif_make_atom(env,"bad_engine_method"); - atom_bad_engine_id = enif_make_atom(env,"bad_engine_id"); - atom_ctrl_cmd_failed = enif_make_atom(env,"ctrl_cmd_failed"); - atom_engine_init_failed = enif_make_atom(env,"engine_init_failed"); - atom_engine_method_not_supported = enif_make_atom(env,"engine_method_not_supported"); - atom_add_engine_failed = enif_make_atom(env,"add_engine_failed"); - atom_remove_engine_failed = enif_make_atom(env,"remove_engine_failed"); - - atom_engine_method_rsa = enif_make_atom(env,"engine_method_rsa"); - atom_engine_method_dsa = enif_make_atom(env,"engine_method_dsa"); - atom_engine_method_dh = enif_make_atom(env,"engine_method_dh"); - atom_engine_method_rand = enif_make_atom(env,"engine_method_rand"); - atom_engine_method_ecdh = enif_make_atom(env,"engine_method_ecdh"); - atom_engine_method_ecdsa = enif_make_atom(env,"engine_method_ecdsa"); - atom_engine_method_store = enif_make_atom(env,"engine_method_store"); - atom_engine_method_ciphers = enif_make_atom(env,"engine_method_ciphers"); - atom_engine_method_digests = enif_make_atom(env,"engine_method_digests"); - atom_engine_method_pkey_meths = enif_make_atom(env,"engine_method_pkey_meths"); - atom_engine_method_pkey_asn1_meths = enif_make_atom(env,"engine_method_pkey_asn1_meths"); - atom_engine_method_ec = enif_make_atom(env,"engine_method_ec"); - - atom_engine = enif_make_atom(env,"engine"); - atom_key_id = enif_make_atom(env,"key_id"); - atom_password = enif_make_atom(env,"password"); -#endif - - #ifdef HAVE_DYNAMIC_CRYPTO_LIB - { - void* handle; - if (!change_basename(&lib_bin, lib_buf, sizeof(lib_buf), crypto_callback_name)) { - return __LINE__; - } - if (!(handle = enif_dlopen(lib_buf, &error_handler, NULL))) { - return __LINE__; - } - if (!(funcp = (get_crypto_callbacks_t*) enif_dlsym(handle, "get_crypto_callbacks", - &error_handler, NULL))) { - return __LINE__; - } - } + if (!change_basename(&lib_bin, lib_buf, sizeof(lib_buf), crypto_callback_name)) + return __LINE__; + if ((handle = enif_dlopen(lib_buf, &error_handler, NULL)) == NULL) + return __LINE__; + if ((funcp = (get_crypto_callbacks_t*) enif_dlsym(handle, "get_crypto_callbacks", + &error_handler, NULL)) == NULL) + return __LINE__; #else /* !HAVE_DYNAMIC_CRYPTO_LIB */ funcp = &get_crypto_callbacks; #endif @@ -1291,7 +239,10 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) return __LINE__; } - CRYPTO_set_mem_functions(ccb->crypto_alloc, ccb->crypto_realloc, ccb->crypto_free); +#ifdef HAS_CRYPTO_MEM_FUNCTIONS + if (!CRYPTO_set_mem_functions(ccb->crypto_alloc, ccb->crypto_realloc, ccb->crypto_free)) + return __LINE__; +#endif #ifdef OPENSSL_THREADS if (nlocks > 0) { @@ -1345,4806 +296,3 @@ static void unload(ErlNifEnv* env, void* priv_data) { --library_refc; } - -static int algo_hash_cnt, algo_hash_fips_cnt; -static ERL_NIF_TERM algo_hash[12]; /* increase when extending the list */ -static int algo_pubkey_cnt, algo_pubkey_fips_cnt; -static ERL_NIF_TERM algo_pubkey[12]; /* increase when extending the list */ -static int algo_cipher_cnt, algo_cipher_fips_cnt; -static ERL_NIF_TERM algo_cipher[25]; /* increase when extending the list */ -static int algo_mac_cnt, algo_mac_fips_cnt; -static ERL_NIF_TERM algo_mac[3]; /* increase when extending the list */ -static int algo_curve_cnt, algo_curve_fips_cnt; -static ERL_NIF_TERM algo_curve[89]; /* increase when extending the list */ -static int algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; -static ERL_NIF_TERM algo_rsa_opts[11]; /* increase when extending the list */ - -static void init_algorithms_types(ErlNifEnv* env) -{ - // Validated algorithms first - algo_hash_cnt = 0; - algo_hash[algo_hash_cnt++] = atom_sha; -#ifdef HAVE_SHA224 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha224"); -#endif -#ifdef HAVE_SHA256 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha256"); -#endif -#ifdef HAVE_SHA384 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha384"); -#endif -#ifdef HAVE_SHA512 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha512"); -#endif -#ifdef HAVE_SHA3_224 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_224"); -#endif -#ifdef HAVE_SHA3_256 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_256"); -#endif -#ifdef HAVE_SHA3_384 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_384"); -#endif -#ifdef HAVE_SHA3_512 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_512"); -#endif - // Non-validated algorithms follow - algo_hash_fips_cnt = algo_hash_cnt; - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md4"); - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md5"); - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "ripemd160"); - - algo_pubkey_cnt = 0; - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "rsa"); - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dss"); - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dh"); -#if defined(HAVE_EC) -#if !defined(OPENSSL_NO_EC2M) - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ec_gf2m"); -#endif - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdsa"); - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdh"); -#endif - // Non-validated algorithms follow - algo_pubkey_fips_cnt = algo_pubkey_cnt; - // Don't know if Edward curves are fips validated -#if defined(HAVE_EDDSA) - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "eddsa"); -#endif - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp"); - - // Validated algorithms first - algo_cipher_cnt = 0; -#ifndef OPENSSL_NO_DES - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des_ede3"); -#ifdef HAVE_DES_ede3_cfb_encrypt - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbf"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cfb"); -#endif -#endif - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc128"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb8"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb128"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc256"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_ctr"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_ecb"); -#if defined(HAVE_GCM) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_gcm"); -#endif -#if defined(HAVE_CCM) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ccm"); -#endif - // Non-validated algorithms follow - algo_cipher_fips_cnt = algo_cipher_cnt; -#ifdef HAVE_AES_IGE - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ige256"); -#endif -#ifndef OPENSSL_NO_DES - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cfb"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_ecb"); -#endif - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cfb64"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ofb64"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ecb"); -#ifndef OPENSSL_NO_RC2 - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc2_cbc"); -#endif -#ifndef OPENSSL_NO_RC4 - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc4"); -#endif -#if defined(HAVE_CHACHA20_POLY1305) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"chacha20_poly1305"); -#endif -#if defined(HAVE_CHACHA20) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"chacha20"); -#endif - - // Validated algorithms first - algo_mac_cnt = 0; - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"hmac"); -#ifdef HAVE_CMAC - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"cmac"); -#endif -#ifdef HAVE_POLY1305 - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"poly1305"); -#endif - // Non-validated algorithms follow - algo_mac_fips_cnt = algo_mac_cnt; - - // Validated algorithms first - algo_curve_cnt = 0; -#if defined(HAVE_EC) - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp192r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp192k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp224k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp224r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp256k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp256r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp384r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp521r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime256v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls7"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls9"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls12"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP160r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP160t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP192r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP192t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP224r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP224t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP256r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP256t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP320r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP320t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP384r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP384t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP512r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP512t1"); -#if !defined(OPENSSL_NO_EC2M) - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect193r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect193r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect233k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect233r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect239k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect283k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect283r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect409k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect409r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect571k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect571r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb176v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb208w1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb272w1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb304w1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb359v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb368w1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb431r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls5"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls10"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls11"); -#endif -#endif - // Non-validated algorithms follow - algo_curve_fips_cnt = algo_curve_cnt; -#if defined(HAVE_EC) - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp112r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp112r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp128r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp128r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls6"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls8"); -#if !defined(OPENSSL_NO_EC2M) - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect113r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect113r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect131r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect131r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls4"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ipsec3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ipsec4"); -#endif -#endif - //-- -#ifdef HAVE_EDDSA - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ed25519"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ed448"); -#endif -#ifdef HAVE_ED_CURVE_DH - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"x25519"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"x448"); -#endif - - // Validated algorithms first - algo_rsa_opts_cnt = 0; -#ifdef HAS_EVP_PKEY_CTX -# ifdef HAVE_RSA_PKCS1_PSS_PADDING - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_pss_padding"); - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pss_saltlen"); -# endif -# ifdef HAVE_RSA_MGF1_MD - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_mgf1_md"); -# endif -# ifdef HAVE_RSA_OAEP_PADDING - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_oaep_padding"); -# endif -# ifdef HAVE_RSA_OAEP_MD - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_oaep_label"); - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_oaep_md"); -# endif - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"signature_md"); -#endif - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_padding"); - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_x931_padding"); -#ifdef HAVE_RSA_SSLV23_PADDING - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_sslv23_padding"); -#endif - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_no_padding"); - algo_rsa_opts_fips_cnt = algo_rsa_opts_cnt; - - - // Check that the max number of algos is updated - ASSERT(algo_hash_cnt <= sizeof(algo_hash)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_cipher_cnt <= sizeof(algo_cipher)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_mac_cnt <= sizeof(algo_mac)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_curve_cnt <= sizeof(algo_curve)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(ERL_NIF_TERM)); -} - -static ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ -#ifdef FIPS_SUPPORT - int fips_mode = FIPS_mode(); - int hash_cnt = fips_mode ? algo_hash_fips_cnt : algo_hash_cnt; - int pubkey_cnt = fips_mode ? algo_pubkey_fips_cnt : algo_pubkey_cnt; - int cipher_cnt = fips_mode ? algo_cipher_fips_cnt : algo_cipher_cnt; - int mac_cnt = fips_mode ? algo_mac_fips_cnt : algo_mac_cnt; - int curve_cnt = fips_mode ? algo_curve_fips_cnt : algo_curve_cnt; - int rsa_opts_cnt = fips_mode ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt; -#else - int hash_cnt = algo_hash_cnt; - int pubkey_cnt = algo_pubkey_cnt; - int cipher_cnt = algo_cipher_cnt; - int mac_cnt = algo_mac_cnt; - int curve_cnt = algo_curve_cnt; - int rsa_opts_cnt = algo_rsa_opts_cnt; -#endif - return enif_make_tuple6(env, - enif_make_list_from_array(env, algo_hash, hash_cnt), - enif_make_list_from_array(env, algo_pubkey, pubkey_cnt), - enif_make_list_from_array(env, algo_cipher, cipher_cnt), - enif_make_list_from_array(env, algo_mac, mac_cnt), - enif_make_list_from_array(env, algo_curve, curve_cnt), - enif_make_list_from_array(env, algo_rsa_opts, rsa_opts_cnt) - ); -} - -static ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - /* [{<<"OpenSSL">>,9470143,<<"OpenSSL 0.9.8k 25 Mar 2009">>}] */ - - static const char libname[] = "OpenSSL"; - unsigned name_sz = strlen(libname); - const char* ver = SSLeay_version(SSLEAY_VERSION); - unsigned ver_sz = strlen(ver); - ERL_NIF_TERM name_term, ver_term; - int ver_num = OPENSSL_VERSION_NUMBER; - /* R16: - * Ignore library version number from SSLeay() and instead show header - * version. Otherwise user might try to call a function that is implemented - * by a newer library but not supported by the headers used at compile time. - * Example: DES_ede3_cfb_encrypt in 0.9.7i but not in 0.9.7d. - * - * Version string is still from library though. - */ - - memcpy(enif_make_new_binary(env, name_sz, &name_term), libname, name_sz); - memcpy(enif_make_new_binary(env, ver_sz, &ver_term), ver, ver_sz); - - return enif_make_list1(env, enif_make_tuple3(env, name_term, - enif_make_int(env, ver_num), - ver_term)); -} - -static ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ -#ifdef FIPS_SUPPORT - return FIPS_mode() ? atom_enabled : atom_not_enabled; -#else - return atom_not_supported; -#endif -} - -static ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Boolean) */ - if (argv[0] == atom_true) { -#ifdef FIPS_SUPPORT - if (FIPS_mode_set(1)) { - return atom_true; - } -#endif - PRINTF_ERR0("CRYPTO: Could not setup FIPS mode"); - return atom_false; - } else if (argv[0] == atom_false) { -#ifdef FIPS_SUPPORT - if (!FIPS_mode_set(0)) { - return atom_false; - } -#endif - return atom_true; - } else { - return enif_make_badarg(env); - } -} - - -#if defined(HAVE_EC) -static ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env) -{ - ERL_NIF_TERM reason; - if (enif_has_pending_exception(env, &reason)) - return reason; /* dummy return value ignored */ - else - return enif_make_badarg(env); -} -#endif - -static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Data) */ - struct digest_type_t *digp = NULL; - const EVP_MD *md; - ErlNifBinary data; - ERL_NIF_TERM ret; - unsigned ret_size; - - digp = get_digest_type(argv[0]); - if (!digp || - !enif_inspect_iolist_as_binary(env, argv[1], &data)) { - return enif_make_badarg(env); - } - md = digp->md.p; - if (!md) { - return atom_notsup; - } - - ret_size = (unsigned)EVP_MD_size(md); - ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE); - if (!EVP_Digest(data.data, data.size, - enif_make_new_binary(env, ret_size, &ret), &ret_size, - md, NULL)) { - return atom_notsup; - } - ASSERT(ret_size == (unsigned)EVP_MD_size(md)); - - CONSUME_REDS(env, data); - return ret; -} - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) - -static ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type) */ - struct digest_type_t *digp = NULL; - struct evp_md_ctx *ctx; - ERL_NIF_TERM ret; - - digp = get_digest_type(argv[0]); - if (!digp) { - return enif_make_badarg(env); - } - if (!digp->md.p) { - return atom_notsup; - } - - ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx)); - ctx->ctx = EVP_MD_CTX_new(); - if (!EVP_DigestInit(ctx->ctx, digp->md.p)) { - enif_release_resource(ctx); - return atom_notsup; - } - ret = enif_make_resource(env, ctx); - enif_release_resource(ctx); - return ret; -} -static ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Context, Data) */ - struct evp_md_ctx *ctx, *new_ctx; - ErlNifBinary data; - ERL_NIF_TERM ret; - - if (!enif_get_resource(env, argv[0], evp_md_ctx_rtype, (void**)&ctx) || - !enif_inspect_iolist_as_binary(env, argv[1], &data)) { - return enif_make_badarg(env); - } - - new_ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx)); - new_ctx->ctx = EVP_MD_CTX_new(); - if (!EVP_MD_CTX_copy(new_ctx->ctx, ctx->ctx) || - !EVP_DigestUpdate(new_ctx->ctx, data.data, data.size)) { - enif_release_resource(new_ctx); - return atom_notsup; - } - - ret = enif_make_resource(env, new_ctx); - enif_release_resource(new_ctx); - CONSUME_REDS(env, data); - return ret; -} -static ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Context) */ - struct evp_md_ctx *ctx; - EVP_MD_CTX *new_ctx; - ERL_NIF_TERM ret; - unsigned ret_size; - - if (!enif_get_resource(env, argv[0], evp_md_ctx_rtype, (void**)&ctx)) { - return enif_make_badarg(env); - } - - ret_size = (unsigned)EVP_MD_CTX_size(ctx->ctx); - ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE); - - new_ctx = EVP_MD_CTX_new(); - if (!EVP_MD_CTX_copy(new_ctx, ctx->ctx) || - !EVP_DigestFinal(new_ctx, - enif_make_new_binary(env, ret_size, &ret), - &ret_size)) { - EVP_MD_CTX_free(new_ctx); - return atom_notsup; - } - EVP_MD_CTX_free(new_ctx); - ASSERT(ret_size == (unsigned)EVP_MD_CTX_size(ctx->ctx)); - - return ret; -} - -#else /* if OPENSSL_VERSION_NUMBER < 1.0 */ - -static ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type) */ - typedef int (*init_fun)(unsigned char*); - struct digest_type_t *digp = NULL; - ERL_NIF_TERM ctx; - size_t ctx_size = 0; - init_fun ctx_init = 0; - - digp = get_digest_type(argv[0]); - if (!digp) { - return enif_make_badarg(env); - } - if (!digp->md.p) { - return atom_notsup; - } - - switch (EVP_MD_type(digp->md.p)) - { - case NID_md4: - ctx_size = MD4_CTX_LEN; - ctx_init = (init_fun)(&MD4_Init); - break; - case NID_md5: - ctx_size = MD5_CTX_LEN; - ctx_init = (init_fun)(&MD5_Init); - break; - case NID_ripemd160: - ctx_size = RIPEMD160_CTX_LEN; - ctx_init = (init_fun)(&RIPEMD160_Init); - break; - case NID_sha1: - ctx_size = sizeof(SHA_CTX); - ctx_init = (init_fun)(&SHA1_Init); - break; -#ifdef HAVE_SHA224 - case NID_sha224: - ctx_size = sizeof(SHA256_CTX); - ctx_init = (init_fun)(&SHA224_Init); - break; -#endif -#ifdef HAVE_SHA256 - case NID_sha256: - ctx_size = sizeof(SHA256_CTX); - ctx_init = (init_fun)(&SHA256_Init); - break; -#endif -#ifdef HAVE_SHA384 - case NID_sha384: - ctx_size = sizeof(SHA512_CTX); - ctx_init = (init_fun)(&SHA384_Init); - break; -#endif -#ifdef HAVE_SHA512 - case NID_sha512: - ctx_size = sizeof(SHA512_CTX); - ctx_init = (init_fun)(&SHA512_Init); - break; -#endif - default: - return atom_notsup; - } - ASSERT(ctx_size); - ASSERT(ctx_init); - - ctx_init(enif_make_new_binary(env, ctx_size, &ctx)); - return enif_make_tuple2(env, argv[0], ctx); -} -static ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* ({Type, Context}, Data) */ - typedef int (*update_fun)(unsigned char*, const unsigned char*, size_t); - ERL_NIF_TERM new_ctx; - ErlNifBinary ctx, data; - const ERL_NIF_TERM *tuple; - int arity; - struct digest_type_t *digp = NULL; - unsigned char *ctx_buff; - size_t ctx_size = 0; - update_fun ctx_update = 0; - - if (!enif_get_tuple(env, argv[0], &arity, &tuple) || - arity != 2 || - !(digp = get_digest_type(tuple[0])) || - !enif_inspect_binary(env, tuple[1], &ctx) || - !enif_inspect_iolist_as_binary(env, argv[1], &data)) { - return enif_make_badarg(env); - } - if (!digp->md.p) { - return atom_notsup; - } - - switch (EVP_MD_type(digp->md.p)) - { - case NID_md4: - ctx_size = MD4_CTX_LEN; - ctx_update = (update_fun)(&MD4_Update); - break; - case NID_md5: - ctx_size = MD5_CTX_LEN; - ctx_update = (update_fun)(&MD5_Update); - break; - case NID_ripemd160: - ctx_size = RIPEMD160_CTX_LEN; - ctx_update = (update_fun)(&RIPEMD160_Update); - break; - case NID_sha1: - ctx_size = sizeof(SHA_CTX); - ctx_update = (update_fun)(&SHA1_Update); - break; -#ifdef HAVE_SHA224 - case NID_sha224: - ctx_size = sizeof(SHA256_CTX); - ctx_update = (update_fun)(&SHA224_Update); - break; -#endif -#ifdef HAVE_SHA256 - case NID_sha256: - ctx_size = sizeof(SHA256_CTX); - ctx_update = (update_fun)(&SHA256_Update); - break; -#endif -#ifdef HAVE_SHA384 - case NID_sha384: - ctx_size = sizeof(SHA512_CTX); - ctx_update = (update_fun)(&SHA384_Update); - break; -#endif -#ifdef HAVE_SHA512 - case NID_sha512: - ctx_size = sizeof(SHA512_CTX); - ctx_update = (update_fun)(&SHA512_Update); - break; -#endif - default: - return atom_notsup; - } - ASSERT(ctx_size); - ASSERT(ctx_update); - - if (ctx.size != ctx_size) { - return enif_make_badarg(env); - } - - ctx_buff = enif_make_new_binary(env, ctx_size, &new_ctx); - memcpy(ctx_buff, ctx.data, ctx_size); - ctx_update(ctx_buff, data.data, data.size); - - CONSUME_REDS(env, data); - return enif_make_tuple2(env, tuple[0], new_ctx); -} -static ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* ({Type, Context}) */ - typedef int (*final_fun)(unsigned char*, void*); - ERL_NIF_TERM ret; - ErlNifBinary ctx; - const ERL_NIF_TERM *tuple; - int arity; - struct digest_type_t *digp = NULL; - const EVP_MD *md; - void *new_ctx; - size_t ctx_size = 0; - final_fun ctx_final = 0; - - if (!enif_get_tuple(env, argv[0], &arity, &tuple) || - arity != 2 || - !(digp = get_digest_type(tuple[0])) || - !enif_inspect_binary(env, tuple[1], &ctx)) { - return enif_make_badarg(env); - } - md = digp->md.p; - if (!md) { - return atom_notsup; - } - - - switch (EVP_MD_type(md)) - { - case NID_md4: - ctx_size = MD4_CTX_LEN; - ctx_final = (final_fun)(&MD4_Final); - break; - case NID_md5: - ctx_size = MD5_CTX_LEN; - ctx_final = (final_fun)(&MD5_Final); - break; - case NID_ripemd160: - ctx_size = RIPEMD160_CTX_LEN; - ctx_final = (final_fun)(&RIPEMD160_Final); - break; - case NID_sha1: - ctx_size = sizeof(SHA_CTX); - ctx_final = (final_fun)(&SHA1_Final); - break; -#ifdef HAVE_SHA224 - case NID_sha224: - ctx_size = sizeof(SHA256_CTX); - ctx_final = (final_fun)(&SHA224_Final); - break; -#endif -#ifdef HAVE_SHA256 - case NID_sha256: - ctx_size = sizeof(SHA256_CTX); - ctx_final = (final_fun)(&SHA256_Final); - break; -#endif -#ifdef HAVE_SHA384 - case NID_sha384: - ctx_size = sizeof(SHA512_CTX); - ctx_final = (final_fun)(&SHA384_Final); - break; -#endif -#ifdef HAVE_SHA512 - case NID_sha512: - ctx_size = sizeof(SHA512_CTX); - ctx_final = (final_fun)(&SHA512_Final); - break; -#endif - default: - return atom_notsup; - } - ASSERT(ctx_size); - ASSERT(ctx_final); - - if (ctx.size != ctx_size) { - return enif_make_badarg(env); - } - - new_ctx = enif_alloc(ctx_size); - memcpy(new_ctx, ctx.data, ctx_size); - ctx_final(enif_make_new_binary(env, (size_t)EVP_MD_size(md), &ret), - new_ctx); - enif_free(new_ctx); - - return ret; -} -#endif /* OPENSSL_VERSION_NUMBER < 1.0 */ - - -static ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key, Data) or (Type, Key, Data, MacSize) */ - struct digest_type_t *digp = NULL; - ErlNifBinary key, data; - unsigned char buff[EVP_MAX_MD_SIZE]; - unsigned size = 0, req_size = 0; - ERL_NIF_TERM ret; - - digp = get_digest_type(argv[0]); - if (!digp || - !enif_inspect_iolist_as_binary(env, argv[1], &key) || - !enif_inspect_iolist_as_binary(env, argv[2], &data) || - (argc == 4 && !enif_get_uint(env, argv[3], &req_size))) { - return enif_make_badarg(env); - } - - if (!digp->md.p || - !HMAC(digp->md.p, - key.data, key.size, - data.data, data.size, - buff, &size)) { - return atom_notsup; - } - ASSERT(0 < size && size <= EVP_MAX_MD_SIZE); - CONSUME_REDS(env, data); - - if (argc == 4) { - if (req_size <= size) { - size = req_size; - } - else { - return enif_make_badarg(env); - } - } - memcpy(enif_make_new_binary(env, size, &ret), buff, size); - return ret; -} - -static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) -{ - if (obj->alive) { - HMAC_CTX_free(obj->ctx); - obj->alive = 0; - } - enif_mutex_destroy(obj->mtx); -} - -static ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key) */ - struct digest_type_t *digp = NULL; - ErlNifBinary key; - ERL_NIF_TERM ret; - struct hmac_context *obj; - - digp = get_digest_type(argv[0]); - if (!digp || - !enif_inspect_iolist_as_binary(env, argv[1], &key)) { - return enif_make_badarg(env); - } - if (!digp->md.p) { - return atom_notsup; - } - - obj = enif_alloc_resource(hmac_context_rtype, sizeof(struct hmac_context)); - obj->mtx = enif_mutex_create("crypto.hmac"); - obj->alive = 1; - obj->ctx = HMAC_CTX_new(); -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) - // Check the return value of HMAC_Init: it may fail in FIPS mode - // for disabled algorithms - if (!HMAC_Init_ex(obj->ctx, key.data, key.size, digp->md.p, NULL)) { - enif_release_resource(obj); - return atom_notsup; - } -#else - HMAC_Init_ex(obj->ctx, key.data, key.size, digp->md.p, NULL); -#endif - - ret = enif_make_resource(env, obj); - enif_release_resource(obj); - return ret; -} - -static ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Context, Data) */ - ErlNifBinary data; - struct hmac_context* obj; - - if (!enif_get_resource(env, argv[0], hmac_context_rtype, (void**)&obj) - || !enif_inspect_iolist_as_binary(env, argv[1], &data)) { - return enif_make_badarg(env); - } - enif_mutex_lock(obj->mtx); - if (!obj->alive) { - enif_mutex_unlock(obj->mtx); - return enif_make_badarg(env); - } - HMAC_Update(obj->ctx, data.data, data.size); - enif_mutex_unlock(obj->mtx); - - CONSUME_REDS(env,data); - return argv[0]; -} - -static ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Context) or (Context, HashLen) */ - ERL_NIF_TERM ret; - struct hmac_context* obj; - unsigned char mac_buf[EVP_MAX_MD_SIZE]; - unsigned char * mac_bin; - unsigned int req_len = 0; - unsigned int mac_len; - - if (!enif_get_resource(env,argv[0],hmac_context_rtype, (void**)&obj) - || (argc == 2 && !enif_get_uint(env, argv[1], &req_len))) { - return enif_make_badarg(env); - } - - enif_mutex_lock(obj->mtx); - if (!obj->alive) { - enif_mutex_unlock(obj->mtx); - return enif_make_badarg(env); - } - - HMAC_Final(obj->ctx, mac_buf, &mac_len); - HMAC_CTX_free(obj->ctx); - obj->alive = 0; - enif_mutex_unlock(obj->mtx); - - if (argc == 2 && req_len < mac_len) { - /* Only truncate to req_len bytes if asked. */ - mac_len = req_len; - } - mac_bin = enif_make_new_binary(env, mac_len, &ret); - memcpy(mac_bin, mac_buf, mac_len); - - return ret; -} - -static ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key, Data) */ -#if defined(HAVE_CMAC) - struct cipher_type_t *cipherp = NULL; - const EVP_CIPHER *cipher; - CMAC_CTX *ctx; - ErlNifBinary key; - ErlNifBinary data; - ERL_NIF_TERM ret; - size_t ret_size; - - if (!enif_inspect_iolist_as_binary(env, argv[1], &key) - || !(cipherp = get_cipher_type(argv[0], key.size)) - || !enif_inspect_iolist_as_binary(env, argv[2], &data)) { - return enif_make_badarg(env); - } - cipher = cipherp->cipher.p; - if (!cipher) { - return enif_raise_exception(env, atom_notsup); - } - - ctx = CMAC_CTX_new(); - if (!CMAC_Init(ctx, key.data, key.size, cipher, NULL)) { - CMAC_CTX_free(ctx); - return atom_notsup; - } - - if (!CMAC_Update(ctx, data.data, data.size) || - !CMAC_Final(ctx, - enif_make_new_binary(env, EVP_CIPHER_block_size(cipher), &ret), - &ret_size)) { - CMAC_CTX_free(ctx); - return atom_notsup; - } - ASSERT(ret_size == (unsigned)EVP_CIPHER_block_size(cipher)); - - CMAC_CTX_free(ctx); - CONSUME_REDS(env, data); - return ret; -#else - /* The CMAC functionality was introduced in OpenSSL 1.0.1 - * Although OTP requires at least version 0.9.8, the versions 0.9.8 and 1.0.0 are - * no longer maintained. */ - return atom_notsup; -#endif -} - -/* For OpenSSL >= 1.1.1 the hmac_nif and cmac_nif could be integrated into poly1305 (with 'type' as parameter) */ -static ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, Text) */ -#ifdef HAVE_POLY1305 - ErlNifBinary key_bin, text, ret_bin; - ERL_NIF_TERM ret = atom_error; - EVP_PKEY *key = NULL; - EVP_MD_CTX *mctx = NULL; - EVP_PKEY_CTX *pctx = NULL; - const EVP_MD *md = NULL; - size_t size; - int type; - - type = EVP_PKEY_POLY1305; - - if (!enif_inspect_binary(env, argv[0], &key_bin) || - !(key_bin.size == 32) ) { - return enif_make_badarg(env); - } - - if (!enif_inspect_binary(env, argv[1], &text) ) { - return enif_make_badarg(env); - } - - key = EVP_PKEY_new_raw_private_key(type, /*engine*/ NULL, key_bin.data, key_bin.size); - - if (!key || - !(mctx = EVP_MD_CTX_new()) || - !EVP_DigestSignInit(mctx, &pctx, md, /*engine*/ NULL, key) || - !EVP_DigestSignUpdate(mctx, text.data, text.size)) { - goto err; - } - - if (!EVP_DigestSignFinal(mctx, NULL, &size) || - !enif_alloc_binary(size, &ret_bin) || - !EVP_DigestSignFinal(mctx, ret_bin.data, &size)) { - goto err; - } - - if ((size != ret_bin.size) && - !enif_realloc_binary(&ret_bin, size)) { - goto err; - } - - ret = enif_make_binary(env, &ret_bin); - - err: - EVP_MD_CTX_free(mctx); - EVP_PKEY_free(key); - return ret; - -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key, Ivec, Text, IsEncrypt) or (Type, Key, Text, IsEncrypt) */ - struct cipher_type_t *cipherp = NULL; - const EVP_CIPHER *cipher; - ErlNifBinary key, ivec, text; - EVP_CIPHER_CTX* ctx; - ERL_NIF_TERM ret; - unsigned char *out; - int ivec_size, out_size = 0; - - if (!enif_inspect_iolist_as_binary(env, argv[1], &key) - || !(cipherp = get_cipher_type(argv[0], key.size)) - || !enif_inspect_iolist_as_binary(env, argv[argc - 2], &text)) { - return enif_make_badarg(env); - } - cipher = cipherp->cipher.p; - if (!cipher) { - return enif_raise_exception(env, atom_notsup); - } - - if (argv[0] == atom_aes_cfb8 - && (key.size == 24 || key.size == 32)) { - /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? - * Fall back on low level API - */ - return aes_cfb_8_crypt(env, argc-1, argv+1); - } - else if (argv[0] == atom_aes_cfb128 - && (key.size == 24 || key.size == 32)) { - /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? - * Fall back on low level API - */ - return aes_cfb_128_crypt_nif(env, argc-1, argv+1); - } - - ivec_size = EVP_CIPHER_iv_length(cipher); - -#ifdef HAVE_ECB_IVEC_BUG - if (argv[0] == atom_aes_ecb || argv[0] == atom_blowfish_ecb || - argv[0] == atom_des_ecb) - ivec_size = 0; /* 0.9.8l returns faulty ivec_size */ -#endif - - if (text.size % EVP_CIPHER_block_size(cipher) != 0 || - (ivec_size == 0 ? argc != 4 - : (argc != 5 || - !enif_inspect_iolist_as_binary(env, argv[2], &ivec) || - ivec.size != ivec_size))) { - return enif_make_badarg(env); - } - - out = enif_make_new_binary(env, text.size, &ret); - - ctx = EVP_CIPHER_CTX_new(); - if (!EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, - (argv[argc - 1] == atom_true)) || - !EVP_CIPHER_CTX_set_key_length(ctx, key.size) || - !(EVP_CIPHER_type(cipher) != NID_rc2_cbc || - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, key.size * 8, NULL)) || - !EVP_CipherInit_ex(ctx, NULL, NULL, - key.data, ivec_size ? ivec.data : NULL, -1) || - !EVP_CIPHER_CTX_set_padding(ctx, 0)) { - - EVP_CIPHER_CTX_free(ctx); - return enif_raise_exception(env, atom_notsup); - } - - if (text.size > 0 && /* OpenSSL 0.9.8h asserts text.size > 0 */ - (!EVP_CipherUpdate(ctx, out, &out_size, text.data, text.size) - || (ASSERT(out_size == text.size), 0) - || !EVP_CipherFinal_ex(ctx, out + out_size, &out_size))) { - - EVP_CIPHER_CTX_free(ctx); - return enif_raise_exception(env, atom_notsup); - } - ASSERT(out_size == 0); - EVP_CIPHER_CTX_free(ctx); - CONSUME_REDS(env, text); - - return ret; -} - -static ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IVec, Data, IsEncrypt) */ - ErlNifBinary key, ivec, text; - AES_KEY aes_key; - unsigned char ivec_clone[16]; /* writable copy */ - int new_ivlen = 0; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key) - || !(key.size == 16 || key.size == 24 || key.size == 32) - || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16 - || !enif_inspect_iolist_as_binary(env, argv[2], &text)) { - return enif_make_badarg(env); - } - - memcpy(ivec_clone, ivec.data, 16); - AES_set_encrypt_key(key.data, key.size * 8, &aes_key); - AES_cfb8_encrypt((unsigned char *) text.data, - enif_make_new_binary(env, text.size, &ret), - text.size, &aes_key, ivec_clone, &new_ivlen, - (argv[3] == atom_true)); - CONSUME_REDS(env,text); - return ret; -} - -static ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IVec, Data, IsEncrypt) */ - ErlNifBinary key, ivec, text; - AES_KEY aes_key; - unsigned char ivec_clone[16]; /* writable copy */ - int new_ivlen = 0; - ERL_NIF_TERM ret; - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key) - || !(key.size == 16 || key.size == 24 || key.size == 32) - || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16 - || !enif_inspect_iolist_as_binary(env, argv[2], &text)) { - return enif_make_badarg(env); - } - - memcpy(ivec_clone, ivec.data, 16); - AES_set_encrypt_key(key.data, key.size * 8, &aes_key); - AES_cfb128_encrypt((unsigned char *) text.data, - enif_make_new_binary(env, text.size, &ret), - text.size, &aes_key, ivec_clone, &new_ivlen, - (argv[3] == atom_true)); - CONSUME_REDS(env,text); - return ret; -} - -static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IVec, Data, IsEncrypt) */ -#ifdef HAVE_AES_IGE - ErlNifBinary key_bin, ivec_bin, data_bin; - AES_KEY aes_key; - unsigned char ivec[32]; - int i; - unsigned char* ret_ptr; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) - || (key_bin.size != 16 && key_bin.size != 32) - || !enif_inspect_binary(env, argv[1], &ivec_bin) - || ivec_bin.size != 32 - || !enif_inspect_iolist_as_binary(env, argv[2], &data_bin) - || data_bin.size % 16 != 0) { - - return enif_make_badarg(env); - } - - if (argv[3] == atom_true) { - i = AES_ENCRYPT; - AES_set_encrypt_key(key_bin.data, key_bin.size*8, &aes_key); - } - else { - i = AES_DECRYPT; - AES_set_decrypt_key(key_bin.data, key_bin.size*8, &aes_key); - } - - ret_ptr = enif_make_new_binary(env, data_bin.size, &ret); - memcpy(ivec, ivec_bin.data, 32); /* writable copy */ - AES_ige_encrypt(data_bin.data, ret_ptr, data_bin.size, &aes_key, ivec, i); - CONSUME_REDS(env,data_bin); - return ret; -#else - return atom_notsup; -#endif -} - - -/* Initializes state for ctr streaming (de)encryption -*/ -#ifdef HAVE_EVP_AES_CTR -static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IVec) */ - ErlNifBinary key_bin, ivec_bin; - struct evp_cipher_ctx *ctx; - const EVP_CIPHER *cipher; - ERL_NIF_TERM ret; - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) - || !enif_inspect_binary(env, argv[1], &ivec_bin) - || ivec_bin.size != 16) { - return enif_make_badarg(env); - } - - switch (key_bin.size) - { - case 16: cipher = EVP_aes_128_ctr(); break; - case 24: cipher = EVP_aes_192_ctr(); break; - case 32: cipher = EVP_aes_256_ctr(); break; - default: return enif_make_badarg(env); - } - - ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); - ctx->ctx = EVP_CIPHER_CTX_new(); - EVP_CipherInit_ex(ctx->ctx, cipher, NULL, - key_bin.data, ivec_bin.data, 1); - EVP_CIPHER_CTX_set_padding(ctx->ctx, 0); - ret = enif_make_resource(env, ctx); - enif_release_resource(ctx); - return ret; -} -static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Context, Data) */ - struct evp_cipher_ctx *ctx, *new_ctx; - ErlNifBinary data_bin; - ERL_NIF_TERM ret, cipher_term; - unsigned char *out; - int outl = 0; - - if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx) - || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { - return enif_make_badarg(env); - } - new_ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); - new_ctx->ctx = EVP_CIPHER_CTX_new(); - EVP_CIPHER_CTX_copy(new_ctx->ctx, ctx->ctx); - out = enif_make_new_binary(env, data_bin.size, &cipher_term); - EVP_CipherUpdate(new_ctx->ctx, out, &outl, data_bin.data, data_bin.size); - ASSERT(outl == data_bin.size); - - ret = enif_make_tuple2(env, enif_make_resource(env, new_ctx), cipher_term); - enif_release_resource(new_ctx); - CONSUME_REDS(env,data_bin); - return ret; -} - -#else /* if not HAVE_EVP_AES_CTR */ - -static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IVec) */ - ErlNifBinary key_bin, ivec_bin; - ERL_NIF_TERM ecount_bin; - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) - || !enif_inspect_binary(env, argv[1], &ivec_bin) - || !(key_bin.size == 16 || key_bin.size == 24 || key_bin.size ==32) - || ivec_bin.size != 16) { - return enif_make_badarg(env); - } - - memset(enif_make_new_binary(env, AES_BLOCK_SIZE, &ecount_bin), - 0, AES_BLOCK_SIZE); - return enif_make_tuple4(env, argv[0], argv[1], ecount_bin, enif_make_int(env, 0)); -} - -static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* ({Key, IVec, ECount, Num}, Data) */ - ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin; - AES_KEY aes_key; - unsigned int num; - ERL_NIF_TERM ret, num2_term, cipher_term, ivec2_term, ecount2_term, new_state_term; - int state_arity; - const ERL_NIF_TERM *state_term; - unsigned char * ivec2_buf; - unsigned char * ecount2_buf; - - if (!enif_get_tuple(env, argv[0], &state_arity, &state_term) - || state_arity != 4 - || !enif_inspect_iolist_as_binary(env, state_term[0], &key_bin) - || AES_set_encrypt_key(key_bin.data, key_bin.size*8, &aes_key) != 0 - || !enif_inspect_binary(env, state_term[1], &ivec_bin) || ivec_bin.size != 16 - || !enif_inspect_binary(env, state_term[2], &ecount_bin) || ecount_bin.size != AES_BLOCK_SIZE - || !enif_get_uint(env, state_term[3], &num) - || !enif_inspect_iolist_as_binary(env, argv[1], &text_bin)) { - return enif_make_badarg(env); - } - - ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term); - ecount2_buf = enif_make_new_binary(env, ecount_bin.size, &ecount2_term); - - memcpy(ivec2_buf, ivec_bin.data, 16); - memcpy(ecount2_buf, ecount_bin.data, ecount_bin.size); - - AES_ctr128_encrypt((unsigned char *) text_bin.data, - enif_make_new_binary(env, text_bin.size, &cipher_term), - text_bin.size, &aes_key, ivec2_buf, ecount2_buf, &num); - - num2_term = enif_make_uint(env, num); - new_state_term = enif_make_tuple4(env, state_term[0], ivec2_term, ecount2_term, num2_term); - ret = enif_make_tuple2(env, new_state_term, cipher_term); - CONSUME_REDS(env,text_bin); - return ret; -} -#endif /* !HAVE_EVP_AES_CTR */ - -static ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type,Key,Iv,AAD,In) */ -#if defined(HAVE_AEAD) - EVP_CIPHER_CTX *ctx; - const EVP_CIPHER *cipher = NULL; - ErlNifBinary key, iv, aad, in; - unsigned int tag_len; - unsigned char *outp, *tagp; - ERL_NIF_TERM type, out, out_tag; - int len, ctx_ctrl_set_ivlen, ctx_ctrl_get_tag; - - type = argv[0]; - - if (!enif_is_atom(env, type) - || !enif_inspect_iolist_as_binary(env, argv[1], &key) - || !enif_inspect_binary(env, argv[2], &iv) - || !enif_inspect_iolist_as_binary(env, argv[3], &aad) - || !enif_inspect_iolist_as_binary(env, argv[4], &in) - || !enif_get_uint(env, argv[5], &tag_len)) { - return enif_make_badarg(env); - } - - /* Use cipher_type some day. Must check block_encrypt|decrypt first */ -#if defined(HAVE_GCM) - if (type == atom_aes_gcm) { - if ((iv.size > 0) - && (1 <= tag_len && tag_len <= 16)) { - ctx_ctrl_set_ivlen = EVP_CTRL_GCM_SET_IVLEN; - ctx_ctrl_get_tag = EVP_CTRL_GCM_GET_TAG; - if (key.size == 16) cipher = EVP_aes_128_gcm(); - else if (key.size == 24) cipher = EVP_aes_192_gcm(); - else if (key.size == 32) cipher = EVP_aes_256_gcm(); - else enif_make_badarg(env); - } else - enif_make_badarg(env); - } else -#endif -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if ((7 <= iv.size && iv.size <= 13) - && (4 <= tag_len && tag_len <= 16) - && ((tag_len & 1) == 0) - ) { - ctx_ctrl_set_ivlen = EVP_CTRL_CCM_SET_IVLEN; - ctx_ctrl_get_tag = EVP_CTRL_CCM_GET_TAG; - if (key.size == 16) cipher = EVP_aes_128_ccm(); - else if (key.size == 24) cipher = EVP_aes_192_ccm(); - else if (key.size == 32) cipher = EVP_aes_256_ccm(); - else enif_make_badarg(env); - } else - enif_make_badarg(env); - } else -#endif -#if defined(HAVE_CHACHA20_POLY1305) - if (type == atom_chacha20_poly1305) { - if ((key.size == 32) - && (1 <= iv.size && iv.size <= 16) - && (tag_len == 16) - ) { - ctx_ctrl_set_ivlen = EVP_CTRL_AEAD_SET_IVLEN; - ctx_ctrl_get_tag = EVP_CTRL_AEAD_GET_TAG, - cipher = EVP_chacha20_poly1305(); - } else enif_make_badarg(env); - } else -#endif - return enif_raise_exception(env, atom_notsup); - - ctx = EVP_CIPHER_CTX_new(); - if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) goto out_err; - if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, iv.size, NULL) != 1) goto out_err; - -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, tag_len, NULL) != 1) goto out_err; - if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto out_err; - if (EVP_EncryptUpdate(ctx, NULL, &len, NULL, in.size) != 1) goto out_err; - } else -#endif - if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto out_err; - - if (EVP_EncryptUpdate(ctx, NULL, &len, aad.data, aad.size) != 1) goto out_err; - - outp = enif_make_new_binary(env, in.size, &out); - - if (EVP_EncryptUpdate(ctx, outp, &len, in.data, in.size) != 1) goto out_err; - if (EVP_EncryptFinal_ex(ctx, outp/*+len*/, &len) != 1) goto out_err; - - tagp = enif_make_new_binary(env, tag_len, &out_tag); - - if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_get_tag, tag_len, tagp) != 1) goto out_err; - - EVP_CIPHER_CTX_free(ctx); - CONSUME_REDS(env, in); - return enif_make_tuple2(env, out, out_tag); - -out_err: - EVP_CIPHER_CTX_free(ctx); - return atom_error; - -#else - return enif_raise_exception(env, atom_notsup); -#endif -} - -static ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type,Key,Iv,AAD,In,Tag) */ -#if defined(HAVE_AEAD) - EVP_CIPHER_CTX *ctx; - const EVP_CIPHER *cipher = NULL; - ErlNifBinary key, iv, aad, in, tag; - unsigned char *outp; - ERL_NIF_TERM type, out; - int len, ctx_ctrl_set_ivlen, ctx_ctrl_set_tag; - - type = argv[0]; -#if defined(HAVE_GCM_EVP_DECRYPT_BUG) - if (type == atom_aes_gcm) - return aes_gcm_decrypt_NO_EVP(env, argc, argv); -#endif - - if (!enif_is_atom(env, type) - || !enif_inspect_iolist_as_binary(env, argv[1], &key) - || !enif_inspect_binary(env, argv[2], &iv) - || !enif_inspect_iolist_as_binary(env, argv[3], &aad) - || !enif_inspect_iolist_as_binary(env, argv[4], &in) - || !enif_inspect_iolist_as_binary(env, argv[5], &tag)) { - return enif_make_badarg(env); - } - - /* Use cipher_type some day. Must check block_encrypt|decrypt first */ -#if defined(HAVE_GCM) - if (type == atom_aes_gcm) { - if (iv.size > 0) { - ctx_ctrl_set_ivlen = EVP_CTRL_GCM_SET_IVLEN; - ctx_ctrl_set_tag = EVP_CTRL_GCM_SET_TAG; - if (key.size == 16) cipher = EVP_aes_128_gcm(); - else if (key.size == 24) cipher = EVP_aes_192_gcm(); - else if (key.size == 32) cipher = EVP_aes_256_gcm(); - else enif_make_badarg(env); - } else - enif_make_badarg(env); - } else -#endif -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if (iv.size > 0) { - ctx_ctrl_set_ivlen = EVP_CTRL_CCM_SET_IVLEN; - if (key.size == 16) cipher = EVP_aes_128_ccm(); - else if (key.size == 24) cipher = EVP_aes_192_ccm(); - else if (key.size == 32) cipher = EVP_aes_256_ccm(); - else enif_make_badarg(env); - } else - enif_make_badarg(env); - } else -#endif -#if defined(HAVE_CHACHA20_POLY1305) - if (type == atom_chacha20_poly1305) { - if ((key.size == 32) - && (1 <= iv.size && iv.size <= 16) - && tag.size == 16 - ) { - ctx_ctrl_set_ivlen = EVP_CTRL_AEAD_SET_IVLEN; - ctx_ctrl_set_tag = EVP_CTRL_AEAD_SET_TAG; - cipher = EVP_chacha20_poly1305(); - } else enif_make_badarg(env); - } else -#endif - return enif_raise_exception(env, atom_notsup); - - outp = enif_make_new_binary(env, in.size, &out); - - ctx = EVP_CIPHER_CTX_new(); - if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) goto out_err; - if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, iv.size, NULL) != 1) goto out_err; - -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, tag.size, tag.data) != 1) goto out_err; - } -#endif - - if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto out_err; - -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if (1 != EVP_DecryptUpdate(ctx, NULL, &len, NULL, in.size)) goto out_err; - } -#endif - - if (EVP_DecryptUpdate(ctx, NULL, &len, aad.data, aad.size) != 1) goto out_err; - if (EVP_DecryptUpdate(ctx, outp, &len, in.data, in.size) != 1) goto out_err; - -#if defined(HAVE_GCM) || defined(HAVE_CHACHA20_POLY1305) - if (type == atom_aes_gcm) { - if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, tag.size, tag.data) != 1) goto out_err; - if (EVP_DecryptFinal_ex(ctx, outp+len, &len) != 1) goto out_err; - } -#endif - EVP_CIPHER_CTX_free(ctx); - - CONSUME_REDS(env, in); - return out; - -out_err: - EVP_CIPHER_CTX_free(ctx); - return atom_error; -#else - return enif_raise_exception(env, atom_notsup); -#endif -} - -#ifdef HAVE_GCM_EVP_DECRYPT_BUG -static ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type,Key,Iv,AAD,In,Tag) */ - GCM128_CONTEXT *ctx; - ErlNifBinary key, iv, aad, in, tag; - AES_KEY aes_key; - unsigned char *outp; - ERL_NIF_TERM out; - - if (!enif_inspect_iolist_as_binary(env, argv[1], &key) - || AES_set_encrypt_key(key.data, key.size*8, &aes_key) != 0 - || !enif_inspect_binary(env, argv[2], &iv) || iv.size == 0 - || !enif_inspect_iolist_as_binary(env, argv[3], &aad) - || !enif_inspect_iolist_as_binary(env, argv[4], &in) - || !enif_inspect_iolist_as_binary(env, argv[5], &tag)) { - return enif_make_badarg(env); - } - - if (!(ctx = CRYPTO_gcm128_new(&aes_key, (block128_f)AES_encrypt))) - return atom_error; - - CRYPTO_gcm128_setiv(ctx, iv.data, iv.size); - - if (CRYPTO_gcm128_aad(ctx, aad.data, aad.size)) - goto out_err; - - outp = enif_make_new_binary(env, in.size, &out); - - /* decrypt */ - if (CRYPTO_gcm128_decrypt(ctx, in.data, outp, in.size)) - goto out_err; - - /* calculate and check the tag */ - if (CRYPTO_gcm128_finish(ctx, tag.data, tag.size)) - goto out_err; - - CRYPTO_gcm128_release(ctx); - CONSUME_REDS(env, in); - - return out; - -out_err: - CRYPTO_gcm128_release(ctx); - return atom_error; -} -#endif /* HAVE_GCM_EVP_DECRYPT_BUG */ - - -static ERL_NIF_TERM chacha20_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IV) */ -#if defined(HAVE_CHACHA20) - ErlNifBinary key_bin, ivec_bin; - struct evp_cipher_ctx *ctx; - const EVP_CIPHER *cipher; - ERL_NIF_TERM ret; - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) - || !enif_inspect_binary(env, argv[1], &ivec_bin) - || key_bin.size != 32 - || ivec_bin.size != 16) { - return enif_make_badarg(env); - } - - cipher = EVP_chacha20(); - - ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); - ctx->ctx = EVP_CIPHER_CTX_new(); - - - EVP_CipherInit_ex(ctx->ctx, cipher, NULL, - key_bin.data, ivec_bin.data, 1); - EVP_CIPHER_CTX_set_padding(ctx->ctx, 0); - ret = enif_make_resource(env, ctx); - enif_release_resource(ctx); - return ret; -#else - return enif_raise_exception(env, atom_notsup); -#endif -}; - -static ERL_NIF_TERM chacha20_stream_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (State, Data) */ -#if defined(HAVE_CHACHA20) - struct evp_cipher_ctx *ctx, *new_ctx; - ErlNifBinary data_bin; - ERL_NIF_TERM ret, cipher_term; - unsigned char *out; - int outl = 0; - - if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx) - || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { - return enif_make_badarg(env); - } - new_ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); - new_ctx->ctx = EVP_CIPHER_CTX_new(); - EVP_CIPHER_CTX_copy(new_ctx->ctx, ctx->ctx); - out = enif_make_new_binary(env, data_bin.size, &cipher_term); - EVP_CipherUpdate(new_ctx->ctx, out, &outl, data_bin.data, data_bin.size); - ASSERT(outl == data_bin.size); - - ret = enif_make_tuple2(env, enif_make_resource(env, new_ctx), cipher_term); - enif_release_resource(new_ctx); - CONSUME_REDS(env,data_bin); - return ret; -#else - return enif_raise_exception(env, atom_notsup); -#endif -}; - - -static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Bytes) */ - unsigned bytes; - unsigned char* data; - ERL_NIF_TERM ret; - - if (!enif_get_uint(env, argv[0], &bytes)) { - return enif_make_badarg(env); - } - data = enif_make_new_binary(env, bytes, &ret); - if ( RAND_bytes(data, bytes) != 1) { - return atom_false; - } - ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes); - return ret; -} - - -static int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) -{ - ErlNifBinary bin; - int sz; - if (!enif_inspect_binary(env,term,&bin)) { - return 0; - } - ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); - sz = bin.size - 4; - if (sz < 0 || get_int32(bin.data) != sz) { - return 0; - } - *bnp = BN_bin2bn(bin.data+4, sz, NULL); - return 1; -} - -static int get_bn_from_bin(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) -{ - ErlNifBinary bin; - if (!enif_inspect_binary(env,term,&bin)) { - return 0; - } - ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); - *bnp = BN_bin2bn(bin.data, bin.size, NULL); - return 1; -} - -static ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn) -{ - int bn_len; - unsigned char *bin_ptr; - ERL_NIF_TERM term; - - /* Copy the bignum into an erlang binary. */ - bn_len = BN_num_bytes(bn); - bin_ptr = enif_make_new_binary(env, bn_len, &term); - BN_bn2bin(bn, bin_ptr); - - return term; -} - -static ERL_NIF_TERM strong_rand_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Range) */ - BIGNUM *bn_range, *bn_rand; - ERL_NIF_TERM ret; - - if(!get_bn_from_bin(env, argv[0], &bn_range)) { - return enif_make_badarg(env); - } - - bn_rand = BN_new(); - if (BN_rand_range(bn_rand, bn_range) != 1) { - ret = atom_false; - } - else { - ret = bin_from_bn(env, bn_rand); - } - BN_free(bn_rand); - BN_free(bn_range); - return ret; -} - -static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Lo,Hi) */ - BIGNUM *bn_from = NULL, *bn_to, *bn_rand; - unsigned char* data; - unsigned dlen; - ERL_NIF_TERM ret; - - if (!get_bn_from_mpint(env, argv[0], &bn_from) - || !get_bn_from_mpint(env, argv[1], &bn_rand)) { - if (bn_from) BN_free(bn_from); - return enif_make_badarg(env); - } - - bn_to = BN_new(); - BN_sub(bn_to, bn_rand, bn_from); - BN_pseudo_rand_range(bn_rand, bn_to); - BN_add(bn_rand, bn_rand, bn_from); - dlen = BN_num_bytes(bn_rand); - data = enif_make_new_binary(env, dlen+4, &ret); - put_int32(data, dlen); - BN_bn2bin(bn_rand, data+4); - ERL_VALGRIND_MAKE_MEM_DEFINED(data+4, dlen); - BN_free(bn_rand); - BN_free(bn_from); - BN_free(bn_to); - return ret; -} - -static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Base,Exponent,Modulo,bin_hdr) */ - BIGNUM *bn_base=NULL, *bn_exponent=NULL, *bn_modulo=NULL, *bn_result; - BN_CTX *bn_ctx; - unsigned char* ptr; - unsigned dlen; - unsigned bin_hdr; /* return type: 0=plain binary, 4: mpint */ - unsigned extra_byte; - ERL_NIF_TERM ret; - - if (!get_bn_from_bin(env, argv[0], &bn_base) - || !get_bn_from_bin(env, argv[1], &bn_exponent) - || !get_bn_from_bin(env, argv[2], &bn_modulo) - || !enif_get_uint(env,argv[3],&bin_hdr) || (bin_hdr & ~4)) { - - if (bn_base) BN_free(bn_base); - if (bn_exponent) BN_free(bn_exponent); - if (bn_modulo) BN_free(bn_modulo); - return enif_make_badarg(env); - } - bn_result = BN_new(); - bn_ctx = BN_CTX_new(); - BN_mod_exp(bn_result, bn_base, bn_exponent, bn_modulo, bn_ctx); - dlen = BN_num_bytes(bn_result); - extra_byte = bin_hdr && BN_is_bit_set(bn_result, dlen*8-1); - ptr = enif_make_new_binary(env, bin_hdr+extra_byte+dlen, &ret); - if (bin_hdr) { - put_int32(ptr, extra_byte+dlen); - ptr[4] = 0; /* extra zeroed byte to ensure a positive mpint */ - ptr += bin_hdr + extra_byte; - } - BN_bn2bin(bn_result, ptr); - BN_free(bn_result); - BN_CTX_free(bn_ctx); - BN_free(bn_modulo); - BN_free(bn_exponent); - BN_free(bn_base); - return ret; -} - -static void init_digest_types(ErlNifEnv* env) -{ - struct digest_type_t* p = digest_types; - - for (p = digest_types; p->type.str; p++) { - p->type.atom = enif_make_atom(env, p->type.str); - if (p->md.funcp) - p->md.p = p->md.funcp(); - } - p->type.atom = atom_false; /* end marker */ -} - -static void init_cipher_types(ErlNifEnv* env) -{ - struct cipher_type_t* p = cipher_types; - - for (p = cipher_types; p->type.str; p++) { - p->type.atom = enif_make_atom(env, p->type.str); - if (p->cipher.funcp) - p->cipher.p = p->cipher.funcp(); - } - p->type.atom = atom_false; /* end marker */ -} - -static struct digest_type_t* get_digest_type(ERL_NIF_TERM type) -{ - struct digest_type_t* p = NULL; - for (p = digest_types; p->type.atom != atom_false; p++) { - if (type == p->type.atom) { - return p; - } - } - return NULL; -} - -static struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len) -{ - struct cipher_type_t* p = NULL; - for (p = cipher_types; p->type.atom != atom_false; p++) { - if (type == p->type.atom && (!p->key_len || key_len == p->key_len)) { - return p; - } - } - return NULL; -} - - -static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Data1, Data2) */ - ErlNifBinary d1, d2; - unsigned char* ret_ptr; - int i; - ERL_NIF_TERM ret; - - if (!enif_inspect_iolist_as_binary(env,argv[0], &d1) - || !enif_inspect_iolist_as_binary(env,argv[1], &d2) - || d1.size != d2.size) { - return enif_make_badarg(env); - } - ret_ptr = enif_make_new_binary(env, d1.size, &ret); - - for (i=0; i<d1.size; i++) { - ret_ptr[i] = d1.data[i] ^ d2.data[i]; - } - CONSUME_REDS(env,d1); - return ret; -} - -static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key) */ -#ifndef OPENSSL_NO_RC4 - ErlNifBinary key; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - if (!enif_inspect_iolist_as_binary(env,argv[0], &key)) { - return enif_make_badarg(env); - } - RC4_set_key((RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &ret), - key.size, key.data); - return ret; -#else - return enif_raise_exception(env, atom_notsup); -#endif -} - -static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (State, Data) */ -#ifndef OPENSSL_NO_RC4 - ErlNifBinary state, data; - RC4_KEY* rc4_key; - ERL_NIF_TERM new_state, new_data; - - CHECK_NO_FIPS_MODE(); - - if (!enif_inspect_iolist_as_binary(env,argv[0], &state) - || state.size != sizeof(RC4_KEY) - || !enif_inspect_iolist_as_binary(env,argv[1], &data)) { - return enif_make_badarg(env); - } - rc4_key = (RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &new_state); - memcpy(rc4_key, state.data, sizeof(RC4_KEY)); - RC4(rc4_key, data.size, data.data, - enif_make_new_binary(env, data.size, &new_data)); - CONSUME_REDS(env,data); - return enif_make_tuple2(env,new_state,new_data); -#else - return enif_raise_exception(env, atom_notsup); -#endif -} - -static int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) -{ - /* key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C] */ - ERL_NIF_TERM head, tail; - BIGNUM *e, *n, *d; - BIGNUM *p, *q; - BIGNUM *dmp1, *dmq1, *iqmp; - - if (!enif_get_list_cell(env, key, &head, &tail) - || !get_bn_from_bin(env, head, &e) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &n) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &d)) { - return 0; - } - (void) RSA_set0_key(rsa, n, e, d); - if (enif_is_empty_list(env, tail)) { - return 1; - } - if (!enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &p) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &q) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dmp1) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dmq1) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &iqmp) - || !enif_is_empty_list(env, tail)) { - return 0; - } - (void) RSA_set0_factors(rsa, p, q); - (void) RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp); - return 1; -} - - -static int get_rsa_public_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) -{ - /* key=[E,N] */ - ERL_NIF_TERM head, tail; - BIGNUM *e, *n; - - if (!enif_get_list_cell(env, key, &head, &tail) - || !get_bn_from_bin(env, head, &e) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &n) - || !enif_is_empty_list(env, tail)) { - return 0; - } - - (void) RSA_set0_key(rsa, n, e, NULL); - return 1; -} - -#ifdef HAVE_EDDSA - static int get_eddsa_key(ErlNifEnv* env, int public, ERL_NIF_TERM key, EVP_PKEY **pkey) -{ - /* key=[K] */ - ERL_NIF_TERM head, tail, tail2, algo; - ErlNifBinary bin; - int type; - - if (!enif_get_list_cell(env, key, &head, &tail) - || !enif_inspect_binary(env, head, &bin) - || !enif_get_list_cell(env, tail, &algo, &tail2) - || !enif_is_empty_list(env, tail2)) { - return 0; - } - if (algo == atom_ed25519) type = EVP_PKEY_ED25519; - else if (algo == atom_ed448) type = EVP_PKEY_ED448; - else - return 0; - - if (public) - *pkey = EVP_PKEY_new_raw_public_key(type, NULL, bin.data, bin.size); - else - *pkey = EVP_PKEY_new_raw_private_key(type, NULL, bin.data, bin.size); - - if (!pkey) - return 0; - return 1; -} -#endif - -static int get_dss_private_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa) -{ - /* key=[P,Q,G,KEY] */ - ERL_NIF_TERM head, tail; - BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL; - BIGNUM *dummy_pub_key, *priv_key = NULL; - - if (!enif_get_list_cell(env, key, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_p) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_q) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_g) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &priv_key) - || !enif_is_empty_list(env,tail)) { - if (dsa_p) BN_free(dsa_p); - if (dsa_q) BN_free(dsa_q); - if (dsa_g) BN_free(dsa_g); - if (priv_key) BN_free(priv_key); - return 0; - } - - /* Note: DSA_set0_key() does not allow setting only the - * private key, although DSA_sign() does not use the - * public key. Work around this limitation by setting - * the public key to a copy of the private key. - */ - dummy_pub_key = BN_dup(priv_key); - - DSA_set0_pqg(dsa, dsa_p, dsa_q, dsa_g); - DSA_set0_key(dsa, dummy_pub_key, priv_key); - return 1; -} - - -static int get_dss_public_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa) -{ - /* key=[P, Q, G, Y] */ - ERL_NIF_TERM head, tail; - BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_y = NULL; - - if (!enif_get_list_cell(env, key, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_p) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_q) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_g) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_y) - || !enif_is_empty_list(env,tail)) { - if (dsa_p) BN_free(dsa_p); - if (dsa_q) BN_free(dsa_q); - if (dsa_g) BN_free(dsa_g); - if (dsa_y) BN_free(dsa_y); - return 0; - } - - DSA_set0_pqg(dsa, dsa_p, dsa_q, dsa_g); - DSA_set0_key(dsa, dsa_y, NULL); - return 1; -} - -/* Creates a term which can be parsed by get_rsa_private_key(). This is a list of plain integer binaries (not mpints). */ -static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa) -{ - ERL_NIF_TERM result[8]; - const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; - - /* Return at least [E,N,D] */ - n = NULL; e = NULL; d = NULL; - RSA_get0_key(rsa, &n, &e, &d); - - result[0] = bin_from_bn(env, e); // Exponent E - result[1] = bin_from_bn(env, n); // Modulus N = p*q - result[2] = bin_from_bn(env, d); // Exponent D - - /* Check whether the optional additional parameters are available */ - p = NULL; q = NULL; - RSA_get0_factors(rsa, &p, &q); - dmp1 = NULL; dmq1 = NULL; iqmp = NULL; - RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); - - if (p && q && dmp1 && dmq1 && iqmp) { - result[3] = bin_from_bn(env, p); // Factor p - result[4] = bin_from_bn(env, q); // Factor q - result[5] = bin_from_bn(env, dmp1); // D mod (p-1) - result[6] = bin_from_bn(env, dmq1); // D mod (q-1) - result[7] = bin_from_bn(env, iqmp); // (1/q) mod p - - return enif_make_list_from_array(env, result, 8); - } else { - return enif_make_list_from_array(env, result, 3); - } -} - -static int check_erlang_interrupt(int maj, int min, BN_GENCB *ctxt) -{ - ErlNifEnv *env = BN_GENCB_get_arg(ctxt); - - if (!enif_is_current_process_alive(env)) { - return 0; - } else { - return 1; - } -} - -static ERL_NIF_TERM rsa_generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (ModulusSize, PublicExponent) */ - int modulus_bits; - BIGNUM *pub_exp, *three; - RSA *rsa; - int success; - ERL_NIF_TERM result; - BN_GENCB *intr_cb; -#ifndef HAVE_OPAQUE_BN_GENCB - BN_GENCB intr_cb_buf; -#endif - - if (!enif_get_int(env, argv[0], &modulus_bits) || modulus_bits < 256) { - return enif_make_badarg(env); - } - - if (!get_bn_from_bin(env, argv[1], &pub_exp)) { - return enif_make_badarg(env); - } - - /* Make sure the public exponent is large enough (at least 3). - * Without this, RSA_generate_key_ex() can run forever. */ - three = BN_new(); - BN_set_word(three, 3); - success = BN_cmp(pub_exp, three); - BN_free(three); - if (success < 0) { - BN_free(pub_exp); - return enif_make_badarg(env); - } - - /* For large keys, prime generation can take many seconds. Set up - * the callback which we use to test whether the process has been - * interrupted. */ -#ifdef HAVE_OPAQUE_BN_GENCB - intr_cb = BN_GENCB_new(); -#else - intr_cb = &intr_cb_buf; -#endif - BN_GENCB_set(intr_cb, check_erlang_interrupt, env); - - rsa = RSA_new(); - success = RSA_generate_key_ex(rsa, modulus_bits, pub_exp, intr_cb); - BN_free(pub_exp); - -#ifdef HAVE_OPAQUE_BN_GENCB - BN_GENCB_free(intr_cb); -#endif - - if (!success) { - RSA_free(rsa); - return atom_error; - } - - result = put_rsa_private_key(env, rsa); - RSA_free(rsa); - - return result; -} - -static ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - /* RSA key generation can take a long time (>1 sec for a large - * modulus), so schedule it as a CPU-bound operation. */ - return enif_schedule_nif(env, "rsa_generate_key", - ERL_NIF_DIRTY_JOB_CPU_BOUND, - rsa_generate_key, argc, argv); -} - -static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */ - DH *dh_params = NULL; - int mpint; /* 0 or 4 */ - - { - ERL_NIF_TERM head, tail; - BIGNUM - *dh_p = NULL, - *dh_g = NULL, - *priv_key_in = NULL; - unsigned long - len = 0; - - if (!(get_bn_from_bin(env, argv[0], &priv_key_in) - || argv[0] == atom_undefined) - || !enif_get_list_cell(env, argv[1], &head, &tail) - || !get_bn_from_bin(env, head, &dh_p) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dh_g) - || !enif_is_empty_list(env, tail) - || !enif_get_int(env, argv[2], &mpint) || (mpint & ~4) - || !enif_get_ulong(env, argv[3], &len) - - /* Load dh_params with values to use by the generator. - Mem mgmnt transfered from dh_p etc to dh_params */ - || !(dh_params = DH_new()) - || (priv_key_in && !DH_set0_key(dh_params, NULL, priv_key_in)) - || !DH_set0_pqg(dh_params, dh_p, NULL, dh_g) - ) { - if (priv_key_in) BN_free(priv_key_in); - if (dh_p) BN_free(dh_p); - if (dh_g) BN_free(dh_g); - if (dh_params) DH_free(dh_params); - return enif_make_badarg(env); - } - - if (len) { - if (len < BN_num_bits(dh_p)) - DH_set_length(dh_params, len); - else { - if (priv_key_in) BN_free(priv_key_in); - if (dh_p) BN_free(dh_p); - if (dh_g) BN_free(dh_g); - if (dh_params) DH_free(dh_params); - return enif_make_badarg(env); - } - } - } - -#ifdef HAS_EVP_PKEY_CTX - { - EVP_PKEY_CTX *ctx; - EVP_PKEY *dhkey, *params; - int success; - - params = EVP_PKEY_new(); - success = EVP_PKEY_set1_DH(params, dh_params); /* set the key referenced by params to dh_params... */ - DH_free(dh_params); /* ...dh_params (and params) must be freed */ - if (!success) return atom_error; - - ctx = EVP_PKEY_CTX_new(params, NULL); - EVP_PKEY_free(params); - if (!ctx) { - return atom_error; - } - - if (!EVP_PKEY_keygen_init(ctx)) { - /* EVP_PKEY_CTX_free(ctx); */ - return atom_error; - } - - dhkey = EVP_PKEY_new(); - if (!EVP_PKEY_keygen(ctx, &dhkey)) { /* "performs a key generation operation, the ... */ - /*... generated key is written to ppkey." (=last arg) */ - /* EVP_PKEY_CTX_free(ctx); */ - /* EVP_PKEY_free(dhkey); */ - return atom_error; - } - - dh_params = EVP_PKEY_get1_DH(dhkey); /* return the referenced key. dh_params and dhkey must be freed */ - EVP_PKEY_free(dhkey); - if (!dh_params) { - /* EVP_PKEY_CTX_free(ctx); */ - return atom_error; - } - EVP_PKEY_CTX_free(ctx); - } -#else - if (!DH_generate_key(dh_params)) return atom_error; -#endif - { - unsigned char *pub_ptr, *prv_ptr; - int pub_len, prv_len; - ERL_NIF_TERM ret_pub, ret_prv; - const BIGNUM *pub_key_gen, *priv_key_gen; - - DH_get0_key(dh_params, - &pub_key_gen, &priv_key_gen); /* Get pub_key_gen and priv_key_gen. - "The values point to the internal representation of - the public key and private key values. This memory - should not be freed directly." says man */ - pub_len = BN_num_bytes(pub_key_gen); - prv_len = BN_num_bytes(priv_key_gen); - pub_ptr = enif_make_new_binary(env, pub_len+mpint, &ret_pub); - prv_ptr = enif_make_new_binary(env, prv_len+mpint, &ret_prv); - if (mpint) { - put_int32(pub_ptr, pub_len); pub_ptr += 4; - put_int32(prv_ptr, prv_len); prv_ptr += 4; - } - BN_bn2bin(pub_key_gen, pub_ptr); - BN_bn2bin(priv_key_gen, prv_ptr); - ERL_VALGRIND_MAKE_MEM_DEFINED(pub_ptr, pub_len); - ERL_VALGRIND_MAKE_MEM_DEFINED(prv_ptr, prv_len); - - DH_free(dh_params); - - return enif_make_tuple2(env, ret_pub, ret_prv); - } -} - -static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */ - BIGNUM *other_pub_key = NULL, - *dh_p = NULL, - *dh_g = NULL; - DH *dh_priv = DH_new(); - - /* Check the arguments and get - my private key (dh_priv), - the peer's public key (other_pub_key), - the parameters p & q - */ - - { - BIGNUM *dummy_pub_key = NULL, - *priv_key = NULL; - ERL_NIF_TERM head, tail; - - if (!get_bn_from_bin(env, argv[0], &other_pub_key) - || !get_bn_from_bin(env, argv[1], &priv_key) - || !enif_get_list_cell(env, argv[2], &head, &tail) - || !get_bn_from_bin(env, head, &dh_p) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dh_g) - || !enif_is_empty_list(env, tail) - - /* Note: DH_set0_key() does not allow setting only the - * private key, although DH_compute_key() does not use the - * public key. Work around this limitation by setting - * the public key to a copy of the private key. - */ - || !(dummy_pub_key = BN_dup(priv_key)) - || !DH_set0_key(dh_priv, dummy_pub_key, priv_key) - || !DH_set0_pqg(dh_priv, dh_p, NULL, dh_g) - ) { - if (dh_p) BN_free(dh_p); - if (dh_g) BN_free(dh_g); - if (other_pub_key) BN_free(other_pub_key); - if (dummy_pub_key) BN_free(dummy_pub_key); - if (priv_key) BN_free(priv_key); - return enif_make_badarg(env); - } - } - { - ErlNifBinary ret_bin; - int size; - - enif_alloc_binary(DH_size(dh_priv), &ret_bin); - size = DH_compute_key(ret_bin.data, other_pub_key, dh_priv); - BN_free(other_pub_key); - DH_free(dh_priv); - if (size<=0) { - enif_release_binary(&ret_bin); - return atom_error; - } - - if (size != ret_bin.size) enif_realloc_binary(&ret_bin, size); - return enif_make_binary(env, &ret_bin); - } -} - - -static ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Multiplier, Verifier, Generator, Exponent, Prime) */ - BIGNUM *bn_verifier = NULL; - BIGNUM *bn_exponent = NULL, *bn_generator = NULL, *bn_prime = NULL, *bn_multiplier = NULL, *bn_result; - BN_CTX *bn_ctx; - unsigned char* ptr; - unsigned dlen; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - if (!get_bn_from_bin(env, argv[0], &bn_multiplier) - || !get_bn_from_bin(env, argv[1], &bn_verifier) - || !get_bn_from_bin(env, argv[2], &bn_generator) - || !get_bn_from_bin(env, argv[3], &bn_exponent) - || !get_bn_from_bin(env, argv[4], &bn_prime)) { - if (bn_multiplier) BN_free(bn_multiplier); - if (bn_verifier) BN_free(bn_verifier); - if (bn_generator) BN_free(bn_generator); - if (bn_exponent) BN_free(bn_exponent); - if (bn_prime) BN_free(bn_prime); - return enif_make_badarg(env); - } - - bn_result = BN_new(); - bn_ctx = BN_CTX_new(); - - /* B = k*v + g^b % N */ - - /* k * v */ - BN_mod_mul(bn_multiplier, bn_multiplier, bn_verifier, bn_prime, bn_ctx); - - /* g^b % N */ - BN_mod_exp(bn_result, bn_generator, bn_exponent, bn_prime, bn_ctx); - - /* k*v + g^b % N */ - BN_mod_add(bn_result, bn_result, bn_multiplier, bn_prime, bn_ctx); - - /* check that B % N != 0, reuse bn_multiplier */ - BN_nnmod(bn_multiplier, bn_result, bn_prime, bn_ctx); - if (BN_is_zero(bn_multiplier)) { - ret = atom_error; - } else { - dlen = BN_num_bytes(bn_result); - ptr = enif_make_new_binary(env, dlen, &ret); - BN_bn2bin(bn_result, ptr); - } - BN_free(bn_result); - BN_CTX_free(bn_ctx); - BN_free(bn_prime); - BN_free(bn_generator); - BN_free(bn_multiplier); - BN_free(bn_exponent); - BN_free(bn_verifier); - return ret; -} - -static ERL_NIF_TERM srp_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (a, u, B, Multiplier, Prime, Exponent, Generator) */ -/* - <premaster secret> = (B - (k * g^x)) ^ (a + (u * x)) % N -*/ - BIGNUM *bn_exponent = NULL, *bn_a = NULL; - BIGNUM *bn_u = NULL, *bn_multiplier = NULL, *bn_exp2, - *bn_base, *bn_prime = NULL, *bn_generator = NULL, - *bn_B = NULL, *bn_result; - BN_CTX *bn_ctx; - unsigned char* ptr; - unsigned dlen; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - if (!get_bn_from_bin(env, argv[0], &bn_a) - || !get_bn_from_bin(env, argv[1], &bn_u) - || !get_bn_from_bin(env, argv[2], &bn_B) - || !get_bn_from_bin(env, argv[3], &bn_multiplier) - || !get_bn_from_bin(env, argv[4], &bn_generator) - || !get_bn_from_bin(env, argv[5], &bn_exponent) - || !get_bn_from_bin(env, argv[6], &bn_prime)) - { - if (bn_exponent) BN_free(bn_exponent); - if (bn_a) BN_free(bn_a); - if (bn_u) BN_free(bn_u); - if (bn_B) BN_free(bn_B); - if (bn_multiplier) BN_free(bn_multiplier); - if (bn_generator) BN_free(bn_generator); - if (bn_prime) BN_free(bn_prime); - return enif_make_badarg(env); - } - - bn_ctx = BN_CTX_new(); - bn_result = BN_new(); - - /* check that B % N != 0 */ - BN_nnmod(bn_result, bn_B, bn_prime, bn_ctx); - if (BN_is_zero(bn_result)) { - BN_free(bn_exponent); - BN_free(bn_a); - BN_free(bn_generator); - BN_free(bn_prime); - BN_free(bn_u); - BN_free(bn_B); - BN_CTX_free(bn_ctx); - - return atom_error; - } - - /* (B - (k * g^x)) */ - bn_base = BN_new(); - BN_mod_exp(bn_result, bn_generator, bn_exponent, bn_prime, bn_ctx); - BN_mod_mul(bn_result, bn_multiplier, bn_result, bn_prime, bn_ctx); - BN_mod_sub(bn_base, bn_B, bn_result, bn_prime, bn_ctx); - - /* a + (u * x) */ - bn_exp2 = BN_new(); - BN_mul(bn_result, bn_u, bn_exponent, bn_ctx); - BN_add(bn_exp2, bn_a, bn_result); - - /* (B - (k * g^x)) ^ (a + (u * x)) % N */ - BN_mod_exp(bn_result, bn_base, bn_exp2, bn_prime, bn_ctx); - - dlen = BN_num_bytes(bn_result); - ptr = enif_make_new_binary(env, dlen, &ret); - BN_bn2bin(bn_result, ptr); - BN_free(bn_result); - BN_CTX_free(bn_ctx); - - BN_free(bn_multiplier); - BN_free(bn_exp2); - BN_free(bn_u); - BN_free(bn_exponent); - BN_free(bn_a); - BN_free(bn_B); - BN_free(bn_base); - BN_free(bn_generator); - BN_free(bn_prime); - return ret; -} - -static ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Verifier, b, u, A, Prime) */ -/* - <premaster secret> = (A * v^u) ^ b % N -*/ - BIGNUM *bn_b = NULL, *bn_verifier = NULL; - BIGNUM *bn_prime = NULL, *bn_A = NULL, *bn_u = NULL, *bn_base, *bn_result; - BN_CTX *bn_ctx; - unsigned char* ptr; - unsigned dlen; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - if (!get_bn_from_bin(env, argv[0], &bn_verifier) - || !get_bn_from_bin(env, argv[1], &bn_b) - || !get_bn_from_bin(env, argv[2], &bn_u) - || !get_bn_from_bin(env, argv[3], &bn_A) - || !get_bn_from_bin(env, argv[4], &bn_prime)) - { - if (bn_verifier) BN_free(bn_verifier); - if (bn_b) BN_free(bn_b); - if (bn_u) BN_free(bn_u); - if (bn_A) BN_free(bn_A); - if (bn_prime) BN_free(bn_prime); - return enif_make_badarg(env); - } - - bn_ctx = BN_CTX_new(); - bn_result = BN_new(); - - /* check that A % N != 0 */ - BN_nnmod(bn_result, bn_A, bn_prime, bn_ctx); - if (BN_is_zero(bn_result)) { - BN_free(bn_b); - BN_free(bn_verifier); - BN_free(bn_prime); - BN_free(bn_A); - BN_CTX_free(bn_ctx); - - return atom_error; - } - - /* (A * v^u) */ - bn_base = BN_new(); - BN_mod_exp(bn_base, bn_verifier, bn_u, bn_prime, bn_ctx); - BN_mod_mul(bn_base, bn_A, bn_base, bn_prime, bn_ctx); - - /* (A * v^u) ^ b % N */ - BN_mod_exp(bn_result, bn_base, bn_b, bn_prime, bn_ctx); - - dlen = BN_num_bytes(bn_result); - ptr = enif_make_new_binary(env, dlen, &ret); - BN_bn2bin(bn_result, ptr); - BN_free(bn_result); - BN_CTX_free(bn_ctx); - - BN_free(bn_u); - BN_free(bn_base); - BN_free(bn_verifier); - BN_free(bn_prime); - BN_free(bn_A); - BN_free(bn_b); - return ret; -} - -#if defined(HAVE_EC) -static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg) -{ - EC_KEY *key = NULL; - int c_arity = -1; - const ERL_NIF_TERM* curve; - ErlNifBinary seed; - BIGNUM *p = NULL; - BIGNUM *a = NULL; - BIGNUM *b = NULL; - BIGNUM *bn_order = NULL; - BIGNUM *cofactor = NULL; - EC_GROUP *group = NULL; - EC_POINT *point = NULL; - - /* {Field, Prime, Point, Order, CoFactor} = Curve */ - if (enif_get_tuple(env,curve_arg,&c_arity,&curve) - && c_arity == 5 - && get_bn_from_bin(env, curve[3], &bn_order) - && (curve[4] != atom_none && get_bn_from_bin(env, curve[4], &cofactor))) { - - int f_arity = -1; - const ERL_NIF_TERM* field; - int p_arity = -1; - const ERL_NIF_TERM* prime; - - long field_bits; - - /* {A, B, Seed} = Prime */ - if (!enif_get_tuple(env,curve[1],&p_arity,&prime) - || !get_bn_from_bin(env, prime[0], &a) - || !get_bn_from_bin(env, prime[1], &b)) - goto out_err; - - if (!enif_get_tuple(env,curve[0],&f_arity,&field)) - goto out_err; - - if (f_arity == 2 && field[0] == atom_prime_field) { - /* {prime_field, Prime} */ - - if (!get_bn_from_bin(env, field[1], &p)) - goto out_err; - - if (BN_is_negative(p) || BN_is_zero(p)) - goto out_err; - - field_bits = BN_num_bits(p); - if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) - goto out_err; - - /* create the EC_GROUP structure */ - group = EC_GROUP_new_curve_GFp(p, a, b, NULL); - - } else if (f_arity == 3 && field[0] == atom_characteristic_two_field) { -#if defined(OPENSSL_NO_EC2M) - enif_raise_exception(env, atom_notsup); - goto out_err; -#else - /* {characteristic_two_field, M, Basis} */ - - int b_arity = -1; - const ERL_NIF_TERM* basis; - unsigned int k1, k2, k3; - - if ((p = BN_new()) == NULL) - goto out_err; - - if (!enif_get_long(env, field[1], &field_bits) - || field_bits > OPENSSL_ECC_MAX_FIELD_BITS) - goto out_err; - - if (enif_get_tuple(env,field[2],&b_arity,&basis)) { - if (b_arity == 2 - && basis[0] == atom_tpbasis - && enif_get_uint(env, basis[1], &k1)) { - /* {tpbasis, k} = Basis */ - - if (!(field_bits > k1 && k1 > 0)) - goto out_err; - - /* create the polynomial */ - if (!BN_set_bit(p, (int)field_bits) - || !BN_set_bit(p, (int)k1) - || !BN_set_bit(p, 0)) - goto out_err; - - } else if (b_arity == 4 - && basis[0] == atom_ppbasis - && enif_get_uint(env, basis[1], &k1) - && enif_get_uint(env, basis[2], &k2) - && enif_get_uint(env, basis[3], &k3)) { - /* {ppbasis, k1, k2, k3} = Basis */ - - if (!(field_bits > k3 && k3 > k2 && k2 > k1 && k1 > 0)) - goto out_err; - - /* create the polynomial */ - if (!BN_set_bit(p, (int)field_bits) - || !BN_set_bit(p, (int)k1) - || !BN_set_bit(p, (int)k2) - || !BN_set_bit(p, (int)k3) - || !BN_set_bit(p, 0)) - goto out_err; - - } else - goto out_err; - } else if (field[2] == atom_onbasis) { - /* onbasis = Basis */ - /* no parameters */ - goto out_err; - - } else - goto out_err; - - group = EC_GROUP_new_curve_GF2m(p, a, b, NULL); -#endif - } else - goto out_err; - - if (!group) - goto out_err; - - if (enif_inspect_binary(env, prime[2], &seed)) { - EC_GROUP_set_seed(group, seed.data, seed.size); - } - - if (!term2point(env, curve[2], group, &point)) - goto out_err; - - if (BN_is_negative(bn_order) - || BN_is_zero(bn_order) - || BN_num_bits(bn_order) > (int)field_bits + 1) - goto out_err; - - if (!EC_GROUP_set_generator(group, point, bn_order, cofactor)) - goto out_err; - - EC_GROUP_set_asn1_flag(group, 0x0); - - key = EC_KEY_new(); - if (!key) - goto out_err; - EC_KEY_set_group(key, group); - } - else { - goto out_err; - } - - - goto out; - -out_err: - if (key) EC_KEY_free(key); - key = NULL; - -out: - /* some OpenSSL structures are mem-dup'ed into the key, - so we have to free our copies here */ - if (p) BN_free(p); - if (a) BN_free(a); - if (b) BN_free(b); - if (bn_order) BN_free(bn_order); - if (cofactor) BN_free(cofactor); - if (group) EC_GROUP_free(group); - if (point) EC_POINT_free(point); - - return key; -} - - -static ERL_NIF_TERM bn2term(ErlNifEnv* env, const BIGNUM *bn) -{ - unsigned dlen; - unsigned char* ptr; - ERL_NIF_TERM ret; - - if (!bn) - return atom_undefined; - - dlen = BN_num_bytes(bn); - ptr = enif_make_new_binary(env, dlen, &ret); - BN_bn2bin(bn, ptr); - ERL_VALGRIND_MAKE_MEM_DEFINED(ptr, dlen); - return ret; -} - -static ERL_NIF_TERM point2term(ErlNifEnv* env, - const EC_GROUP *group, - const EC_POINT *point, - point_conversion_form_t form) -{ - unsigned dlen; - ErlNifBinary bin; - - dlen = EC_POINT_point2oct(group, point, form, NULL, 0, NULL); - if (dlen == 0) - return atom_undefined; - - if (!enif_alloc_binary(dlen, &bin)) - return enif_make_badarg(env); - - if (!EC_POINT_point2oct(group, point, form, bin.data, bin.size, NULL)) { - enif_release_binary(&bin); - return enif_make_badarg(env); - } - ERL_VALGRIND_MAKE_MEM_DEFINED(bin.data, bin.size); - return enif_make_binary(env, &bin); -} - -static int term2point(ErlNifEnv* env, ERL_NIF_TERM term, - EC_GROUP *group, EC_POINT **pptr) -{ - int ret = 0; - ErlNifBinary bin; - EC_POINT *point; - - if (!enif_inspect_binary(env,term,&bin)) { - return 0; - } - - if ((*pptr = point = EC_POINT_new(group)) == NULL) { - return 0; - } - - /* set the point conversion form */ - EC_GROUP_set_point_conversion_form(group, (point_conversion_form_t)(bin.data[0] & ~0x01)); - - /* extract the ec point */ - if (!EC_POINT_oct2point(group, point, bin.data, bin.size, NULL)) { - EC_POINT_free(point); - *pptr = NULL; - } else - ret = 1; - - return ret; -} - -static int get_ec_key(ErlNifEnv* env, - ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub, - EC_KEY** res) -{ - EC_KEY *key = NULL; - BIGNUM *priv_key = NULL; - EC_POINT *pub_key = NULL; - EC_GROUP *group = NULL; - - if (!(priv == atom_undefined || get_bn_from_bin(env, priv, &priv_key)) - || !(pub == atom_undefined || enif_is_binary(env, pub))) { - goto out_err; - } - - key = ec_key_new(env, curve); - - if (!key) { - goto out_err; - } - - if (!group) - group = EC_GROUP_dup(EC_KEY_get0_group(key)); - - if (term2point(env, pub, group, &pub_key)) { - if (!EC_KEY_set_public_key(key, pub_key)) { - goto out_err; - } - } - if (priv != atom_undefined - && !BN_is_zero(priv_key)) { - if (!EC_KEY_set_private_key(key, priv_key)) - goto out_err; - - /* calculate public key (if necessary) */ - if (EC_KEY_get0_public_key(key) == NULL) - { - /* the public key was not included in the SEC1 private - * key => calculate the public key */ - pub_key = EC_POINT_new(group); - if (pub_key == NULL - || !EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group)) - || !EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, NULL) - || !EC_KEY_set_public_key(key, pub_key)) - goto out_err; - } - } - - goto out; - -out_err: - if (key) EC_KEY_free(key); - key = NULL; - -out: - /* some OpenSSL structures are mem-dup'ed into the key, - so we have to free our copies here */ - if (priv_key) BN_clear_free(priv_key); - if (pub_key) EC_POINT_free(pub_key); - if (group) EC_GROUP_free(group); - if (!key) - return 0; - *res = key; - return 1; -} -#endif /* HAVE_EC */ - -static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ -#if defined(HAVE_EC) - EC_KEY *key = NULL; - const EC_GROUP *group; - const EC_POINT *public_key; - ERL_NIF_TERM priv_key; - ERL_NIF_TERM pub_key = atom_undefined; - - if (!get_ec_key(env, argv[0], argv[1], atom_undefined, &key)) - goto badarg; - - if (argv[1] == atom_undefined) { - if (!EC_KEY_generate_key(key)) - goto badarg; - } - - group = EC_KEY_get0_group(key); - public_key = EC_KEY_get0_public_key(key); - - if (group && public_key) { - pub_key = point2term(env, group, public_key, - EC_KEY_get_conv_form(key)); - } - priv_key = bn2term(env, EC_KEY_get0_private_key(key)); - EC_KEY_free(key); - return enif_make_tuple2(env, pub_key, priv_key); - -badarg: - if (key) - EC_KEY_free(key); - return make_badarg_maybe(env); -#else - return atom_notsup; -#endif -} - -/* - (_OthersPublicKey, _MyPrivateKey) - (_OthersPublicKey, _MyEC_Point) -*/ -static ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -/* (OtherPublicKey, Curve, My) */ -{ -#if defined(HAVE_EC) - ERL_NIF_TERM ret; - unsigned char *p; - EC_KEY* key = NULL; - int field_size = 0; - int i; - EC_GROUP *group; - const BIGNUM *priv_key; - EC_POINT *my_ecpoint = NULL; - EC_KEY *other_ecdh = NULL; - - if (!get_ec_key(env, argv[1], argv[2], atom_undefined, &key)) - return make_badarg_maybe(env); - - group = EC_GROUP_dup(EC_KEY_get0_group(key)); - priv_key = EC_KEY_get0_private_key(key); - - if (!term2point(env, argv[0], group, &my_ecpoint)) { - goto out_err; - } - - if ((other_ecdh = EC_KEY_new()) == NULL - || !EC_KEY_set_group(other_ecdh, group) - || !EC_KEY_set_private_key(other_ecdh, priv_key)) - goto out_err; - - field_size = EC_GROUP_get_degree(group); - if (field_size <= 0) - goto out_err; - - p = enif_make_new_binary(env, (field_size+7)/8, &ret); - i = ECDH_compute_key(p, (field_size+7)/8, my_ecpoint, other_ecdh, NULL); - - if (i < 0) - goto out_err; -out: - if (group) EC_GROUP_free(group); - if (my_ecpoint) EC_POINT_free(my_ecpoint); - if (other_ecdh) EC_KEY_free(other_ecdh); - if (key) EC_KEY_free(key); - - return ret; - -out_err: - ret = enif_make_badarg(env); - goto out; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM evp_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) - /* (Curve, PeerBin, MyBin) */ -{ -#ifdef HAVE_ED_CURVE_DH - int type; - EVP_PKEY_CTX *ctx = NULL; - ErlNifBinary peer_bin, my_bin, key_bin; - EVP_PKEY *peer_key = NULL, *my_key = NULL; - size_t max_size; - - if (argv[0] == atom_x25519) type = EVP_PKEY_X25519; - else if (argv[0] == atom_x448) type = EVP_PKEY_X448; - else return enif_make_badarg(env); - - if (!enif_inspect_binary(env, argv[1], &peer_bin) || - !enif_inspect_binary(env, argv[2], &my_bin)) - goto return_badarg; - - if (!(my_key = EVP_PKEY_new_raw_private_key(type, NULL, my_bin.data, my_bin.size)) || - !(ctx = EVP_PKEY_CTX_new(my_key, NULL))) - goto return_badarg; - - if (!EVP_PKEY_derive_init(ctx)) - goto return_badarg; - - if (!(peer_key = EVP_PKEY_new_raw_public_key(type, NULL, peer_bin.data, peer_bin.size)) || - !EVP_PKEY_derive_set_peer(ctx, peer_key)) - goto return_badarg; - - if (!EVP_PKEY_derive(ctx, NULL, &max_size)) - goto return_badarg; - - if (!enif_alloc_binary(max_size, &key_bin) || - !EVP_PKEY_derive(ctx, key_bin.data, &key_bin.size)) - goto return_badarg; - - if (key_bin.size < max_size) { - size_t actual_size = key_bin.size; - if (!enif_realloc_binary(&key_bin, actual_size)) - goto return_badarg; - } - - EVP_PKEY_free(my_key); - EVP_PKEY_free(peer_key); - EVP_PKEY_CTX_free(ctx); - return enif_make_binary(env, &key_bin); - -return_badarg: - if (my_key) EVP_PKEY_free(my_key); - if (peer_key) EVP_PKEY_free(peer_key); - if (ctx) EVP_PKEY_CTX_free(ctx); - return enif_make_badarg(env); -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -/* (Curve) */ -{ -#ifdef HAVE_ED_CURVE_DH - int type; - EVP_PKEY_CTX *ctx = NULL; - EVP_PKEY *pkey = NULL; - ERL_NIF_TERM ret_pub, ret_prv; - size_t key_len; - - if (argv[0] == atom_x25519) type = EVP_PKEY_X25519; - else if (argv[0] == atom_x448) type = EVP_PKEY_X448; - else return enif_make_badarg(env); - - if (!(ctx = EVP_PKEY_CTX_new_id(type, NULL))) return enif_make_badarg(env); - - if (!EVP_PKEY_keygen_init(ctx)) goto return_error; - if (!EVP_PKEY_keygen(ctx, &pkey)) goto return_error; - - if (!EVP_PKEY_get_raw_public_key(pkey, NULL, &key_len)) goto return_error; - if (!EVP_PKEY_get_raw_public_key(pkey, - enif_make_new_binary(env, key_len, &ret_pub), - &key_len)) - goto return_error; - - if (!EVP_PKEY_get_raw_private_key(pkey, NULL, &key_len)) goto return_error; - if (!EVP_PKEY_get_raw_private_key(pkey, - enif_make_new_binary(env, key_len, &ret_prv), - &key_len)) - goto return_error; - - EVP_PKEY_free(pkey); - EVP_PKEY_CTX_free(ctx); - return enif_make_tuple2(env, ret_pub, ret_prv); - -return_error: - if (pkey) EVP_PKEY_free(pkey); - if (ctx) EVP_PKEY_CTX_free(ctx); - return atom_error; - -#else - return atom_notsup; -#endif -} - -/*================================================================*/ -#define PKEY_BADARG -1 -#define PKEY_NOTSUP 0 -#define PKEY_OK 1 - -typedef struct PKeyCryptOptions { - const EVP_MD *rsa_mgf1_md; - ErlNifBinary rsa_oaep_label; - const EVP_MD *rsa_oaep_md; - int rsa_padding; - const EVP_MD *signature_md; -} PKeyCryptOptions; - -typedef struct PKeySignOptions { - const EVP_MD *rsa_mgf1_md; - int rsa_padding; - int rsa_pss_saltlen; -} PKeySignOptions; - -static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM type, - const EVP_MD **md) -{ - struct digest_type_t *digp = NULL; - *md = NULL; - - if (type == atom_none && algorithm == atom_rsa) return PKEY_OK; -#ifdef HAVE_EDDSA - if (algorithm == atom_eddsa) return PKEY_OK; -#endif - digp = get_digest_type(type); - if (!digp) return PKEY_BADARG; - if (!digp->md.p) return PKEY_NOTSUP; - - *md = digp->md.p; - return PKEY_OK; -} - - -static int get_pkey_sign_digest(ErlNifEnv *env, ERL_NIF_TERM algorithm, - ERL_NIF_TERM type, ERL_NIF_TERM data, - unsigned char *md_value, const EVP_MD **mdp, - unsigned char **tbsp, size_t *tbslenp) -{ - int i; - const ERL_NIF_TERM *tpl_terms; - int tpl_arity; - ErlNifBinary tbs_bin; - EVP_MD_CTX *mdctx; - const EVP_MD *md = *mdp; - unsigned char *tbs = *tbsp; - size_t tbslen = *tbslenp; - unsigned int tbsleni; - - if ((i = get_pkey_digest_type(env, algorithm, type, &md)) != PKEY_OK) { - return i; - } - if (enif_get_tuple(env, data, &tpl_arity, &tpl_terms)) { - if (tpl_arity != 2 || tpl_terms[0] != atom_digest - || !enif_inspect_binary(env, tpl_terms[1], &tbs_bin) - || (md != NULL && tbs_bin.size != EVP_MD_size(md))) { - return PKEY_BADARG; - } - /* We have a digest (= hashed text) in tbs_bin */ - tbs = tbs_bin.data; - tbslen = tbs_bin.size; - } else if (md == NULL) { - if (!enif_inspect_binary(env, data, &tbs_bin)) { - return PKEY_BADARG; - } - /* md == NULL, that is no hashing because DigestType argument was atom_none */ - tbs = tbs_bin.data; - tbslen = tbs_bin.size; - } else { - if (!enif_inspect_binary(env, data, &tbs_bin)) { - return PKEY_BADARG; - } - /* We have the cleartext in tbs_bin and the hash algo info in md */ - tbs = md_value; - mdctx = EVP_MD_CTX_create(); - if (!mdctx) { - return PKEY_BADARG; - } - /* Looks well, now hash the plain text into a digest according to md */ - if (EVP_DigestInit_ex(mdctx, md, NULL) <= 0) { - EVP_MD_CTX_destroy(mdctx); - return PKEY_BADARG; - } - if (EVP_DigestUpdate(mdctx, tbs_bin.data, tbs_bin.size) <= 0) { - EVP_MD_CTX_destroy(mdctx); - return PKEY_BADARG; - } - if (EVP_DigestFinal_ex(mdctx, tbs, &tbsleni) <= 0) { - EVP_MD_CTX_destroy(mdctx); - return PKEY_BADARG; - } - tbslen = (size_t)(tbsleni); - EVP_MD_CTX_destroy(mdctx); - } - - *mdp = md; - *tbsp = tbs; - *tbslenp = tbslen; - - return PKEY_OK; -} - - -static int get_pkey_sign_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, - const EVP_MD *md, PKeySignOptions *opt) -{ - ERL_NIF_TERM head, tail; - const ERL_NIF_TERM *tpl_terms; - int tpl_arity; - const EVP_MD *opt_md; - int i; - - if (!enif_is_list(env, options)) { - return PKEY_BADARG; - } - - /* defaults */ - if (algorithm == atom_rsa) { - opt->rsa_mgf1_md = NULL; - opt->rsa_padding = RSA_PKCS1_PADDING; - opt->rsa_pss_saltlen = -2; - } - - if (enif_is_empty_list(env, options)) { - return PKEY_OK; - } - - if (algorithm == atom_rsa) { - tail = options; - while (enif_get_list_cell(env, tail, &head, &tail)) { - if (enif_get_tuple(env, head, &tpl_arity, &tpl_terms) && tpl_arity == 2) { - if (tpl_terms[0] == atom_rsa_mgf1_md && enif_is_atom(env, tpl_terms[1])) { - i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); - if (i != PKEY_OK) { - return i; - } - opt->rsa_mgf1_md = opt_md; - } else if (tpl_terms[0] == atom_rsa_padding) { - if (tpl_terms[1] == atom_rsa_pkcs1_padding) { - opt->rsa_padding = RSA_PKCS1_PADDING; - } else if (tpl_terms[1] == atom_rsa_pkcs1_pss_padding) { -#ifdef HAVE_RSA_PKCS1_PSS_PADDING - opt->rsa_padding = RSA_PKCS1_PSS_PADDING; - if (opt->rsa_mgf1_md == NULL) { - opt->rsa_mgf1_md = md; - } -#else - return PKEY_NOTSUP; -#endif - } else if (tpl_terms[1] == atom_rsa_x931_padding) { - opt->rsa_padding = RSA_X931_PADDING; - } else if (tpl_terms[1] == atom_rsa_no_padding) { - opt->rsa_padding = RSA_NO_PADDING; - } else { - return PKEY_BADARG; - } - } else if (tpl_terms[0] == atom_rsa_pss_saltlen) { - if (!enif_get_int(env, tpl_terms[1], &(opt->rsa_pss_saltlen)) - || opt->rsa_pss_saltlen < -2) { - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } - } - } else { - return PKEY_BADARG; - } - - return PKEY_OK; -} - - -#ifdef HAS_ENGINE_SUPPORT -static int get_engine_and_key_id(ErlNifEnv *env, ERL_NIF_TERM key, char ** id, ENGINE **e) -{ - ERL_NIF_TERM engine_res, key_id_term; - struct engine_ctx *ctx; - ErlNifBinary key_id_bin; - - if (!enif_get_map_value(env, key, atom_engine, &engine_res) || - !enif_get_resource(env, engine_res, engine_ctx_rtype, (void**)&ctx) || - !enif_get_map_value(env, key, atom_key_id, &key_id_term) || - !enif_inspect_binary(env, key_id_term, &key_id_bin)) { - return 0; - } - else { - *e = ctx->engine; - return zero_terminate(key_id_bin, id); - } -} - - -static char *get_key_password(ErlNifEnv *env, ERL_NIF_TERM key) { - ERL_NIF_TERM tmp_term; - ErlNifBinary pwd_bin; - char *pwd = NULL; - if (enif_get_map_value(env, key, atom_password, &tmp_term) && - enif_inspect_binary(env, tmp_term, &pwd_bin) && - zero_terminate(pwd_bin, &pwd) - ) return pwd; - - return NULL; -} - -static int zero_terminate(ErlNifBinary bin, char **buf) { - *buf = enif_alloc(bin.size+1); - if (!*buf) - return 0; - memcpy(*buf, bin.data, bin.size); - *(*buf+bin.size) = 0; - return 1; -} -#endif - -static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, EVP_PKEY **pkey) -{ - if (enif_is_map(env, key)) { -#ifdef HAS_ENGINE_SUPPORT - /* Use key stored in engine */ - ENGINE *e; - char *id = NULL; - char *password; - - if (!get_engine_and_key_id(env, key, &id, &e)) - return PKEY_BADARG; - password = get_key_password(env, key); - *pkey = ENGINE_load_private_key(e, id, NULL, password); - if (password) enif_free(password); - enif_free(id); - if (!*pkey) - return PKEY_BADARG; -#else - return PKEY_BADARG; -#endif - } - else if (algorithm == atom_rsa) { - RSA *rsa = RSA_new(); - - if (!get_rsa_private_key(env, key, rsa)) { - RSA_free(rsa); - return PKEY_BADARG; - } - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_RSA(*pkey, rsa)) { - EVP_PKEY_free(*pkey); - RSA_free(rsa); - return PKEY_BADARG; - } - } else if (algorithm == atom_ecdsa) { -#if defined(HAVE_EC) - EC_KEY *ec = NULL; - const ERL_NIF_TERM *tpl_terms; - int tpl_arity; - - if (enif_get_tuple(env, key, &tpl_arity, &tpl_terms) && tpl_arity == 2 - && enif_is_tuple(env, tpl_terms[0]) && enif_is_binary(env, tpl_terms[1]) - && get_ec_key(env, tpl_terms[0], tpl_terms[1], atom_undefined, &ec)) { - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_EC_KEY(*pkey, ec)) { - EVP_PKEY_free(*pkey); - EC_KEY_free(ec); - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } -#else - return PKEY_NOTSUP; -#endif - } else if (algorithm == atom_eddsa) { -#if defined(HAVE_EDDSA) - if (!get_eddsa_key(env, 0, key, pkey)) { - return PKEY_BADARG; - } -#else - return PKEY_NOTSUP; -#endif - } else if (algorithm == atom_dss) { - DSA *dsa = DSA_new(); - - if (!get_dss_private_key(env, key, dsa)) { - DSA_free(dsa); - return PKEY_BADARG; - } - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_DSA(*pkey, dsa)) { - EVP_PKEY_free(*pkey); - DSA_free(dsa); - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } - - return PKEY_OK; -} - - -static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, - EVP_PKEY **pkey) -{ - if (enif_is_map(env, key)) { -#ifdef HAS_ENGINE_SUPPORT - /* Use key stored in engine */ - ENGINE *e; - char *id = NULL; - char *password; - - if (!get_engine_and_key_id(env, key, &id, &e)) - return PKEY_BADARG; - password = get_key_password(env, key); - *pkey = ENGINE_load_public_key(e, id, NULL, password); - if (password) enif_free(password); - enif_free(id); - if (!pkey) - return PKEY_BADARG; -#else - return PKEY_BADARG; -#endif - } else if (algorithm == atom_rsa) { - RSA *rsa = RSA_new(); - - if (!get_rsa_public_key(env, key, rsa)) { - RSA_free(rsa); - return PKEY_BADARG; - } - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_RSA(*pkey, rsa)) { - EVP_PKEY_free(*pkey); - RSA_free(rsa); - return PKEY_BADARG; - } - } else if (algorithm == atom_ecdsa) { -#if defined(HAVE_EC) - EC_KEY *ec = NULL; - const ERL_NIF_TERM *tpl_terms; - int tpl_arity; - - if (enif_get_tuple(env, key, &tpl_arity, &tpl_terms) && tpl_arity == 2 - && enif_is_tuple(env, tpl_terms[0]) && enif_is_binary(env, tpl_terms[1]) - && get_ec_key(env, tpl_terms[0], atom_undefined, tpl_terms[1], &ec)) { - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_EC_KEY(*pkey, ec)) { - EVP_PKEY_free(*pkey); - EC_KEY_free(ec); - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } -#else - return PKEY_NOTSUP; -#endif - } else if (algorithm == atom_eddsa) { -#if defined(HAVE_EDDSA) - if (!get_eddsa_key(env, 1, key, pkey)) { - return PKEY_BADARG; - } -#else - return PKEY_NOTSUP; -#endif - } else if (algorithm == atom_dss) { - DSA *dsa = DSA_new(); - - if (!get_dss_public_key(env, key, dsa)) { - DSA_free(dsa); - return PKEY_BADARG; - } - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_DSA(*pkey, dsa)) { - EVP_PKEY_free(*pkey); - DSA_free(dsa); - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } - - return PKEY_OK; -} - -static ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) -{/* (Algorithm, Type, Data|{digest,Digest}, Key|#{}, Options) */ - int i; - const EVP_MD *md = NULL; - unsigned char md_value[EVP_MAX_MD_SIZE]; - EVP_PKEY *pkey; -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX *ctx; - size_t siglen; -#else - unsigned len, siglen; -#endif - PKeySignOptions sig_opt; - ErlNifBinary sig_bin; /* signature */ - unsigned char *tbs; /* data to be signed */ - size_t tbslen; -/*char buf[1024]; -enif_get_atom(env,argv[0],buf,1024,ERL_NIF_LATIN1); printf("algo=%s ",buf); -enif_get_atom(env,argv[1],buf,1024,ERL_NIF_LATIN1); printf("hash=%s ",buf); -printf("\r\n"); -*/ - -#ifndef HAS_ENGINE_SUPPORT - if (enif_is_map(env, argv[3])) { - return atom_notsup; - } -#endif - - i = get_pkey_sign_digest(env, argv[0], argv[1], argv[2], md_value, &md, &tbs, &tbslen); - if (i != PKEY_OK) { - if (i == PKEY_NOTSUP) - return atom_notsup; - else - return enif_make_badarg(env); - } - - i = get_pkey_sign_options(env, argv[0], argv[4], md, &sig_opt); - if (i != PKEY_OK) { - if (i == PKEY_NOTSUP) - return atom_notsup; - else - return enif_make_badarg(env); - } - - if (get_pkey_private_key(env, argv[0], argv[3], &pkey) != PKEY_OK) { - return enif_make_badarg(env); - } - -#ifdef HAS_EVP_PKEY_CTX - ctx = EVP_PKEY_CTX_new(pkey, NULL); - if (!ctx) goto badarg; - - if (argv[0] != atom_eddsa) { - if (EVP_PKEY_sign_init(ctx) <= 0) goto badarg; - if (md != NULL && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) goto badarg; - } - - if (argv[0] == atom_rsa) { - if (EVP_PKEY_CTX_set_rsa_padding(ctx, sig_opt.rsa_padding) <= 0) goto badarg; -# ifdef HAVE_RSA_PKCS1_PSS_PADDING - if (sig_opt.rsa_padding == RSA_PKCS1_PSS_PADDING) { - if (sig_opt.rsa_mgf1_md != NULL) { -# ifdef HAVE_RSA_MGF1_MD - if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, sig_opt.rsa_mgf1_md) <= 0) goto badarg; -# else - EVP_PKEY_CTX_free(ctx); - EVP_PKEY_free(pkey); - return atom_notsup; -# endif - } - if (sig_opt.rsa_pss_saltlen > -2 - && EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, sig_opt.rsa_pss_saltlen) <= 0) - goto badarg; - } -#endif - } - - if (argv[0] == atom_eddsa) { -#ifdef HAVE_EDDSA - EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); - if (!EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey)) { - if (mdctx) EVP_MD_CTX_free(mdctx); - goto badarg; - } - - if (!EVP_DigestSign(mdctx, NULL, &siglen, tbs, tbslen)) { - EVP_MD_CTX_free(mdctx); - goto badarg; - } - enif_alloc_binary(siglen, &sig_bin); - - if (!EVP_DigestSign(mdctx, sig_bin.data, &siglen, tbs, tbslen)) { - EVP_MD_CTX_free(mdctx); - goto badarg; - } - EVP_MD_CTX_free(mdctx); -#else - goto badarg; -#endif - } - else - { - if (EVP_PKEY_sign(ctx, NULL, &siglen, tbs, tbslen) <= 0) goto badarg; - enif_alloc_binary(siglen, &sig_bin); - - if (md != NULL) { - ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md)); - } - i = EVP_PKEY_sign(ctx, sig_bin.data, &siglen, tbs, tbslen); - } - - EVP_PKEY_CTX_free(ctx); -#else -/*printf("Old interface\r\n"); - */ - if (argv[0] == atom_rsa) { - RSA *rsa = EVP_PKEY_get1_RSA(pkey); - enif_alloc_binary(RSA_size(rsa), &sig_bin); - len = EVP_MD_size(md); - ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); - i = RSA_sign(md->type, tbs, len, sig_bin.data, &siglen, rsa); - RSA_free(rsa); - } else if (argv[0] == atom_dss) { - DSA *dsa = EVP_PKEY_get1_DSA(pkey); - enif_alloc_binary(DSA_size(dsa), &sig_bin); - len = EVP_MD_size(md); - ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); - i = DSA_sign(md->type, tbs, len, sig_bin.data, &siglen, dsa); - DSA_free(dsa); - } else if (argv[0] == atom_ecdsa) { -#if defined(HAVE_EC) - EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); - enif_alloc_binary(ECDSA_size(ec), &sig_bin); - len = EVP_MD_size(md); - ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); - i = ECDSA_sign(md->type, tbs, len, sig_bin.data, &siglen, ec); - EC_KEY_free(ec); -#else - EVP_PKEY_free(pkey); - return atom_notsup; -#endif - } else { - goto badarg; - } -#endif - - EVP_PKEY_free(pkey); - if (i == 1) { - ERL_VALGRIND_MAKE_MEM_DEFINED(sig_bin.data, siglen); - if (siglen != sig_bin.size) { - enif_realloc_binary(&sig_bin, siglen); - ERL_VALGRIND_ASSERT_MEM_DEFINED(sig_bin.data, siglen); - } - return enif_make_binary(env, &sig_bin); - } else { - enif_release_binary(&sig_bin); - return atom_error; - } - - badarg: -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX_free(ctx); -#endif - EVP_PKEY_free(pkey); - return enif_make_badarg(env); -} - - -static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) -{/* (Algorithm, Type, Data|{digest,Digest}, Signature, Key, Options) */ - int i; - const EVP_MD *md = NULL; - unsigned char md_value[EVP_MAX_MD_SIZE]; - EVP_PKEY *pkey; -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX *ctx; -#else -#endif - PKeySignOptions sig_opt; - ErlNifBinary sig_bin; /* signature */ - unsigned char *tbs; /* data to be signed */ - size_t tbslen; - -#ifndef HAS_ENGINE_SUPPORT - if (enif_is_map(env, argv[4])) { - return atom_notsup; - } -#endif - - if (!enif_inspect_binary(env, argv[3], &sig_bin)) { - return enif_make_badarg(env); - } - - i = get_pkey_sign_digest(env, argv[0], argv[1], argv[2], md_value, &md, &tbs, &tbslen); - if (i != PKEY_OK) { - if (i == PKEY_NOTSUP) - return atom_notsup; - else - return enif_make_badarg(env); - } - - i = get_pkey_sign_options(env, argv[0], argv[5], md, &sig_opt); - if (i != PKEY_OK) { - if (i == PKEY_NOTSUP) - return atom_notsup; - else - return enif_make_badarg(env); - } - - if (get_pkey_public_key(env, argv[0], argv[4], &pkey) != PKEY_OK) { - return enif_make_badarg(env); - } - -#ifdef HAS_EVP_PKEY_CTX -/* printf("EVP interface\r\n"); - */ - ctx = EVP_PKEY_CTX_new(pkey, NULL); - if (!ctx) goto badarg; - - if (argv[0] != atom_eddsa) { - if (EVP_PKEY_verify_init(ctx) <= 0) goto badarg; - if (md != NULL && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) goto badarg; - } - - if (argv[0] == atom_rsa) { - if (EVP_PKEY_CTX_set_rsa_padding(ctx, sig_opt.rsa_padding) <= 0) goto badarg; - if (sig_opt.rsa_padding == RSA_PKCS1_PSS_PADDING) { - if (sig_opt.rsa_mgf1_md != NULL) { -# ifdef HAVE_RSA_MGF1_MD - if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, sig_opt.rsa_mgf1_md) <= 0) goto badarg; -# else - EVP_PKEY_CTX_free(ctx); - EVP_PKEY_free(pkey); - return atom_notsup; -# endif - } - if (sig_opt.rsa_pss_saltlen > -2 - && EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, sig_opt.rsa_pss_saltlen) <= 0) - goto badarg; - } - } - - if (argv[0] == atom_eddsa) { -#ifdef HAVE_EDDSA - EVP_MD_CTX* mdctx = EVP_MD_CTX_create(); - - if (!EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey)) { - if (mdctx) EVP_MD_CTX_destroy(mdctx); - goto badarg; - } - - i = EVP_DigestVerify(mdctx, sig_bin.data, sig_bin.size, tbs, tbslen); - EVP_MD_CTX_destroy(mdctx); -#else - goto badarg; -#endif - } - else - { - if (md != NULL) { - ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md)); - } - i = EVP_PKEY_verify(ctx, sig_bin.data, sig_bin.size, tbs, tbslen); - } - - EVP_PKEY_CTX_free(ctx); -#else -/*printf("Old interface\r\n"); -*/ - if (argv[0] == atom_rsa) { - RSA *rsa = EVP_PKEY_get1_RSA(pkey); - i = RSA_verify(md->type, tbs, tbslen, sig_bin.data, sig_bin.size, rsa); - RSA_free(rsa); - } else if (argv[0] == atom_dss) { - DSA *dsa = EVP_PKEY_get1_DSA(pkey); - i = DSA_verify(0, tbs, tbslen, sig_bin.data, sig_bin.size, dsa); - DSA_free(dsa); - } else if (argv[0] == atom_ecdsa) { -#if defined(HAVE_EC) - EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); - i = ECDSA_verify(EVP_MD_type(md), tbs, tbslen, sig_bin.data, sig_bin.size, ec); - EC_KEY_free(ec); -#else - EVP_PKEY_free(pkey); - return atom_notsup; -#endif - } else { - goto badarg; - } -#endif - - EVP_PKEY_free(pkey); - if (i == 1) { - return atom_true; - } else { - return atom_false; - } - - badarg: -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX_free(ctx); -#endif - EVP_PKEY_free(pkey); - return enif_make_badarg(env); -} - - -/*--------------------------------*/ - -static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, - PKeyCryptOptions *opt) -{ - ERL_NIF_TERM head, tail; - const ERL_NIF_TERM *tpl_terms; - int tpl_arity; - const EVP_MD *opt_md; - int i; - - if (!enif_is_list(env, options)) { - return PKEY_BADARG; - } - - /* defaults */ - if (algorithm == atom_rsa) { - opt->rsa_mgf1_md = NULL; - opt->rsa_oaep_label.data = NULL; - opt->rsa_oaep_label.size = 0; - opt->rsa_oaep_md = NULL; - opt->rsa_padding = RSA_PKCS1_PADDING; - opt->signature_md = NULL; - } - - if (enif_is_empty_list(env, options)) { - return PKEY_OK; - } - - if (algorithm == atom_rsa) { - tail = options; - while (enif_get_list_cell(env, tail, &head, &tail)) { - if (enif_get_tuple(env, head, &tpl_arity, &tpl_terms) && tpl_arity == 2) { - if (tpl_terms[0] == atom_rsa_padding - || tpl_terms[0] == atom_rsa_pad /* Compatibility */ - ) { - if (tpl_terms[1] == atom_rsa_pkcs1_padding) { - opt->rsa_padding = RSA_PKCS1_PADDING; -#ifdef HAVE_RSA_OAEP_PADDING - } else if (tpl_terms[1] == atom_rsa_pkcs1_oaep_padding) { - opt->rsa_padding = RSA_PKCS1_OAEP_PADDING; -#endif -#ifdef HAVE_RSA_SSLV23_PADDING - } else if (tpl_terms[1] == atom_rsa_sslv23_padding) { - opt->rsa_padding = RSA_SSLV23_PADDING; -#endif - } else if (tpl_terms[1] == atom_rsa_x931_padding) { - opt->rsa_padding = RSA_X931_PADDING; - } else if (tpl_terms[1] == atom_rsa_no_padding) { - opt->rsa_padding = RSA_NO_PADDING; - } else { - return PKEY_BADARG; - } - } else if (tpl_terms[0] == atom_signature_md && enif_is_atom(env, tpl_terms[1])) { - i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); - if (i != PKEY_OK) { - return i; - } - opt->signature_md = opt_md; - } else if (tpl_terms[0] == atom_rsa_mgf1_md && enif_is_atom(env, tpl_terms[1])) { -#ifndef HAVE_RSA_MGF1_MD - if (tpl_terms[1] != atom_sha) - return PKEY_NOTSUP; -#endif - i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); - if (i != PKEY_OK) { - return i; - } - opt->rsa_mgf1_md = opt_md; - } else if (tpl_terms[0] == atom_rsa_oaep_label - && enif_inspect_binary(env, tpl_terms[1], &(opt->rsa_oaep_label))) { -#ifdef HAVE_RSA_OAEP_MD - continue; -#else - return PKEY_NOTSUP; -#endif - } else if (tpl_terms[0] == atom_rsa_oaep_md && enif_is_atom(env, tpl_terms[1])) { -#ifndef HAVE_RSA_OAEP_MD - if (tpl_terms[1] != atom_sha) - return PKEY_NOTSUP; -#endif - i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); - if (i != PKEY_OK) { - return i; - } - opt->rsa_oaep_md = opt_md; - } else { - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } - } - } else { - return PKEY_BADARG; - } - - return PKEY_OK; -} - -static size_t size_of_RSA(EVP_PKEY *pkey) { - size_t tmplen; - RSA *rsa = EVP_PKEY_get1_RSA(pkey); - if (rsa == NULL) return 0; - tmplen = RSA_size(rsa); - RSA_free(rsa); - return tmplen; -} - -static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) -{/* (Algorithm, Data, PublKey=[E,N]|[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Options, IsPrivate, IsEncrypt) */ - int i; - EVP_PKEY *pkey; -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX *ctx; -#else - RSA *rsa; -#endif - PKeyCryptOptions crypt_opt; - ErlNifBinary in_bin, out_bin, tmp_bin; - size_t outlen; -#ifdef HAVE_RSA_SSLV23_PADDING - size_t tmplen; -#endif - int is_private = (argv[4] == atom_true), - is_encrypt = (argv[5] == atom_true); - int algo_init = 0; - -/* char algo[1024]; */ - -#ifndef HAS_ENGINE_SUPPORT - if (enif_is_map(env, argv[2])) { - return atom_notsup; - } -#endif - - if (!enif_inspect_binary(env, argv[1], &in_bin)) { - return enif_make_badarg(env); - } - - i = get_pkey_crypt_options(env, argv[0], argv[3], &crypt_opt); - if (i != PKEY_OK) { - if (i == PKEY_NOTSUP) - return atom_notsup; - else - return enif_make_badarg(env); - } - - if (is_private) { - if (get_pkey_private_key(env, argv[0], argv[2], &pkey) != PKEY_OK) { - return enif_make_badarg(env); - } - } else { - if (get_pkey_public_key(env, argv[0], argv[2], &pkey) != PKEY_OK) { - return enif_make_badarg(env); - } - } - - out_bin.data = NULL; - out_bin.size = 0; - tmp_bin.data = NULL; - tmp_bin.size = 0; - -#ifdef HAS_EVP_PKEY_CTX - ctx = EVP_PKEY_CTX_new(pkey, NULL); - if (!ctx) goto badarg; - -/* enif_get_atom(env,argv[0],algo,1024,ERL_NIF_LATIN1); */ - - if (is_private) { - if (is_encrypt) { - /* private encrypt */ - if ((algo_init=EVP_PKEY_sign_init(ctx)) <= 0) { - /* fprintf(stderr,"BADARG %s private encrypt algo_init=%d %s:%d\r\n", algo, algo_init, __FILE__, __LINE__); */ - goto badarg; - } - } else { - /* private decrypt */ - if ((algo_init=EVP_PKEY_decrypt_init(ctx)) <= 0) { - /* fprintf(stderr,"BADARG %s private decrypt algo_init=%d %s:%d\r\n", algo, algo_init, __FILE__, __LINE__); */ - goto badarg; - } - } - } else { - if (is_encrypt) { - /* public encrypt */ - if ((algo_init=EVP_PKEY_encrypt_init(ctx)) <= 0) { - /* fprintf(stderr,"BADARG %s public encrypt algo_init=%d %s:%d\r\n", algo,algo_init,__FILE__, __LINE__); */ - goto badarg; - } - } else { - /* public decrypt */ - if ((algo_init=EVP_PKEY_verify_recover_init(ctx)) <= 0) { - /* fprintf(stderr,"BADARG %s public decrypt algo_init=%d %s:%d\r\n", algo,algo_init,__FILE__, __LINE__); */ - goto badarg; - } - } - } - - if (argv[0] == atom_rsa) { - if (crypt_opt.signature_md != NULL - && EVP_PKEY_CTX_set_signature_md(ctx, crypt_opt.signature_md) <= 0) - goto badarg; -#ifdef HAVE_RSA_SSLV23_PADDING - if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) { - if (is_encrypt) { - tmplen = size_of_RSA(pkey); - if (tmplen == 0) goto badarg; - if (!enif_alloc_binary(tmplen, &tmp_bin)) goto badarg; - if (RSA_padding_add_SSLv23(tmp_bin.data, tmplen, in_bin.data, in_bin.size) <= 0) - goto badarg; - in_bin = tmp_bin; - } - if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0) goto badarg; - } else -#endif - { - if (EVP_PKEY_CTX_set_rsa_padding(ctx, crypt_opt.rsa_padding) <= 0) goto badarg; - } -#ifdef HAVE_RSA_OAEP_MD - if (crypt_opt.rsa_padding == RSA_PKCS1_OAEP_PADDING) { - if (crypt_opt.rsa_oaep_md != NULL - && EVP_PKEY_CTX_set_rsa_oaep_md(ctx, crypt_opt.rsa_oaep_md) <= 0) - goto badarg; - if (crypt_opt.rsa_mgf1_md != NULL - && EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, crypt_opt.rsa_mgf1_md) <= 0) goto badarg; - if (crypt_opt.rsa_oaep_label.data != NULL && crypt_opt.rsa_oaep_label.size > 0) { - unsigned char *label_copy = NULL; - label_copy = OPENSSL_malloc(crypt_opt.rsa_oaep_label.size); - if (label_copy == NULL) goto badarg; - memcpy((void *)(label_copy), (const void *)(crypt_opt.rsa_oaep_label.data), - crypt_opt.rsa_oaep_label.size); - if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, - crypt_opt.rsa_oaep_label.size) <= 0) { - OPENSSL_free(label_copy); - label_copy = NULL; - goto badarg; - } - } - } -#endif - } - - if (is_private) { - if (is_encrypt) { - /* private_encrypt */ - i = EVP_PKEY_sign(ctx, NULL, &outlen, in_bin.data, in_bin.size); - } else { - /* private_decrypt */ - i = EVP_PKEY_decrypt(ctx, NULL, &outlen, in_bin.data, in_bin.size); - } - } else { - if (is_encrypt) { - /* public_encrypt */ - i = EVP_PKEY_encrypt(ctx, NULL, &outlen, in_bin.data, in_bin.size); - } else { - /* public_decrypt */ - i = EVP_PKEY_verify_recover(ctx, NULL, &outlen, in_bin.data, in_bin.size); - } - } - /* fprintf(stderr,"i = %d %s:%d\r\n", i, __FILE__, __LINE__); */ - - if (i != 1) goto badarg; - - enif_alloc_binary(outlen, &out_bin); - - if (is_private) { - if (is_encrypt) { - /* private_encrypt */ - i = EVP_PKEY_sign(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); - } else { - /* private_decrypt */ - i = EVP_PKEY_decrypt(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); - } - } else { - if (is_encrypt) { - /* public_encrypt */ - i = EVP_PKEY_encrypt(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); - } else { - /* public_decrypt */ - i = EVP_PKEY_verify_recover(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); - } - } - -#else - /* Non-EVP cryptolib. Only support RSA */ - - if (argv[0] != atom_rsa) { - algo_init = -2; /* exitcode: notsup */ - goto badarg; - } - rsa = EVP_PKEY_get1_RSA(pkey); - enif_alloc_binary(RSA_size(rsa), &out_bin); - - if (is_private) { - if (is_encrypt) { - /* non-evp rsa private encrypt */ - ERL_VALGRIND_ASSERT_MEM_DEFINED(in_bin.data,in_bin.size); - i = RSA_private_encrypt(in_bin.size, in_bin.data, - out_bin.data, rsa, crypt_opt.rsa_padding); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); - } - } else { - /* non-evp rsa private decrypt */ - i = RSA_private_decrypt(in_bin.size, in_bin.data, - out_bin.data, rsa, crypt_opt.rsa_padding); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); - enif_realloc_binary(&out_bin, i); - } - } - } else { - if (is_encrypt) { - /* non-evp rsa public encrypt */ - ERL_VALGRIND_ASSERT_MEM_DEFINED(in_bin.data,in_bin.size); - i = RSA_public_encrypt(in_bin.size, in_bin.data, - out_bin.data, rsa, crypt_opt.rsa_padding); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); - } - } else { - /* non-evp rsa public decrypt */ - i = RSA_public_decrypt(in_bin.size, in_bin.data, - out_bin.data, rsa, crypt_opt.rsa_padding); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); - enif_realloc_binary(&out_bin, i); - } - } - } - - outlen = i; - RSA_free(rsa); -#endif - - if ((i > 0) && argv[0] == atom_rsa && !is_encrypt) { -#ifdef HAVE_RSA_SSLV23_PADDING - if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) { - unsigned char *p; - tmplen = size_of_RSA(pkey); - if (tmplen == 0) goto badarg; - if (!enif_alloc_binary(tmplen, &tmp_bin)) - goto badarg; - p = out_bin.data; - p++; - i = RSA_padding_check_SSLv23(tmp_bin.data, tmplen, p, out_bin.size - 1, tmplen); - if (i >= 0) { - outlen = i; - in_bin = out_bin; - out_bin = tmp_bin; - tmp_bin = in_bin; - i = 1; - } - } -#endif - } - - if (tmp_bin.data != NULL) { - enif_release_binary(&tmp_bin); - } - -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX_free(ctx); -#else -#endif - EVP_PKEY_free(pkey); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, outlen); - if (outlen != out_bin.size) { - enif_realloc_binary(&out_bin, outlen); - ERL_VALGRIND_ASSERT_MEM_DEFINED(out_bin.data, outlen); - } - return enif_make_binary(env, &out_bin); - } else { - enif_release_binary(&out_bin); - return atom_error; - } - - badarg: - if (out_bin.data != NULL) { - enif_release_binary(&out_bin); - } - if (tmp_bin.data != NULL) { - enif_release_binary(&tmp_bin); - } -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX_free(ctx); -#else -#endif - EVP_PKEY_free(pkey); - if (algo_init == -2) - return atom_notsup; - else - return enif_make_badarg(env); -} - - - -/*--------------------------------*/ -static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ /* (Algorithm, PrivKey | KeyMap) */ - EVP_PKEY *pkey; - ERL_NIF_TERM alg = argv[0]; - ERL_NIF_TERM result[8]; - if (get_pkey_private_key(env, alg, argv[1], &pkey) != PKEY_OK) { - return enif_make_badarg(env); - } - - if (alg == atom_rsa) { - const BIGNUM *n = NULL, *e = NULL, *d = NULL; - RSA *rsa = EVP_PKEY_get1_RSA(pkey); - if (rsa) { - RSA_get0_key(rsa, &n, &e, &d); - result[0] = bin_from_bn(env, e); // Exponent E - result[1] = bin_from_bn(env, n); // Modulus N = p*q - RSA_free(rsa); - EVP_PKEY_free(pkey); - return enif_make_list_from_array(env, result, 2); - } - - } else if (argv[0] == atom_dss) { - const BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL; - DSA *dsa = EVP_PKEY_get1_DSA(pkey); - if (dsa) { - DSA_get0_pqg(dsa, &p, &q, &g); - DSA_get0_key(dsa, &pub_key, NULL); - result[0] = bin_from_bn(env, p); - result[1] = bin_from_bn(env, q); - result[2] = bin_from_bn(env, g); - result[3] = bin_from_bn(env, pub_key); - DSA_free(dsa); - EVP_PKEY_free(pkey); - return enif_make_list_from_array(env, result, 4); - } - - } else if (argv[0] == atom_ecdsa) { -#if defined(HAVE_EC) - /* not yet implemented - EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); - if (ec) { - / * Example of result: - { - Curve = {Field, Prime, Point, Order, CoFactor} = - { - Field = {prime_field,<<255,...,255>>}, - Prime = {<<255,...,252>>, - <<90,...,75>>, - <<196,...,144>> - }, - Point = <<4,...,245>>, - Order = <<255,...,81>>, - CoFactor = <<1>> - }, - Key = <<151,...,62>> - } - or - { - Curve = - {characteristic_two_field, - M, - Basis = {tpbasis, _} - | {ppbasis, k1, k2, k3} - }, - Key - } - * / - EVP_PKEY_free(pkey); - return enif_make_list_from_array(env, ..., ...); - */ -#endif - } - - if (pkey) EVP_PKEY_free(pkey); - return enif_make_badarg(env); -} - -/*================================================================*/ - -static ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary seed_bin; - - if (!enif_inspect_binary(env, argv[0], &seed_bin)) - return enif_make_badarg(env); - RAND_seed(seed_bin.data,seed_bin.size); - return atom_ok; -} - -/*================================================================*/ -/* Engine */ -/*================================================================*/ -static ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (EngineId) */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM ret; - ErlNifBinary engine_id_bin; - char *engine_id; - ENGINE *engine; - struct engine_ctx *ctx; - - // Get Engine Id - if(!enif_inspect_binary(env, argv[0], &engine_id_bin)) { - PRINTF_ERR0("engine_by_id_nif Leaved: badarg"); - return enif_make_badarg(env); - } else { - engine_id = enif_alloc(engine_id_bin.size+1); - (void) memcpy(engine_id, engine_id_bin.data, engine_id_bin.size); - engine_id[engine_id_bin.size] = '\0'; - } - - engine = ENGINE_by_id(engine_id); - if(!engine) { - enif_free(engine_id); - PRINTF_ERR0("engine_by_id_nif Leaved: {error, bad_engine_id}"); - return enif_make_tuple2(env, atom_error, atom_bad_engine_id); - } - - ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx)); - ctx->engine = engine; - ctx->id = engine_id; - - ret = enif_make_resource(env, ctx); - enif_release_resource(ctx); - - return enif_make_tuple2(env, atom_ok, ret); -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM ret = atom_ok; - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_init_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - if (!ENGINE_init(ctx->engine)) { - //ERR_print_errors_fp(stderr); - PRINTF_ERR0("engine_init_nif Leaved: {error, engine_init_failed}"); - return enif_make_tuple2(env, atom_error, atom_engine_init_failed); - } - - return ret; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_free_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - ENGINE_free(ctx->engine); - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_finish_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - ENGINE_finish(ctx->engine); - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* () */ -#ifdef HAS_ENGINE_SUPPORT - ENGINE_load_dynamic(); - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine, Commands) */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM ret = atom_ok; - unsigned int cmds_len = 0; - char **cmds = NULL; - struct engine_ctx *ctx; - int i, optional = 0; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - PRINTF_ERR1("Engine Id: %s\r\n", ENGINE_get_id(ctx->engine)); - - // Get Command List - if(!enif_get_list_length(env, argv[1], &cmds_len)) { - PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Bad Command List"); - return enif_make_badarg(env); - } else { - cmds_len *= 2; // Key-Value list from erlang - cmds = enif_alloc((cmds_len+1)*sizeof(char*)); - if(get_engine_load_cmd_list(env, argv[1], cmds, 0)) { - PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Couldn't read Command List"); - ret = enif_make_badarg(env); - goto error; - } - } - - if(!enif_get_int(env, argv[2], &optional)) { - PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Parameter optional not an integer"); - return enif_make_badarg(env); - } - - for(i = 0; i < cmds_len; i+=2) { - PRINTF_ERR2("Cmd: %s:%s\r\n", - cmds[i] ? cmds[i] : "(NULL)", - cmds[i+1] ? cmds[i+1] : "(NULL)"); - if(!ENGINE_ctrl_cmd_string(ctx->engine, cmds[i], cmds[i+1], optional)) { - PRINTF_ERR2("Command failed: %s:%s\r\n", - cmds[i] ? cmds[i] : "(NULL)", - cmds[i+1] ? cmds[i+1] : "(NULL)"); - //ENGINE_free(ctx->engine); - ret = enif_make_tuple2(env, atom_error, atom_ctrl_cmd_failed); - PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: {error, ctrl_cmd_failed}"); - goto error; - } - } - - error: - for(i = 0; cmds != NULL && cmds[i] != NULL; i++) - enif_free(cmds[i]); - enif_free(cmds); - return ret; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_add_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - if (!ENGINE_add(ctx->engine)) { - PRINTF_ERR0("engine_add_nif Leaved: {error, add_engine_failed}"); - return enif_make_tuple2(env, atom_error, atom_add_engine_failed); - } - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_remove_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - if (!ENGINE_remove(ctx->engine)) { - PRINTF_ERR0("engine_remove_nif Leaved: {error, remove_engine_failed}"); - return enif_make_tuple2(env, atom_error, atom_remove_engine_failed); - } - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine, EngineMethod) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - unsigned int method; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_register_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - // Get Method - if (!enif_get_uint(env, argv[1], &method)) { - PRINTF_ERR0("engine_register_nif Leaved: Parameter Method not an uint"); - return enif_make_badarg(env); - } - - switch(method) - { -#ifdef ENGINE_METHOD_RSA - case ENGINE_METHOD_RSA: - if (!ENGINE_register_RSA(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_DSA - case ENGINE_METHOD_DSA: - if (!ENGINE_register_DSA(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_DH - case ENGINE_METHOD_DH: - if (!ENGINE_register_DH(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_RAND - case ENGINE_METHOD_RAND: - if (!ENGINE_register_RAND(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_ECDH - case ENGINE_METHOD_ECDH: - if (!ENGINE_register_ECDH(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_ECDSA - case ENGINE_METHOD_ECDSA: - if (!ENGINE_register_ECDSA(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_STORE - case ENGINE_METHOD_STORE: - if (!ENGINE_register_STORE(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_CIPHERS - case ENGINE_METHOD_CIPHERS: - if (!ENGINE_register_ciphers(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_DIGESTS - case ENGINE_METHOD_DIGESTS: - if (!ENGINE_register_digests(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_PKEY_METHS - case ENGINE_METHOD_PKEY_METHS: - if (!ENGINE_register_pkey_meths(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_PKEY_ASN1_METHS - case ENGINE_METHOD_PKEY_ASN1_METHS: - if (!ENGINE_register_pkey_asn1_meths(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_EC - case ENGINE_METHOD_EC: - if (!ENGINE_register_EC(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif - default: - return enif_make_tuple2(env, atom_error, atom_engine_method_not_supported); - break; - } - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine, EngineMethod) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - unsigned int method; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_unregister_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - // Get Method - if (!enif_get_uint(env, argv[1], &method)) { - PRINTF_ERR0("engine_unregister_nif Leaved: Parameter Method not an uint"); - return enif_make_badarg(env); - } - - switch(method) - { -#ifdef ENGINE_METHOD_RSA - case ENGINE_METHOD_RSA: - ENGINE_unregister_RSA(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_DSA - case ENGINE_METHOD_DSA: - ENGINE_unregister_DSA(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_DH - case ENGINE_METHOD_DH: - ENGINE_unregister_DH(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_RAND - case ENGINE_METHOD_RAND: - ENGINE_unregister_RAND(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_ECDH - case ENGINE_METHOD_ECDH: - ENGINE_unregister_ECDH(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_ECDSA - case ENGINE_METHOD_ECDSA: - ENGINE_unregister_ECDSA(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_STORE - case ENGINE_METHOD_STORE: - ENGINE_unregister_STORE(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_CIPHERS - case ENGINE_METHOD_CIPHERS: - ENGINE_unregister_ciphers(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_DIGESTS - case ENGINE_METHOD_DIGESTS: - ENGINE_unregister_digests(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_PKEY_METHS - case ENGINE_METHOD_PKEY_METHS: - ENGINE_unregister_pkey_meths(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_PKEY_ASN1_METHS - case ENGINE_METHOD_PKEY_ASN1_METHS: - ENGINE_unregister_pkey_asn1_meths(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_EC - case ENGINE_METHOD_EC: - ENGINE_unregister_EC(ctx->engine); - break; -#endif - default: - break; - } - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM ret; - ENGINE *engine; - ErlNifBinary engine_bin; - struct engine_ctx *ctx; - - engine = ENGINE_get_first(); - if(!engine) { - enif_alloc_binary(0, &engine_bin); - engine_bin.size = 0; - return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin)); - } - - ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx)); - ctx->engine = engine; - ctx->id = NULL; - - ret = enif_make_resource(env, ctx); - enif_release_resource(ctx); - - return enif_make_tuple2(env, atom_ok, ret); -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM ret; - ENGINE *engine; - ErlNifBinary engine_bin; - struct engine_ctx *ctx, *next_ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_get_next_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - engine = ENGINE_get_next(ctx->engine); - if (!engine) { - enif_alloc_binary(0, &engine_bin); - engine_bin.size = 0; - return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin)); - } - - next_ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx)); - next_ctx->engine = engine; - next_ctx->id = NULL; - - ret = enif_make_resource(env, next_ctx); - enif_release_resource(next_ctx); - - return enif_make_tuple2(env, atom_ok, ret); -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - ErlNifBinary engine_id_bin; - const char *engine_id; - int size; - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_get_id_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - engine_id = ENGINE_get_id(ctx->engine); - if (!engine_id) { - enif_alloc_binary(0, &engine_id_bin); - engine_id_bin.size = 0; - return enif_make_binary(env, &engine_id_bin); - } - - size = strlen(engine_id); - enif_alloc_binary(size, &engine_id_bin); - engine_id_bin.size = size; - memcpy(engine_id_bin.data, engine_id, size); - - return enif_make_binary(env, &engine_id_bin); -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_get_name_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - ErlNifBinary engine_name_bin; - const char *engine_name; - int size; - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_get_id_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - engine_name = ENGINE_get_name(ctx->engine); - if (!engine_name) { - enif_alloc_binary(0, &engine_name_bin); - engine_name_bin.size = 0; - return enif_make_binary(env, &engine_name_bin); - } - - size = strlen(engine_name); - enif_alloc_binary(size, &engine_name_bin); - engine_name_bin.size = size; - memcpy(engine_name_bin.data, engine_name, size); - - return enif_make_binary(env, &engine_name_bin); -#else - return atom_notsup; -#endif -} - -#ifdef HAS_ENGINE_SUPPORT -static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i) -{ - ERL_NIF_TERM head, tail; - const ERL_NIF_TERM *tmp_tuple; - ErlNifBinary tmpbin; - int arity; - char* tmpstr; - - if(!enif_is_empty_list(env, term)) { - if(!enif_get_list_cell(env, term, &head, &tail)) { - cmds[i] = NULL; - return -1; - } else { - if(!enif_get_tuple(env, head, &arity, &tmp_tuple) || arity != 2) { - cmds[i] = NULL; - return -1; - } else { - if(!enif_inspect_binary(env, tmp_tuple[0], &tmpbin)) { - cmds[i] = NULL; - return -1; - } else { - tmpstr = enif_alloc(tmpbin.size+1); - (void) memcpy(tmpstr, tmpbin.data, tmpbin.size); - tmpstr[tmpbin.size] = '\0'; - cmds[i++] = tmpstr; - } - if(!enif_inspect_binary(env, tmp_tuple[1], &tmpbin)) { - cmds[i] = NULL; - return -1; - } else { - if(tmpbin.size == 0) - cmds[i++] = NULL; - else { - tmpstr = enif_alloc(tmpbin.size+1); - (void) memcpy(tmpstr, tmpbin.data, tmpbin.size); - tmpstr[tmpbin.size] = '\0'; - cmds[i++] = tmpstr; - } - } - return get_engine_load_cmd_list(env, tail, cmds, i); - } - } - } else { - cmds[i] = NULL; - return 0; - } -} -#endif - -static ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* () */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM method_array[12]; - int i = 0; - -#ifdef ENGINE_METHOD_RSA - method_array[i++] = atom_engine_method_rsa; -#endif -#ifdef ENGINE_METHOD_DSA - method_array[i++] = atom_engine_method_dsa; -#endif -#ifdef ENGINE_METHOD_DH - method_array[i++] = atom_engine_method_dh; -#endif -#ifdef ENGINE_METHOD_RAND - method_array[i++] = atom_engine_method_rand; -#endif -#ifdef ENGINE_METHOD_ECDH - method_array[i++] = atom_engine_method_ecdh; -#endif -#ifdef ENGINE_METHOD_ECDSA - method_array[i++] = atom_engine_method_ecdsa; -#endif -#ifdef ENGINE_METHOD_STORE - method_array[i++] = atom_engine_method_store; -#endif -#ifdef ENGINE_METHOD_CIPHERS - method_array[i++] = atom_engine_method_ciphers; -#endif -#ifdef ENGINE_METHOD_DIGESTS - method_array[i++] = atom_engine_method_digests; -#endif -#ifdef ENGINE_METHOD_PKEY_METHS - method_array[i++] = atom_engine_method_pkey_meths; -#endif -#ifdef ENGINE_METHOD_PKEY_ASN1_METHS - method_array[i++] = atom_engine_method_pkey_asn1_meths; -#endif -#ifdef ENGINE_METHOD_EC - method_array[i++] = atom_engine_method_ec; -#endif - - return enif_make_list_from_array(env, method_array, i); -#else - return atom_notsup; -#endif -} diff --git a/lib/crypto/c_src/crypto_callback.c b/lib/crypto/c_src/crypto_callback.c index 0cc7dd609d..0141ccd840 100644 --- a/lib/crypto/c_src/crypto_callback.c +++ b/lib/crypto/c_src/crypto_callback.c @@ -21,6 +21,7 @@ #include <stdio.h> #include <string.h> #include <openssl/opensslconf.h> +#include <stdint.h> #include <erl_nif.h> #include "crypto_callback.h" @@ -64,22 +65,36 @@ static void nomem(size_t size, const char* op) static void* crypto_alloc(size_t size CCB_FILE_LINE_ARGS) { - void *ret = enif_alloc(size); + void *ret; - if (!ret && size) - nomem(size, "allocate"); + if ((ret = enif_alloc(size)) == NULL) + goto err; return ret; + + err: + if (size) + nomem(size, "allocate"); + return NULL; } static void* crypto_realloc(void* ptr, size_t size CCB_FILE_LINE_ARGS) { - void* ret = enif_realloc(ptr, size); + void* ret; - if (!ret && size) - nomem(size, "reallocate"); + if ((ret = enif_realloc(ptr, size)) == NULL) + goto err; return ret; + + err: + if (size) + nomem(size, "reallocate"); + return NULL; } + static void crypto_free(void* ptr CCB_FILE_LINE_ARGS) { + if (ptr == NULL) + return; + enif_free(ptr); } @@ -160,19 +175,26 @@ DLLEXPORT struct crypto_callbacks* get_crypto_callbacks(int nlocks) #ifdef OPENSSL_THREADS if (nlocks > 0) { int i; - lock_vec = enif_alloc(nlocks*sizeof(*lock_vec)); - if (lock_vec==NULL) return NULL; - memset(lock_vec, 0, nlocks*sizeof(*lock_vec)); - + + if ((size_t)nlocks > SIZE_MAX / sizeof(*lock_vec)) + goto err; + if ((lock_vec = enif_alloc((size_t)nlocks * sizeof(*lock_vec))) == NULL) + goto err; + + memset(lock_vec, 0, (size_t)nlocks * sizeof(*lock_vec)); + for (i=nlocks-1; i>=0; --i) { - lock_vec[i] = enif_rwlock_create("crypto_stat"); - if (lock_vec[i]==NULL) return NULL; + if ((lock_vec[i] = enif_rwlock_create("crypto_stat")) == NULL) + goto err; } } #endif is_initialized = 1; } return &the_struct; + + err: + return NULL; } #ifdef HAVE_DYNAMIC_CRYPTO_LIB diff --git a/lib/crypto/c_src/dh.c b/lib/crypto/c_src/dh.c new file mode 100644 index 0000000000..38eb534d99 --- /dev/null +++ b/lib/crypto/c_src/dh.c @@ -0,0 +1,294 @@ +/* + * %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 "dh.h" +#include "bn.h" + +ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */ + DH *dh_params = NULL; + unsigned int mpint; /* 0 or 4 */ + ERL_NIF_TERM head, tail; + BIGNUM *dh_p = NULL; + BIGNUM *dh_p_shared; + BIGNUM *dh_g = NULL; + BIGNUM *priv_key_in = NULL; + unsigned long len = 0; + unsigned char *pub_ptr, *prv_ptr; + int pub_len, prv_len; + ERL_NIF_TERM ret_pub, ret_prv, ret; + const BIGNUM *pub_key_gen, *priv_key_gen; +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *dhkey = NULL, *params = NULL; +#endif + + ASSERT(argc == 4); + + if (argv[0] != atom_undefined) { + if (!get_bn_from_bin(env, argv[0], &priv_key_in)) + goto bad_arg; + } + if (!enif_get_list_cell(env, argv[1], &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &dh_p)) + goto bad_arg; + + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &dh_g)) + goto bad_arg; + + if (!enif_is_empty_list(env, tail)) + goto bad_arg; + + if (!enif_get_uint(env, argv[2], &mpint)) + goto bad_arg; + if (mpint != 0 && mpint != 4) + goto bad_arg; + + if (!enif_get_ulong(env, argv[3], &len)) + goto bad_arg; + if (len > LONG_MAX) + goto bad_arg; + + /* Load dh_params with values to use by the generator. + Mem mgmnt transfered from dh_p etc to dh_params */ + if ((dh_params = DH_new()) == NULL) + goto bad_arg; + if (priv_key_in) { + if (!DH_set0_key(dh_params, NULL, priv_key_in)) + goto bad_arg; + /* On success, dh_params owns priv_key_in */ + priv_key_in = NULL; + } + if (!DH_set0_pqg(dh_params, dh_p, NULL, dh_g)) + goto bad_arg; + dh_p_shared = dh_p; /* Don't free this because dh_params owns it */ + /* On success, dh_params owns dh_p and dh_g */ + dh_p = NULL; + dh_g = NULL; + + if (len) { + int bn_len; + + if ((bn_len = BN_num_bits(dh_p_shared)) < 0) + goto bad_arg; + dh_p_shared = NULL; /* dh_params owns the reference */ + if (len >= (size_t)bn_len) + goto bad_arg; + + if (!DH_set_length(dh_params, (long)len)) + goto bad_arg; + } + +#ifdef HAS_EVP_PKEY_CTX + if ((params = EVP_PKEY_new()) == NULL) + goto err; + + /* set the key referenced by params to dh_params... */ + if (EVP_PKEY_set1_DH(params, dh_params) != 1) + goto err; + + if ((ctx = EVP_PKEY_CTX_new(params, NULL)) == NULL) + goto err; + + if (EVP_PKEY_keygen_init(ctx) != 1) + goto err; + + if ((dhkey = EVP_PKEY_new()) == NULL) + goto err; + + /* key gen op, key written to ppkey (=last arg) */ + if (EVP_PKEY_keygen(ctx, &dhkey) != 1) + goto err; + + DH_free(dh_params); + if ((dh_params = EVP_PKEY_get1_DH(dhkey)) == NULL) + goto err; + +#else + if (!DH_generate_key(dh_params)) + goto err; +#endif + + DH_get0_key(dh_params, &pub_key_gen, &priv_key_gen); + + if ((pub_len = BN_num_bytes(pub_key_gen)) < 0) + goto err; + if ((prv_len = BN_num_bytes(priv_key_gen)) < 0) + goto err; + + if ((pub_ptr = enif_make_new_binary(env, (size_t)pub_len+mpint, &ret_pub)) == NULL) + goto err; + if ((prv_ptr = enif_make_new_binary(env, (size_t)prv_len+mpint, &ret_prv)) == NULL) + goto err; + + if (mpint) { + put_uint32(pub_ptr, (unsigned int)pub_len); + pub_ptr += 4; + + put_uint32(prv_ptr, (unsigned int)prv_len); + prv_ptr += 4; + } + + if (BN_bn2bin(pub_key_gen, pub_ptr) < 0) + goto err; + if (BN_bn2bin(priv_key_gen, prv_ptr) < 0) + goto err; + + ERL_VALGRIND_MAKE_MEM_DEFINED(pub_ptr, pub_len); + ERL_VALGRIND_MAKE_MEM_DEFINED(prv_ptr, prv_len); + + ret = enif_make_tuple2(env, ret_pub, ret_prv); + goto done; + + bad_arg: + ret = enif_make_badarg(env); + goto done; + + err: + ret = atom_error; + + done: + if (priv_key_in) + BN_free(priv_key_in); + if (dh_p) + BN_free(dh_p); + if (dh_g) + BN_free(dh_g); + if (dh_params) + DH_free(dh_params); + +#ifdef HAS_EVP_PKEY_CTX + if (ctx) + EVP_PKEY_CTX_free(ctx); + if (dhkey) + EVP_PKEY_free(dhkey); + if (params) + EVP_PKEY_free(params); +#endif + + return ret; +} + +ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */ + BIGNUM *other_pub_key = NULL; + BIGNUM *dh_p = NULL; + BIGNUM *dh_g = NULL; + BIGNUM *dummy_pub_key = NULL; + BIGNUM *priv_key = NULL; + DH *dh_priv = NULL; + ERL_NIF_TERM head, tail, ret; + ErlNifBinary ret_bin; + int size; + int ret_bin_alloc = 0; + int dh_size; + + /* Check the arguments and get + my private key (dh_priv), + the peer's public key (other_pub_key), + the parameters p & q + */ + ASSERT(argc == 3); + + if (!get_bn_from_bin(env, argv[0], &other_pub_key)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[1], &priv_key)) + goto bad_arg; + + if (!enif_get_list_cell(env, argv[2], &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &dh_p)) + goto bad_arg; + + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &dh_g)) + goto bad_arg; + + if (!enif_is_empty_list(env, tail)) + goto bad_arg; + + /* Note: DH_set0_key() does not allow setting only the + * private key, although DH_compute_key() does not use the + * public key. Work around this limitation by setting + * the public key to a copy of the private key. + */ + if ((dummy_pub_key = BN_dup(priv_key)) == NULL) + goto err; + if ((dh_priv = DH_new()) == NULL) + goto err; + + if (!DH_set0_key(dh_priv, dummy_pub_key, priv_key)) + goto err; + /* dh_priv owns dummy_pub_key and priv_key now */ + dummy_pub_key = NULL; + priv_key = NULL; + + if (!DH_set0_pqg(dh_priv, dh_p, NULL, dh_g)) + goto err; + /* dh_priv owns dh_p and dh_g now */ + dh_p = NULL; + dh_g = NULL; + + if ((dh_size = DH_size(dh_priv)) < 0) + goto err; + if (!enif_alloc_binary((size_t)dh_size, &ret_bin)) + goto err; + ret_bin_alloc = 1; + + if ((size = DH_compute_key(ret_bin.data, other_pub_key, dh_priv)) < 0) + goto err; + if (size == 0) + goto err; + + if ((size_t)size != ret_bin.size) { + if (!enif_realloc_binary(&ret_bin, (size_t)size)) + goto err; + } + + ret = enif_make_binary(env, &ret_bin); + ret_bin_alloc = 0; + goto done; + + bad_arg: + err: + if (ret_bin_alloc) + enif_release_binary(&ret_bin); + ret = enif_make_badarg(env); + + done: + if (other_pub_key) + BN_free(other_pub_key); + if (priv_key) + BN_free(priv_key); + if (dh_p) + BN_free(dh_p); + if (dh_g) + BN_free(dh_g); + if (dummy_pub_key) + BN_free(dummy_pub_key); + if (dh_priv) + DH_free(dh_priv); + + return ret; +} diff --git a/lib/crypto/c_src/dh.h b/lib/crypto/c_src/dh.h new file mode 100644 index 0000000000..a996b0ea28 --- /dev/null +++ b/lib/crypto/c_src/dh.h @@ -0,0 +1,29 @@ +/* + * %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% + */ + +#ifndef E_DH_H__ +#define E_DH_H__ 1 + +#include "common.h" + +ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_DH_H__ */ diff --git a/lib/crypto/c_src/digest.c b/lib/crypto/c_src/digest.c new file mode 100644 index 0000000000..fec286c000 --- /dev/null +++ b/lib/crypto/c_src/digest.c @@ -0,0 +1,125 @@ +/* + * %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 "digest.h" + +static struct digest_type_t digest_types[] = +{ + {{"md4"}, {&EVP_md4}}, + {{"md5"}, {&EVP_md5}}, + {{"ripemd160"}, {&EVP_ripemd160}}, + {{"sha"}, {&EVP_sha1}}, + {{"sha224"}, +#ifdef HAVE_SHA224 + {&EVP_sha224} +#else + {NULL} +#endif + }, + {{"sha256"}, +#ifdef HAVE_SHA256 + {&EVP_sha256} +#else + {NULL} +#endif + }, + {{"sha384"}, +#ifdef HAVE_SHA384 + {&EVP_sha384} +#else + {NULL} +#endif + }, + {{"sha512"}, +#ifdef HAVE_SHA512 + {&EVP_sha512} +#else + {NULL} +#endif + }, + {{"sha3_224"}, +#ifdef HAVE_SHA3_224 + {&EVP_sha3_224} +#else + {NULL} +#endif + }, + {{"sha3_256"}, +#ifdef HAVE_SHA3_256 + {&EVP_sha3_256} +#else + {NULL} +#endif + }, + {{"sha3_384"}, +#ifdef HAVE_SHA3_384 + {&EVP_sha3_384} +#else + {NULL} +#endif + }, + {{"sha3_512"}, +#ifdef HAVE_SHA3_512 + {&EVP_sha3_512} +#else + {NULL} +#endif + }, + {{"blake2b"}, +#ifdef HAVE_BLAKE2 + {&EVP_blake2b512} +#else + {NULL} +#endif + }, + {{"blake2s"}, +#ifdef HAVE_BLAKE2 + {&EVP_blake2s256} +#else + {NULL} +#endif + }, + + {{NULL}, {NULL}} +}; + +void init_digest_types(ErlNifEnv* env) +{ + struct digest_type_t* p = digest_types; + + for (p = digest_types; p->type.str; p++) { + p->type.atom = enif_make_atom(env, p->type.str); + if (p->md.funcp) + p->md.p = p->md.funcp(); + } + p->type.atom = atom_false; /* end marker */ +} + +struct digest_type_t* get_digest_type(ERL_NIF_TERM type) +{ + struct digest_type_t* p = NULL; + for (p = digest_types; p->type.atom != atom_false; p++) { + if (type == p->type.atom) { + return p; + } + } + return NULL; +} + diff --git a/lib/crypto/c_src/digest.h b/lib/crypto/c_src/digest.h new file mode 100644 index 0000000000..06852416cf --- /dev/null +++ b/lib/crypto/c_src/digest.h @@ -0,0 +1,40 @@ +/* + * %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% + */ + +#ifndef E_DIGEST_H__ +#define E_DIGEST_H__ 1 + +#include "common.h" + +struct digest_type_t { + union { + const char* str; /* before init, NULL for end-of-table */ + ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ + }type; + union { + const EVP_MD* (*funcp)(void); /* before init, NULL if notsup */ + const EVP_MD* p; /* after init, NULL if notsup */ + }md; +}; + +void init_digest_types(ErlNifEnv* env); +struct digest_type_t* get_digest_type(ERL_NIF_TERM type); + +#endif /* E_DIGEST_H__ */ diff --git a/lib/crypto/c_src/dss.c b/lib/crypto/c_src/dss.c new file mode 100644 index 0000000000..9bf8eb3ce0 --- /dev/null +++ b/lib/crypto/c_src/dss.c @@ -0,0 +1,144 @@ +/* + * %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 "dss.h" +#include "bn.h" + +int get_dss_private_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa) +{ + /* key=[P,Q,G,KEY] */ + ERL_NIF_TERM head, tail; + BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL; + BIGNUM *dummy_pub_key = NULL, *priv_key = NULL; + + if (!enif_get_list_cell(env, key, &head, &tail)) + goto err; + if (!get_bn_from_bin(env, head, &dsa_p)) + goto err; + + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto err; + if (!get_bn_from_bin(env, head, &dsa_q)) + goto err; + + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto err; + if (!get_bn_from_bin(env, head, &dsa_g)) + goto err; + + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto err; + if (!get_bn_from_bin(env, head, &priv_key)) + goto err; + + if (!enif_is_empty_list(env, tail)) + goto err; + + /* Note: DSA_set0_key() does not allow setting only the + * private key, although DSA_sign() does not use the + * public key. Work around this limitation by setting + * the public key to a copy of the private key. + */ + if ((dummy_pub_key = BN_dup(priv_key)) == NULL) + goto err; + + if (!DSA_set0_pqg(dsa, dsa_p, dsa_q, dsa_g)) + goto err; + /* dsa takes ownership on success */ + dsa_p = NULL; + dsa_q = NULL; + dsa_g = NULL; + + if (!DSA_set0_key(dsa, dummy_pub_key, priv_key)) + goto err; + /* dsa takes ownership on success */ + dummy_pub_key = NULL; + priv_key = NULL; + + return 1; + + err: + if (dsa_p) + BN_free(dsa_p); + if (dsa_q) + BN_free(dsa_q); + if (dsa_g) + BN_free(dsa_g); + if (priv_key) + BN_free(priv_key); + if (dummy_pub_key) + BN_free(dummy_pub_key); + return 0; +} + +int get_dss_public_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa) +{ + /* key=[P, Q, G, Y] */ + ERL_NIF_TERM head, tail; + BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_y = NULL; + + if (!enif_get_list_cell(env, key, &head, &tail)) + goto err; + if (!get_bn_from_bin(env, head, &dsa_p)) + goto err; + + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto err; + if (!get_bn_from_bin(env, head, &dsa_q)) + goto err; + + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto err; + if (!get_bn_from_bin(env, head, &dsa_g)) + goto err; + + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto err; + if (!get_bn_from_bin(env, head, &dsa_y)) + goto err; + + if (!enif_is_empty_list(env,tail)) + goto err; + + if (!DSA_set0_pqg(dsa, dsa_p, dsa_q, dsa_g)) + goto err; + /* dsa takes ownership on success */ + dsa_p = NULL; + dsa_q = NULL; + dsa_g = NULL; + + if (!DSA_set0_key(dsa, dsa_y, NULL)) + goto err; + /* dsa takes ownership on success */ + dsa_y = NULL; + + return 1; + + err: + if (dsa_p) + BN_free(dsa_p); + if (dsa_q) + BN_free(dsa_q); + if (dsa_g) + BN_free(dsa_g); + if (dsa_y) + BN_free(dsa_y); + return 0; +} diff --git a/lib/crypto/c_src/dss.h b/lib/crypto/c_src/dss.h new file mode 100644 index 0000000000..3275657e98 --- /dev/null +++ b/lib/crypto/c_src/dss.h @@ -0,0 +1,29 @@ +/* + * %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% + */ + +#ifndef E_DSS_H__ +#define E_DSS_H__ 1 + +#include "common.h" + +int get_dss_private_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa); +int get_dss_public_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa); + +#endif /* E_DSS_H__ */ diff --git a/lib/crypto/c_src/ec.c b/lib/crypto/c_src/ec.c new file mode 100644 index 0000000000..51a3547694 --- /dev/null +++ b/lib/crypto/c_src/ec.c @@ -0,0 +1,414 @@ +/* + * %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 "ec.h" +#include "bn.h" + +#ifdef HAVE_EC +static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg); +static ERL_NIF_TERM point2term(ErlNifEnv* env, + const EC_GROUP *group, + const EC_POINT *point, + point_conversion_form_t form); + +ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env) +{ + ERL_NIF_TERM reason; + if (enif_has_pending_exception(env, &reason)) + return reason; /* dummy return value ignored */ + else + return enif_make_badarg(env); +} + +static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg) +{ + EC_KEY *key = NULL; + int c_arity = -1; + const ERL_NIF_TERM* curve; + ErlNifBinary seed; + BIGNUM *p = NULL; + BIGNUM *a = NULL; + BIGNUM *b = NULL; + BIGNUM *bn_order = NULL; + BIGNUM *cofactor = NULL; + EC_GROUP *group = NULL; + EC_POINT *point = NULL; + int f_arity = -1; + const ERL_NIF_TERM *field; + int p_arity = -1; + const ERL_NIF_TERM *prime; + long field_bits; + + /* {Field, Prime, Point, Order, CoFactor} = Curve */ + if (!enif_get_tuple(env, curve_arg, &c_arity, &curve)) + goto err; + if (c_arity != 5) + goto err; + if (!get_bn_from_bin(env, curve[3], &bn_order)) + goto err; + if (curve[4] != atom_none) { + if (!get_bn_from_bin(env, curve[4], &cofactor)) + goto err; + } + + /* {A, B, Seed} = Prime */ + if (!enif_get_tuple(env, curve[1], &p_arity, &prime)) + goto err; + if (!get_bn_from_bin(env, prime[0], &a)) + goto err; + if (!get_bn_from_bin(env, prime[1], &b)) + goto err; + + if (!enif_get_tuple(env, curve[0], &f_arity, &field)) + goto err; + + if (f_arity == 2 && field[0] == atom_prime_field) { + /* {prime_field, Prime} */ + if (!get_bn_from_bin(env, field[1], &p)) + goto err; + if (BN_is_negative(p)) + goto err; + if (BN_is_zero(p)) + goto err; + + field_bits = BN_num_bits(p); + if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) + goto err; + + /* create the EC_GROUP structure */ + if ((group = EC_GROUP_new_curve_GFp(p, a, b, NULL)) == NULL) + goto err; + + } else if (f_arity == 3 && field[0] == atom_characteristic_two_field) { +#if defined(OPENSSL_NO_EC2M) + enif_raise_exception(env, atom_notsup); + goto err; +#else + /* {characteristic_two_field, M, Basis} */ + int b_arity = -1; + const ERL_NIF_TERM* basis; + + if ((p = BN_new()) == NULL) + goto err; + if (!enif_get_long(env, field[1], &field_bits)) + goto err; + if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS || field_bits > INT_MAX) + goto err; + + if (enif_get_tuple(env, field[2], &b_arity, &basis)) { + if (b_arity == 2) { + unsigned int k1; + + if (basis[0] != atom_tpbasis) + goto err; + if (!enif_get_uint(env, basis[1], &k1)) + goto err; + + /* {tpbasis, k} = Basis */ + if (field_bits <= k1 || k1 == 0 || k1 > INT_MAX) + goto err; + + /* create the polynomial */ + if (!BN_set_bit(p, (int)field_bits)) + goto err; + if (!BN_set_bit(p, (int)k1)) + goto err; + if (!BN_set_bit(p, 0)) + goto err; + + } else if (b_arity == 4) { + unsigned int k1, k2, k3; + + if (basis[0] != atom_ppbasis) + goto err; + if (!enif_get_uint(env, basis[1], &k1)) + goto err; + if (!enif_get_uint(env, basis[2], &k2)) + goto err; + if (!enif_get_uint(env, basis[3], &k3)) + goto err; + + /* {ppbasis, k1, k2, k3} = Basis */ + if (field_bits <= k3 || k3 <= k2 || k2 <= k1 || k1 == 0 || k3 > INT_MAX || k2 > INT_MAX || k1 > INT_MAX) + goto err; + + /* create the polynomial */ + if (!BN_set_bit(p, (int)field_bits)) + goto err; + if (!BN_set_bit(p, (int)k1)) + goto err; + if (!BN_set_bit(p, (int)k2)) + goto err; + if (!BN_set_bit(p, (int)k3)) + goto err; + if (!BN_set_bit(p, 0)) + goto err; + + } else + goto err; + } else if (field[2] == atom_onbasis) { + /* onbasis = Basis */ + /* no parameters */ + goto err; + + } else + goto err; + + if ((group = EC_GROUP_new_curve_GF2m(p, a, b, NULL)) == NULL) + goto err; +#endif + } else + goto err; + + if (enif_inspect_binary(env, prime[2], &seed)) { + if (!EC_GROUP_set_seed(group, seed.data, seed.size)) + goto err; + } + + if (!term2point(env, curve[2], group, &point)) + goto err; + + if (BN_is_negative(bn_order)) + goto err; + if (BN_is_zero(bn_order)) + goto err; + if (BN_num_bits(bn_order) > (int)field_bits + 1) + goto err; + + if (!EC_GROUP_set_generator(group, point, bn_order, cofactor)) + goto err; + + EC_GROUP_set_asn1_flag(group, 0x0); + + if ((key = EC_KEY_new()) == NULL) + goto err; + + if (!EC_KEY_set_group(key, group)) + goto err; + + goto done; + + err: + if (key) + EC_KEY_free(key); + key = NULL; + + done: + /* some OpenSSL structures are mem-dup'ed into the key, + so we have to free our copies here */ + if (bn_order) + BN_free(bn_order); + if (cofactor) + BN_free(cofactor); + if (a) + BN_free(a); + if (b) + BN_free(b); + if (p) + BN_free(p); + if (group) + EC_GROUP_free(group); + if (point) + EC_POINT_free(point); + + return key; +} + +static ERL_NIF_TERM point2term(ErlNifEnv* env, + const EC_GROUP *group, + const EC_POINT *point, + point_conversion_form_t form) +{ + ERL_NIF_TERM ret; + size_t dlen; + ErlNifBinary bin; + int bin_alloc = 0; + + if ((dlen = EC_POINT_point2oct(group, point, form, NULL, 0, NULL)) == 0) + return atom_undefined; + + if (!enif_alloc_binary(dlen, &bin)) + goto err; + bin_alloc = 1; + + if (!EC_POINT_point2oct(group, point, form, bin.data, bin.size, NULL)) + goto err; + + ERL_VALGRIND_MAKE_MEM_DEFINED(bin.data, bin.size); + + ret = enif_make_binary(env, &bin); + bin_alloc = 0; + goto done; + + err: + if (bin_alloc) + enif_release_binary(&bin); + ret = enif_make_badarg(env); + + done: + return ret; +} + +int term2point(ErlNifEnv* env, ERL_NIF_TERM term, EC_GROUP *group, EC_POINT **pptr) +{ + ErlNifBinary bin; + EC_POINT *point = NULL; + + if (!enif_inspect_binary(env, term, &bin)) + goto err; + + if ((point = EC_POINT_new(group)) == NULL) + goto err; + + /* set the point conversion form */ + EC_GROUP_set_point_conversion_form(group, (point_conversion_form_t)(bin.data[0] & ~0x01)); + + /* extract the ec point */ + if (!EC_POINT_oct2point(group, point, bin.data, bin.size, NULL)) + goto err; + + *pptr = point; + return 1; + + err: + if (point) + EC_POINT_free(point); + return 0; +} + +int get_ec_key(ErlNifEnv* env, + ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub, + EC_KEY** res) +{ + EC_KEY *key = NULL; + BIGNUM *priv_key = NULL; + EC_POINT *pub_key = NULL; + EC_GROUP *group = NULL; + + if (priv != atom_undefined) { + if (!get_bn_from_bin(env, priv, &priv_key)) + goto err; + } + if (pub != atom_undefined) { + if (!enif_is_binary(env, pub)) + goto err; + } + + if ((key = ec_key_new(env, curve)) == NULL) + goto err; + + if ((group = EC_GROUP_dup(EC_KEY_get0_group(key))) == NULL) + goto err; + + if (term2point(env, pub, group, &pub_key)) { + if (!EC_KEY_set_public_key(key, pub_key)) + goto err; + } + + if (priv != atom_undefined && !BN_is_zero(priv_key)) { + if (!EC_KEY_set_private_key(key, priv_key)) + goto err; + + /* calculate public key (if necessary) */ + if (EC_KEY_get0_public_key(key) == NULL) { + /* the public key was not included in the SEC1 private + * key => calculate the public key */ + if ((pub_key = EC_POINT_new(group)) == NULL) + goto err; + if (!EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group))) + goto err; + if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, NULL)) + goto err; + if (!EC_KEY_set_public_key(key, pub_key)) + goto err; + } + } + goto done; + + err: + if (key) + EC_KEY_free(key); + key = NULL; + + done: + /* some OpenSSL structures are mem-dup'ed into the key, + so we have to free our copies here */ + if (priv_key) + BN_clear_free(priv_key); + if (group) + EC_GROUP_free(group); + if (pub_key) + EC_POINT_free(pub_key); + + if (key == NULL) + return 0; + + *res = key; + return 1; +} + +#endif /* HAVE_EC */ + +ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#if defined(HAVE_EC) + EC_KEY *key = NULL; + const EC_GROUP *group; + const EC_POINT *public_key; + ERL_NIF_TERM priv_key; + ERL_NIF_TERM pub_key; + ERL_NIF_TERM ret; + + if (!get_ec_key(env, argv[0], argv[1], atom_undefined, &key)) + goto bad_arg; + + if (argv[1] == atom_undefined) { + if (!EC_KEY_generate_key(key)) + goto err; + } + + group = EC_KEY_get0_group(key); + public_key = EC_KEY_get0_public_key(key); + + if (group == NULL || public_key == NULL) { + pub_key = atom_undefined; + + } else { + pub_key = point2term(env, group, public_key, + EC_KEY_get_conv_form(key)); + } + + priv_key = bn2term(env, EC_KEY_get0_private_key(key)); + ret = enif_make_tuple2(env, pub_key, priv_key); + goto done; + + err: + bad_arg: + ret = make_badarg_maybe(env); + + done: + if (key) + EC_KEY_free(key); + return ret; + +#else + return atom_notsup; +#endif +} diff --git a/lib/crypto/c_src/ec.h b/lib/crypto/c_src/ec.h new file mode 100644 index 0000000000..b7e1cc5a46 --- /dev/null +++ b/lib/crypto/c_src/ec.h @@ -0,0 +1,35 @@ +/* + * %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% + */ + +#ifndef E_EC_H__ +#define E_EC_H__ 1 + +#include "common.h" + +#if defined(HAVE_EC) +int get_ec_key(ErlNifEnv* env, ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub, + EC_KEY** res); +int term2point(ErlNifEnv* env, ERL_NIF_TERM term, EC_GROUP *group, EC_POINT **pptr); +ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env); +#endif + +ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_EC_H__ */ diff --git a/lib/crypto/c_src/ecdh.c b/lib/crypto/c_src/ecdh.c new file mode 100644 index 0000000000..9e3f460519 --- /dev/null +++ b/lib/crypto/c_src/ecdh.c @@ -0,0 +1,94 @@ +/* + * %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 "ecdh.h" +#include "ec.h" + +/* + (_OthersPublicKey, _MyPrivateKey) + (_OthersPublicKey, _MyEC_Point) +*/ +ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +/* (OtherPublicKey, Curve, My) */ +{ +#if defined(HAVE_EC) + ERL_NIF_TERM ret; + unsigned char *p; + EC_KEY* key = NULL; + int degree; + size_t field_size; + EC_GROUP *group = NULL; + const BIGNUM *priv_key; + EC_POINT *my_ecpoint = NULL; + EC_KEY *other_ecdh = NULL; + + ASSERT(argc == 3); + + if (!get_ec_key(env, argv[1], argv[2], atom_undefined, &key)) + goto bad_arg; + if ((group = EC_GROUP_dup(EC_KEY_get0_group(key))) == NULL) + goto bad_arg; + priv_key = EC_KEY_get0_private_key(key); + + if (!term2point(env, argv[0], group, &my_ecpoint)) { + goto err; + } + + if ((other_ecdh = EC_KEY_new()) == NULL) + goto err; + if (!EC_KEY_set_group(other_ecdh, group)) + goto err; + if (!EC_KEY_set_private_key(other_ecdh, priv_key)) + goto err; + + if ((degree = EC_GROUP_get_degree(group)) <= 0) + goto err; + + field_size = (size_t)degree; + if ((p = enif_make_new_binary(env, (field_size+7)/8, &ret)) == NULL) + goto err; + if (ECDH_compute_key(p, (field_size+7)/8, my_ecpoint, other_ecdh, NULL) < 1) + goto err; + + goto done; + + bad_arg: + ret = make_badarg_maybe(env); + goto done; + + err: + ret = enif_make_badarg(env); + + done: + if (group) + EC_GROUP_free(group); + if (my_ecpoint) + EC_POINT_free(my_ecpoint); + if (other_ecdh) + EC_KEY_free(other_ecdh); + if (key) + EC_KEY_free(key); + + return ret; + +#else + return atom_notsup; +#endif +} diff --git a/lib/crypto/c_src/ecdh.h b/lib/crypto/c_src/ecdh.h new file mode 100644 index 0000000000..5ed331e676 --- /dev/null +++ b/lib/crypto/c_src/ecdh.h @@ -0,0 +1,28 @@ +/* + * %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% + */ + +#ifndef E_ECDH_H__ +#define E_ECDH_H__ 1 + +#include "common.h" + +ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_ECDH_H__ */ diff --git a/lib/crypto/c_src/eddsa.c b/lib/crypto/c_src/eddsa.c new file mode 100644 index 0000000000..0c89f9f6db --- /dev/null +++ b/lib/crypto/c_src/eddsa.c @@ -0,0 +1,63 @@ +/* + * %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 "eddsa.h" + +#ifdef HAVE_EDDSA +int get_eddsa_key(ErlNifEnv* env, int public, ERL_NIF_TERM key, EVP_PKEY **pkey) +{ + /* key=[K] */ + EVP_PKEY *result; + ERL_NIF_TERM head, tail, tail2, algo; + ErlNifBinary bin; + int type; + + if (!enif_get_list_cell(env, key, &head, &tail)) + goto err; + if (!enif_inspect_binary(env, head, &bin)) + goto err; + if (!enif_get_list_cell(env, tail, &algo, &tail2)) + goto err; + if (!enif_is_empty_list(env, tail2)) + goto err; + + if (algo == atom_ed25519) { + type = EVP_PKEY_ED25519; + } else if (algo == atom_ed448) { + type = EVP_PKEY_ED448; + } else { + goto err; + } + + if (public) + result = EVP_PKEY_new_raw_public_key(type, NULL, bin.data, bin.size); + else + result = EVP_PKEY_new_raw_private_key(type, NULL, bin.data, bin.size); + + if (result == NULL) + goto err; + + *pkey = result; + return 1; + + err: + return 0; +} +#endif diff --git a/lib/crypto/c_src/eddsa.h b/lib/crypto/c_src/eddsa.h new file mode 100644 index 0000000000..4b30247cab --- /dev/null +++ b/lib/crypto/c_src/eddsa.h @@ -0,0 +1,30 @@ +/* + * %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% + */ + +#ifndef E_EDDSA_H__ +#define E_EDDSA_H__ 1 + +#include "common.h" + +#ifdef HAVE_EDDSA +int get_eddsa_key(ErlNifEnv* env, int public, ERL_NIF_TERM key, EVP_PKEY **pkey); +#endif + +#endif /* E_EDDSA_H__ */ diff --git a/lib/crypto/c_src/engine.c b/lib/crypto/c_src/engine.c new file mode 100644 index 0000000000..7ffbb9e70d --- /dev/null +++ b/lib/crypto/c_src/engine.c @@ -0,0 +1,842 @@ +/* + * %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 "engine.h" + +#ifdef HAS_ENGINE_SUPPORT +struct engine_ctx { + ENGINE *engine; + char *id; +}; + +#define ERROR_Term(Env, ReasonTerm) enif_make_tuple2((Env), atom_error, (ReasonTerm)) +#define ERROR_Atom(Env, ReasonString) ERROR_Term((Env), enif_make_atom((Env),(ReasonString))) + +static ErlNifResourceType* engine_ctx_rtype; + +static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i); +static int zero_terminate(ErlNifBinary bin, char **buf); + +static void engine_ctx_dtor(ErlNifEnv* env, struct engine_ctx* ctx) { + if (ctx == NULL) + return; + + PRINTF_ERR0("engine_ctx_dtor"); + if(ctx->id) { + PRINTF_ERR1(" non empty ctx->id=%s", ctx->id); + enif_free(ctx->id); + } else + PRINTF_ERR0(" empty ctx->id=NULL"); +} + +int get_engine_and_key_id(ErlNifEnv *env, ERL_NIF_TERM key, char ** id, ENGINE **e) +{ + ERL_NIF_TERM engine_res, key_id_term; + struct engine_ctx *ctx; + ErlNifBinary key_id_bin; + + if (!enif_get_map_value(env, key, atom_engine, &engine_res)) + goto err; + if (!enif_get_resource(env, engine_res, engine_ctx_rtype, (void**)&ctx)) + goto err; + if (!enif_get_map_value(env, key, atom_key_id, &key_id_term)) + goto err; + if (!enif_inspect_binary(env, key_id_term, &key_id_bin)) + goto err; + + *e = ctx->engine; + return zero_terminate(key_id_bin, id); + + err: + return 0; +} + +char *get_key_password(ErlNifEnv *env, ERL_NIF_TERM key) { + ERL_NIF_TERM tmp_term; + ErlNifBinary pwd_bin; + char *pwd = NULL; + + if (!enif_get_map_value(env, key, atom_password, &tmp_term)) + goto err; + if (!enif_inspect_binary(env, tmp_term, &pwd_bin)) + goto err; + if (!zero_terminate(pwd_bin, &pwd)) + goto err; + + return pwd; + + err: + return NULL; +} + +static int zero_terminate(ErlNifBinary bin, char **buf) { + if ((*buf = enif_alloc(bin.size + 1)) == NULL) + goto err; + + memcpy(*buf, bin.data, bin.size); + *(*buf + bin.size) = 0; + + return 1; + + err: + return 0; +} +#endif /* HAS_ENGINE_SUPPORT */ + +int init_engine_ctx(ErlNifEnv *env) { +#ifdef HAS_ENGINE_SUPPORT + engine_ctx_rtype = enif_open_resource_type(env, NULL, "ENGINE_CTX", + (ErlNifResourceDtor*) engine_ctx_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (engine_ctx_rtype == NULL) + goto err; +#endif + + return 1; + + err: + PRINTF_ERR0("CRYPTO: Could not open resource type 'ENGINE_CTX'"); + return 0; +} + +ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (EngineId) */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM ret, result; + ErlNifBinary engine_id_bin; + char *engine_id = NULL; + ENGINE *engine; + struct engine_ctx *ctx = NULL; + + // Get Engine Id + ASSERT(argc == 1); + + if (!enif_inspect_binary(env, argv[0], &engine_id_bin)) + goto bad_arg; + + if ((engine_id = enif_alloc(engine_id_bin.size+1)) == NULL) + goto err; + (void) memcpy(engine_id, engine_id_bin.data, engine_id_bin.size); + engine_id[engine_id_bin.size] = '\0'; + + if ((engine = ENGINE_by_id(engine_id)) == NULL) { + PRINTF_ERR0("engine_by_id_nif Leaved: {error, bad_engine_id}"); + ret = ERROR_Atom(env, "bad_engine_id"); + goto done; + } + + if ((ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx))) == NULL) + goto err; + ctx->engine = engine; + ctx->id = engine_id; + /* ctx now owns engine_id */ + engine_id = NULL; + + result = enif_make_resource(env, ctx); + ret = enif_make_tuple2(env, atom_ok, result); + goto done; + + bad_arg: + err: + ret = enif_make_badarg(env); + + done: + if (engine_id) + enif_free(engine_id); + if (ctx) + enif_release_resource(ctx); + return ret; + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + + // Get Engine + ASSERT(argc == 1); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + + if (!ENGINE_init(ctx->engine)) + return ERROR_Atom(env, "engine_init_failed"); + + return atom_ok; + + bad_arg: + return enif_make_badarg(env); + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + + // Get Engine + ASSERT(argc == 1); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + + if (!ENGINE_free(ctx->engine)) + goto err; + return atom_ok; + + bad_arg: + err: + return enif_make_badarg(env); +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + + // Get Engine + ASSERT(argc == 1); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + + if (!ENGINE_finish(ctx->engine)) + goto err; + return atom_ok; + + bad_arg: + err: + return enif_make_badarg(env); + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ +#ifdef HAS_ENGINE_SUPPORT + ASSERT(argc == 0); + + ENGINE_load_dynamic(); + return atom_ok; +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine, Commands, Optional) */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM ret; + unsigned int cmds_len = 0; + char **cmds = NULL; + struct engine_ctx *ctx; + unsigned int i; + int optional = 0; + int cmds_loaded = 0; + + // Get Engine + ASSERT(argc == 3); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + + PRINTF_ERR1("Engine Id: %s\r\n", ENGINE_get_id(ctx->engine)); + // Get Command List + if (!enif_get_list_length(env, argv[1], &cmds_len)) + goto bad_arg; + + if (cmds_len > (UINT_MAX / 2) - 1) + goto err; + cmds_len *= 2; // Key-Value list from erlang + + if ((size_t)cmds_len + 1 > SIZE_MAX / sizeof(char*)) + goto err; + if ((cmds = enif_alloc((cmds_len + 1) * sizeof(char*))) == NULL) + goto err; + if (get_engine_load_cmd_list(env, argv[1], cmds, 0)) + goto err; + cmds_loaded = 1; + if (!enif_get_int(env, argv[2], &optional)) + goto err; + + for(i = 0; i < cmds_len; i+=2) { + PRINTF_ERR2("Cmd: %s:%s\r\n", + cmds[i] ? cmds[i] : "(NULL)", + cmds[i+1] ? cmds[i+1] : "(NULL)"); + if(!ENGINE_ctrl_cmd_string(ctx->engine, cmds[i], cmds[i+1], optional)) { + PRINTF_ERR2("Command failed: %s:%s\r\n", + cmds[i] ? cmds[i] : "(NULL)", + cmds[i+1] ? cmds[i+1] : "(NULL)"); + goto cmd_failed; + } + } + ret = atom_ok; + goto done; + + bad_arg: + err: + ret = enif_make_badarg(env); + goto done; + + cmd_failed: + ret = ERROR_Atom(env, "ctrl_cmd_failed"); + + done: + if (cmds_loaded) { + for (i = 0; cmds != NULL && cmds[i] != NULL; i++) + enif_free(cmds[i]); + } + + if (cmds != NULL) + enif_free(cmds); + + return ret; + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + + // Get Engine + ASSERT(argc == 1); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + + if (!ENGINE_add(ctx->engine)) + goto failed; + + return atom_ok; + + bad_arg: + return enif_make_badarg(env); + + failed: + return ERROR_Atom(env, "add_engine_failed"); + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + + // Get Engine + ASSERT(argc == 1); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + + if (!ENGINE_remove(ctx->engine)) + goto failed; + + return atom_ok; + + bad_arg: + return enif_make_badarg(env); + + failed: + return ERROR_Atom(env, "remove_engine_failed"); +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine, EngineMethod) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + unsigned int method; + + // Get Engine + ASSERT(argc == 2); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + if (!enif_get_uint(env, argv[1], &method)) + goto bad_arg; + + switch(method) + { +#ifdef ENGINE_METHOD_RSA + case ENGINE_METHOD_RSA: + if (!ENGINE_register_RSA(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_DSA + case ENGINE_METHOD_DSA: + if (!ENGINE_register_DSA(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_DH + case ENGINE_METHOD_DH: + if (!ENGINE_register_DH(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_RAND + case ENGINE_METHOD_RAND: + if (!ENGINE_register_RAND(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_ECDH + case ENGINE_METHOD_ECDH: + if (!ENGINE_register_ECDH(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_ECDSA + case ENGINE_METHOD_ECDSA: + if (!ENGINE_register_ECDSA(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_STORE + case ENGINE_METHOD_STORE: + if (!ENGINE_register_STORE(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_CIPHERS + case ENGINE_METHOD_CIPHERS: + if (!ENGINE_register_ciphers(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_DIGESTS + case ENGINE_METHOD_DIGESTS: + if (!ENGINE_register_digests(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_PKEY_METHS + case ENGINE_METHOD_PKEY_METHS: + if (!ENGINE_register_pkey_meths(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_PKEY_ASN1_METHS + case ENGINE_METHOD_PKEY_ASN1_METHS: + if (!ENGINE_register_pkey_asn1_meths(ctx->engine)) + goto failed; + break; +#endif +#ifdef ENGINE_METHOD_EC + case ENGINE_METHOD_EC: + if (!ENGINE_register_EC(ctx->engine)) + goto failed; + break; +#endif + default: + return ERROR_Atom(env, "engine_method_not_supported"); + } + + return atom_ok; + + bad_arg: + return enif_make_badarg(env); + + failed: + return ERROR_Atom(env, "register_engine_failed"); + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine, EngineMethod) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + unsigned int method; + + // Get Engine + ASSERT(argc == 2); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + if (!enif_get_uint(env, argv[1], &method)) + goto bad_arg; + + switch(method) + { +#ifdef ENGINE_METHOD_RSA + case ENGINE_METHOD_RSA: + ENGINE_unregister_RSA(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_DSA + case ENGINE_METHOD_DSA: + ENGINE_unregister_DSA(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_DH + case ENGINE_METHOD_DH: + ENGINE_unregister_DH(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_RAND + case ENGINE_METHOD_RAND: + ENGINE_unregister_RAND(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_ECDH + case ENGINE_METHOD_ECDH: + ENGINE_unregister_ECDH(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_ECDSA + case ENGINE_METHOD_ECDSA: + ENGINE_unregister_ECDSA(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_STORE + case ENGINE_METHOD_STORE: + ENGINE_unregister_STORE(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_CIPHERS + case ENGINE_METHOD_CIPHERS: + ENGINE_unregister_ciphers(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_DIGESTS + case ENGINE_METHOD_DIGESTS: + ENGINE_unregister_digests(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_PKEY_METHS + case ENGINE_METHOD_PKEY_METHS: + ENGINE_unregister_pkey_meths(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_PKEY_ASN1_METHS + case ENGINE_METHOD_PKEY_ASN1_METHS: + ENGINE_unregister_pkey_asn1_meths(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_EC + case ENGINE_METHOD_EC: + ENGINE_unregister_EC(ctx->engine); + break; +#endif + default: + break; + } + + return atom_ok; + + bad_arg: + return enif_make_badarg(env); + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM ret, result; + ENGINE *engine; + ErlNifBinary engine_bin; + struct engine_ctx *ctx = NULL; + + ASSERT(argc == 0); + + if ((engine = ENGINE_get_first()) == NULL) { + if (!enif_alloc_binary(0, &engine_bin)) + goto err; + engine_bin.size = 0; + return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin)); + } + + if ((ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx))) == NULL) + goto err; + ctx->engine = engine; + ctx->id = NULL; + + result = enif_make_resource(env, ctx); + ret = enif_make_tuple2(env, atom_ok, result); + goto done; + + err: + ret = enif_make_badarg(env); + + done: + if (ctx) + enif_release_resource(ctx); + return ret; + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM ret, result; + ENGINE *engine; + ErlNifBinary engine_bin; + struct engine_ctx *ctx, *next_ctx = NULL; + + // Get Engine + ASSERT(argc == 1); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + + if ((engine = ENGINE_get_next(ctx->engine)) == NULL) { + if (!enif_alloc_binary(0, &engine_bin)) + goto err; + engine_bin.size = 0; + return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin)); + } + + if ((next_ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx))) == NULL) + goto err; + next_ctx->engine = engine; + next_ctx->id = NULL; + + result = enif_make_resource(env, next_ctx); + ret = enif_make_tuple2(env, atom_ok, result); + goto done; + + bad_arg: + err: + ret = enif_make_badarg(env); + + done: + if (next_ctx) + enif_release_resource(next_ctx); + return ret; + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + ErlNifBinary engine_id_bin; + const char *engine_id; + size_t size; + struct engine_ctx *ctx = NULL; + + // Get Engine + ASSERT(argc == 1); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + + if ((engine_id = ENGINE_get_id(ctx->engine)) == NULL) { + if (!enif_alloc_binary(0, &engine_id_bin)) + goto err; + engine_id_bin.size = 0; + return enif_make_binary(env, &engine_id_bin); + } + + size = strlen(engine_id); + if (!enif_alloc_binary(size, &engine_id_bin)) + goto err; + engine_id_bin.size = size; + memcpy(engine_id_bin.data, engine_id, size); + + return enif_make_binary(env, &engine_id_bin); + + bad_arg: + err: + return enif_make_badarg(env); + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_get_name_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + ErlNifBinary engine_name_bin; + const char *engine_name; + size_t size; + struct engine_ctx *ctx; + + // Get Engine + ASSERT(argc == 1); + + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) + goto bad_arg; + + if ((engine_name = ENGINE_get_name(ctx->engine)) == NULL) { + if (!enif_alloc_binary(0, &engine_name_bin)) + goto err; + engine_name_bin.size = 0; + return enif_make_binary(env, &engine_name_bin); + } + + size = strlen(engine_name); + if (!enif_alloc_binary(size, &engine_name_bin)) + goto err; + engine_name_bin.size = size; + memcpy(engine_name_bin.data, engine_name, size); + + return enif_make_binary(env, &engine_name_bin); + + bad_arg: + err: + return enif_make_badarg(env); + +#else + return atom_notsup; +#endif +} + +#ifdef HAS_ENGINE_SUPPORT +static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i) +{ + ERL_NIF_TERM head, tail; + const ERL_NIF_TERM *tmp_tuple; + ErlNifBinary tmpbin; + int arity; + char *tuple1 = NULL, *tuple2 = NULL; + + if (enif_is_empty_list(env, term)) { + cmds[i] = NULL; + return 0; + } + + if (!enif_get_list_cell(env, term, &head, &tail)) + goto err; + if (!enif_get_tuple(env, head, &arity, &tmp_tuple)) + goto err; + if (arity != 2) + goto err; + if (!enif_inspect_binary(env, tmp_tuple[0], &tmpbin)) + goto err; + + if ((tuple1 = enif_alloc(tmpbin.size + 1)) == NULL) + goto err; + + (void) memcpy(tuple1, tmpbin.data, tmpbin.size); + tuple1[tmpbin.size] = '\0'; + cmds[i] = tuple1; + i++; + + if (!enif_inspect_binary(env, tmp_tuple[1], &tmpbin)) + goto err; + + if (tmpbin.size == 0) { + cmds[i] = NULL; + } else { + if ((tuple2 = enif_alloc(tmpbin.size + 1)) == NULL) + goto err; + (void) memcpy(tuple2, tmpbin.data, tmpbin.size); + tuple2[tmpbin.size] = '\0'; + cmds[i] = tuple2; + } + i++; + return get_engine_load_cmd_list(env, tail, cmds, i); + + err: + if (tuple1 != NULL) { + i--; + enif_free(tuple1); + } + cmds[i] = NULL; + return -1; +} +#endif /* HAS_ENGINE_SUPPORT */ + +ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM method_array[12]; + unsigned int i = 0; + + ASSERT(argc == 0); + +#ifdef ENGINE_METHOD_RSA + method_array[i++] = atom_engine_method_rsa; +#endif +#ifdef ENGINE_METHOD_DSA + method_array[i++] = atom_engine_method_dsa; +#endif +#ifdef ENGINE_METHOD_DH + method_array[i++] = atom_engine_method_dh; +#endif +#ifdef ENGINE_METHOD_RAND + method_array[i++] = atom_engine_method_rand; +#endif +#ifdef ENGINE_METHOD_ECDH + method_array[i++] = atom_engine_method_ecdh; +#endif +#ifdef ENGINE_METHOD_ECDSA + method_array[i++] = atom_engine_method_ecdsa; +#endif +#ifdef ENGINE_METHOD_STORE + method_array[i++] = atom_engine_method_store; +#endif +#ifdef ENGINE_METHOD_CIPHERS + method_array[i++] = atom_engine_method_ciphers; +#endif +#ifdef ENGINE_METHOD_DIGESTS + method_array[i++] = atom_engine_method_digests; +#endif +#ifdef ENGINE_METHOD_PKEY_METHS + method_array[i++] = atom_engine_method_pkey_meths; +#endif +#ifdef ENGINE_METHOD_PKEY_ASN1_METHS + method_array[i++] = atom_engine_method_pkey_asn1_meths; +#endif +#ifdef ENGINE_METHOD_EC + method_array[i++] = atom_engine_method_ec; +#endif + + return enif_make_list_from_array(env, method_array, i); +#else + return atom_notsup; +#endif +} diff --git a/lib/crypto/c_src/engine.h b/lib/crypto/c_src/engine.h new file mode 100644 index 0000000000..4a2eed9672 --- /dev/null +++ b/lib/crypto/c_src/engine.h @@ -0,0 +1,49 @@ +/* + * %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% + */ + +#ifndef E_ENGINE_H__ +#define E_ENGINE_H__ 1 + +#include "common.h" + +#ifdef HAS_ENGINE_SUPPORT +int get_engine_and_key_id(ErlNifEnv *env, ERL_NIF_TERM key, char ** id, ENGINE **e); +char *get_key_password(ErlNifEnv *env, ERL_NIF_TERM key); +#endif /* HAS_ENGINE_SUPPORT */ + +int init_engine_ctx(ErlNifEnv *env); + +ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_get_name_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_ENGINE_H__ */ diff --git a/lib/crypto/c_src/evp.c b/lib/crypto/c_src/evp.c new file mode 100644 index 0000000000..3bf66bfffe --- /dev/null +++ b/lib/crypto/c_src/evp.c @@ -0,0 +1,164 @@ +/* + * %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 "evp.h" + +ERL_NIF_TERM evp_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) + /* (Curve, PeerBin, MyBin) */ +{ +#ifdef HAVE_ED_CURVE_DH + ERL_NIF_TERM ret; + int type; + EVP_PKEY_CTX *ctx = NULL; + ErlNifBinary peer_bin, my_bin, key_bin; + EVP_PKEY *peer_key = NULL, *my_key = NULL; + size_t max_size; + int key_bin_alloc = 0; + + ASSERT(argc == 3); + + if (argv[0] == atom_x25519) + type = EVP_PKEY_X25519; + else if (argv[0] == atom_x448) + type = EVP_PKEY_X448; + else + goto bad_arg; + + if (!enif_inspect_binary(env, argv[1], &peer_bin)) + goto bad_arg; + if (!enif_inspect_binary(env, argv[2], &my_bin)) + goto bad_arg; + + if ((my_key = EVP_PKEY_new_raw_private_key(type, NULL, my_bin.data, my_bin.size)) == NULL) + goto err; + if ((ctx = EVP_PKEY_CTX_new(my_key, NULL)) == NULL) + goto err; + + if (EVP_PKEY_derive_init(ctx) != 1) + goto err; + + if ((peer_key = EVP_PKEY_new_raw_public_key(type, NULL, peer_bin.data, peer_bin.size)) == NULL) + goto err; + if (EVP_PKEY_derive_set_peer(ctx, peer_key) != 1) + goto err; + + if (EVP_PKEY_derive(ctx, NULL, &max_size) != 1) + goto err; + + if (!enif_alloc_binary(max_size, &key_bin)) + goto err; + key_bin_alloc = 1; + if (EVP_PKEY_derive(ctx, key_bin.data, &key_bin.size) != 1) + goto err; + + if (key_bin.size < max_size) { + if (!enif_realloc_binary(&key_bin, (size_t)key_bin.size)) + goto err; + } + + ret = enif_make_binary(env, &key_bin); + key_bin_alloc = 0; + goto done; + + bad_arg: + err: + if (key_bin_alloc) + enif_release_binary(&key_bin); + ret = enif_make_badarg(env); + + done: + if (my_key) + EVP_PKEY_free(my_key); + if (peer_key) + EVP_PKEY_free(peer_key); + if (ctx) + EVP_PKEY_CTX_free(ctx); + + return ret; + +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +/* (Curve) */ +{ +#ifdef HAVE_ED_CURVE_DH + int type; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + ERL_NIF_TERM ret_pub, ret_prv, ret; + size_t key_len; + unsigned char *out_pub = NULL, *out_priv = NULL; + + ASSERT(argc == 1); + + if (argv[0] == atom_x25519) + type = EVP_PKEY_X25519; + else if (argv[0] == atom_x448) + type = EVP_PKEY_X448; + else + goto bad_arg; + + if ((ctx = EVP_PKEY_CTX_new_id(type, NULL)) == NULL) + goto bad_arg; + + if (EVP_PKEY_keygen_init(ctx) != 1) + goto err; + if (EVP_PKEY_keygen(ctx, &pkey) != 1) + goto err; + + if (EVP_PKEY_get_raw_public_key(pkey, NULL, &key_len) != 1) + goto err; + if ((out_pub = enif_make_new_binary(env, key_len, &ret_pub)) == NULL) + goto err; + if (EVP_PKEY_get_raw_public_key(pkey, out_pub, &key_len) != 1) + goto err; + + if (EVP_PKEY_get_raw_private_key(pkey, NULL, &key_len) != 1) + goto err; + if ((out_priv = enif_make_new_binary(env, key_len, &ret_prv)) == NULL) + goto err; + if (EVP_PKEY_get_raw_private_key(pkey, out_priv, &key_len) != 1) + goto err; + + ret = enif_make_tuple2(env, ret_pub, ret_prv); + goto done; + + bad_arg: + ret = enif_make_badarg(env); + goto done; + + err: + ret = atom_error; + + done: + if (pkey) + EVP_PKEY_free(pkey); + if (ctx) + EVP_PKEY_CTX_free(ctx); + return ret; + +#else + return atom_notsup; +#endif +} + diff --git a/lib/crypto/c_src/evp.h b/lib/crypto/c_src/evp.h new file mode 100644 index 0000000000..d767260262 --- /dev/null +++ b/lib/crypto/c_src/evp.h @@ -0,0 +1,29 @@ +/* + * %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% + */ + +#ifndef E_EVP_H__ +#define E_EVP_H__ 1 + +#include "common.h" + +ERL_NIF_TERM evp_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_EVP_H__ */ diff --git a/lib/crypto/c_src/evp_compat.h b/lib/crypto/c_src/evp_compat.h new file mode 100644 index 0000000000..dc94a61d8e --- /dev/null +++ b/lib/crypto/c_src/evp_compat.h @@ -0,0 +1,210 @@ +/* + * %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% + */ + +#ifndef E_EVP_COMPAT_H__ +#define E_EVP_COMPAT_H__ 1 + +/* + * In OpenSSL 1.1.0, most structs are opaque. That means that + * the structs cannot be allocated as automatic variables on the + * C stack (because the size is unknown) and that it is necessary + * to use access functions. + * + * For backward compatibility to previous versions of OpenSSL, define + * on our versions of the new functions defined in 1.1.0 here, so that + * we don't have to sprinkle ifdefs throughout the code. + */ + +static INLINE HMAC_CTX *HMAC_CTX_new(void); +static INLINE void HMAC_CTX_free(HMAC_CTX *ctx); + +static INLINE HMAC_CTX *HMAC_CTX_new() +{ + HMAC_CTX *ctx; + + if ((ctx = CRYPTO_malloc(sizeof(HMAC_CTX), __FILE__, __LINE__)) == NULL) + return NULL; + + HMAC_CTX_init(ctx); + return ctx; +} + +static INLINE void HMAC_CTX_free(HMAC_CTX *ctx) +{ + if (ctx == NULL) + return; + + HMAC_CTX_cleanup(ctx); + CRYPTO_free(ctx); +} + +/* Renamed in 1.1.0 */ +#define EVP_MD_CTX_new() EVP_MD_CTX_create() +#define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy((ctx)) + +static INLINE void *BN_GENCB_get_arg(BN_GENCB *cb); + +static INLINE void *BN_GENCB_get_arg(BN_GENCB *cb) +{ + return cb->arg; +} + +static INLINE int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); +static INLINE void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d); +static INLINE int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q); +static INLINE void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); +static INLINE int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp); +static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp); + +static INLINE int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +{ + r->n = n; + r->e = e; + r->d = d; + return 1; +} + +static INLINE void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) +{ + *n = r->n; + *e = r->e; + *d = r->d; +} + +static INLINE int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) +{ + r->p = p; + r->q = q; + return 1; +} + +static INLINE void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) +{ + *p = r->p; + *q = r->q; +} + +static INLINE int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) +{ + r->dmp1 = dmp1; + r->dmq1 = dmq1; + r->iqmp = iqmp; + return 1; +} + +static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp) +{ + *dmp1 = r->dmp1; + *dmq1 = r->dmq1; + *iqmp = r->iqmp; +} + +static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); +static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); +static INLINE void DSA_get0_pqg(const DSA *dsa, + const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); +static INLINE void DSA_get0_key(const DSA *dsa, + const BIGNUM **pub_key, const BIGNUM **priv_key); + +static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) +{ + d->pub_key = pub_key; + d->priv_key = priv_key; + return 1; +} + +static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + d->p = p; + d->q = q; + d->g = g; + return 1; +} + +static INLINE void +DSA_get0_pqg(const DSA *dsa, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +{ + *p = dsa->p; + *q = dsa->q; + *g = dsa->g; +} + +static INLINE void +DSA_get0_key(const DSA *dsa, const BIGNUM **pub_key, const BIGNUM **priv_key) +{ + if (pub_key) + *pub_key = dsa->pub_key; + + if (priv_key) + *priv_key = dsa->priv_key; +} + + + +static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key); +static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); +static INLINE int DH_set_length(DH *dh, long length); +static INLINE void DH_get0_pqg(const DH *dh, + const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); +static INLINE void DH_get0_key(const DH *dh, + const BIGNUM **pub_key, const BIGNUM **priv_key); + +static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) +{ + dh->pub_key = pub_key; + dh->priv_key = priv_key; + return 1; +} + +static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + dh->p = p; + dh->q = q; + dh->g = g; + return 1; +} + +static INLINE int DH_set_length(DH *dh, long length) +{ + dh->length = length; + return 1; +} + + + +static INLINE void +DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +{ + *p = dh->p; + *q = dh->q; + *g = dh->g; +} + +static INLINE void +DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) +{ + if (pub_key) + *pub_key = dh->pub_key; + + if (priv_key) + *priv_key = dh->priv_key; +} + +#endif /* E_EVP_COMPAT_H__ */ diff --git a/lib/crypto/c_src/fips.c b/lib/crypto/c_src/fips.c new file mode 100644 index 0000000000..b2d892d00b --- /dev/null +++ b/lib/crypto/c_src/fips.c @@ -0,0 +1,52 @@ +/* + * %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 "fips.h" + +ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef FIPS_SUPPORT + return FIPS_mode() ? atom_enabled : atom_not_enabled; +#else + return atom_not_supported; +#endif +} + +ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Boolean) */ + if (argv[0] == atom_true) { +#ifdef FIPS_SUPPORT + if (FIPS_mode_set(1)) { + return atom_true; + } +#endif + PRINTF_ERR0("CRYPTO: Could not setup FIPS mode"); + return atom_false; + } else if (argv[0] == atom_false) { +#ifdef FIPS_SUPPORT + if (!FIPS_mode_set(0)) { + return atom_false; + } +#endif + return atom_true; + } else { + return enif_make_badarg(env); + } +} diff --git a/lib/crypto/c_src/fips.h b/lib/crypto/c_src/fips.h new file mode 100644 index 0000000000..9a436bd202 --- /dev/null +++ b/lib/crypto/c_src/fips.h @@ -0,0 +1,29 @@ +/* + * %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% + */ + +#ifndef E_FIPS_H__ +#define E_FIPS_H__ 1 + +#include "common.h" + +ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_FIPS_H__ */ diff --git a/lib/crypto/c_src/hash.c b/lib/crypto/c_src/hash.c new file mode 100644 index 0000000000..457e9d071a --- /dev/null +++ b/lib/crypto/c_src/hash.c @@ -0,0 +1,499 @@ +/* + * %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 "hash.h" +#include "digest.h" + +#define MD5_CTX_LEN (sizeof(MD5_CTX)) +#define MD4_CTX_LEN (sizeof(MD4_CTX)) +#define RIPEMD160_CTX_LEN (sizeof(RIPEMD160_CTX)) + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) +struct evp_md_ctx { + EVP_MD_CTX* ctx; +}; + +/* Define resource types for OpenSSL context structures. */ +static ErlNifResourceType* evp_md_ctx_rtype; + +static void evp_md_ctx_dtor(ErlNifEnv* env, struct evp_md_ctx *ctx) { + if (ctx == NULL) + return; + + if (ctx->ctx) + EVP_MD_CTX_free(ctx->ctx); +} +#endif + +int init_hash_ctx(ErlNifEnv* env) { +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + evp_md_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_MD_CTX", + (ErlNifResourceDtor*) evp_md_ctx_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (evp_md_ctx_rtype == NULL) + goto err; +#endif + + return 1; + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + err: + PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_MD_CTX'"); + return 0; +#endif +} + +ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Data) */ + struct digest_type_t *digp = NULL; + const EVP_MD *md; + ErlNifBinary data; + ERL_NIF_TERM ret; + unsigned ret_size; + unsigned char *outp; + + ASSERT(argc == 2); + + if ((digp = get_digest_type(argv[0])) == NULL) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &data)) + goto bad_arg; + + if ((md = digp->md.p) == NULL) + goto err; + + ret_size = (unsigned)EVP_MD_size(md); + ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE); + + if ((outp = enif_make_new_binary(env, ret_size, &ret)) == NULL) + goto err; + if (EVP_Digest(data.data, data.size, outp, &ret_size, md, NULL) != 1) + goto err; + + ASSERT(ret_size == (unsigned)EVP_MD_size(md)); + + CONSUME_REDS(env, data); + return ret; + + bad_arg: + return enif_make_badarg(env); + + err: + return atom_notsup; +} + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + +ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type) */ + struct digest_type_t *digp = NULL; + struct evp_md_ctx *ctx = NULL; + ERL_NIF_TERM ret; + + ASSERT(argc == 1); + + if ((digp = get_digest_type(argv[0])) == NULL) + goto bad_arg; + if (digp->md.p == NULL) + goto err; + + if ((ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx))) == NULL) + goto err; + if ((ctx->ctx = EVP_MD_CTX_new()) == NULL) + goto err; + if (EVP_DigestInit(ctx->ctx, digp->md.p) != 1) + goto err; + + ret = enif_make_resource(env, ctx); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = atom_notsup; + + done: + if (ctx) + enif_release_resource(ctx); + return ret; +} + +ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + struct evp_md_ctx *ctx, *new_ctx = NULL; + ErlNifBinary data; + ERL_NIF_TERM ret; + + ASSERT(argc == 2); + + if (!enif_get_resource(env, argv[0], evp_md_ctx_rtype, (void**)&ctx)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &data)) + goto bad_arg; + + if ((new_ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx))) == NULL) + goto err; + if ((new_ctx->ctx = EVP_MD_CTX_new()) == NULL) + goto err; + if (EVP_MD_CTX_copy(new_ctx->ctx, ctx->ctx) != 1) + goto err; + if (EVP_DigestUpdate(new_ctx->ctx, data.data, data.size) != 1) + goto err; + + ret = enif_make_resource(env, new_ctx); + CONSUME_REDS(env, data); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = atom_notsup; + + done: + if (new_ctx) + enif_release_resource(new_ctx); + return ret; +} + +ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) */ + struct evp_md_ctx *ctx; + EVP_MD_CTX *new_ctx; + ERL_NIF_TERM ret; + unsigned ret_size; + unsigned char *outp; + + ASSERT(argc == 1); + + if (!enif_get_resource(env, argv[0], evp_md_ctx_rtype, (void**)&ctx)) + goto bad_arg; + + ret_size = (unsigned)EVP_MD_CTX_size(ctx->ctx); + ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE); + + if ((new_ctx = EVP_MD_CTX_new()) == NULL) + goto err; + if (EVP_MD_CTX_copy(new_ctx, ctx->ctx) != 1) + goto err; + if ((outp = enif_make_new_binary(env, ret_size, &ret)) == NULL) + goto err; + if (EVP_DigestFinal(new_ctx, outp, &ret_size) != 1) + goto err; + + ASSERT(ret_size == (unsigned)EVP_MD_CTX_size(ctx->ctx)); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = atom_notsup; + + done: + if (new_ctx) + EVP_MD_CTX_free(new_ctx); + return ret; +} + +#else /* if OPENSSL_VERSION_NUMBER < 1.0 */ + +ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type) */ + typedef int (*init_fun)(unsigned char*); + struct digest_type_t *digp = NULL; + ERL_NIF_TERM ctx; + size_t ctx_size = 0; + init_fun ctx_init = 0; + unsigned char *outp; + + ASSERT(argc == 1); + + if ((digp = get_digest_type(argv[0])) == NULL) + goto bad_arg; + if (digp->md.p == NULL) + goto err; + + switch (EVP_MD_type(digp->md.p)) + { + case NID_md4: + ctx_size = MD4_CTX_LEN; + ctx_init = (init_fun)(&MD4_Init); + break; + case NID_md5: + ctx_size = MD5_CTX_LEN; + ctx_init = (init_fun)(&MD5_Init); + break; + case NID_ripemd160: + ctx_size = RIPEMD160_CTX_LEN; + ctx_init = (init_fun)(&RIPEMD160_Init); + break; + case NID_sha1: + ctx_size = sizeof(SHA_CTX); + ctx_init = (init_fun)(&SHA1_Init); + break; +#ifdef HAVE_SHA224 + case NID_sha224: + ctx_size = sizeof(SHA256_CTX); + ctx_init = (init_fun)(&SHA224_Init); + break; +#endif +#ifdef HAVE_SHA256 + case NID_sha256: + ctx_size = sizeof(SHA256_CTX); + ctx_init = (init_fun)(&SHA256_Init); + break; +#endif +#ifdef HAVE_SHA384 + case NID_sha384: + ctx_size = sizeof(SHA512_CTX); + ctx_init = (init_fun)(&SHA384_Init); + break; +#endif +#ifdef HAVE_SHA512 + case NID_sha512: + ctx_size = sizeof(SHA512_CTX); + ctx_init = (init_fun)(&SHA512_Init); + break; +#endif + default: + goto err; + } + ASSERT(ctx_size); + ASSERT(ctx_init); + + if ((outp = enif_make_new_binary(env, ctx_size, &ctx)) == NULL) + goto err; + + if (ctx_init(outp) != 1) + goto err; + + return enif_make_tuple2(env, argv[0], ctx); + + bad_arg: + return enif_make_badarg(env); + + err: + return atom_notsup; +} + +ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* ({Type, Context}, Data) */ + typedef int (*update_fun)(unsigned char*, const unsigned char*, size_t); + ERL_NIF_TERM new_ctx; + ErlNifBinary ctx, data; + const ERL_NIF_TERM *tuple; + int arity; + struct digest_type_t *digp = NULL; + unsigned char *ctx_buff; + size_t ctx_size = 0; + update_fun ctx_update = 0; + + ASSERT(argc == 2); + + if (!enif_get_tuple(env, argv[0], &arity, &tuple)) + goto bad_arg; + if (arity != 2) + goto bad_arg; + if ((digp = get_digest_type(tuple[0])) == NULL) + goto bad_arg; + if (!enif_inspect_binary(env, tuple[1], &ctx)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &data)) + goto bad_arg; + + if (digp->md.p == NULL) + goto err; + + switch (EVP_MD_type(digp->md.p)) + { + case NID_md4: + ctx_size = MD4_CTX_LEN; + ctx_update = (update_fun)(&MD4_Update); + break; + case NID_md5: + ctx_size = MD5_CTX_LEN; + ctx_update = (update_fun)(&MD5_Update); + break; + case NID_ripemd160: + ctx_size = RIPEMD160_CTX_LEN; + ctx_update = (update_fun)(&RIPEMD160_Update); + break; + case NID_sha1: + ctx_size = sizeof(SHA_CTX); + ctx_update = (update_fun)(&SHA1_Update); + break; +#ifdef HAVE_SHA224 + case NID_sha224: + ctx_size = sizeof(SHA256_CTX); + ctx_update = (update_fun)(&SHA224_Update); + break; +#endif +#ifdef HAVE_SHA256 + case NID_sha256: + ctx_size = sizeof(SHA256_CTX); + ctx_update = (update_fun)(&SHA256_Update); + break; +#endif +#ifdef HAVE_SHA384 + case NID_sha384: + ctx_size = sizeof(SHA512_CTX); + ctx_update = (update_fun)(&SHA384_Update); + break; +#endif +#ifdef HAVE_SHA512 + case NID_sha512: + ctx_size = sizeof(SHA512_CTX); + ctx_update = (update_fun)(&SHA512_Update); + break; +#endif + default: + goto err; + } + ASSERT(ctx_size); + ASSERT(ctx_update); + + if (ctx.size != ctx_size) + goto bad_arg; + + if ((ctx_buff = enif_make_new_binary(env, ctx_size, &new_ctx)) == NULL) + goto err; + memcpy(ctx_buff, ctx.data, ctx_size); + + if (ctx_update(ctx_buff, data.data, data.size) != 1) + goto err; + + CONSUME_REDS(env, data); + return enif_make_tuple2(env, tuple[0], new_ctx); + + bad_arg: + return enif_make_badarg(env); + + err: + return atom_notsup; +} + +ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* ({Type, Context}) */ + typedef int (*final_fun)(unsigned char*, void*); + ERL_NIF_TERM ret; + ErlNifBinary ctx; + const ERL_NIF_TERM *tuple; + int arity; + struct digest_type_t *digp = NULL; + const EVP_MD *md; + void *new_ctx = NULL; + size_t ctx_size = 0; + final_fun ctx_final = 0; + unsigned char *outp; + + ASSERT(argc == 1); + + if (!enif_get_tuple(env, argv[0], &arity, &tuple)) + goto bad_arg; + if (arity != 2) + goto bad_arg; + if ((digp = get_digest_type(tuple[0])) == NULL) + goto bad_arg; + if (!enif_inspect_binary(env, tuple[1], &ctx)) + goto bad_arg; + + if ((md = digp->md.p) == NULL) + goto err; + + switch (EVP_MD_type(md)) + { + case NID_md4: + ctx_size = MD4_CTX_LEN; + ctx_final = (final_fun)(&MD4_Final); + break; + case NID_md5: + ctx_size = MD5_CTX_LEN; + ctx_final = (final_fun)(&MD5_Final); + break; + case NID_ripemd160: + ctx_size = RIPEMD160_CTX_LEN; + ctx_final = (final_fun)(&RIPEMD160_Final); + break; + case NID_sha1: + ctx_size = sizeof(SHA_CTX); + ctx_final = (final_fun)(&SHA1_Final); + break; +#ifdef HAVE_SHA224 + case NID_sha224: + ctx_size = sizeof(SHA256_CTX); + ctx_final = (final_fun)(&SHA224_Final); + break; +#endif +#ifdef HAVE_SHA256 + case NID_sha256: + ctx_size = sizeof(SHA256_CTX); + ctx_final = (final_fun)(&SHA256_Final); + break; +#endif +#ifdef HAVE_SHA384 + case NID_sha384: + ctx_size = sizeof(SHA512_CTX); + ctx_final = (final_fun)(&SHA384_Final); + break; +#endif +#ifdef HAVE_SHA512 + case NID_sha512: + ctx_size = sizeof(SHA512_CTX); + ctx_final = (final_fun)(&SHA512_Final); + break; +#endif + default: + goto err; + } + ASSERT(ctx_size); + ASSERT(ctx_final); + + if (ctx.size != ctx_size) + goto bad_arg; + + if ((new_ctx = enif_alloc(ctx_size)) == NULL) + goto err; + + memcpy(new_ctx, ctx.data, ctx_size); + + if ((outp = enif_make_new_binary(env, (size_t)EVP_MD_size(md), &ret)) == NULL) + goto err; + + if (ctx_final(outp, new_ctx) != 1) + goto err; + + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = atom_notsup; + + done: + if (new_ctx) + enif_free(new_ctx); + return ret; +} + +#endif /* OPENSSL_VERSION_NUMBER < 1.0 */ diff --git a/lib/crypto/c_src/hash.h b/lib/crypto/c_src/hash.h new file mode 100644 index 0000000000..8bae07f39a --- /dev/null +++ b/lib/crypto/c_src/hash.h @@ -0,0 +1,33 @@ +/* + * %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% + */ + +#ifndef E_HASH_H__ +#define E_HASH_H__ 1 + +#include "common.h" + +int init_hash_ctx(ErlNifEnv *env); + +ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_HASH_H__ */ diff --git a/lib/crypto/c_src/hmac.c b/lib/crypto/c_src/hmac.c new file mode 100644 index 0000000000..c41e50eb35 --- /dev/null +++ b/lib/crypto/c_src/hmac.c @@ -0,0 +1,270 @@ +/* + * %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 "hmac.h" +#include "digest.h" + +struct hmac_context +{ + ErlNifMutex* mtx; + int alive; + HMAC_CTX* ctx; +}; + +static ErlNifResourceType* hmac_context_rtype; + +static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context*); + +int init_hmac_ctx(ErlNifEnv *env) { + hmac_context_rtype = enif_open_resource_type(env, NULL, "hmac_context", + (ErlNifResourceDtor*) hmac_context_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (hmac_context_rtype == NULL) + goto err; + + return 1; + + err: + PRINTF_ERR0("CRYPTO: Could not open resource type 'hmac_context'"); + return 0; +} + +ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Key, Data) or (Type, Key, Data, MacSize) */ + struct digest_type_t *digp = NULL; + ErlNifBinary key, data; + unsigned char buff[EVP_MAX_MD_SIZE]; + unsigned size = 0, req_size = 0; + ERL_NIF_TERM ret; + unsigned char *outp; + + ASSERT(argc == 3 || argc == 4); + + if ((digp = get_digest_type(argv[0])) == NULL) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) + goto bad_arg; + if (key.size > INT_MAX) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[2], &data)) + goto bad_arg; + if (argc == 4) { + if (!enif_get_uint(env, argv[3], &req_size)) + goto bad_arg; + } + + if (digp->md.p == NULL) + goto err; + if (HMAC(digp->md.p, + key.data, (int)key.size, + data.data, data.size, + buff, &size) == NULL) + goto err; + + ASSERT(0 < size && size <= EVP_MAX_MD_SIZE); + CONSUME_REDS(env, data); + + if (argc == 4) { + if (req_size > size) + goto bad_arg; + + size = req_size; + } + + if ((outp = enif_make_new_binary(env, size, &ret)) == NULL) + goto err; + + memcpy(outp, buff, size); + return ret; + + bad_arg: + return enif_make_badarg(env); + + err: + return atom_notsup; +} + +static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) +{ + if (obj == NULL) + return; + + if (obj->alive) { + if (obj->ctx) + HMAC_CTX_free(obj->ctx); + obj->alive = 0; + } + + if (obj->mtx != NULL) + enif_mutex_destroy(obj->mtx); +} + +ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Key) */ + struct digest_type_t *digp = NULL; + ErlNifBinary key; + ERL_NIF_TERM ret; + struct hmac_context *obj = NULL; + + ASSERT(argc == 2); + + if ((digp = get_digest_type(argv[0])) == NULL) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) + goto bad_arg; + if (key.size > INT_MAX) + goto bad_arg; + + if (digp->md.p == NULL) + goto err; + + if ((obj = enif_alloc_resource(hmac_context_rtype, sizeof(struct hmac_context))) == NULL) + goto err; + obj->ctx = NULL; + obj->mtx = NULL; + obj->alive = 0; + + if ((obj->ctx = HMAC_CTX_new()) == NULL) + goto err; + obj->alive = 1; + if ((obj->mtx = enif_mutex_create("crypto.hmac")) == NULL) + goto err; + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + // Check the return value of HMAC_Init: it may fail in FIPS mode + // for disabled algorithms + if (!HMAC_Init_ex(obj->ctx, key.data, (int)key.size, digp->md.p, NULL)) + goto err; +#else + // In ancient versions of OpenSSL, this was a void function. + HMAC_Init_ex(obj->ctx, key.data, (int)key.size, digp->md.p, NULL); +#endif + + ret = enif_make_resource(env, obj); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = atom_notsup; + + done: + if (obj) + enif_release_resource(obj); + return ret; +} + +ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + ERL_NIF_TERM ret; + ErlNifBinary data; + struct hmac_context *obj = NULL; + + ASSERT(argc == 2); + + if (!enif_get_resource(env, argv[0], hmac_context_rtype, (void**)&obj)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &data)) + goto bad_arg; + + enif_mutex_lock(obj->mtx); + if (!obj->alive) + goto err; + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + if (!HMAC_Update(obj->ctx, data.data, data.size)) + goto err; +#else + // In ancient versions of OpenSSL, this was a void function. + HMAC_Update(obj->ctx, data.data, data.size); +#endif + + CONSUME_REDS(env,data); + ret = argv[0]; + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = enif_make_badarg(env); + + done: + enif_mutex_unlock(obj->mtx); + return ret; +} + +ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) or (Context, HashLen) */ + ERL_NIF_TERM ret; + struct hmac_context* obj; + unsigned char mac_buf[EVP_MAX_MD_SIZE]; + unsigned char * mac_bin; + unsigned int req_len = 0; + unsigned int mac_len; + + ASSERT(argc == 1 || argc == 2); + + if (!enif_get_resource(env, argv[0], hmac_context_rtype, (void**)&obj)) + goto bad_arg; + if (argc == 2) { + if (!enif_get_uint(env, argv[1], &req_len)) + goto bad_arg; + } + + enif_mutex_lock(obj->mtx); + if (!obj->alive) + goto err; + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + if (!HMAC_Final(obj->ctx, mac_buf, &mac_len)) + goto err; +#else + // In ancient versions of OpenSSL, this was a void function. + HMAC_Final(obj->ctx, mac_buf, &mac_len); +#endif + + if (obj->ctx) + HMAC_CTX_free(obj->ctx); + obj->alive = 0; + + if (argc == 2 && req_len < mac_len) { + /* Only truncate to req_len bytes if asked. */ + mac_len = req_len; + } + if ((mac_bin = enif_make_new_binary(env, mac_len, &ret)) == NULL) + goto err; + + memcpy(mac_bin, mac_buf, mac_len); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = enif_make_badarg(env); + + done: + enif_mutex_unlock(obj->mtx); + return ret; +} + diff --git a/lib/crypto/c_src/hmac.h b/lib/crypto/c_src/hmac.h new file mode 100644 index 0000000000..1f0e0ca632 --- /dev/null +++ b/lib/crypto/c_src/hmac.h @@ -0,0 +1,33 @@ +/* + * %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% + */ + +#ifndef E_HMAC_H__ +#define E_HMAC_H__ 1 + +#include "common.h" + +int init_hmac_ctx(ErlNifEnv *env); + +ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_HMAC_H__ */ diff --git a/lib/crypto/c_src/info.c b/lib/crypto/c_src/info.c new file mode 100644 index 0000000000..42f477fead --- /dev/null +++ b/lib/crypto/c_src/info.c @@ -0,0 +1,107 @@ +/* + * %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 "info.h" + +#ifdef HAVE_DYNAMIC_CRYPTO_LIB + +# if defined(DEBUG) +char *crypto_callback_name = "crypto_callback.debug"; +# elif defined(VALGRIND) +char *crypto_callback_name = "crypto_callback.valgrind"; +# else +char *crypto_callback_name = "crypto_callback"; +# endif + +int change_basename(ErlNifBinary* bin, char* buf, size_t bufsz, const char* newfile) +{ + size_t i; + size_t newlen; + + for (i = bin->size; i > 0; i--) { + if (bin->data[i-1] == '/') + break; + } + + newlen = strlen(newfile); + if (i > SIZE_MAX - newlen) + goto err; + + if (i + newlen >= bufsz) + goto err; + + memcpy(buf, bin->data, i); + strcpy(buf+i, newfile); + + return 1; + + err: + return 0; +} + +void error_handler(void* null, const char* errstr) +{ + PRINTF_ERR1("CRYPTO LOADING ERROR: '%s'", errstr); +} +#endif /* HAVE_DYNAMIC_CRYPTO_LIB */ + +ERL_NIF_TERM info_lib(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ + /* [{<<"OpenSSL">>,9470143,<<"OpenSSL 0.9.8k 25 Mar 2009">>}] */ + + ERL_NIF_TERM name_term, ver_term; + static const char libname[] = "OpenSSL"; + size_t name_sz; + const char* ver; + size_t ver_sz; + int ver_num; + unsigned char *out_name, *out_ver; + + ASSERT(argc == 0); + + name_sz = strlen(libname); + ver = SSLeay_version(SSLEAY_VERSION); + ver_sz = strlen(ver); + ver_num = OPENSSL_VERSION_NUMBER; + + /* R16: + * Ignore library version number from SSLeay() and instead show header + * version. Otherwise user might try to call a function that is implemented + * by a newer library but not supported by the headers used at compile time. + * Example: DES_ede3_cfb_encrypt in 0.9.7i but not in 0.9.7d. + * + * Version string is still from library though. + */ + + if ((out_name = enif_make_new_binary(env, name_sz, &name_term)) == NULL) + goto err; + if ((out_ver = enif_make_new_binary(env, ver_sz, &ver_term)) == NULL) + goto err; + + memcpy(out_name, libname, name_sz); + memcpy(out_ver, ver, ver_sz); + + return enif_make_list1(env, enif_make_tuple3(env, name_term, + enif_make_int(env, ver_num), + ver_term)); + + err: + return enif_make_badarg(env); +} diff --git a/lib/crypto/c_src/info.h b/lib/crypto/c_src/info.h new file mode 100644 index 0000000000..67690625c9 --- /dev/null +++ b/lib/crypto/c_src/info.h @@ -0,0 +1,35 @@ +/* + * %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% + */ + +#ifndef E_INFO_H__ +#define E_INFO_H__ 1 + +#include "common.h" + +#ifdef HAVE_DYNAMIC_CRYPTO_LIB +extern char *crypto_callback_name; + +int change_basename(ErlNifBinary* bin, char* buf, size_t bufsz, const char* newfile); +void error_handler(void* null, const char* errstr); +#endif + +ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_INFO_H__ */ diff --git a/lib/crypto/c_src/math.c b/lib/crypto/c_src/math.c new file mode 100644 index 0000000000..85494bbc93 --- /dev/null +++ b/lib/crypto/c_src/math.c @@ -0,0 +1,53 @@ +/* + * %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 "math.h" + +ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data1, Data2) */ + ErlNifBinary d1, d2; + unsigned char* ret_ptr; + size_t i; + ERL_NIF_TERM ret; + + ASSERT(argc == 2); + + if (!enif_inspect_iolist_as_binary(env, argv[0], &d1)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &d2)) + goto bad_arg; + if (d1.size != d2.size) + goto bad_arg; + + if ((ret_ptr = enif_make_new_binary(env, d1.size, &ret)) == NULL) + goto err; + + for (i=0; i<d1.size; i++) { + ret_ptr[i] = d1.data[i] ^ d2.data[i]; + } + + CONSUME_REDS(env,d1); + return ret; + + bad_arg: + err: + return enif_make_badarg(env); +} + diff --git a/lib/crypto/c_src/math.h b/lib/crypto/c_src/math.h new file mode 100644 index 0000000000..b8d68ea654 --- /dev/null +++ b/lib/crypto/c_src/math.h @@ -0,0 +1,28 @@ +/* + * %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% + */ + +#ifndef E_MATH_H__ +#define E_MATH_H__ 1 + +#include "common.h" + +ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_MATH_H__ */ diff --git a/lib/crypto/c_src/openssl_config.h b/lib/crypto/c_src/openssl_config.h new file mode 100644 index 0000000000..45144a0c25 --- /dev/null +++ b/lib/crypto/c_src/openssl_config.h @@ -0,0 +1,354 @@ +/* + * %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% + */ + +#ifndef E_OPENSSL_CONFIG_H__ +#define E_OPENSSL_CONFIG_H__ 1 + +#define OPENSSL_THREAD_DEFINES +#include <openssl/opensslconf.h> + +#include <openssl/crypto.h> +#ifndef OPENSSL_NO_DES +#include <openssl/des.h> +#endif /* #ifndef OPENSSL_NO_DES */ +/* #include <openssl/idea.h> This is not supported on the openssl OTP requires */ +#include <openssl/dsa.h> +#include <openssl/rsa.h> +#include <openssl/aes.h> +#include <openssl/md5.h> +#include <openssl/md4.h> +#include <openssl/sha.h> +#include <openssl/ripemd.h> +#include <openssl/bn.h> +#include <openssl/objects.h> +#ifndef OPENSSL_NO_RC4 + #include <openssl/rc4.h> +#endif /* OPENSSL_NO_RC4 */ +#ifndef OPENSSL_NO_RC2 + #include <openssl/rc2.h> +#endif +#include <openssl/blowfish.h> +#include <openssl/rand.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/err.h> + +/* Helper macro to construct a OPENSSL_VERSION_NUMBER. + * See openssl/opensslv.h + */ +#define PACKED_OPENSSL_VERSION(MAJ, MIN, FIX, P) \ + ((((((((MAJ << 8) | MIN) << 8 ) | FIX) << 8) | (P-'a'+1)) << 4) | 0xf) + +#define PACKED_OPENSSL_VERSION_PLAIN(MAJ, MIN, FIX) \ + PACKED_OPENSSL_VERSION(MAJ,MIN,FIX,('a'-1)) + + +/* LibreSSL was cloned from OpenSSL 1.0.1g and claims to be API and BPI compatible + * with 1.0.1. + * + * LibreSSL has the same names on include files and symbols as OpenSSL, but defines + * the OPENSSL_VERSION_NUMBER to be >= 2.0.0 + * + * Therefor works tests like this as intendend: + * OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + * (The test is for example "2.4.2" >= "1.0.0" although the test + * with the cloned OpenSSL test would be "1.0.1" >= "1.0.0") + * + * But tests like this gives wrong result: + * OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) + * (The test is false since "2.4.2" < "1.1.0". It should have been + * true because the LibreSSL API version is "1.0.1") + * + */ + +#ifdef LIBRESSL_VERSION_NUMBER +/* A macro to test on in this file */ +#define HAS_LIBRESSL +#endif + +#ifdef HAS_LIBRESSL +/* LibreSSL dislikes FIPS */ +# ifdef FIPS_SUPPORT +# undef FIPS_SUPPORT +# endif + +/* LibreSSL has never supported the custom mem functions */ +#ifndef HAS_LIBRESSL +# define HAS_CRYPTO_MEM_FUNCTIONS +#endif + +# if LIBRESSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(2,7,0) +/* LibreSSL wants the 1.0.1 API */ +# define NEED_EVP_COMPATIBILITY_FUNCTIONS +# endif +#endif + + +#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) +# define NEED_EVP_COMPATIBILITY_FUNCTIONS +#endif + + +#ifndef HAS_LIBRESSL +# if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) +# define HAS_EVP_PKEY_CTX +# endif +#endif + + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) +#include <openssl/modes.h> +#endif + +#include "crypto_callback.h" + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ + && !defined(OPENSSL_NO_SHA224) && defined(NID_sha224) \ + && !defined(OPENSSL_NO_SHA256) /* disabled like this in my sha.h (?) */ +# define HAVE_SHA224 +#endif +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ + && !defined(OPENSSL_NO_SHA256) && defined(NID_sha256) +# define HAVE_SHA256 +#endif +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ + && !defined(OPENSSL_NO_SHA384) && defined(NID_sha384)\ + && !defined(OPENSSL_NO_SHA512) /* disabled like this in my sha.h (?) */ +# define HAVE_SHA384 +#endif +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ + && !defined(OPENSSL_NO_SHA512) && defined(NID_sha512) +# define HAVE_SHA512 +#endif +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,7,'e') +# define HAVE_DES_ede3_cfb_encrypt +#endif + +// SHA3: +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,1) +// An error in beta releases of 1.1.1 fixed in production release +# ifdef NID_sha3_224 +# define HAVE_SHA3_224 +# endif +# ifdef NID_sha3_256 +# define HAVE_SHA3_256 +# endif +#endif +# ifdef NID_sha3_384 +# define HAVE_SHA3_384 +# endif +# ifdef NID_sha3_512 +# define HAVE_SHA3_512 +# endif + +// BLAKE2: +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,1) \ + && !defined(HAS_LIBRESSL) \ + && !defined(OPENSSL_NO_BLAKE2) +# define HAVE_BLAKE2 +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'o') \ + && !defined(OPENSSL_NO_EC) \ + && !defined(OPENSSL_NO_ECDH) \ + && !defined(OPENSSL_NO_ECDSA) +# define HAVE_EC +#endif + +// (test for >= 1.1.1pre8) +#if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1) -7) \ + && !defined(HAS_LIBRESSL) \ + && defined(HAVE_EC) +# define HAVE_ED_CURVE_DH +# if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1)) +# define HAVE_EDDSA +# endif +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'c') +# define HAVE_AES_IGE +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,1) +# define HAVE_EVP_AES_CTR +# define HAVE_AEAD +# define HAVE_GCM +# define HAVE_CCM +# define HAVE_CMAC +# if defined(RSA_PKCS1_OAEP_PADDING) +# define HAVE_RSA_OAEP_PADDING +# endif +# define HAVE_RSA_MGF1_MD +# if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION(1,0,1,'d') +# define HAVE_GCM_EVP_DECRYPT_BUG +# endif +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,0) +# ifndef HAS_LIBRESSL +# define HAVE_CHACHA20 +# define HAVE_CHACHA20_POLY1305 +# define HAVE_RSA_OAEP_MD +# endif +#endif + +// OPENSSL_VERSION_NUMBER >= 1.1.1-pre8 +#if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1)-7) +# ifndef HAS_LIBRESSL +# define HAVE_POLY1305 +# endif +#endif + +#if OPENSSL_VERSION_NUMBER <= PACKED_OPENSSL_VERSION(0,9,8,'l') +# define HAVE_ECB_IVEC_BUG +#endif + +#ifndef HAS_LIBRESSL +# ifdef RSA_SSLV23_PADDING +# define HAVE_RSA_SSLV23_PADDING +# endif +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) +# ifdef RSA_PKCS1_PSS_PADDING +# define HAVE_RSA_PKCS1_PSS_PADDING +# endif +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'h') \ + && defined(HAVE_EC) +/* If OPENSSL_NO_EC is set, there will be an error in ec.h included from engine.h + So if EC is disabled, you can't use Engine either.... +*/ +# define HAS_ENGINE_SUPPORT +#endif + + +#if defined(HAS_ENGINE_SUPPORT) +# include <openssl/engine.h> +#endif + +#if defined(HAVE_CMAC) +#include <openssl/cmac.h> +#endif + +#if defined(HAVE_EC) +#include <openssl/ec.h> +#include <openssl/ecdh.h> +#include <openssl/ecdsa.h> +#endif + +#ifdef VALGRIND + # include <valgrind/memcheck.h> + +/* libcrypto mixes supplied buffer contents into its entropy pool, + which makes valgrind complain about the use of uninitialized data. + We use this valgrind "request" to make sure that no such seemingly + undefined data is returned. +*/ + # define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size) \ + VALGRIND_MAKE_MEM_DEFINED(ptr,size) + + # define ERL_VALGRIND_ASSERT_MEM_DEFINED(Ptr,Size) \ + do { \ + int __erl_valgrind_mem_defined = VALGRIND_CHECK_MEM_IS_DEFINED((Ptr),(Size)); \ + if (__erl_valgrind_mem_defined != 0) { \ + fprintf(stderr,"\r\n####### VALGRIND_ASSSERT(%p,%ld) failed at %s:%d\r\n", \ + (Ptr),(long)(Size), __FILE__, __LINE__); \ + abort(); \ + } \ + } while (0) + +#else + # define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size) + # define ERL_VALGRIND_ASSERT_MEM_DEFINED(ptr,size) +#endif + +#ifdef DEBUG + # define ASSERT(e) \ + ((void) ((e) ? 1 : (fprintf(stderr,"Assert '%s' failed at %s:%d\n",\ + #e, __FILE__, __LINE__), abort(), 0))) +#else + # define ASSERT(e) ((void) 1) +#endif + +#ifdef __GNUC__ + # define INLINE __inline__ +#elif defined(__WIN32__) + # define INLINE __forceinline +#else + # define INLINE +#endif + + +#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \ + (((unsigned char*) (s))[1] << 16) | \ + (((unsigned char*) (s))[2] << 8) | \ + (((unsigned char*) (s))[3])) + +#define put_uint32(s,i) \ +{ (s)[0] = (unsigned char)(((i) >> 24) & 0xff);\ + (s)[1] = (unsigned char)(((i) >> 16) & 0xff);\ + (s)[2] = (unsigned char)(((i) >> 8) & 0xff);\ + (s)[3] = (unsigned char)((i) & 0xff);\ +} + +/* This shall correspond to the similar macro in crypto.erl */ +/* Current value is: erlang:system_info(context_reductions) * 10 */ +#define MAX_BYTES_TO_NIF 20000 + +#define CONSUME_REDS(NifEnv, Ibin) \ +do { \ + size_t _cost = (Ibin).size; \ + if (_cost > SIZE_MAX / 100) \ + _cost = 100; \ + else \ + _cost = (_cost * 100) / MAX_BYTES_TO_NIF; \ + \ + if (_cost) { \ + (void) enif_consume_timeslice((NifEnv), \ + (_cost > 100) ? 100 : (int)_cost); \ + } \ + } while (0) + +#ifdef NEED_EVP_COMPATIBILITY_FUNCTIONS +# include "evp_compat.h" +#else +# define HAVE_OPAQUE_BN_GENCB +#endif + +#if 0 +# define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n") +# define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1) +# define PRINTF_ERR2(FMT, A1, A2) enif_fprintf(stderr, FMT "\n", A1, A2) +#else +# define PRINTF_ERR0(FMT) +# define PRINTF_ERR1(FMT,A1) +# define PRINTF_ERR2(FMT,A1,A2) +#endif + +#ifdef FIPS_SUPPORT +/* In FIPS mode non-FIPS algorithms are disabled and return badarg. */ +#define CHECK_NO_FIPS_MODE() { if (FIPS_mode()) return atom_notsup; } +#else +#define CHECK_NO_FIPS_MODE() +#endif + +#endif /* E_OPENSSL_CONFIG_H__ */ diff --git a/lib/crypto/c_src/otp_test_engine.c b/lib/crypto/c_src/otp_test_engine.c index 2c8cce094e..fd26b7cb5d 100644 --- a/lib/crypto/c_src/otp_test_engine.c +++ b/lib/crypto/c_src/otp_test_engine.c @@ -21,8 +21,11 @@ #ifdef _WIN32 #define OPENSSL_OPT_WINDLL #endif + #include <stdio.h> #include <string.h> +#include <limits.h> +#include <stdint.h> #include <openssl/md5.h> #include <openssl/rsa.h> @@ -87,13 +90,12 @@ static int test_init(ENGINE *e) { printf("OTP Test Engine Initializatzion!\r\n"); #if defined(FAKE_RSA_IMPL) - if ( !RSA_meth_set_finish(test_rsa_method, test_rsa_free) - || !RSA_meth_set_sign(test_rsa_method, test_rsa_sign) - || !RSA_meth_set_verify(test_rsa_method, test_rsa_verify) - ) { - fprintf(stderr, "Setup RSA_METHOD failed\r\n"); - return 0; - } + if (!RSA_meth_set_finish(test_rsa_method, test_rsa_free)) + goto err; + if (!RSA_meth_set_sign(test_rsa_method, test_rsa_sign)) + goto err; + if (!RSA_meth_set_verify(test_rsa_method, test_rsa_verify)) + goto err; #endif /* if defined(FAKE_RSA_IMPL) */ /* Load all digest and cipher algorithms. Needed for password protected private keys */ @@ -101,6 +103,12 @@ static int test_init(ENGINE *e) { OpenSSL_add_all_digests(); return 111; + +#if defined(FAKE_RSA_IMPL) +err: + fprintf(stderr, "Setup RSA_METHOD failed\r\n"); + return 0; +#endif } static void add_test_data(unsigned char *md, unsigned int len) @@ -152,15 +160,15 @@ static int test_engine_md5_update(EVP_MD_CTX *ctx,const void *data, size_t count static int test_engine_md5_final(EVP_MD_CTX *ctx,unsigned char *md) { #ifdef OLD - int ret; - fprintf(stderr, "MD5 final size of EVP_MD: %lu\r\n", sizeof(EVP_MD)); - ret = MD5_Final(md, data(ctx)); + if (!MD5_Final(md, data(ctx))) + goto err; - if (ret > 0) { - add_test_data(md, MD5_DIGEST_LENGTH); - } - return ret; + add_test_data(md, MD5_DIGEST_LENGTH); + return 1; + + err: + return 0; #else fprintf(stderr, "MD5 final\r\n"); add_test_data(md, MD5_DIGEST_LENGTH); @@ -190,7 +198,6 @@ static int test_digest_ids[] = {NID_md5}; static int test_engine_digest_selector(ENGINE *e, const EVP_MD **digest, const int **nids, int nid) { - int ok = 1; if (!digest) { *nids = test_digest_ids; fprintf(stderr, "Digest is empty! Nid:%d\r\n", nid); @@ -201,64 +208,82 @@ static int test_engine_digest_selector(ENGINE *e, const EVP_MD **digest, #ifdef OLD *digest = &test_engine_md5_method; #else - EVP_MD *md = EVP_MD_meth_new(NID_md5, NID_undef); - if (!md || - !EVP_MD_meth_set_result_size(md, MD5_DIGEST_LENGTH) || - !EVP_MD_meth_set_flags(md, 0) || - !EVP_MD_meth_set_init(md, test_engine_md5_init) || - !EVP_MD_meth_set_update(md, test_engine_md5_update) || - !EVP_MD_meth_set_final(md, test_engine_md5_final) || - !EVP_MD_meth_set_copy(md, NULL) || - !EVP_MD_meth_set_cleanup(md, NULL) || - !EVP_MD_meth_set_input_blocksize(md, MD5_CBLOCK) || - !EVP_MD_meth_set_app_datasize(md, sizeof(EVP_MD *) + sizeof(MD5_CTX)) || - !EVP_MD_meth_set_ctrl(md, NULL)) - { - ok = 0; - *digest = NULL; - } else - { - *digest = md; - } + EVP_MD *md; + + if ((md = EVP_MD_meth_new(NID_md5, NID_undef)) == NULL) + goto err; + if (EVP_MD_meth_set_result_size(md, MD5_DIGEST_LENGTH) != 1) + goto err; + if (EVP_MD_meth_set_flags(md, 0) != 1) + goto err; + if (EVP_MD_meth_set_init(md, test_engine_md5_init) != 1) + goto err; + if (EVP_MD_meth_set_update(md, test_engine_md5_update) != 1) + goto err; + if (EVP_MD_meth_set_final(md, test_engine_md5_final) != 1) + goto err; + if (EVP_MD_meth_set_copy(md, NULL) != 1) + goto err; + if (EVP_MD_meth_set_cleanup(md, NULL) != 1) + goto err; + if (EVP_MD_meth_set_input_blocksize(md, MD5_CBLOCK) != 1) + goto err; + if (EVP_MD_meth_set_app_datasize(md, sizeof(EVP_MD *) + sizeof(MD5_CTX)) != 1) + goto err; + if (EVP_MD_meth_set_ctrl(md, NULL) != 1) + goto err; + + *digest = md; #endif } else { - ok = 0; - *digest = NULL; + goto err; } - return ok; + return 1; + + err: + *digest = NULL; + return 0; } static int bind_helper(ENGINE * e, const char *id) { #if defined(FAKE_RSA_IMPL) - test_rsa_method = RSA_meth_new("OTP test RSA method", 0); - if (test_rsa_method == NULL) { + if ((test_rsa_method = RSA_meth_new("OTP test RSA method", 0)) == NULL) { fprintf(stderr, "RSA_meth_new failed\r\n"); - return 0; + goto err; } #endif /* if defined(FAKE_RSA_IMPL) */ - if (!ENGINE_set_id(e, test_engine_id) - || !ENGINE_set_name(e, test_engine_name) - || !ENGINE_set_init_function(e, test_init) - || !ENGINE_set_digests(e, &test_engine_digest_selector) - /* For testing of key storage in an Engine: */ - || !ENGINE_set_load_privkey_function(e, &test_privkey_load) - || !ENGINE_set_load_pubkey_function(e, &test_pubkey_load) - ) - return 0; + if (!ENGINE_set_id(e, test_engine_id)) + goto err; + if (!ENGINE_set_name(e, test_engine_name)) + goto err; + if (!ENGINE_set_init_function(e, test_init)) + goto err; + if (!ENGINE_set_digests(e, &test_engine_digest_selector)) + goto err; + /* For testing of key storage in an Engine: */ + if (!ENGINE_set_load_privkey_function(e, &test_privkey_load)) + goto err; + if (!ENGINE_set_load_pubkey_function(e, &test_pubkey_load)) + goto err; #if defined(FAKE_RSA_IMPL) - if ( !ENGINE_set_RSA(e, test_rsa_method) ) { - RSA_meth_free(test_rsa_method); - test_rsa_method = NULL; - return 0; - } + if (!ENGINE_set_RSA(e, test_rsa_method)) + goto err; #endif /* if defined(FAKE_RSA_IMPL) */ return 1; + + err: +#if defined(FAKE_RSA_IMPL) + if (test_rsa_method) + RSA_meth_free(test_rsa_method); + test_rsa_method = NULL; +#endif + return 0; } IMPLEMENT_DYNAMIC_CHECK_FN(); @@ -304,7 +329,7 @@ EVP_PKEY* test_key_load(ENGINE *eng, const char *id, UI_METHOD *ui_method, void fprintf(stderr, "Contents of file \"%s\":\r\n",id); f = fopen(id, "r"); { /* Print the contents of the key file */ - char c; + int c; while (!feof(f)) { switch (c=fgetc(f)) { case '\n': @@ -324,23 +349,28 @@ EVP_PKEY* test_key_load(ENGINE *eng, const char *id, UI_METHOD *ui_method, void int pem_passwd_cb_fun(char *buf, int size, int rwflag, void *password) { - int i; + size_t i; + + if (size < 0) + return 0; fprintf(stderr, "In pem_passwd_cb_fun\r\n"); if (!password) return 0; i = strlen(password); - if (i < size) { - /* whole pwd (incl terminating 0) fits */ - fprintf(stderr, "Got FULL pwd %d(%d) chars\r\n", i, size); - memcpy(buf, (char*)password, i+1); - return i+1; - } else { - fprintf(stderr, "Got TO LONG pwd %d(%d) chars\r\n", i, size); - /* meaningless with a truncated password */ - return 0; - } + if (i >= (size_t)size || i > INT_MAX - 1) + goto err; + + /* whole pwd (incl terminating 0) fits */ + fprintf(stderr, "Got FULL pwd %zu(%d) chars\r\n", i, size); + memcpy(buf, (char*)password, i+1); + return (int)i+1; + + err: + fprintf(stderr, "Got TO LONG pwd %zu(%d) chars\r\n", i, size); + /* meaningless with a truncated password */ + return 0; } #endif @@ -349,7 +379,7 @@ int pem_passwd_cb_fun(char *buf, int size, int rwflag, void *password) /* RSA sign. This returns a fixed string so the test case can test that it was called instead of the cryptolib default RSA sign */ -unsigned char fake_flag[] = {255,3,124,180,35,10,180,151,101,247,62,59,80,122,220, +static unsigned char fake_flag[] = {255,3,124,180,35,10,180,151,101,247,62,59,80,122,220, 142,24,180,191,34,51,150,112,27,43,142,195,60,245,213,80,179}; int test_rsa_sign(int dtype, @@ -360,11 +390,10 @@ int test_rsa_sign(int dtype, /* The key */ const RSA *rsa) { - int slen; fprintf(stderr, "test_rsa_sign (dtype=%i) called m_len=%u *siglen=%u\r\n", dtype, m_len, *siglen); if (!sigret) { fprintf(stderr, "sigret = NULL\r\n"); - return -1; + goto err; } /* {int i; @@ -376,14 +405,20 @@ int test_rsa_sign(int dtype, if ((sizeof(fake_flag) == m_len) && bcmp(m,fake_flag,m_len) == 0) { + int slen; + printf("To be faked\r\n"); /* To be faked */ - slen = RSA_size(rsa); - add_test_data(sigret, slen); /* The signature is 0,1,2...255,0,1... */ - *siglen = slen; /* Must set this. Why? */ + if ((slen = RSA_size(rsa)) < 0) + goto err; + add_test_data(sigret, (unsigned int)slen); /* The signature is 0,1,2...255,0,1... */ + *siglen = (unsigned int)slen; /* Must set this. Why? */ return 1; /* 1 = success */ } return 0; + + err: + return -1; } int test_rsa_verify(int dtype, @@ -398,8 +433,13 @@ int test_rsa_verify(int dtype, if ((sizeof(fake_flag) == m_len) && bcmp(m,fake_flag,m_len) == 0) { + int size; + + if ((size = RSA_size(rsa)) < 0) + return 0; + printf("To be faked\r\n"); - return (siglen == RSA_size(rsa)) + return (siglen == (unsigned int)size) && chk_test_data(sigret, siglen); } return 0; diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c new file mode 100644 index 0000000000..393358d173 --- /dev/null +++ b/lib/crypto/c_src/pkey.c @@ -0,0 +1,1456 @@ +/* + * %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 "pkey.h" +#include "bn.h" +#include "digest.h" +#include "dss.h" +#include "ec.h" +#include "eddsa.h" +#include "engine.h" +#include "rsa.h" + +#define PKEY_BADARG -1 +#define PKEY_NOTSUP 0 +#define PKEY_OK 1 + +typedef struct PKeyCryptOptions { + const EVP_MD *rsa_mgf1_md; + ErlNifBinary rsa_oaep_label; + const EVP_MD *rsa_oaep_md; + int rsa_padding; + const EVP_MD *signature_md; +} PKeyCryptOptions; + +typedef struct PKeySignOptions { + const EVP_MD *rsa_mgf1_md; + int rsa_padding; + int rsa_pss_saltlen; +} PKeySignOptions; + + +static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM type, + const EVP_MD **md); +static int get_pkey_sign_digest(ErlNifEnv *env, ERL_NIF_TERM algorithm, + ERL_NIF_TERM type, ERL_NIF_TERM data, + unsigned char *md_value, const EVP_MD **mdp, + unsigned char **tbsp, size_t *tbslenp); +static int get_pkey_sign_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, + const EVP_MD *md, PKeySignOptions *opt); +static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, EVP_PKEY **pkey); +static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, + EVP_PKEY **pkey); +static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, + PKeyCryptOptions *opt); +static size_t size_of_RSA(EVP_PKEY *pkey); + + +static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM type, + const EVP_MD **md) +{ + struct digest_type_t *digp = NULL; + *md = NULL; + + if (type == atom_none && algorithm == atom_rsa) + return PKEY_OK; + if (algorithm == atom_eddsa) { +#ifdef HAVE_EDDSA + if (!FIPS_mode()) return PKEY_OK; +#else + return PKEY_NOTSUP; +#endif + } + if ((digp = get_digest_type(type)) == NULL) + return PKEY_BADARG; + if (digp->md.p == NULL) + return PKEY_NOTSUP; + + *md = digp->md.p; + return PKEY_OK; +} + +static int get_pkey_sign_digest(ErlNifEnv *env, ERL_NIF_TERM algorithm, + ERL_NIF_TERM type, ERL_NIF_TERM data, + unsigned char *md_value, const EVP_MD **mdp, + unsigned char **tbsp, size_t *tbslenp) +{ + int i, ret; + const ERL_NIF_TERM *tpl_terms; + int tpl_arity; + ErlNifBinary tbs_bin; + EVP_MD_CTX *mdctx = NULL; + const EVP_MD *md; + unsigned char *tbs; + size_t tbslen; + unsigned int tbsleni; + + md = *mdp; + tbs = *tbsp; + tbslen = *tbslenp; + + if ((i = get_pkey_digest_type(env, algorithm, type, &md)) != PKEY_OK) + return i; + + if (enif_get_tuple(env, data, &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2) + goto bad_arg; + if (tpl_terms[0] != atom_digest) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, tpl_terms[1], &tbs_bin)) + goto bad_arg; + if (tbs_bin.size > INT_MAX) + goto bad_arg; + if (md != NULL) { + if ((int)tbs_bin.size != EVP_MD_size(md)) + goto bad_arg; + } + + /* We have a digest (= hashed text) in tbs_bin */ + tbs = tbs_bin.data; + tbslen = tbs_bin.size; + } else if (md == NULL) { + if (!enif_inspect_iolist_as_binary(env, data, &tbs_bin)) + goto bad_arg; + + /* md == NULL, that is no hashing because DigestType argument was atom_none */ + tbs = tbs_bin.data; + tbslen = tbs_bin.size; + } else { + if (!enif_inspect_iolist_as_binary(env, data, &tbs_bin)) + goto bad_arg; + + /* We have the cleartext in tbs_bin and the hash algo info in md */ + tbs = md_value; + + if ((mdctx = EVP_MD_CTX_create()) == NULL) + goto err; + + /* Looks well, now hash the plain text into a digest according to md */ + if (EVP_DigestInit_ex(mdctx, md, NULL) != 1) + goto err; + if (EVP_DigestUpdate(mdctx, tbs_bin.data, tbs_bin.size) != 1) + goto err; + if (EVP_DigestFinal_ex(mdctx, tbs, &tbsleni) != 1) + goto err; + + tbslen = (size_t)tbsleni; + } + + *mdp = md; + *tbsp = tbs; + *tbslenp = tbslen; + + ret = PKEY_OK; + goto done; + + bad_arg: + err: + ret = PKEY_BADARG; + + done: + if (mdctx) + EVP_MD_CTX_destroy(mdctx); + return ret; +} + +static int get_pkey_sign_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, + const EVP_MD *md, PKeySignOptions *opt) +{ + ERL_NIF_TERM head, tail; + const ERL_NIF_TERM *tpl_terms; + int tpl_arity; + const EVP_MD *opt_md; + + if (!enif_is_list(env, options)) + goto bad_arg; + + /* defaults */ + if (algorithm == atom_rsa) { + opt->rsa_mgf1_md = NULL; + opt->rsa_padding = RSA_PKCS1_PADDING; + opt->rsa_pss_saltlen = -2; + } + + if (enif_is_empty_list(env, options)) + return PKEY_OK; + + if (algorithm != atom_rsa) + goto bad_arg; + + tail = options; + while (enif_get_list_cell(env, tail, &head, &tail)) { + if (!enif_get_tuple(env, head, &tpl_arity, &tpl_terms)) + goto bad_arg; + if (tpl_arity != 2) + goto bad_arg; + + if (tpl_terms[0] == atom_rsa_mgf1_md && enif_is_atom(env, tpl_terms[1])) { + int result; + + result = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); + if (result != PKEY_OK) + return result; + + opt->rsa_mgf1_md = opt_md; + + } else if (tpl_terms[0] == atom_rsa_padding) { + if (tpl_terms[1] == atom_rsa_pkcs1_padding) { + opt->rsa_padding = RSA_PKCS1_PADDING; + + } else if (tpl_terms[1] == atom_rsa_pkcs1_pss_padding) { +#ifdef HAVE_RSA_PKCS1_PSS_PADDING + opt->rsa_padding = RSA_PKCS1_PSS_PADDING; + if (opt->rsa_mgf1_md == NULL) + opt->rsa_mgf1_md = md; +#else + return PKEY_NOTSUP; +#endif + + } else if (tpl_terms[1] == atom_rsa_x931_padding) { + opt->rsa_padding = RSA_X931_PADDING; + + } else if (tpl_terms[1] == atom_rsa_no_padding) { + opt->rsa_padding = RSA_NO_PADDING; + + } else { + goto bad_arg; + } + + } else if (tpl_terms[0] == atom_rsa_pss_saltlen) { + if (!enif_get_int(env, tpl_terms[1], &(opt->rsa_pss_saltlen))) + goto bad_arg; + if (opt->rsa_pss_saltlen < -2) + goto bad_arg; + + } else { + goto bad_arg; + } + } + + return PKEY_OK; + + bad_arg: + return PKEY_BADARG; +} + +static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, EVP_PKEY **pkey) +{ + EVP_PKEY *result = NULL; + RSA *rsa = NULL; + DSA *dsa = NULL; +#if defined(HAVE_EC) + EC_KEY *ec = NULL; +#endif + char *id = NULL; + char *password = NULL; + + if (enif_is_map(env, key)) { +#ifdef HAS_ENGINE_SUPPORT + /* Use key stored in engine */ + ENGINE *e; + + if (!get_engine_and_key_id(env, key, &id, &e)) + goto err; + + password = get_key_password(env, key); + result = ENGINE_load_private_key(e, id, NULL, password); + +#else + return PKEY_BADARG; +#endif + } else if (algorithm == atom_rsa) { + if ((rsa = RSA_new()) == NULL) + goto err; + + if (!get_rsa_private_key(env, key, rsa)) + goto err; + if ((result = EVP_PKEY_new()) == NULL) + goto err; + if (EVP_PKEY_assign_RSA(result, rsa) != 1) + goto err; + /* On success, result owns rsa */ + rsa = NULL; + + } else if (algorithm == atom_ecdsa) { +#if defined(HAVE_EC) + const ERL_NIF_TERM *tpl_terms; + int tpl_arity; + + if (!enif_get_tuple(env, key, &tpl_arity, &tpl_terms)) + goto err; + if (tpl_arity != 2) + goto err; + if (!enif_is_tuple(env, tpl_terms[0])) + goto err; + if (!enif_is_binary(env, tpl_terms[1])) + goto err; + if (!get_ec_key(env, tpl_terms[0], tpl_terms[1], atom_undefined, &ec)) + goto err; + + if ((result = EVP_PKEY_new()) == NULL) + goto err; + if (EVP_PKEY_assign_EC_KEY(result, ec) != 1) + goto err; + /* On success, result owns ec */ + ec = NULL; + +#else + return PKEY_NOTSUP; +#endif + } else if (algorithm == atom_eddsa) { +#ifdef HAVE_EDDSA + if (!FIPS_mode()) + { + if (!get_eddsa_key(env, 0, key, &result)) + goto err; + else + goto done; // Not nice.... + } +#else + return PKEY_NOTSUP; +#endif + } else if (algorithm == atom_dss) { + if ((dsa = DSA_new()) == NULL) + goto err; + if (!get_dss_private_key(env, key, dsa)) + goto err; + + if ((result = EVP_PKEY_new()) == NULL) + goto err; + if (EVP_PKEY_assign_DSA(result, dsa) != 1) + goto err; + /* On success, result owns dsa */ + dsa = NULL; + + } else { + return PKEY_BADARG; + } + + goto done; + + err: + if (result) + EVP_PKEY_free(result); + result = NULL; + + done: + if (password) + enif_free(password); + if (id) + enif_free(id); + if (rsa) + RSA_free(rsa); + if (dsa) + DSA_free(dsa); +#ifdef HAVE_EC + if (ec) + EC_KEY_free(ec); +#endif + + if (result == NULL) { + return PKEY_BADARG; + } else { + *pkey = result; + return PKEY_OK; + } +} + +static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, + EVP_PKEY **pkey) +{ + EVP_PKEY *result = NULL; + RSA *rsa = NULL; + DSA *dsa = NULL; +#if defined(HAVE_EC) + EC_KEY *ec = NULL; +#endif + char *id = NULL; + char *password = NULL; + + if (enif_is_map(env, key)) { +#ifdef HAS_ENGINE_SUPPORT + /* Use key stored in engine */ + ENGINE *e; + + if (!get_engine_and_key_id(env, key, &id, &e)) + goto err; + + password = get_key_password(env, key); + result = ENGINE_load_public_key(e, id, NULL, password); + +#else + return PKEY_BADARG; +#endif + } else if (algorithm == atom_rsa) { + if ((rsa = RSA_new()) == NULL) + goto err; + + if (!get_rsa_public_key(env, key, rsa)) + goto err; + + if ((result = EVP_PKEY_new()) == NULL) + goto err; + if (EVP_PKEY_assign_RSA(result, rsa) != 1) + goto err; + /* On success, result owns rsa */ + rsa = NULL; + + } else if (algorithm == atom_ecdsa) { +#if defined(HAVE_EC) + const ERL_NIF_TERM *tpl_terms; + int tpl_arity; + + if (!enif_get_tuple(env, key, &tpl_arity, &tpl_terms)) + goto err; + if (tpl_arity != 2) + goto err; + if (!enif_is_tuple(env, tpl_terms[0])) + goto err; + if (!enif_is_binary(env, tpl_terms[1])) + goto err; + if (!get_ec_key(env, tpl_terms[0], atom_undefined, tpl_terms[1], &ec)) + goto err; + + if ((result = EVP_PKEY_new()) == NULL) + goto err; + + if (EVP_PKEY_assign_EC_KEY(result, ec) != 1) + goto err; + /* On success, result owns ec */ + ec = NULL; + +#else + return PKEY_NOTSUP; +#endif + } else if (algorithm == atom_eddsa) { +#ifdef HAVE_EDDSA + if (!FIPS_mode()) { + if (!get_eddsa_key(env, 1, key, &result)) + goto err; + } +#else + return PKEY_NOTSUP; +#endif + } else if (algorithm == atom_dss) { + if ((dsa = DSA_new()) == NULL) + goto err; + + if (!get_dss_public_key(env, key, dsa)) + goto err; + + if ((result = EVP_PKEY_new()) == NULL) + goto err; + if (EVP_PKEY_assign_DSA(result, dsa) != 1) + goto err; + /* On success, result owns dsa */ + dsa = NULL; + + } else { + return PKEY_BADARG; + } + + goto done; + + err: + if (result) + EVP_PKEY_free(result); + result = NULL; + + done: + if (password) + enif_free(password); + if (id) + enif_free(id); + if (rsa) + RSA_free(rsa); + if (dsa) + DSA_free(dsa); +#ifdef HAVE_EC + if (ec) + EC_KEY_free(ec); +#endif + + if (result == NULL) { + return PKEY_BADARG; + } else { + *pkey = result; + return PKEY_OK; + } +} + +ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{/* (Algorithm, Type, Data|{digest,Digest}, Key|#{}, Options) */ + int i; + int sig_bin_alloc = 0; + ERL_NIF_TERM ret; + const EVP_MD *md = NULL; + unsigned char md_value[EVP_MAX_MD_SIZE]; + EVP_PKEY *pkey = NULL; +#ifdef HAVE_EDDSA + EVP_MD_CTX *mdctx = NULL; +#endif +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX *ctx = NULL; + size_t siglen; +#else + int len; + unsigned int siglen; +#endif + PKeySignOptions sig_opt; + ErlNifBinary sig_bin; /* signature */ + unsigned char *tbs; /* data to be signed */ + size_t tbslen; + RSA *rsa = NULL; + DSA *dsa = NULL; +#if defined(HAVE_EC) + EC_KEY *ec = NULL; +#endif +/*char buf[1024]; +enif_get_atom(env,argv[0],buf,1024,ERL_NIF_LATIN1); printf("algo=%s ",buf); +enif_get_atom(env,argv[1],buf,1024,ERL_NIF_LATIN1); printf("hash=%s ",buf); +*/ + +#ifndef HAS_ENGINE_SUPPORT + if (enif_is_map(env, argv[3])) + return atom_notsup; +#endif + + i = get_pkey_sign_digest(env, argv[0], argv[1], argv[2], md_value, &md, &tbs, &tbslen); + switch (i) { + case PKEY_OK: + break; + case PKEY_NOTSUP: + goto notsup; + default: + goto bad_arg; + } + + i = get_pkey_sign_options(env, argv[0], argv[4], md, &sig_opt); + switch (i) { + case PKEY_OK: + break; + case PKEY_NOTSUP: + goto notsup; + default: + goto bad_arg; + } + + if (get_pkey_private_key(env, argv[0], argv[3], &pkey) != PKEY_OK) + goto bad_arg; + +#ifdef HAS_EVP_PKEY_CTX + if ((ctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL) + goto err; + + if (argv[0] != atom_eddsa) { + if (EVP_PKEY_sign_init(ctx) != 1) + goto err; + if (md != NULL) { + if (EVP_PKEY_CTX_set_signature_md(ctx, md) != 1) + goto err; + } + } + + if (argv[0] == atom_rsa) { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, sig_opt.rsa_padding) != 1) + goto err; +# ifdef HAVE_RSA_PKCS1_PSS_PADDING + if (sig_opt.rsa_padding == RSA_PKCS1_PSS_PADDING) { + if (sig_opt.rsa_mgf1_md != NULL) { +# ifdef HAVE_RSA_MGF1_MD + if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, sig_opt.rsa_mgf1_md) != 1) + goto err; +# else + goto notsup; +# endif + } + if (sig_opt.rsa_pss_saltlen > -2) { + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, sig_opt.rsa_pss_saltlen) != 1) + goto err; + } + } +#endif + } + + if (argv[0] == atom_eddsa) { +#ifdef HAVE_EDDSA + if (!FIPS_mode()) { + if ((mdctx = EVP_MD_CTX_new()) == NULL) + goto err; + + if (EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey) != 1) + goto err; + if (EVP_DigestSign(mdctx, NULL, &siglen, tbs, tbslen) != 1) + goto err; + if (!enif_alloc_binary(siglen, &sig_bin)) + goto err; + sig_bin_alloc = 1; + + if (EVP_DigestSign(mdctx, sig_bin.data, &siglen, tbs, tbslen) != 1) + goto bad_key; + } + else +#endif + goto notsup; + } else { + if (EVP_PKEY_sign(ctx, NULL, &siglen, tbs, tbslen) != 1) + goto err; + if (!enif_alloc_binary(siglen, &sig_bin)) + goto err; + sig_bin_alloc = 1; + + if (md != NULL) { + ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md)); + } + if (EVP_PKEY_sign(ctx, sig_bin.data, &siglen, tbs, tbslen) != 1) + goto bad_key; + } +#else +/*printf("Old interface\r\n"); + */ + if (argv[0] == atom_rsa) { + if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) + goto err; + if ((len = RSA_size(rsa)) < 0) + goto err; + if (!enif_alloc_binary((size_t)len, &sig_bin)) + goto err; + sig_bin_alloc = 1; + + if ((len = EVP_MD_size(md)) < 0) + goto err; + ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); + + if (RSA_sign(md->type, tbs, (unsigned int)len, sig_bin.data, &siglen, rsa) != 1) + goto bad_key; + } else if (argv[0] == atom_dss) { + if ((dsa = EVP_PKEY_get1_DSA(pkey)) == NULL) + goto err; + if ((len = DSA_size(dsa)) < 0) + goto err; + if (!enif_alloc_binary((size_t)len, &sig_bin)) + goto err; + sig_bin_alloc = 1; + + if ((len = EVP_MD_size(md)) < 0) + goto err; + ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); + + if (DSA_sign(md->type, tbs, len, sig_bin.data, &siglen, dsa) != 1) + goto bad_key; + } else if (argv[0] == atom_ecdsa) { +#if defined(HAVE_EC) + if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) + goto err; + if ((len = ECDSA_size(ec)) < 0) + goto err; + if (!enif_alloc_binary((size_t)len, &sig_bin)) + goto err; + sig_bin_alloc = 1; + + len = EVP_MD_size(md); + ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); + + if (ECDSA_sign(md->type, tbs, len, sig_bin.data, &siglen, ec) != 1) + goto bad_key; +#else + goto notsup; +#endif + } else { + goto bad_arg; + } +#endif + + ERL_VALGRIND_MAKE_MEM_DEFINED(sig_bin.data, siglen); + if (siglen != sig_bin.size) { + if (!enif_realloc_binary(&sig_bin, siglen)) + goto err; + ERL_VALGRIND_ASSERT_MEM_DEFINED(sig_bin.data, siglen); + } + ret = enif_make_binary(env, &sig_bin); + sig_bin_alloc = 0; + goto done; + + bad_key: + ret = atom_error; + goto done; + + notsup: + ret = atom_notsup; + goto done; + + bad_arg: + err: + ret = enif_make_badarg(env); + goto done; + + done: + if (sig_bin_alloc) + enif_release_binary(&sig_bin); + if (rsa) + RSA_free(rsa); + if (dsa) + DSA_free(dsa); +#ifdef HAVE_EC + if (ec) + EC_KEY_free(ec); +#endif +#ifdef HAS_EVP_PKEY_CTX + if (ctx) + EVP_PKEY_CTX_free(ctx); +#endif + if (pkey) + EVP_PKEY_free(pkey); + + return ret; +} + +ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{/* (Algorithm, Type, Data|{digest,Digest}, Signature, Key, Options) */ + int i; + int result; + const EVP_MD *md = NULL; + unsigned char md_value[EVP_MAX_MD_SIZE]; + EVP_PKEY *pkey = NULL; +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX *ctx = NULL; +#else +#endif + PKeySignOptions sig_opt; + ErlNifBinary sig_bin; /* signature */ + unsigned char *tbs; /* data to be signed */ + size_t tbslen; + ERL_NIF_TERM ret; + RSA *rsa = NULL; + DSA *dsa = NULL; +#ifdef HAVE_EC + EC_KEY *ec = NULL; +#endif +#ifdef HAVE_EDDSA + EVP_MD_CTX *mdctx = NULL; +#endif + +#ifndef HAS_ENGINE_SUPPORT + if (enif_is_map(env, argv[4])) + return atom_notsup; +#endif + + if (!enif_inspect_binary(env, argv[3], &sig_bin)) + return enif_make_badarg(env); + + i = get_pkey_sign_digest(env, argv[0], argv[1], argv[2], md_value, &md, &tbs, &tbslen); + switch (i) { + case PKEY_OK: + break; + case PKEY_NOTSUP: + goto notsup; + default: + goto bad_arg; + } + + i = get_pkey_sign_options(env, argv[0], argv[5], md, &sig_opt); + switch (i) { + case PKEY_OK: + break; + case PKEY_NOTSUP: + goto notsup; + default: + goto bad_arg; + } + + if (get_pkey_public_key(env, argv[0], argv[4], &pkey) != PKEY_OK) { + goto bad_arg; + } + +#ifdef HAS_EVP_PKEY_CTX +/* printf("EVP interface\r\n"); + */ + if ((ctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL) + goto err; + + if (argv[0] != atom_eddsa) { + if (EVP_PKEY_verify_init(ctx) != 1) + goto err; + if (md != NULL) { + if (EVP_PKEY_CTX_set_signature_md(ctx, md) != 1) + goto err; + } + } + + if (argv[0] == atom_rsa) { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, sig_opt.rsa_padding) != 1) + goto err; + if (sig_opt.rsa_padding == RSA_PKCS1_PSS_PADDING) { + if (sig_opt.rsa_mgf1_md != NULL) { +# ifdef HAVE_RSA_MGF1_MD + if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, sig_opt.rsa_mgf1_md) != 1) + goto err; +# else + goto notsup; +# endif + } + if (sig_opt.rsa_pss_saltlen > -2) { + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, sig_opt.rsa_pss_saltlen) != 1) + goto err; + } + } + } + + if (argv[0] == atom_eddsa) { +#ifdef HAVE_EDDSA + if (!FIPS_mode()) { + if ((mdctx = EVP_MD_CTX_new()) == NULL) + goto err; + + if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) + goto err; + + result = EVP_DigestVerify(mdctx, sig_bin.data, sig_bin.size, tbs, tbslen); + } + else +#endif + goto notsup; + } else { + if (md != NULL) { + ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md)); + } + result = EVP_PKEY_verify(ctx, sig_bin.data, sig_bin.size, tbs, tbslen); + } +#else +/*printf("Old interface\r\n"); +*/ + if (tbslen > INT_MAX) + goto bad_arg; + if (sig_bin.size > INT_MAX) + goto bad_arg; + if (argv[0] == atom_rsa) { + if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) + goto err; + result = RSA_verify(md->type, tbs, (unsigned int)tbslen, sig_bin.data, (unsigned int)sig_bin.size, rsa); + } else if (argv[0] == atom_dss) { + if ((dsa = EVP_PKEY_get1_DSA(pkey)) == NULL) + goto err; + result = DSA_verify(0, tbs, (int)tbslen, sig_bin.data, (int)sig_bin.size, dsa); + } else if (argv[0] == atom_ecdsa) { +#if defined(HAVE_EC) + if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) + goto err; + result = ECDSA_verify(EVP_MD_type(md), tbs, (int)tbslen, sig_bin.data, (int)sig_bin.size, ec); +#else + goto notsup; +#endif + } else { + goto bad_arg; + } +#endif + + ret = (result == 1 ? atom_true : atom_false); + goto done; + + bad_arg: + err: + ret = enif_make_badarg(env); + goto done; + + notsup: + ret = atom_notsup; + + done: +#ifdef HAS_EVP_PKEY_CTX + if (ctx) + EVP_PKEY_CTX_free(ctx); +#endif +#ifdef HAVE_EDDSA + if (mdctx) + EVP_MD_CTX_free(mdctx); +#endif + if (pkey) + EVP_PKEY_free(pkey); + if (rsa) + RSA_free(rsa); + if (dsa) + DSA_free(dsa); +#ifdef HAVE_EC + if (ec) + EC_KEY_free(ec); +#endif + + return ret; +} + +static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, + PKeyCryptOptions *opt) +{ + ERL_NIF_TERM head, tail; + const ERL_NIF_TERM *tpl_terms; + int tpl_arity; + const EVP_MD *opt_md; + + if (!enif_is_list(env, options)) + goto bad_arg; + + /* defaults */ + if (algorithm == atom_rsa) { + opt->rsa_mgf1_md = NULL; + opt->rsa_oaep_label.data = NULL; + opt->rsa_oaep_label.size = 0; + opt->rsa_oaep_md = NULL; + opt->rsa_padding = RSA_PKCS1_PADDING; + opt->signature_md = NULL; + } + + if (enif_is_empty_list(env, options)) + return PKEY_OK; + + if (algorithm != atom_rsa) + goto bad_arg; + + tail = options; + while (enif_get_list_cell(env, tail, &head, &tail)) { + if (!enif_get_tuple(env, head, &tpl_arity, &tpl_terms)) + goto bad_arg; + if (tpl_arity != 2) + goto bad_arg; + + if (tpl_terms[0] == atom_rsa_padding + || tpl_terms[0] == atom_rsa_pad /* Compatibility */ + ) { + if (tpl_terms[1] == atom_rsa_pkcs1_padding) { + opt->rsa_padding = RSA_PKCS1_PADDING; + +#ifdef HAVE_RSA_OAEP_PADDING + } else if (tpl_terms[1] == atom_rsa_pkcs1_oaep_padding) { + opt->rsa_padding = RSA_PKCS1_OAEP_PADDING; +#endif + +#ifdef HAVE_RSA_SSLV23_PADDING + } else if (tpl_terms[1] == atom_rsa_sslv23_padding) { + opt->rsa_padding = RSA_SSLV23_PADDING; +#endif + + } else if (tpl_terms[1] == atom_rsa_x931_padding) { + opt->rsa_padding = RSA_X931_PADDING; + + } else if (tpl_terms[1] == atom_rsa_no_padding) { + opt->rsa_padding = RSA_NO_PADDING; + + } else { + goto bad_arg; + } + + } else if (tpl_terms[0] == atom_signature_md && enif_is_atom(env, tpl_terms[1])) { + int i; + i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); + if (i != PKEY_OK) { + return i; + } + opt->signature_md = opt_md; + + } else if (tpl_terms[0] == atom_rsa_mgf1_md && enif_is_atom(env, tpl_terms[1])) { + int i; +#ifndef HAVE_RSA_MGF1_MD + if (tpl_terms[1] != atom_sha) + return PKEY_NOTSUP; +#endif + i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); + if (i != PKEY_OK) { + return i; + } + opt->rsa_mgf1_md = opt_md; + + } else if (tpl_terms[0] == atom_rsa_oaep_label + && enif_inspect_binary(env, tpl_terms[1], &(opt->rsa_oaep_label))) { +#ifdef HAVE_RSA_OAEP_MD + continue; +#else + return PKEY_NOTSUP; +#endif + + } else if (tpl_terms[0] == atom_rsa_oaep_md && enif_is_atom(env, tpl_terms[1])) { + int i; +#ifndef HAVE_RSA_OAEP_MD + if (tpl_terms[1] != atom_sha) + return PKEY_NOTSUP; +#endif + i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); + if (i != PKEY_OK) { + return i; + } + opt->rsa_oaep_md = opt_md; + + } else { + goto bad_arg; + } + } + + return PKEY_OK; + + bad_arg: + return PKEY_BADARG; +} + +static size_t size_of_RSA(EVP_PKEY *pkey) { + int ret = 0; + RSA *rsa = NULL; + + if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) + goto err; + ret = RSA_size(rsa); + + err: + if (rsa) + RSA_free(rsa); + + return (ret < 0) ? 0 : (size_t)ret; +} + +ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{/* (Algorithm, Data, PublKey=[E,N]|[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Options, IsPrivate, IsEncrypt) */ + ERL_NIF_TERM ret; + int i; + int result = 0; + int tmp_bin_alloc = 0; + int out_bin_alloc = 0; + EVP_PKEY *pkey = NULL; +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX *ctx = NULL; +#else + int len; + RSA *rsa = NULL; +#endif + PKeyCryptOptions crypt_opt; + ErlNifBinary in_bin, out_bin, tmp_bin; + size_t outlen; +#ifdef HAVE_RSA_SSLV23_PADDING + size_t tmplen; +#endif + int is_private, is_encrypt; + int algo_init = 0; + unsigned char *label_copy = NULL; + + ASSERT(argc == 6); + + is_private = (argv[4] == atom_true); + is_encrypt = (argv[5] == atom_true); + +/* char algo[1024]; */ + +#ifndef HAS_ENGINE_SUPPORT + if (enif_is_map(env, argv[2])) + return atom_notsup; +#endif + + if (!enif_inspect_binary(env, argv[1], &in_bin)) + goto bad_arg; + + i = get_pkey_crypt_options(env, argv[0], argv[3], &crypt_opt); + switch (i) { + case PKEY_OK: + break; + case PKEY_NOTSUP: + goto notsup; + default: + goto bad_arg; + } + + if (is_private) { + if (get_pkey_private_key(env, argv[0], argv[2], &pkey) != PKEY_OK) + goto bad_arg; + } else { + if (get_pkey_public_key(env, argv[0], argv[2], &pkey) != PKEY_OK) + goto bad_arg; + } + +#ifdef HAS_EVP_PKEY_CTX + if ((ctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL) + goto err; + +/* enif_get_atom(env,argv[0],algo,1024,ERL_NIF_LATIN1); */ + + if (is_private) { + if (is_encrypt) { + /* private encrypt */ + if ((algo_init = EVP_PKEY_sign_init(ctx)) != 1) + goto bad_arg; + } else { + /* private decrypt */ + if ((algo_init = EVP_PKEY_decrypt_init(ctx)) != 1) + goto bad_arg; + } + } else { + if (is_encrypt) { + /* public encrypt */ + if ((algo_init = EVP_PKEY_encrypt_init(ctx)) != 1) + goto bad_arg; + } else { + /* public decrypt */ + if ((algo_init = EVP_PKEY_verify_recover_init(ctx)) != 1) + goto bad_arg; + } + } + + if (argv[0] == atom_rsa) { + if (crypt_opt.signature_md != NULL) { + if (EVP_PKEY_CTX_set_signature_md(ctx, crypt_opt.signature_md) != 1) + goto bad_arg; + } + +#ifdef HAVE_RSA_SSLV23_PADDING + if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) { + if (is_encrypt) { + tmplen = size_of_RSA(pkey); + if (tmplen < 1 || tmplen > INT_MAX) + goto err; + if (!enif_alloc_binary(tmplen, &tmp_bin)) + goto err; + tmp_bin_alloc = 1; + if (in_bin.size > INT_MAX) + goto err; + if (!RSA_padding_add_SSLv23(tmp_bin.data, (int)tmplen, in_bin.data, (int)in_bin.size)) + goto err; + in_bin = tmp_bin; + } + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) != 1) + goto err; + } else +#endif + { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, crypt_opt.rsa_padding) != 1) + goto err; + } + +#ifdef HAVE_RSA_OAEP_MD + if (crypt_opt.rsa_padding == RSA_PKCS1_OAEP_PADDING) { + if (crypt_opt.rsa_oaep_md != NULL) { + if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, crypt_opt.rsa_oaep_md) != 1) + goto err; + } + + if (crypt_opt.rsa_mgf1_md != NULL) { + if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, crypt_opt.rsa_mgf1_md) != 1) + goto err; + } + + if (crypt_opt.rsa_oaep_label.data != NULL && crypt_opt.rsa_oaep_label.size > 0) { + if (crypt_opt.rsa_oaep_label.size > INT_MAX) + goto err; + if ((label_copy = OPENSSL_malloc(crypt_opt.rsa_oaep_label.size)) == NULL) + goto err; + + memcpy((void *)(label_copy), (const void *)(crypt_opt.rsa_oaep_label.data), + crypt_opt.rsa_oaep_label.size); + + if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, + (int)crypt_opt.rsa_oaep_label.size) != 1) + goto err; + /* On success, label_copy is owned by ctx */ + label_copy = NULL; + } + } +#endif + } + + if (is_private) { + if (is_encrypt) { + /* private_encrypt */ + result = EVP_PKEY_sign(ctx, NULL, &outlen, in_bin.data, in_bin.size); + } else { + /* private_decrypt */ + result = EVP_PKEY_decrypt(ctx, NULL, &outlen, in_bin.data, in_bin.size); + } + } else { + if (is_encrypt) { + /* public_encrypt */ + result = EVP_PKEY_encrypt(ctx, NULL, &outlen, in_bin.data, in_bin.size); + } else { + /* public_decrypt */ + result = EVP_PKEY_verify_recover(ctx, NULL, &outlen, in_bin.data, in_bin.size); + } + } + /* fprintf(stderr,"i = %d %s:%d\r\n", i, __FILE__, __LINE__); */ + + if (result != 1) + goto err; + + if (!enif_alloc_binary(outlen, &out_bin)) + goto err; + out_bin_alloc = 1; + + if (is_private) { + if (is_encrypt) { + /* private_encrypt */ + result = EVP_PKEY_sign(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); + } else { + /* private_decrypt */ + result = EVP_PKEY_decrypt(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); + } + } else { + if (is_encrypt) { + /* public_encrypt */ + result = EVP_PKEY_encrypt(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); + } else { + /* public_decrypt */ + result = EVP_PKEY_verify_recover(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); + } + } + +#else + /* Non-EVP cryptolib. Only support RSA */ + + if (argv[0] != atom_rsa) { + algo_init = -2; /* exitcode: notsup */ + goto bad_arg; + } + + if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) + goto err; + if ((len = RSA_size(rsa)) < 0) + goto err; + if (!enif_alloc_binary((size_t)len, &out_bin)) + goto err; + out_bin_alloc = 1; + + if (in_bin.size > INT_MAX) + goto err; + if (is_private) { + if (is_encrypt) { + /* non-evp rsa private encrypt */ + ERL_VALGRIND_ASSERT_MEM_DEFINED(in_bin.data,in_bin.size); + result = RSA_private_encrypt((int)in_bin.size, in_bin.data, + out_bin.data, rsa, crypt_opt.rsa_padding); + if (result > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, result); + } + } else { + /* non-evp rsa private decrypt */ + result = RSA_private_decrypt((int)in_bin.size, in_bin.data, + out_bin.data, rsa, crypt_opt.rsa_padding); + if (result > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, result); + if (!enif_realloc_binary(&out_bin, (size_t)result)) + goto err; + } + } + } else { + if (is_encrypt) { + /* non-evp rsa public encrypt */ + ERL_VALGRIND_ASSERT_MEM_DEFINED(in_bin.data,in_bin.size); + result = RSA_public_encrypt((int)in_bin.size, in_bin.data, + out_bin.data, rsa, crypt_opt.rsa_padding); + if (result > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, result); + } + } else { + /* non-evp rsa public decrypt */ + result = RSA_public_decrypt((int)in_bin.size, in_bin.data, + out_bin.data, rsa, crypt_opt.rsa_padding); + if (result > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, result); + if (!enif_realloc_binary(&out_bin, (size_t)result)) + goto err; + } + } + } + + outlen = (size_t)result; +#endif + + if ((result > 0) && argv[0] == atom_rsa && !is_encrypt) { +#ifdef HAVE_RSA_SSLV23_PADDING + if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) { + unsigned char *p; + + tmplen = size_of_RSA(pkey); + if (tmplen < 1 || tmplen > INT_MAX) + goto err; + if (!enif_alloc_binary(tmplen, &tmp_bin)) + goto err; + tmp_bin_alloc = 1; + if (out_bin.size > INT_MAX) + goto err; + + p = out_bin.data; + p++; + + result = RSA_padding_check_SSLv23(tmp_bin.data, (int)tmplen, p, (int)out_bin.size - 1, (int)tmplen); + if (result >= 0) { + outlen = (size_t)result; + in_bin = out_bin; + out_bin = tmp_bin; + tmp_bin = in_bin; + result = 1; + } + } +#endif + } + + if (result > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, outlen); + if (outlen != out_bin.size) { + if (!enif_realloc_binary(&out_bin, outlen)) + goto err; + ERL_VALGRIND_ASSERT_MEM_DEFINED(out_bin.data, outlen); + } + ret = enif_make_binary(env, &out_bin); + out_bin_alloc = 0; + } else { + ret = atom_error; + } + goto done; + + notsup: + ret = atom_notsup; + goto done; + + bad_arg: + err: + if (algo_init == -2) + ret = atom_notsup; + else + ret = enif_make_badarg(env); + + done: + if (out_bin_alloc) + enif_release_binary(&out_bin); + if (tmp_bin_alloc) + enif_release_binary(&tmp_bin); + +#ifdef HAS_EVP_PKEY_CTX + if (ctx) + EVP_PKEY_CTX_free(ctx); +#else + if (rsa) + RSA_free(rsa); +#endif + if (pkey) + EVP_PKEY_free(pkey); + + if (label_copy) + OPENSSL_free(label_copy); + + return ret; +} + +ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ /* (Algorithm, PrivKey | KeyMap) */ + ERL_NIF_TERM ret; + EVP_PKEY *pkey = NULL; + RSA *rsa = NULL; + DSA *dsa = NULL; + ERL_NIF_TERM result[8]; + + ASSERT(argc == 2); + + if (get_pkey_private_key(env, argv[0], argv[1], &pkey) != PKEY_OK) + goto bad_arg; + + if (argv[0] == atom_rsa) { + const BIGNUM *n = NULL, *e = NULL, *d = NULL; + + if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) + goto err; + + RSA_get0_key(rsa, &n, &e, &d); + + // Exponent E + if ((result[0] = bin_from_bn(env, e)) == atom_error) + goto err; + // Modulus N = p*q + if ((result[1] = bin_from_bn(env, n)) == atom_error) + goto err; + + ret = enif_make_list_from_array(env, result, 2); + + } else if (argv[0] == atom_dss) { + const BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL; + + if ((dsa = EVP_PKEY_get1_DSA(pkey)) == NULL) + goto err; + + DSA_get0_pqg(dsa, &p, &q, &g); + DSA_get0_key(dsa, &pub_key, NULL); + + if ((result[0] = bin_from_bn(env, p)) == atom_error) + goto err; + if ((result[1] = bin_from_bn(env, q)) == atom_error) + goto err; + if ((result[2] = bin_from_bn(env, g)) == atom_error) + goto err; + if ((result[3] = bin_from_bn(env, pub_key)) == atom_error) + goto err; + + ret = enif_make_list_from_array(env, result, 4); + + } else if (argv[0] == atom_ecdsa) { +#if defined(HAVE_EC) + /* not yet implemented + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); + if (ec) { + / * Example of result: + { + Curve = {Field, Prime, Point, Order, CoFactor} = + { + Field = {prime_field,<<255,...,255>>}, + Prime = {<<255,...,252>>, + <<90,...,75>>, + <<196,...,144>> + }, + Point = <<4,...,245>>, + Order = <<255,...,81>>, + CoFactor = <<1>> + }, + Key = <<151,...,62>> + } + or + { + Curve = + {characteristic_two_field, + M, + Basis = {tpbasis, _} + | {ppbasis, k1, k2, k3} + }, + Key + } + * / + EVP_PKEY_free(pkey); + return enif_make_list_from_array(env, ..., ...); + */ +#endif + goto bad_arg; + } else { + goto bad_arg; + } + + goto done; + + bad_arg: + err: + ret = enif_make_badarg(env); + + done: + if (rsa) + RSA_free(rsa); + if (dsa) + DSA_free(dsa); + if (pkey) + EVP_PKEY_free(pkey); + + return ret; +} diff --git a/lib/crypto/c_src/pkey.h b/lib/crypto/c_src/pkey.h new file mode 100644 index 0000000000..f647a4a160 --- /dev/null +++ b/lib/crypto/c_src/pkey.h @@ -0,0 +1,31 @@ +/* + * %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% + */ + +#ifndef E_PKEY_H__ +#define E_PKEY_H__ 1 + +#include "common.h" + +ERL_NIF_TERM pkey_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM pkey_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_PKEY_H__ */ diff --git a/lib/crypto/c_src/poly1305.c b/lib/crypto/c_src/poly1305.c new file mode 100644 index 0000000000..db3433dce3 --- /dev/null +++ b/lib/crypto/c_src/poly1305.c @@ -0,0 +1,90 @@ +/* + * %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 "poly1305.h" + +/* For OpenSSL >= 1.1.1 the hmac_nif and cmac_nif could be integrated into poly1305 (with 'type' as parameter) */ +ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Text) */ +#ifdef HAVE_POLY1305 + ErlNifBinary key_bin, text, ret_bin; + ERL_NIF_TERM ret; + EVP_PKEY *key = NULL; + EVP_MD_CTX *mctx = NULL; + EVP_PKEY_CTX *pctx = NULL; + const EVP_MD *md = NULL; + size_t size; + int ret_bin_alloc = 0; + + ASSERT(argc == 2); + + if (!enif_inspect_binary(env, argv[0], &key_bin)) + goto bad_arg; + if (key_bin.size != 32) + goto bad_arg; + if (!enif_inspect_binary(env, argv[1], &text)) + goto bad_arg; + + if ((key = EVP_PKEY_new_raw_private_key(EVP_PKEY_POLY1305, /*engine*/ NULL, key_bin.data, key_bin.size)) == NULL) + goto err; + + if ((mctx = EVP_MD_CTX_new()) == NULL) + goto err; + if (EVP_DigestSignInit(mctx, &pctx, md, /*engine*/ NULL, key) != 1) + goto err; + if (EVP_DigestSignUpdate(mctx, text.data, text.size) != 1) + goto err; + + if (EVP_DigestSignFinal(mctx, NULL, &size) != 1) + goto err; + if (!enif_alloc_binary(size, &ret_bin)) + goto err; + ret_bin_alloc = 1; + if (EVP_DigestSignFinal(mctx, ret_bin.data, &size) != 1) + goto err; + + if (size != ret_bin.size) { + if (!enif_realloc_binary(&ret_bin, size)) + goto err; + } + + ret = enif_make_binary(env, &ret_bin); + ret_bin_alloc = 0; + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + if (ret_bin_alloc) + enif_release_binary(&ret_bin); + ret = atom_error; + + done: + if (mctx) + EVP_MD_CTX_free(mctx); + if (key) + EVP_PKEY_free(key); + return ret; + +#else + return atom_notsup; +#endif +} diff --git a/lib/crypto/c_src/poly1305.h b/lib/crypto/c_src/poly1305.h new file mode 100644 index 0000000000..4bf45e6218 --- /dev/null +++ b/lib/crypto/c_src/poly1305.h @@ -0,0 +1,28 @@ +/* + * %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% + */ + +#ifndef E_POLY1305_H__ +#define E_POLY1305_H__ 1 + +#include "common.h" + +ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_POLY1305_H__ */ diff --git a/lib/crypto/c_src/rand.c b/lib/crypto/c_src/rand.c new file mode 100644 index 0000000000..3812ae0991 --- /dev/null +++ b/lib/crypto/c_src/rand.c @@ -0,0 +1,149 @@ +/* + * %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 "rand.h" +#include "bn.h" + +ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Bytes) */ + unsigned bytes; + unsigned char* data; + ERL_NIF_TERM ret; + + ASSERT(argc == 1); + + if (!enif_get_uint(env, argv[0], &bytes)) + goto bad_arg; + if (bytes > INT_MAX) + goto bad_arg; + + if ((data = enif_make_new_binary(env, bytes, &ret)) == NULL) + goto err; + if (RAND_bytes(data, (int)bytes) != 1) + goto err; + + ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes); + return ret; + + bad_arg: + return enif_make_badarg(env); + + err: + return atom_false; +} + +ERL_NIF_TERM strong_rand_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Range) */ + BIGNUM *bn_range = NULL, *bn_rand = NULL; + ERL_NIF_TERM ret; + + ASSERT(argc == 1); + + if (!get_bn_from_bin(env, argv[0], &bn_range)) + goto bad_arg; + + if ((bn_rand = BN_new()) == NULL) + goto err; + if (!BN_rand_range(bn_rand, bn_range)) + goto err; + + if ((ret = bin_from_bn(env, bn_rand)) == atom_error) + goto err; + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = atom_false; + + done: + if (bn_rand) + BN_free(bn_rand); + if (bn_range) + BN_free(bn_range); + return ret; +} + +ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Lo,Hi) */ + BIGNUM *bn_from = NULL, *bn_to = NULL, *bn_rand = NULL; + unsigned char* data; + int dlen; + ERL_NIF_TERM ret; + + ASSERT(argc == 2); + + if (!get_bn_from_mpint(env, argv[0], &bn_from)) + goto bad_arg; + if (!get_bn_from_mpint(env, argv[1], &bn_rand)) + goto bad_arg; + + if ((bn_to = BN_new()) == NULL) + goto err; + + if (!BN_sub(bn_to, bn_rand, bn_from)) + goto err; + if (!BN_pseudo_rand_range(bn_rand, bn_to)) + goto err; + if (!BN_add(bn_rand, bn_rand, bn_from)) + goto err; + + if ((dlen = BN_num_bytes(bn_rand)) < 0) + goto err; + if ((data = enif_make_new_binary(env, (size_t)dlen+4, &ret)) == NULL) + goto err; + + put_uint32(data, (unsigned int)dlen); + BN_bn2bin(bn_rand, data+4); + ERL_VALGRIND_MAKE_MEM_DEFINED(data+4, dlen); + goto done; + + bad_arg: + err: + ret = enif_make_badarg(env); + + done: + if (bn_rand) + BN_free(bn_rand); + if (bn_from) + BN_free(bn_from); + if (bn_to) + BN_free(bn_to); + return ret; +} + +ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Seed) */ + ErlNifBinary seed_bin; + + ASSERT(argc == 1); + + if (!enif_inspect_binary(env, argv[0], &seed_bin)) + goto bad_arg; + if (seed_bin.size > INT_MAX) + goto bad_arg; + + RAND_seed(seed_bin.data, (int)seed_bin.size); + return atom_ok; + + bad_arg: + return enif_make_badarg(env); +} diff --git a/lib/crypto/c_src/rand.h b/lib/crypto/c_src/rand.h new file mode 100644 index 0000000000..9c23d343ec --- /dev/null +++ b/lib/crypto/c_src/rand.h @@ -0,0 +1,31 @@ +/* + * %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% + */ + +#ifndef E_RAND_H__ +#define E_RAND_H__ 1 + +#include "common.h" + +ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM strong_rand_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_RAND_H__ */ diff --git a/lib/crypto/c_src/rc4.c b/lib/crypto/c_src/rc4.c new file mode 100644 index 0000000000..e423661097 --- /dev/null +++ b/lib/crypto/c_src/rc4.c @@ -0,0 +1,92 @@ +/* + * %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 "rc4.h" + +ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key) */ +#ifndef OPENSSL_NO_RC4 + ErlNifBinary key; + ERL_NIF_TERM ret; + RC4_KEY *rc4_key; + + CHECK_NO_FIPS_MODE(); + + ASSERT(argc == 1); + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key)) + goto bad_arg; + if (key.size > INT_MAX) + goto bad_arg; + + if ((rc4_key = (RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &ret)) == NULL) + goto err; + + RC4_set_key(rc4_key, (int)key.size, key.data); + return ret; + + bad_arg: + err: + return enif_make_badarg(env); + +#else + return enif_raise_exception(env, atom_notsup); +#endif +} + +ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (State, Data) */ +#ifndef OPENSSL_NO_RC4 + ErlNifBinary state, data; + RC4_KEY* rc4_key; + ERL_NIF_TERM new_state, new_data; + unsigned char *outp; + + CHECK_NO_FIPS_MODE(); + + ASSERT(argc == 2); + + if (!enif_inspect_iolist_as_binary(env, argv[0], &state)) + goto bad_arg; + if (state.size != sizeof(RC4_KEY)) + goto bad_arg; + if (!enif_inspect_iolist_as_binary(env, argv[1], &data)) + goto bad_arg; + + if ((rc4_key = (RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &new_state)) == NULL) + goto err; + if ((outp = enif_make_new_binary(env, data.size, &new_data)) == NULL) + goto err; + + memcpy(rc4_key, state.data, sizeof(RC4_KEY)); + RC4(rc4_key, data.size, data.data, outp); + + CONSUME_REDS(env, data); + return enif_make_tuple2(env, new_state, new_data); + + bad_arg: + err: + return enif_make_badarg(env); + +#else + return enif_raise_exception(env, atom_notsup); +#endif +} + diff --git a/lib/crypto/c_src/rc4.h b/lib/crypto/c_src/rc4.h new file mode 100644 index 0000000000..28bf674253 --- /dev/null +++ b/lib/crypto/c_src/rc4.h @@ -0,0 +1,29 @@ +/* + * %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% + */ + +#ifndef E_RC4_H__ +#define E_RC4_H__ 1 + +#include "common.h" + +ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_RC4_H__ */ diff --git a/lib/crypto/c_src/rsa.c b/lib/crypto/c_src/rsa.c new file mode 100644 index 0000000000..e9f29aa496 --- /dev/null +++ b/lib/crypto/c_src/rsa.c @@ -0,0 +1,282 @@ +/* + * %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 "rsa.h" +#include "bn.h" + +static ERL_NIF_TERM rsa_generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa); +static int check_erlang_interrupt(int maj, int min, BN_GENCB *ctxt); + +int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) +{ + /* key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C] */ + ERL_NIF_TERM head, tail; + BIGNUM *e = NULL, *n = NULL, *d = NULL; + BIGNUM *p = NULL, *q = NULL; + BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL; + + if (!enif_get_list_cell(env, key, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &e)) + goto bad_arg; + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &n)) + goto bad_arg; + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &d)) + goto bad_arg; + + if (!RSA_set0_key(rsa, n, e, d)) + goto err; + /* rsa now owns n, e, and d */ + n = NULL; + e = NULL; + d = NULL; + + if (enif_is_empty_list(env, tail)) + return 1; + + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &p)) + goto bad_arg; + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &q)) + goto bad_arg; + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &dmp1)) + goto bad_arg; + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &dmq1)) + goto bad_arg; + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &iqmp)) + goto bad_arg; + if (!enif_is_empty_list(env, tail)) + goto bad_arg; + + if (!RSA_set0_factors(rsa, p, q)) + goto err; + /* rsa now owns p and q */ + p = NULL; + q = NULL; + + if (!RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp)) + goto err; + /* rsa now owns dmp1, dmq1, and iqmp */ + dmp1 = NULL; + dmq1 = NULL; + iqmp = NULL; + + return 1; + + bad_arg: + err: + if (e) + BN_free(e); + if (n) + BN_free(n); + if (d) + BN_free(d); + if (p) + BN_free(p); + if (q) + BN_free(q); + if (dmp1) + BN_free(dmp1); + if (dmq1) + BN_free(dmq1); + if (iqmp) + BN_free(iqmp); + + return 0; +} + +int get_rsa_public_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) +{ + /* key=[E,N] */ + ERL_NIF_TERM head, tail; + BIGNUM *e = NULL, *n = NULL; + + if (!enif_get_list_cell(env, key, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &e)) + goto bad_arg; + if (!enif_get_list_cell(env, tail, &head, &tail)) + goto bad_arg; + if (!get_bn_from_bin(env, head, &n)) + goto bad_arg; + if (!enif_is_empty_list(env, tail)) + goto bad_arg; + + if (!RSA_set0_key(rsa, n, e, NULL)) + goto err; + /* rsa now owns n and e */ + n = NULL; + e = NULL; + + return 1; + + bad_arg: + err: + if (e) + BN_free(e); + if (n) + BN_free(n); + + return 0; +} + +/* Creates a term which can be parsed by get_rsa_private_key(). This is a list of plain integer binaries (not mpints). */ +static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa) +{ + ERL_NIF_TERM result[8]; + const BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL, *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL; + + /* Return at least [E,N,D] */ + RSA_get0_key(rsa, &n, &e, &d); + + if ((result[0] = bin_from_bn(env, e)) == atom_error) // Exponent E + goto err; + if ((result[1] = bin_from_bn(env, n)) == atom_error) // Modulus N = p*q + goto err; + if ((result[2] = bin_from_bn(env, d)) == atom_error) // Exponent D + goto err; + + /* Check whether the optional additional parameters are available */ + RSA_get0_factors(rsa, &p, &q); + RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); + + if (p && q && dmp1 && dmq1 && iqmp) { + if ((result[3] = bin_from_bn(env, p)) == atom_error) // Factor p + goto err; + if ((result[4] = bin_from_bn(env, q)) == atom_error) // Factor q + goto err; + if ((result[5] = bin_from_bn(env, dmp1)) == atom_error) // D mod (p-1) + goto err; + if ((result[6] = bin_from_bn(env, dmq1)) == atom_error) // D mod (q-1) + goto err; + if ((result[7] = bin_from_bn(env, iqmp)) == atom_error) // (1/q) mod p + goto err; + + return enif_make_list_from_array(env, result, 8); + } else { + return enif_make_list_from_array(env, result, 3); + } + + err: + return enif_make_badarg(env); +} + +static int check_erlang_interrupt(int maj, int min, BN_GENCB *ctxt) +{ + ErlNifEnv *env = BN_GENCB_get_arg(ctxt); + + if (!enif_is_current_process_alive(env)) { + return 0; + } else { + return 1; + } +} + +static ERL_NIF_TERM rsa_generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (ModulusSize, PublicExponent) */ + ERL_NIF_TERM ret; + int modulus_bits; + BIGNUM *pub_exp = NULL, *three = NULL; + RSA *rsa = NULL; + BN_GENCB *intr_cb = NULL; +#ifndef HAVE_OPAQUE_BN_GENCB + BN_GENCB intr_cb_buf; +#endif + + ASSERT(argc == 2); + + if (!enif_get_int(env, argv[0], &modulus_bits)) + goto bad_arg; + if (modulus_bits < 256) + goto bad_arg; + if (!get_bn_from_bin(env, argv[1], &pub_exp)) + goto bad_arg; + + /* Make sure the public exponent is large enough (at least 3). + * Without this, RSA_generate_key_ex() can run forever. */ + if ((three = BN_new()) == NULL) + goto err; + if (!BN_set_word(three, 3)) + goto err; + if (BN_cmp(pub_exp, three) < 0) + goto err; + + /* For large keys, prime generation can take many seconds. Set up + * the callback which we use to test whether the process has been + * interrupted. */ +#ifdef HAVE_OPAQUE_BN_GENCB + if ((intr_cb = BN_GENCB_new()) == NULL) + goto err; +#else + intr_cb = &intr_cb_buf; +#endif + BN_GENCB_set(intr_cb, check_erlang_interrupt, env); + + if ((rsa = RSA_new()) == NULL) + goto err; + + if (!RSA_generate_key_ex(rsa, modulus_bits, pub_exp, intr_cb)) + goto err; + + ret = put_rsa_private_key(env, rsa); + goto done; + + bad_arg: + return enif_make_badarg(env); + + err: + ret = atom_error; + + done: + if (pub_exp) + BN_free(pub_exp); + if (three) + BN_free(three); +#ifdef HAVE_OPAQUE_BN_GENCB + if (intr_cb) + BN_GENCB_free(intr_cb); +#endif + if (rsa) + RSA_free(rsa); + return ret; +} + +ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + /* RSA key generation can take a long time (>1 sec for a large + * modulus), so schedule it as a CPU-bound operation. */ + return enif_schedule_nif(env, "rsa_generate_key", + ERL_NIF_DIRTY_JOB_CPU_BOUND, + rsa_generate_key, argc, argv); +} diff --git a/lib/crypto/c_src/rsa.h b/lib/crypto/c_src/rsa.h new file mode 100644 index 0000000000..69c02aa2cb --- /dev/null +++ b/lib/crypto/c_src/rsa.h @@ -0,0 +1,31 @@ +/* + * %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% + */ + +#ifndef E_RSA_H__ +#define E_RSA_H__ 1 + +#include "common.h" + +int get_rsa_public_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa); +int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa); + +ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_RSA_H__ */ diff --git a/lib/crypto/c_src/srp.c b/lib/crypto/c_src/srp.c new file mode 100644 index 0000000000..2979048006 --- /dev/null +++ b/lib/crypto/c_src/srp.c @@ -0,0 +1,307 @@ +/* + * %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 "srp.h" +#include "bn.h" + +ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Multiplier, Verifier, Generator, Exponent, Prime) */ + BIGNUM *bn_verifier = NULL; + BIGNUM *bn_exponent = NULL, *bn_generator = NULL, *bn_prime = NULL, *bn_multiplier = NULL, *bn_result = NULL; + BN_CTX *bn_ctx = NULL; + unsigned char* ptr; + int dlen; + ERL_NIF_TERM ret; + + CHECK_NO_FIPS_MODE(); + + ASSERT(argc == 5); + + if (!get_bn_from_bin(env, argv[0], &bn_multiplier)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[1], &bn_verifier)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[2], &bn_generator)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[3], &bn_exponent)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[4], &bn_prime)) + goto bad_arg; + + if ((bn_result = BN_new()) == NULL) + goto err; + if ((bn_ctx = BN_CTX_new()) == NULL) + goto err; + + /* B = k*v + g^b % N */ + + /* k * v */ + if (!BN_mod_mul(bn_multiplier, bn_multiplier, bn_verifier, bn_prime, bn_ctx)) + goto err; + + /* g^b % N */ + if (!BN_mod_exp(bn_result, bn_generator, bn_exponent, bn_prime, bn_ctx)) + goto err; + + /* k*v + g^b % N */ + if (!BN_mod_add(bn_result, bn_result, bn_multiplier, bn_prime, bn_ctx)) + goto err; + + /* check that B % N != 0, reuse bn_multiplier */ + if (!BN_nnmod(bn_multiplier, bn_result, bn_prime, bn_ctx)) + goto err; + + if (BN_is_zero(bn_multiplier)) + goto err; + + if ((dlen = BN_num_bytes(bn_result)) < 0) + goto err; + if ((ptr = enif_make_new_binary(env, (size_t)dlen, &ret)) == NULL) + goto err; + + if (BN_bn2bin(bn_result, ptr) < 0) + goto err; + + goto done; + + bad_arg: + ret = enif_make_badarg(env); + goto done; + + err: + ret = atom_error; + + done: + if (bn_multiplier) + BN_free(bn_multiplier); + if (bn_verifier) + BN_free(bn_verifier); + if (bn_generator) + BN_free(bn_generator); + if (bn_exponent) + BN_free(bn_exponent); + if (bn_prime) + BN_free(bn_prime); + if (bn_result) + BN_free(bn_result); + if (bn_ctx) + BN_CTX_free(bn_ctx); + + return ret; +} + +ERL_NIF_TERM srp_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (a, u, B, Multiplier, Prime, Exponent, Generator) */ +/* + <premaster secret> = (B - (k * g^x)) ^ (a + (u * x)) % N +*/ + BIGNUM *bn_exponent = NULL, *bn_a = NULL; + BIGNUM *bn_u = NULL, *bn_multiplier = NULL, *bn_exp2 = NULL; + BIGNUM *bn_base = NULL, *bn_prime = NULL, *bn_generator = NULL; + BIGNUM *bn_B = NULL, *bn_result = NULL; + BN_CTX *bn_ctx = NULL; + unsigned char *ptr; + int dlen; + ERL_NIF_TERM ret; + + CHECK_NO_FIPS_MODE(); + + ASSERT(argc == 7); + + if (!get_bn_from_bin(env, argv[0], &bn_a)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[1], &bn_u)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[2], &bn_B)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[3], &bn_multiplier)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[4], &bn_generator)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[5], &bn_exponent)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[6], &bn_prime)) + goto bad_arg; + + if ((bn_ctx = BN_CTX_new()) == NULL) + goto err; + if ((bn_result = BN_new()) == NULL) + goto err; + + /* check that B % N != 0 */ + if (!BN_nnmod(bn_result, bn_B, bn_prime, bn_ctx)) + goto err; + if (BN_is_zero(bn_result)) + goto err; + + /* (B - (k * g^x)) */ + if ((bn_base = BN_new()) == NULL) + goto err; + if (!BN_mod_exp(bn_result, bn_generator, bn_exponent, bn_prime, bn_ctx)) + goto err; + if (!BN_mod_mul(bn_result, bn_multiplier, bn_result, bn_prime, bn_ctx)) + goto err; + if (!BN_mod_sub(bn_base, bn_B, bn_result, bn_prime, bn_ctx)) + goto err; + + /* a + (u * x) */ + if ((bn_exp2 = BN_new()) == NULL) + goto err; + if (!BN_mul(bn_result, bn_u, bn_exponent, bn_ctx)) + goto err; + if (!BN_add(bn_exp2, bn_a, bn_result)) + goto err; + + /* (B - (k * g^x)) ^ (a + (u * x)) % N */ + if (!BN_mod_exp(bn_result, bn_base, bn_exp2, bn_prime, bn_ctx)) + goto err; + + if ((dlen = BN_num_bytes(bn_result)) < 0) + goto err; + if ((ptr = enif_make_new_binary(env, (size_t)dlen, &ret)) == NULL) + goto err; + + if (BN_bn2bin(bn_result, ptr) < 0) + goto err; + + goto done; + + bad_arg: + ret = enif_make_badarg(env); + goto done; + + err: + ret = atom_error; + + done: + if (bn_a) + BN_free(bn_a); + if (bn_u) + BN_free(bn_u); + if (bn_B) + BN_free(bn_B); + if (bn_multiplier) + BN_free(bn_multiplier); + if (bn_generator) + BN_free(bn_generator); + if (bn_exponent) + BN_free(bn_exponent); + if (bn_prime) + BN_free(bn_prime); + if (bn_ctx) + BN_CTX_free(bn_ctx); + if (bn_result) + BN_free(bn_result); + if (bn_base) + BN_free(bn_base); + if (bn_exp2) + BN_free(bn_exp2); + + return ret; +} + +ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Verifier, b, u, A, Prime) */ +/* + <premaster secret> = (A * v^u) ^ b % N +*/ + BIGNUM *bn_b = NULL, *bn_verifier = NULL; + BIGNUM *bn_prime = NULL, *bn_A = NULL, *bn_u = NULL, *bn_base = NULL, *bn_result = NULL; + BN_CTX *bn_ctx = NULL; + unsigned char *ptr; + int dlen; + ERL_NIF_TERM ret; + + CHECK_NO_FIPS_MODE(); + + ASSERT(argc == 5); + + if (!get_bn_from_bin(env, argv[0], &bn_verifier)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[1], &bn_b)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[2], &bn_u)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[3], &bn_A)) + goto bad_arg; + if (!get_bn_from_bin(env, argv[4], &bn_prime)) + goto bad_arg; + + if ((bn_ctx = BN_CTX_new()) == NULL) + goto err; + if ((bn_result = BN_new()) == NULL) + goto err; + + /* check that A % N != 0 */ + if (!BN_nnmod(bn_result, bn_A, bn_prime, bn_ctx)) + goto err; + if (BN_is_zero(bn_result)) + goto err; + + /* (A * v^u) */ + if ((bn_base = BN_new()) == NULL) + goto err; + if (!BN_mod_exp(bn_base, bn_verifier, bn_u, bn_prime, bn_ctx)) + goto err; + if (!BN_mod_mul(bn_base, bn_A, bn_base, bn_prime, bn_ctx)) + goto err; + + /* (A * v^u) ^ b % N */ + if (!BN_mod_exp(bn_result, bn_base, bn_b, bn_prime, bn_ctx)) + goto err; + + if ((dlen = BN_num_bytes(bn_result)) < 0) + goto err; + if ((ptr = enif_make_new_binary(env, (size_t)dlen, &ret)) == NULL) + goto err; + + if (BN_bn2bin(bn_result, ptr) < 0) + goto err; + + goto done; + + bad_arg: + ret = enif_make_badarg(env); + goto done; + + err: + ret = atom_error; + + done: + if (bn_verifier) + BN_free(bn_verifier); + if (bn_b) + BN_free(bn_b); + if (bn_u) + BN_free(bn_u); + if (bn_A) + BN_free(bn_A); + if (bn_prime) + BN_free(bn_prime); + if (bn_ctx) + BN_CTX_free(bn_ctx); + if (bn_result) + BN_free(bn_result); + if (bn_base) + BN_free(bn_base); + + return ret; +} + diff --git a/lib/crypto/c_src/srp.h b/lib/crypto/c_src/srp.h new file mode 100644 index 0000000000..c356690470 --- /dev/null +++ b/lib/crypto/c_src/srp.h @@ -0,0 +1,30 @@ +/* + * %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% + */ + +#ifndef E_SRP_H__ +#define E_SRP_H__ 1 + +#include "common.h" + +ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM srp_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_SRP_H__ */ diff --git a/lib/crypto/configure.in b/lib/crypto/configure.in new file mode 100644 index 0000000000..a3b6673f29 --- /dev/null +++ b/lib/crypto/configure.in @@ -0,0 +1,775 @@ +dnl Process this file with autoconf to produce a configure script. -*-m4-*- +dnl +dnl %CopyrightBegin% +dnl +dnl Copyright Ericsson AB 2018. All Rights Reserved. +dnl +dnl Licensed under the Apache License, Version 2.0 (the "License"); +dnl you may not use this file except in compliance with the License. +dnl You may obtain a copy of the License at +dnl +dnl http://www.apache.org/licenses/LICENSE-2.0 +dnl +dnl Unless required by applicable law or agreed to in writing, software +dnl distributed under the License is distributed on an "AS IS" BASIS, +dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +dnl See the License for the specific language governing permissions and +dnl limitations under the License. +dnl +dnl %CopyrightEnd% +dnl + +dnl define([AC_CACHE_LOAD], )dnl +dnl define([AC_CACHE_SAVE], )dnl + + +AC_INIT(vsn.mk) + +if test -z "$ERL_TOP" || test ! -d "$ERL_TOP" ; then + AC_CONFIG_AUX_DIRS(autoconf) +else + erl_top=${ERL_TOP} + AC_CONFIG_AUX_DIRS($erl_top/erts/autoconf) +fi + +if test "X$host" != "Xfree_source" -a "X$host" != "Xwin32"; then + AC_CANONICAL_HOST +else + host_os=win32 +fi + +LM_PRECIOUS_VARS + +if test "$cross_compiling" = "yes"; then + CROSS_COMPILING=yes +else + CROSS_COMPILING=no +fi +AC_SUBST(CROSS_COMPILING) + +ERL_XCOMP_SYSROOT_INIT + +AC_PROG_CC +LM_WINDOWS_ENVIRONMENT + +ERL_DED + +dnl +dnl SSL, SSH and CRYPTO need the OpenSSL libraries +dnl +dnl Check flags --with-ssl, --without-ssl --with-ssl=PATH. +dnl If no option is given or --with-ssl is set without a path then we +dnl search for OpenSSL libraries and header files in the standard locations. +dnl If set to --without-ssl we disable the use of SSL, SSH and CRYPTO. +dnl If set to --with-ssl=PATH we use that path as the prefix, i.e. we +dnl use "PATH/include" and "PATH/lib". + +AC_CHECK_SIZEOF(void *) + +std_ssl_locations="/usr/local /usr/sfw /usr /opt/local /usr/pkg /usr/local/openssl /usr/lib/openssl /usr/openssl /usr/local/ssl /usr/lib/ssl /usr/ssl /" + +AC_ARG_WITH(ssl-zlib, +AS_HELP_STRING([--with-ssl-zlib=PATH], + [specify location of ZLib to be used by OpenSSL]) +AS_HELP_STRING([--with-ssl-zlib], + [link SSL with Zlib (default if found)]) +AS_HELP_STRING([--without-ssl-zlib], + [don't link SSL with ZLib])) + +if test "x$with_ssl_zlib" = "xno"; then + SSL_LINK_WITH_ZLIB=no + STATIC_ZLIB_LIBS= +elif test "x$with_ssl_zlib" = "xyes" || test "x$with_ssl_zlib" = "x"; then + if test $erl_xcomp_without_sysroot = yes; then + AC_MSG_WARN([Cannot search for zlib; missing cross system root (erl_xcomp_sysroot).]) + SSL_LINK_WITH_ZLIB=no + STATIC_ZLIB_LIBS= + elif test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then + SSL_LINK_WITH_ZLIB=no + STATIC_ZLIB_LIBS= + else + SSL_LINK_WITH_ZLIB=no + STATIC_ZLIB_LIBS= + AC_MSG_CHECKING(for static ZLib to be used by SSL in standard locations) + for rdir in $std_ssl_locations; do + dir="$erl_xcomp_sysroot$rdir" + if test "x$ac_cv_sizeof_void_p" = "x8"; then + if test -f "$dir/lib64/libz.a"; then + SSL_LINK_WITH_ZLIB=yes + STATIC_ZLIB_LIBS="$dir/lib64/libz.a" + break + elif test -f "$dir/lib/64/libz.a"; then + SSL_LINK_WITH_ZLIB=yes + STATIC_ZLIB_LIBS="$dir/lib/64/libz.a" + break + fi + fi + if test -f "$dir/lib/libz.a"; then + SSL_LINK_WITH_ZLIB=yes + STATIC_ZLIB_LIBS="$dir/lib/libz.a" + break + fi + done + if test "x$SSL_LINK_WITH_ZLIB" = "xno"; then + AC_MSG_RESULT([no]) + else + AC_MSG_RESULT([$STATIC_ZLIB_LIBS]) + fi + fi +else + SSL_LINK_WITH_ZLIB=no + STATIC_ZLIB_LIBS= + if test -f "$with_ssl_zlib/libz.a"; then + SSL_LINK_WITH_ZLIB=yes + STATIC_ZLIB_LIBS=$with_ssl_zlib/libz.a + elif test -f "$with_ssl_zlib/lib/libz.a"; then + SSL_LINK_WITH_ZLIB=yes + STATIC_ZLIB_LIBS=$with_ssl_zlib/lib/libz.a + fi + if test "x$ac_cv_sizeof_void_p" = "x8"; then + if test -f "$with_ssl_zlib/lib64/libz.a"; then + SSL_LINK_WITH_ZLIB=yes + STATIC_ZLIB_LIBS=$with_ssl_zlib/lib64/libz.a + elif test -f "$with_ssl_zlib/lib/64/libz.a"; then + SSL_LINK_WITH_ZLIB=yes + STATIC_ZLIB_LIBS=$with_ssl_zlib/lib/64/libz.a + fi + fi + if test "x$SSL_LINK_WITH_ZLIB" = "xno"; then + AC_MSG_ERROR(Invalid path to option --with-ssl-zlib=PATH) + fi +fi + + +AC_ARG_WITH(ssl, +AS_HELP_STRING([--with-ssl=PATH], [specify location of OpenSSL include and lib]) +AS_HELP_STRING([--with-ssl], [use SSL (default)]) +AS_HELP_STRING([--without-ssl], [don't use SSL])) + +AC_ARG_WITH(ssl-incl, +AS_HELP_STRING([--with-ssl-incl=PATH], [location of OpenSSL include dir, if different than specified by --with-ssl=PATH]), +[ +case X$with_ssl in + X | Xyes | Xno) AC_MSG_ERROR([--with-ssl-incl=PATH set without --with-ssl=PATH]);; +esac +], +[with_ssl_incl=$with_ssl]) #default + +AC_ARG_WITH(ssl-rpath, +AS_HELP_STRING([--with-ssl-rpath=yes|no|PATHS], + [runtime library path for OpenSSL. Default is "yes", which equates to a + number of standard locations. If "no", then no runtime + library paths will be used. Anything else should be a + comma separated list of paths.]), +[ +case X$with_ssl in + Xno) AC_MSG_ERROR([--with-ssl-rpath set without --with-ssl]);; +esac +], +[with_ssl_rpath=yes]) #default + + +AC_ARG_ENABLE(dynamic-ssl-lib, +AS_HELP_STRING([--disable-dynamic-ssl-lib], + [disable using dynamic openssl libraries]), +[ case "$enableval" in + no) enable_dynamic_ssl=no ;; + *) enable_dynamic_ssl=yes ;; + esac ], enable_dynamic_ssl=yes) + +#---------------------------------------------------------------------- +# We actually might do the SSL tests twice due to late discovery of +# kerberos problems with static linking, in case we redo it all trying +# dynamic SSL libraries instead. +#---------------------------------------------------------------------- + +ssl_done=no + +while test "x$ssl_done" != "xyes"; do + +ssl_done=yes # Default only one run + +# Remove all SKIP files from previous runs +for a in ssl crypto ssh; do + rm -f "$ERL_TOP/lib/$a/SKIP" +done + +SSL_DYNAMIC_ONLY=$enable_dynamic_ssl +SSL_STATIC_ONLY=no + +case "$erl_xcomp_without_sysroot-$with_ssl" in + yes-* | no-no) + SSL_APP= + CRYPTO_APP= + SSH_APP= + if test "$with_ssl" = "no"; then + skip="User gave --without-ssl option" + else + skip="Cannot search for ssl; missing cross system root (erl_xcomp_sysroot)." + fi + for a in ssl crypto ssh; do + echo "$skip" > $ERL_TOP/lib/$a/SKIP + done + ;; + no-yes | no- ) + # On windows, we could try to find the installation + # of Shining Light OpenSSL, which can be found by poking in + # the uninstall section in the registry, it's worth a try... + extra_dir="" + if test "x$MIXED_CYGWIN" = "xyes"; then + AC_CHECK_PROG(REGTOOL, regtool, regtool, false) + if test "$ac_cv_prog_REGTOOL" != false; then + wrp="/machine/software/microsoft/windows/currentversion/" + if test "x$ac_cv_sizeof_void_p" = "x8"; then + urp="uninstall/openssl (64-bit)_is1/inno setup: app path" + regtool_subsystem=-w + else + urp="uninstall/openssl (32-bit)_is1/inno setup: app path" + regtool_subsystem=-W + fi + rp="$wrp$urp" + if regtool -q $regtool_subsystem get "$rp" > /dev/null; then + true + else + # Fallback to unspecified wordlength + urp="uninstall/openssl_is1/inno setup: app path" + rp="$wrp$urp" + fi + if regtool -q $regtool_subsystem get "$rp" > /dev/null; then + ssl_install_dir=`regtool -q $regtool_subsystem get "$rp"` + # Try hard to get rid of spaces... + if cygpath -d "$ssl_install_dir" > /dev/null 2>&1; then + ssl_install_dir=`cygpath -d "$ssl_install_dir"` + fi + extra_dir=`cygpath $ssl_install_dir` + fi + fi + elif test "x$MIXED_MSYS" = "xyes"; then + AC_CHECK_PROG(REGTOOL, reg_query.sh, reg_query.sh, false) + if test "$ac_cv_prog_REGTOOL" != false; then + if test "x$ac_cv_sizeof_void_p" = "x8"; then + rp="HKLM/SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall/OpenSSL (64-bit)_is1" + else + rp="HKLM/SOFTWARE/Microsoft/Windows/CurrentVersion/Uninstall/OpenSSL_is1" + fi + key="Inno Setup: App Path" + if "$ac_cv_prog_REGTOOL" "$rp" "$key" > /dev/null; then + ssl_install_dir=`"$ac_cv_prog_REGTOOL" "$rp" "$key"` + extra_dir=`win2msys_path.sh "$ssl_install_dir"` + fi + fi + fi + # We search for OpenSSL in the common OS standard locations. + SSL_APP=ssl + CRYPTO_APP=crypto + SSH_APP=ssh + + SSL_CRYPTO_LIBNAME=crypto + SSL_SSL_LIBNAME=ssl + + if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then + if test "x$ac_cv_sizeof_void_p" = "x8"; then + std_win_ssl_locations="/cygdrive/c/OpenSSL-Win64 /c/OpenSSL-Win64 /opt/local64/pgm/OpenSSL" + else + std_win_ssl_locations="/cygdrive/c/OpenSSL-Win32 /c/OpenSSL-Win32 /cygdrive/c/OpenSSL /c/OpenSSL /opt/local/pgm/OpenSSL" + fi + else + std_win_ssl_locations="" + fi + + + AC_MSG_CHECKING(for OpenSSL >= 0.9.8c in standard locations) + for rdir in $extra_dir $std_win_ssl_locations $std_ssl_locations; do + dir="$erl_xcomp_sysroot$rdir" + if test -f "$erl_xcomp_isysroot$rdir/include/openssl/opensslv.h"; then + is_real_ssl=yes + SSL_INCDIR="$dir" + if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then + if test -f "$dir/lib/VC/libeay32.lib"; then + SSL_RUNTIME_LIBDIR="$rdir/lib/VC" + SSL_LIBDIR="$dir/lib/VC" + SSL_CRYPTO_LIBNAME=libeay32 + SSL_SSL_LIBNAME=ssleay32 + elif test -f "$dir/lib/VC/openssl.lib"; then + SSL_RUNTIME_LIBDIR="$rdir/lib/VC" + SSL_LIBDIR="$dir/lib/VC" + elif test -f $dir/lib/VC/libeay32MD.lib; then + SSL_CRYPTO_LIBNAME=libeay32MD + SSL_SSL_LIBNAME=ssleay32MD + if test "x$enable_dynamic_ssl" = "xno" && \ + test -f $dir/lib/VC/static/libeay32MD.lib; then + SSL_RUNTIME_LIBDIR="$rdir/lib/VC/static" + SSL_LIBDIR="$dir/lib/VC/static" + else + SSL_RUNTIME_LIBDIR="$rdir/lib/VC" + SSL_LIBDIR="$dir/lib/VC" + fi + elif test -f "$dir/lib/libeay32.lib"; then + SSL_RUNTIME_LIBDIR="$rdir/lib" + SSL_LIBDIR="$dir/lib" + SSL_CRYPTO_LIBNAME=libeay32 + SSL_SSL_LIBNAME=ssleay32 + elif test -f "$dir/lib/openssl.lib"; then + SSL_RUNTIME_LIBDIR="$rdir/lib" + SSL_LIBDIR="$dir/lib" + else + is_real_ssl=no + fi + elif test -f "$dir/lib/powerpc/libsslcrypto.a"; then + SSL_CRYPTO_LIBNAME=sslcrypto + SSL_LIBDIR="$dir/lib/powerpc/" + SSL_RUNTIME_LIBDIR="$rdir/lib/powerpc/" + else + if test "x$ac_cv_sizeof_void_p" = "x8"; then + if test -f "$dir/lib64/libcrypto.a"; then + SSL_RUNTIME_LIBDIR="$rdir/lib64" + SSL_LIBDIR="$dir/lib64" + elif test -f "$dir/lib/64/libcrypto.a"; then + SSL_RUNTIME_LIBDIR="$rdir/lib/64" + SSL_LIBDIR="$dir/lib/64" + elif test -f "$dir/lib64/libcrypto.so"; then + SSL_RUNTIME_LIBDIR="$rdir/lib64" + SSL_LIBDIR="$dir/lib64" + elif test -f "$dir/lib/64/libcrypto.so"; then + SSL_RUNTIME_LIBDIR="$rdir/lib/64" + SSL_LIBDIR="$dir/lib/64" + else + SSL_RUNTIME_LIBDIR="$rdir/lib" + SSL_LIBDIR="$dir/lib" + fi + else + SSL_RUNTIME_LIBDIR="$rdir/lib" + SSL_LIBDIR="$dir/lib" + fi + fi + if test '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.a"; then + SSL_DYNAMIC_ONLY=yes + elif test '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.so" -a '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.dylib"; then + SSL_STATIC_ONLY=yes + fi + SSL_BINDIR="$rdir/bin" + if test "x$is_real_ssl" = "xyes" ; then + SSL_INCLUDE="-I$dir/include" + old_CPPFLAGS=$CPPFLAGS + CPPFLAGS=$SSL_INCLUDE + AC_EGREP_CPP(^yes$,[ +#include <openssl/opensslv.h> +#if OPENSSL_VERSION_NUMBER >= 0x0090803fL +yes +#endif + ],[ + ssl_found=yes + ],[ + SSL_APP= + ssl_found=no + ]) + CPPFLAGS=$old_CPPFLAGS + if test "x$ssl_found" = "xyes"; then + if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then + ssl_linkable=yes + elif test "x${SSL_CRYPTO_LIBNAME}" = "xsslcrypto"; then + # This should only be triggered seen OSE + ssl_linkable=yes + else + saveCFLAGS="$CFLAGS" + saveLDFLAGS="$LDFLAGS" + saveLIBS="$LIBS" + CFLAGS="$CFLAGS $SSL_INCLUDE" + if test "x$SSL_STATIC_ONLY" = "xyes"; then + LIBS="${SSL_LIBDIR}/lib${SSL_CRYPTO_LIBNAME}.a" + else + LDFLAGS="$LDFLAGS -L$SSL_LIBDIR" + LIBS="$LIBS -l${SSL_CRYPTO_LIBNAME}" + fi + AC_TRY_LINK([ + #include <stdio.h> + #include <openssl/hmac.h>], + [ + HMAC(0, 0, 0, 0, 0, 0, 0); + ], + [ssl_linkable=yes], + [ssl_linkable=no]) + CFLAGS="$saveCFLAGS" + LDFLAGS="$saveLDFLAGS" + LIBS="$saveLIBS" + fi + fi + if test "x$ssl_found" = "xyes" && test "x$ssl_linkable" = "xyes"; then + AC_MSG_RESULT([$dir]) + break; + fi + fi + fi + done + + if test "x$ssl_found" != "xyes" ; then + dnl + dnl If no SSL found above, check whether we are running on OpenBSD. + dnl + case $host_os in + openbsd*) + if test -f "$erl_xcomp_isysroot/usr/include/openssl/opensslv.h"; then + # Trust OpenBSD to have everything the in the correct locations. + ssl_found=yes + ssl_linkable=yes + SSL_INCDIR="$erl_xcomp_sysroot/usr" + AC_MSG_RESULT([$SSL_INCDIR]) + SSL_RUNTIME_LIB="/usr/lib" + SSL_LIB="$erl_xcomp_sysroot/usr/lib" + SSL_BINDIR="/usr/sbin" + dnl OpenBSD requires us to link with -L and -l + SSL_DYNAMIC_ONLY="yes" + fi + ;; + esac + fi +dnl Now, certain linuxes have a 64bit libcrypto +dnl that cannot build shared libraries (i.e. not PIC) +dnl One could argue that this is wrong, but +dnl so it is - be adoptable + if test "$ssl_found" = "yes" && test "$ssl_linkable" = "yes" && test "$SSL_DYNAMIC_ONLY" != "yes"; then + case $host_os in + linux*) + saveCFLAGS="$CFLAGS" + saveLDFLAGS="$LDFLAGS" + saveLIBS="$LIBS" + CFLAGS="$DED_CFLAGS $SSL_INCLUDE" + LDFLAGS="$DED_LDFLAGS" + LIBS="$SSL_LIBDIR/libcrypto.a $STATIC_ZLIB_LIBS" + AC_TRY_LINK([ + #include <stdio.h> + #include <openssl/hmac.h>], + [ + HMAC(0, 0, 0, 0, 0, 0, 0); + ], + [ssl_dyn_linkable=yes], + [ssl_dyn_linkable=no]) + CFLAGS="$saveCFLAGS" + LDFLAGS="$saveLDFLAGS" + LIBS="$saveLIBS" + if test "x$ssl_dyn_linkable" != "xyes"; then + SSL_DYNAMIC_ONLY=yes + AC_MSG_WARN([SSL will be linked against dynamic lib as static lib is not purely relocatable]) + fi + ;; + esac + fi + + + + + if test "x$ssl_found" != "xyes" || test "x$ssl_linkable" != "xyes"; then + if test "x$ssl_found" = "xyes"; then + AC_MSG_RESULT([found; but not usable]) + else + AC_MSG_RESULT([no]) + fi + SSL_APP= + CRYPTO_APP= + SSH_APP= + AC_MSG_WARN([No (usable) OpenSSL found, skipping ssl, ssh and crypto applications]) + + for a in ssl crypto ssh; do + echo "No usable OpenSSL found" > $ERL_TOP/lib/$a/SKIP + done + fi + ;; + *) + # Option given with PATH to package + if test ! -d "$with_ssl" ; then + AC_MSG_ERROR(Invalid path to option --with-ssl=PATH) + fi + if test ! -d "$with_ssl_incl" ; then + AC_MSG_ERROR(Invalid path to option --with-ssl-incl=PATH) + fi + SSL_INCDIR="$with_ssl_incl" + SSL_CRYPTO_LIBNAME=crypto + SSL_SSL_LIBNAME=ssl + if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes" && test -d "$with_ssl/lib/VC"; then + if test -f "$with_ssl/lib/VC/libeay32.lib"; then + SSL_LIBDIR="$with_ssl/lib/VC" + SSL_CRYPTO_LIBNAME=libeay32 + SSL_SSL_LIBNAME=ssleay32 + elif test -f "$with_ssl/lib/VC/openssl.lib"; then + SSL_LIBDIR="$with_ssl/lib/VC" + elif test -f $with_ssl/lib/VC/libeay32MD.lib; then + SSL_CRYPTO_LIBNAME=libeay32MD + SSL_SSL_LIBNAME=ssleay32MD + if test "x$enable_dynamic_ssl" = "xno" && \ + test -f $with_ssl/lib/VC/static/libeay32MD.lib; then + SSL_LIBDIR="$with_ssl/lib/VC/static" + else + SSL_LIBDIR="$with_ssl/lib/VC" + fi + elif test -f "$with_ssl/lib/libeay32.lib"; then + SSL_LIBDIR="$with_ssl/lib" + SSL_CRYPTO_LIBNAME=libeay32 + SSL_SSL_LIBNAME=ssleay32 + else + # This probably wont work, but that's what the user said, so... + SSL_LIBDIR="$with_ssl/lib" + fi + elif test -f "$dir/lib/powerpc/libsslcrypto.a"; then + SSL_CRYPTO_LIBNAME=sslcrypto + SSL_LIBDIR="$with_ssl/lib/powerpc/" + elif test "x$ac_cv_sizeof_void_p" = "x8"; then + if test -f "$with_ssl/lib64/libcrypto.a"; then + SSL_LIBDIR="$with_ssl/lib64" + elif test -f "$with_ssl/lib/64/libcrypto.a"; then + SSL_LIBDIR="$with_ssl/lib/64" + elif test -f "$with_ssl/lib64/libcrypto.so"; then + SSL_LIBDIR="$with_ssl/lib64" + elif test -f "$with_ssl/lib/64/libcrypto.so"; then + SSL_LIBDIR="$with_ssl/lib/64" + else + SSL_LIBDIR="$with_ssl/lib" + fi + else + SSL_LIBDIR="$with_ssl/lib" + fi + if test '!' -f "${SSL_LIBDIR}/lib${SSL_CRYPTO_LIBNAME}.a"; then + SSL_DYNAMIC_ONLY=yes + elif test '!' -f ${SSL_LIBDIR}/lib${SSL_CRYPTO_LIBNAME}.so -a '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.dylib"; then + SSL_STATIC_ONLY=yes + fi + SSL_INCLUDE="-I$with_ssl_incl/include" + SSL_APP=ssl + CRYPTO_APP=crypto + SSH_APP=ssh + if test "$cross_compiling" = "yes"; then + SSL_RUNTIME_LIBDIR=`echo "$SSL_LIBDIR" | sed -n "s|^$erl_xcomp_sysroot\(/*\)\(.*\)\$|/\2|p"` + else + SSL_RUNTIME_LIBDIR="$SSL_LIBDIR" + fi +esac + +if test "x$SSL_APP" != "x" ; then + dnl We found openssl, now check if we use kerberos 5 support + dnl FIXME: Do we still support platforms that have Kerberos? + AC_MSG_CHECKING(for OpenSSL kerberos 5 support) + old_CPPFLAGS=$CPPFLAGS + CPPFLAGS=$SSL_INCLUDE + AC_EGREP_CPP(^yes$,[ +#include <openssl/opensslv.h> +#include <openssl/opensslconf.h> +#if OPENSSL_VERSION_NUMBER < 0x1010000fL && !defined(OPENSSL_NO_KRB5) +yes +#endif + ],[ + AC_MSG_RESULT([yes]) + ssl_krb5_enabled=yes + if test "x$SSL_DYNAMIC_ONLY" != "xyes"; then + if test -f "$SSL_LIBDIR/libkrb5.a"; then + SSL_LINK_WITH_KERBEROS=yes + STATIC_KERBEROS_LIBS="$SSL_LIBDIR/libkrb5.a" + if test -f "$SSL_LIBDIR/libkrb5support.a"; then + STATIC_KERBEROS_LIBS="$STATIC_KERBEROS_LIBS $SSL_LIBDIR/libkrb5support.a" + fi + if test -f "$SSL_LIBDIR/libk5crypto.a"; then + STATIC_KERBEROS_LIBS="$STATIC_KERBEROS_LIBS $SSL_LIBDIR/libk5crypto.a" + fi + if test -f "$SSL_LIBDIR/libresolv.a"; then + STATIC_KERBEROS_LIBS="$STATIC_KERBEROS_LIBS $SSL_LIBDIR/libresolv.a" + fi + if test -f "$SSL_LIBDIR/libcom_err.a"; then + STATIC_KERBEROS_LIBS="$STATIC_KERBEROS_LIBS $SSL_LIBDIR/libcom_err.a" + fi + else + AC_MSG_WARN([Kerberos needed but no kerberos static libraries found]) + AC_MSG_WARN([Rescanning for dynamic SSL libraries]) + enable_dynamic_ssl=yes + ssl_done=no + SSL_LINK_WITH_KERBEROS=no + STATIC_KERBEROS_LIBS="" + ssl_krb5_enabled=no + SSL_WITH_KERBEROS=no + fi + else + SSL_LINK_WITH_KERBEROS=no + STATIC_KERBEROS_LIBS="" + fi + ],[ + AC_MSG_RESULT([no]) + ssl_krb5_enabled=no + SSL_WITH_KERBEROS=no + ]) + CPPFLAGS=$old_CPPFLAGS + SSL_KRB5_INCLUDE= + if test "x$ssl_krb5_enabled" = "xyes" ; then + AC_MSG_CHECKING(for krb5.h in standard locations) + for dir in $extra_dir "$SSL_INCDIR/include" "$SSL_INCDIR/include/openssl" \ + "$SSL_INCDIR/include/kerberos" \ + "$erl_xcomp_isysroot/cygdrive/c/kerberos/include" \ + "$erl_xcomp_isysroot/usr/local/kerberos/include" \ + "$erl_xcomp_isysroot/usr/kerberos/include" \ + "$erl_xcomp_isysroot/usr/include" + do + if test -f "$dir/krb5.h" ; then + SSL_KRB5_INCLUDE="$dir" + break + fi + done + if test "x$SSL_KRB5_INCLUDE" = "x" ; then + AC_MSG_RESULT([not found]) + SSL_APP= + CRYPTO_APP= + SSH_APP= + AC_MSG_WARN([OpenSSL is configured for kerberos but no krb5.h found]) + for a in ssl crypto ssh ; do + echo "OpenSSL is configured for kerberos but no krb5.h found" > $ERL_TOP/lib/$a/SKIP + done + else + AC_MSG_RESULT([found in $SSL_KRB5_INCLUDE]) + SSL_INCLUDE="$SSL_INCLUDE -I$SSL_KRB5_INCLUDE" + fi + fi +fi + +done # while test ssl_done != yes + +SSL_DED_LD_RUNTIME_LIBRARY_PATH= +ded_ld_rflg="$DED_LD_FLAG_RUNTIME_LIBRARY_PATH" + + +case "$with_ssl_rpath" in + +yes) # Use standard lib locations for ssl runtime library path + + if test "$SSL_APP" != "" && test "$SSL_DYNAMIC_ONLY" = "yes" && test "$ded_ld_rflg" != ""; then + + AC_MSG_CHECKING(for ssl runtime library path to use) + + libdirs="/lib" + + if test "$ac_cv_sizeof_void_p" = "8"; then + dir_lib64=no + dir_lib_64=no + + case "$SSL_RUNTIME_LIBDIR" in + */lib/64 | */lib/64/ ) dir_lib_64=yes;; + */lib64 | */lib64/ ) dir_lib64=yes;; + *) ;; + esac + + for dir in $std_ssl_locations; do + test $dir_lib_64 = no && + test -d "$erl_xcomp_sysroot$dir/lib/64" && + dir_lib_64=yes + test $dir_lib64 = no && + test -d "$erl_xcomp_sysroot$dir/lib64" && + dir_lib64=yes + done + + test $dir_lib_64 = yes && libdirs="/lib/64 $libdirs" + test $dir_lib64 = yes && libdirs="/lib64 $libdirs" + fi + + for type in std x_std curr; do + + ded_ld_rpath="$ded_ld_rflg$SSL_RUNTIME_LIBDIR" + rpath="$SSL_RUNTIME_LIBDIR" + + if test $type != curr; then + for ldir in $libdirs; do + for dir in $std_ssl_locations; do + test "$SSL_LIBDIR" != "$dir$ldir" || continue + test $type != x_std || test -d "$dir$ldir" || continue + if test "$dir" = "/"; then + libdir="$ldir" + else + libdir="$dir$ldir" + fi + ded_ld_rpath="$ded_ld_rpath $ded_ld_rflg$libdir" + rpath="$rpath:$libdir" + done + done + fi + + saveCFLAGS="$CFLAGS" + saveLDFLAGS="$LDFLAGS" + saveLIBS="$LIBS" + CFLAGS="$CFLAGS $SSL_INCLUDE" + LDFLAGS="$LDFLAGS $ld_rpath -L$SSL_LIBDIR" + LIBS="-lcrypto" + AC_TRY_LINK([ + #include <stdio.h> + #include <openssl/hmac.h> + ], + [ + HMAC(0, 0, 0, 0, 0, 0, 0); + ], + [rpath_success=yes], + [rpath_success=no]) + CFLAGS="$saveCFLAGS" + LDFLAGS="$saveLDFLAGS" + LIBS="$saveLIBS" + + test "$rpath_success" = "yes" && break + done + + test "$rpath_success" = "yes" || { ded_ld_rpath=; rpath=; } + + SSL_DED_LD_RUNTIME_LIBRARY_PATH="$ded_ld_rpath" + + AC_MSG_RESULT([$rpath]) + test "$rpath" != "" || AC_MSG_WARN([Cannot set run path during linking]) + fi + ;; + +no) # Use no ssl runtime library path + SSL_DED_LD_RUNTIME_LIBRARY_PATH= + ;; + +*) # Use ssl runtime library paths set by --with-ssl-rpath (without any check) + ded_ld_rpath= + delimit= + for dir in `echo $with_ssl_rpath | sed "s/,/ /g"`; do + ded_ld_rpath="$ded_ld_rpath$delimit$ded_ld_rflg$dir" + delimit=" " + done + SSL_DED_LD_RUNTIME_LIBRARY_PATH="$ded_ld_rpath" + ;; + +esac + + +AC_ARG_ENABLE(fips, +AS_HELP_STRING([--enable-fips], [enable OpenSSL FIPS mode support]) +AS_HELP_STRING([--disable-fips], [disable OpenSSL FIPS mode support (default)]), +[ case "$enableval" in + yes) enable_fips_support=yes ;; + *) enable_fips_support=no ;; + esac ], enable_fips_support=no) + +if test "x$enable_fips_support" = "xyes" && test "$CRYPTO_APP" != ""; then + saveCFLAGS="$CFLAGS" + saveLDFLAGS="$LDFLAGS" + saveLIBS="$LIBS" + CFLAGS="$CFLAGS $SSL_INCLUDE" + LDFLAGS="$LDFLAGS $ded_ld_rpath -L$SSL_LIBDIR" + LIBS="-lcrypto" + AC_CHECK_FUNC([FIPS_mode_set], + [SSL_FLAGS="-DFIPS_SUPPORT"], + [SSL_FLAGS=]) + CFLAGS="$saveCFLAGS" + LDFLAGS="$saveLDFLAGS" + LIBS="$saveLIBS" +else + SSL_FLAGS= +fi + +AC_SUBST(SSL_INCLUDE) +AC_SUBST(SSL_INCDIR) +AC_SUBST(SSL_LIBDIR) +AC_SUBST(SSL_FLAGS) +AC_SUBST(SSL_CRYPTO_LIBNAME) +AC_SUBST(SSL_SSL_LIBNAME) +AC_SUBST(SSL_DED_LD_RUNTIME_LIBRARY_PATH) +AC_SUBST(SSL_DYNAMIC_ONLY) +AC_SUBST(SSL_LINK_WITH_KERBEROS) +AC_SUBST(STATIC_KERBEROS_LIBS) +AC_SUBST(SSL_LINK_WITH_ZLIB) +AC_SUBST(STATIC_ZLIB_LIBS) + +AC_OUTPUT(c_src/$host/Makefile:c_src/Makefile.in) + diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index b33db0d6e4..83e10c4c78 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> @@ -23,7 +22,7 @@ <title>crypto</title> </header> - <module>crypto</module> + <module since="">crypto</module> <modulesummary>Crypto Functions</modulesummary> <description> <p>This module provides a set of cryptographic functions. @@ -45,6 +44,10 @@ SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions [FIPS PUB 202] </url> </item> + <tag>BLAKE2</tag> + <item> + <url href="https://blake2.net/">BLAKE2 — fast secure hashing</url> + </item> <tag>MD5</tag> <item> <url href="http://www.ietf.org/rfc/rfc1321.txt">The MD5 Message Digest Algorithm [RFC 1321]</url> @@ -190,10 +193,12 @@ <datatype_title>Ciphers</datatype_title> <datatype> <name name="stream_cipher"/> + <name name="stream_cipher_iv"/> + <name name="stream_cipher_no_iv"/> <desc> <p>Stream ciphers for - <seealso marker="#stream_encrypt-2">stream_encrypt/2</seealso> and - <seealso marker="#stream_decrypt-2">stream_decrypt/2</seealso> . + <seealso marker="#stream_init-3">stream_init/3</seealso> and + <seealso marker="#stream_init-2">stream_init/2</seealso> . </p> </desc> </datatype> @@ -211,6 +216,18 @@ </datatype> <datatype> + <name name="alias_cfb"/> + <name name="alias_cbc"/> + <desc> + <p>Names that are replaced by more common names. They may deprecated in futer releases.</p> + <p><c>des3_cbc</c> and <c>des_ede3</c> should be replaced by <c>des_ede3_cbc</c></p> + <p><c>des_ede3_cbf</c>, <c>des3_cbf</c> and <c>des3_cfb</c> should be replaced by <c>des_ede3_cfb</c>.</p> + <p><c>aes_cbc128</c> should be replaced by <c>aes_128_cbc</c>.</p> + <p><c>aes_cbc256</c> should be replaced by <c>aes_256_cbc</c>.</p> + </desc> + </datatype> + + <datatype> <name name="block_cipher_without_iv"/> <name name="ecb_cipher"/> <desc> @@ -236,6 +253,7 @@ <name name="sha1"/> <name name="sha2"/> <name name="sha3"/> + <name name="blake2"/> <desc> </desc> </datatype> @@ -524,7 +542,7 @@ <!--================ FUNCTIONS ================--> <funcs> <func> - <name name="block_encrypt" arity="3"/> + <name name="block_encrypt" arity="3" since="OTP 18.0"/> <fsummary>Encrypt <c>PlainText</c> according to <c>Type</c> block cipher</fsummary> <desc> <p>Encrypt <c>PlainText</c> according to <c>Type</c> block cipher.</p> @@ -537,7 +555,7 @@ </func> <func> - <name name="block_decrypt" arity="3"/> + <name name="block_decrypt" arity="3" since="OTP 18.0"/> <fsummary>Decrypt <c>CipherText</c> according to <c>Type</c> block cipher</fsummary> <desc> <p>Decrypt <c>CipherText</c> according to <c>Type</c> block cipher.</p> @@ -550,9 +568,9 @@ </func> <func> - <name>block_encrypt(Type, Key, Ivec, PlainText) -> CipherText</name> - <name>block_encrypt(AeadType, Key, Ivec, {AAD, PlainText}) -> {CipherText, CipherTag}</name> - <name>block_encrypt(aes_gcm | aes_ccm, Key, Ivec, {AAD, PlainText, TagLength}) -> {CipherText, CipherTag}</name> + <name since="OTP R16B01">block_encrypt(Type, Key, Ivec, PlainText) -> CipherText</name> + <name since="OTP R16B01">block_encrypt(AeadType, Key, Ivec, {AAD, PlainText}) -> {CipherText, CipherTag}</name> + <name since="OTP R16B01">block_encrypt(aes_gcm | aes_ccm, Key, Ivec, {AAD, PlainText, TagLength}) -> {CipherText, CipherTag}</name> <fsummary>Encrypt <c>PlainText</c> according to <c>Type</c> block cipher</fsummary> <type> <v>Type = <seealso marker="#type-block_cipher_with_iv">block_cipher_with_iv()</seealso></v> @@ -577,8 +595,8 @@ </func> <func> - <name>block_decrypt(Type, Key, Ivec, CipherText) -> PlainText</name> - <name>block_decrypt(AeadType, Key, Ivec, {AAD, CipherText, CipherTag}) -> PlainText | error</name> + <name since="OTP R16B01">block_decrypt(Type, Key, Ivec, CipherText) -> PlainText</name> + <name since="OTP R16B01">block_decrypt(AeadType, Key, Ivec, {AAD, CipherText, CipherTag}) -> PlainText | error</name> <fsummary>Decrypt <c>CipherText</c> according to <c>Type</c> block cipher</fsummary> <type> <v>Type = <seealso marker="#type-block_cipher_with_iv">block_cipher_with_iv()</seealso></v> @@ -603,7 +621,7 @@ </func> <func> - <name name="bytes_to_integer" arity="1"/> + <name name="bytes_to_integer" arity="1" since="OTP R16B01"/> <fsummary>Convert binary representation, of an integer, to an Erlang integer.</fsummary> <desc> <p>Convert binary representation, of an integer, to an Erlang integer. @@ -612,7 +630,7 @@ </func> <func> - <name name="compute_key" arity="4"/> + <name name="compute_key" arity="4" since="OTP R16B01"/> <fsummary>Computes the shared secret</fsummary> <desc> <p>Computes the shared secret from the private key and the other party's public key. @@ -622,7 +640,7 @@ </func> <func> - <name name="exor" arity="2"/> + <name name="exor" arity="2" since=""/> <fsummary>XOR data</fsummary> <desc> <p>Performs bit-wise XOR (exclusive or) on the data supplied.</p> @@ -631,8 +649,8 @@ <func> - <name name="generate_key" arity="2"/> - <name name="generate_key" arity="3"/> + <name name="generate_key" arity="2" since="OTP R16B01"/> + <name name="generate_key" arity="3" since="OTP R16B01"/> <fsummary>Generates a public key of type <c>Type</c></fsummary> <desc> <p>Generates a public key of type <c>Type</c>. @@ -653,7 +671,7 @@ </func> <func> - <name name="hash" arity="2"/> + <name name="hash" arity="2" since="OTP R15B02"/> <fsummary></fsummary> <desc> <p>Computes a message digest of type <c>Type</c> from <c>Data</c>.</p> @@ -663,7 +681,7 @@ </func> <func> - <name name="hash_init" arity="1"/> + <name name="hash_init" arity="1" since="OTP R15B02"/> <fsummary></fsummary> <desc> <p>Initializes the context for streaming hash operations. <c>Type</c> determines @@ -675,7 +693,7 @@ </func> <func> - <name name="hash_update" arity="2"/> + <name name="hash_update" arity="2" since="OTP R15B02"/> <fsummary></fsummary> <desc> <p>Updates the digest represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> @@ -687,7 +705,7 @@ </func> <func> - <name name="hash_final" arity="1"/> + <name name="hash_final" arity="1" since="OTP R15B02"/> <fsummary></fsummary> <desc> <p>Finalizes the hash operation referenced by <c>Context</c> returned @@ -698,8 +716,8 @@ </func> <func> - <name name="hmac" arity="3"/> - <name name="hmac" arity="4"/> + <name name="hmac" arity="3" since="OTP R16B"/> + <name name="hmac" arity="4" since="OTP R16B"/> <fsummary></fsummary> <desc> <p>Computes a HMAC of type <c>Type</c> from <c>Data</c> using @@ -709,7 +727,7 @@ </func> <func> - <name name="hmac_init" arity="2"/> + <name name="hmac_init" arity="2" since="OTP R14B03"/> <fsummary></fsummary> <desc> <p>Initializes the context for streaming HMAC operations. <c>Type</c> determines @@ -719,7 +737,7 @@ </func> <func> - <name name="hmac_update" arity="2"/> + <name name="hmac_update" arity="2" since="OTP R14B03"/> <fsummary></fsummary> <desc> <p>Updates the HMAC represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> @@ -738,7 +756,7 @@ </func> <func> - <name name="hmac_final" arity="1"/> + <name name="hmac_final" arity="1" since="OTP R14B03"/> <fsummary></fsummary> <desc> <p>Finalizes the HMAC operation referenced by <c>Context</c>. The size of the resultant MAC is @@ -747,7 +765,7 @@ </func> <func> - <name name="hmac_final_n" arity="2"/> + <name name="hmac_final_n" arity="2" since="OTP R14B03"/> <fsummary></fsummary> <desc> <p>Finalizes the HMAC operation referenced by <c>Context</c>. <c>HashLen</c> must be greater than @@ -756,8 +774,8 @@ </func> <func> - <name name="cmac" arity="3"/> - <name name="cmac" arity="4"/> + <name name="cmac" arity="3" since="OTP 20.0"/> + <name name="cmac" arity="4" since="OTP 20.0"/> <fsummary>Calculates the Cipher-based Message Authentication Code.</fsummary> <desc> <p>Computes a CMAC of type <c>Type</c> from <c>Data</c> using @@ -767,7 +785,7 @@ </func> <func> - <name name="info_fips" arity="0"/> + <name name="info_fips" arity="0" since="OTP 20.0"/> <fsummary>Provides information about the FIPS operating status.</fsummary> <desc> <p>Provides information about the FIPS operating status of @@ -790,7 +808,7 @@ </func> <func> - <name name="enable_fips_mode" arity="1"/> + <name name="enable_fips_mode" arity="1" since="OTP 21.1"/> <fsummary>Change FIPS mode.</fsummary> <desc> <p>Enables (<c>Enable = true</c>) or disables (<c>Enable = false</c>) FIPS mode. Returns <c>true</c> if @@ -805,7 +823,7 @@ </func> <func> - <name name="info_lib" arity="0"/> + <name name="info_lib" arity="0" since=""/> <fsummary>Provides information about the libraries used by crypto.</fsummary> <desc> <p>Provides the name and version of the libraries used by crypto.</p> @@ -826,7 +844,7 @@ </func> <func> - <name name="mod_pow" arity="3"/> + <name name="mod_pow" arity="3" since="OTP R16B01"/> <fsummary>Computes the function: N^P mod M</fsummary> <desc> <p>Computes the function <c>N^P mod M</c>.</p> @@ -834,8 +852,8 @@ </func> <func> - <name name="next_iv" arity="2"/> - <name name="next_iv" arity="3"/> + <name name="next_iv" arity="2" since="OTP R16B01"/> + <name name="next_iv" arity="3" since="OTP R16B01"/> <fsummary></fsummary> <desc> <p>Returns the initialization vector to be used in the next @@ -847,7 +865,7 @@ </func> <func> - <name name="poly1305" arity="2"/> + <name name="poly1305" arity="2" since="OTP 21.1"/> <fsummary></fsummary> <desc> <p>Computes a POLY1305 message authentication code (<c>Mac</c>) from <c>Data</c> using @@ -856,7 +874,7 @@ </func> <func> - <name name="private_decrypt" arity="4"/> + <name name="private_decrypt" arity="4" since="OTP R16B01"/> <fsummary>Decrypts CipherText using the private Key.</fsummary> <desc> <p>Decrypts the <c>CipherText</c>, encrypted with @@ -870,7 +888,7 @@ </func> <func> - <name name="private_encrypt" arity="4"/> + <name name="private_encrypt" arity="4" since="OTP R16B01"/> <fsummary>Encrypts PlainText using the private Key.</fsummary> <desc> <p>Encrypts the <c>PlainText</c> using the <c>PrivateKey</c> @@ -883,7 +901,7 @@ </func> <func> - <name name="public_decrypt" arity="4"/> + <name name="public_decrypt" arity="4" since="OTP R16B01"/> <fsummary>Decrypts CipherText using the public Key.</fsummary> <desc> <p>Decrypts the <c>CipherText</c>, encrypted with @@ -897,7 +915,7 @@ </func> <func> - <name name="public_encrypt" arity="4"/> + <name name="public_encrypt" arity="4" since="OTP R16B01"/> <fsummary>Encrypts PlainText using the public Key.</fsummary> <desc> <p>Encrypts the <c>PlainText</c> (message digest) using the <c>PublicKey</c> @@ -909,7 +927,7 @@ </func> <func> - <name name="rand_seed" arity="1"/> + <name name="rand_seed" arity="1" since="OTP 17.0"/> <fsummary>Set the seed for random bytes generation</fsummary> <desc> <p>Set the seed for PRNG to the given binary. This calls the @@ -922,7 +940,7 @@ </func> <func> - <name>rand_uniform(Lo, Hi) -> N</name> + <name since="">rand_uniform(Lo, Hi) -> N</name> <fsummary>Generate a random number</fsummary> <type> <v>Lo, Hi, N = integer()</v> @@ -935,7 +953,7 @@ </func> <func> - <name name="start" arity="0"/> + <name name="start" arity="0" since=""/> <fsummary> Equivalent to application:start(crypto). </fsummary> <desc> <p> Equivalent to application:start(crypto).</p> @@ -943,7 +961,7 @@ </func> <func> - <name name="stop" arity="0"/> + <name name="stop" arity="0" since=""/> <fsummary> Equivalent to application:stop(crypto).</fsummary> <desc> <p> Equivalent to application:stop(crypto).</p> @@ -951,7 +969,7 @@ </func> <func> - <name name="strong_rand_bytes" arity="1"/> + <name name="strong_rand_bytes" arity="1" since="OTP R14B03"/> <fsummary>Generate a binary of random bytes</fsummary> <desc> <p>Generates N bytes randomly uniform 0..255, and returns the @@ -964,7 +982,7 @@ </func> <func> - <name name="rand_seed" arity="0"/> + <name name="rand_seed" arity="0" since="OTP 20.0"/> <fsummary>Strong random number generation plugin state</fsummary> <desc> <p> @@ -992,7 +1010,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="rand_seed_s" arity="0"/> + <name name="rand_seed_s" arity="0" since="OTP 20.0"/> <fsummary>Strong random number generation plugin state</fsummary> <desc> <p> @@ -1011,7 +1029,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </p> <note> <p> - The state returned from this function can not be used + The state returned from this function cannot be used to get a reproducable random sequence as from the other <seealso marker="stdlib:rand">rand</seealso> @@ -1027,7 +1045,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name>rand_seed_alg(Alg) -> rand:state()</name> + <name since="OTP 21.0">rand_seed_alg(Alg) -> rand:state()</name> <fsummary>Strong random number generation plugin state</fsummary> <type> <v>Alg = crypto | crypto_cache</v> @@ -1037,7 +1055,8 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> <p> Creates state object for <seealso marker="stdlib:rand">random number generation</seealso>, - in order to generate cryptographically strong random numbers. + in order to generate cryptographically strong random numbers, + and saves it in the process dictionary before returning it as well. See also <seealso marker="stdlib:rand#seed-1">rand:seed/1</seealso> and <seealso marker="#rand_seed_alg_s-1">rand_seed_alg_s/1</seealso>. @@ -1048,12 +1067,6 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> may raise exception <c>error:low_entropy</c> in case the random generator failed due to lack of secure "randomness". </p> - <p> - The cache size can be changed from its default value using the - <seealso marker="crypto_app"> - crypto app's - </seealso> configuration parameter <c>rand_cache_size</c>. - </p> <p><em>Example</em></p> <pre> _ = crypto:rand_seed_alg(crypto_cache), @@ -1063,7 +1076,35 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name>rand_seed_alg_s(Alg) -> rand:state()</name> + <name since="OTP-22.0">rand_seed_alg(Alg, Seed) -> rand:state()</name> + <fsummary>Strong random number generation plugin state</fsummary> + <type> + <v>Alg = crypto_aes</v> + </type> + <desc> + <marker id="rand_seed_alg-2" /> + <p> + Creates a state object for + <seealso marker="stdlib:rand">random number generation</seealso>, + in order to generate cryptographically unpredictable random numbers, + and saves it in the process dictionary before returning it as well. + See also + <seealso marker="#rand_seed_alg_s-2">rand_seed_alg_s/2</seealso>. + </p> + <p><em>Example</em></p> + <pre> +_ = crypto:rand_seed_alg(crypto_aes, "my seed"), +IntegerValue = rand:uniform(42), % [1; 42] +FloatValue = rand:uniform(), % [0.0; 1.0[ +_ = crypto:rand_seed_alg(crypto_aes, "my seed"), +IntegerValue = rand:uniform(42), % Same values +FloatValue = rand:uniform(). % again + </pre> + </desc> + </func> + + <func> + <name since="OTP 21.0">rand_seed_alg_s(Alg) -> rand:state()</name> <fsummary>Strong random number generation plugin state</fsummary> <type> <v>Alg = crypto | crypto_cache</v> @@ -1099,9 +1140,15 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> crypto app's </seealso> configuration parameter <c>rand_cache_size</c>. </p> + <p> + When using the state object from this function the + <seealso marker="stdlib:rand">rand</seealso> functions using it + may throw exception <c>low_entropy</c> in case the random generator + failed due to lack of secure "randomness". + </p> <note> <p> - The state returned from this function can not be used + The state returned from this function cannot be used to get a reproducable random sequence as from the other <seealso marker="stdlib:rand">rand</seealso> @@ -1121,7 +1168,73 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="stream_init" arity="2"/> + <name since="OTP 22.0">rand_seed_alg_s(Alg, Seed) -> rand:state()</name> + <fsummary>Strong random number generation plugin state</fsummary> + <type> + <v>Alg = crypto_aes</v> + </type> + <desc> + <marker id="rand_seed_alg_s-2" /> + <p> + Creates a state object for + <seealso marker="stdlib:rand">random number generation</seealso>, + in order to generate cryptographically unpredictable random numbers. + See also + <seealso marker="#rand_seed_alg-1">rand_seed_alg/1</seealso>. + </p> + <p> + To get a long period the Xoroshiro928 generator from the + <seealso marker="stdlib:rand">rand</seealso> + module is used as a counter (with period 2^928 - 1) + and the generator states are scrambled through AES + to create 58-bit pseudo random values. + </p> + <p> + The result should be statistically completely unpredictable + random values, since the scrambling is cryptographically strong + and the period is ridiculously long. But the generated numbers + are not to be regarded as cryptographically strong since + there is no re-keying schedule. + </p> + <list type="bulleted"> + <item> + <p> + If you need cryptographically strong random numbers use + <seealso marker="#rand_seed_alg_s-1">rand_seed_alg_s/1</seealso> + with <c>Alg =:= crypto</c> or <c>Alg =:= crypto_cache</c>. + </p> + </item> + <item> + <p> + If you need to be able to repeat the sequence use this function. + </p> + </item> + <item> + <p> + If you do not need the statistical quality of this function, + there are faster algorithms in the + <seealso marker="stdlib:rand">rand</seealso> + module. + </p> + </item> + </list> + <p> + Thanks to the used generator the state object supports the + <seealso marker="stdlib:rand#jump-0"><c>rand:jump/0,1</c></seealso> + function with distance 2^512. + </p> + <p> + Numbers are generated in batches and cached for speed reasons. + The cache size can be changed from its default value using the + <seealso marker="crypto_app"> + crypto app's + </seealso> configuration parameter <c>rand_cache_size</c>. + </p> + </desc> + </func> + + <func> + <name name="stream_init" arity="2" since="OTP R16B01"/> <fsummary></fsummary> <desc> <p>Initializes the state for use in RC4 stream encryption @@ -1134,7 +1247,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="stream_init" arity="3"/> + <name name="stream_init" arity="3" since="OTP R16B01"/> <fsummary></fsummary> <desc> <p>Initializes the state for use in streaming AES encryption using Counter mode (CTR). @@ -1149,7 +1262,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="stream_encrypt" arity="2"/> + <name name="stream_encrypt" arity="2" since="OTP R16B01"/> <fsummary></fsummary> <desc> <p>Encrypts <c>PlainText</c> according to the stream cipher <c>Type</c> specified in stream_init/3. @@ -1160,7 +1273,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="stream_decrypt" arity="2"/> + <name name="stream_decrypt" arity="2" since="OTP R16B01"/> <fsummary></fsummary> <desc> <p>Decrypts <c>CipherText</c> according to the stream cipher <c>Type</c> specified in stream_init/3. @@ -1171,7 +1284,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="supports" arity="0"/> + <name name="supports" arity="0" since="OTP R16B01"/> <fsummary>Provide a list of available crypto algorithms.</fsummary> <desc> <p> Can be used to determine which crypto algorithms that are supported @@ -1183,7 +1296,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ec_curves" arity="0"/> + <name name="ec_curves" arity="0" since="OTP 17.0"/> <fsummary>Provide a list of available named elliptic curves.</fsummary> <desc> <p>Can be used to determine which named elliptic curves are supported.</p> @@ -1191,7 +1304,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ec_curve" arity="1"/> + <name name="ec_curve" arity="1" since="OTP 17.0"/> <fsummary>Get the defining parameters of a elliptic curve.</fsummary> <desc> <p>Return the defining parameters of a elliptic curve.</p> @@ -1199,8 +1312,8 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="sign" arity="4"/> - <name name="sign" arity="5"/> + <name name="sign" arity="4" since="OTP R16B01"/> + <name name="sign" arity="5" since="OTP 20.1"/> <fsummary> Create digital signature.</fsummary> <desc> <p>Creates a digital signature.</p> @@ -1214,8 +1327,8 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="verify" arity="5"/> - <name name="verify" arity="6"/> + <name name="verify" arity="5" since="OTP R16B01"/> + <name name="verify" arity="6" since="OTP 20.1"/> <fsummary>Verifies a digital signature.</fsummary> <desc> <p>Verifies a digital signature</p> @@ -1231,7 +1344,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> <!-- Engine functions --> <func> - <name name="privkey_to_pubkey" arity="2"/> + <name name="privkey_to_pubkey" arity="2" since="OTP 20.2"/> <fsummary>Fetches a public key from an Engine stored private key.</fsummary> <desc> <p>Fetches the corresponding public key from a private key stored in an Engine. @@ -1241,7 +1354,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_get_all_methods" arity="0"/> + <name name="engine_get_all_methods" arity="0" since="OTP 20.2"/> <fsummary>Return list of all possible engine methods</fsummary> <desc> <p> @@ -1259,7 +1372,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_load" arity="3"/> + <name name="engine_load" arity="3" since="OTP 20.2"/> <fsummary>Dynamical load an encryption engine</fsummary> <desc> <p> @@ -1281,7 +1394,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_load" arity="4"/> + <name name="engine_load" arity="4" since="OTP 20.2"/> <fsummary>Dynamical load an encryption engine</fsummary> <desc> <p> @@ -1301,7 +1414,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_unload" arity="1"/> + <name name="engine_unload" arity="1" since="OTP 20.2"/> <fsummary>Dynamical load an encryption engine</fsummary> <desc> <p> @@ -1321,7 +1434,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_by_id" arity="1"/> + <name name="engine_by_id" arity="1" since="OTP 21.0.6"/> <fsummary>Get a reference to an already loaded engine</fsummary> <desc> <p> @@ -1341,7 +1454,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_ctrl_cmd_string" arity="3"/> + <name name="engine_ctrl_cmd_string" arity="3" since="OTP 20.2"/> <fsummary>Sends ctrl commands to an OpenSSL engine</fsummary> <desc> <p> @@ -1358,7 +1471,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_ctrl_cmd_string" arity="4"/> + <name name="engine_ctrl_cmd_string" arity="4" since="OTP 20.2"/> <fsummary>Sends ctrl commands to an OpenSSL engine</fsummary> <desc> <p> @@ -1379,7 +1492,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_add" arity="1"/> + <name name="engine_add" arity="1" since="OTP 21.0.6"/> <fsummary>Add engine to OpenSSL internal list</fsummary> <desc> <p>Add the engine to OpenSSL's internal list.</p> @@ -1392,7 +1505,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_remove" arity="1"/> + <name name="engine_remove" arity="1" since="OTP 21.0.6"/> <fsummary>Remove engine to OpenSSL internal list</fsummary> <desc> <p>Remove the engine from OpenSSL's internal list.</p> @@ -1405,7 +1518,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_get_id" arity="1"/> + <name name="engine_get_id" arity="1" since="OTP 21.0.6"/> <fsummary>Fetch engine ID</fsummary> <desc> <p>Return the ID for the engine, or an empty binary if there is no id set.</p> @@ -1418,7 +1531,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_get_name" arity="1"/> + <name name="engine_get_name" arity="1" since="OTP 21.0.6"/> <fsummary>Fetch engine name</fsummary> <desc> <p>Return the name (eg a description) for the engine, or an empty binary if there is no name set.</p> @@ -1431,7 +1544,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="engine_list" arity="0"/> + <name name="engine_list" arity="0" since="OTP 20.2"/> <fsummary>List the known engine ids</fsummary> <desc> <p>List the id's of all engines in OpenSSL's internal list.</p> @@ -1451,7 +1564,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ensure_engine_loaded" arity="2"/> + <name name="ensure_engine_loaded" arity="2" since="OTP 21.0.6"/> <fsummary>Ensure encryption engine just loaded once</fsummary> <desc> <p> @@ -1473,7 +1586,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ensure_engine_loaded" arity="3"/> + <name name="ensure_engine_loaded" arity="3" since="OTP 21.0.6"/> <fsummary>Ensure encryption engine just loaded once</fsummary> <desc> <p> @@ -1496,7 +1609,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ensure_engine_unloaded" arity="1"/> + <name name="ensure_engine_unloaded" arity="1" since="OTP 21.0.6"/> <fsummary>Unload an engine loaded with the ensure function</fsummary> <desc> <p> @@ -1519,7 +1632,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre> </func> <func> - <name name="ensure_engine_unloaded" arity="2"/> + <name name="ensure_engine_unloaded" arity="2" since="OTP 21.0.6"/> <fsummary>Unload an engine loaded with the ensure function</fsummary> <desc> <p> diff --git a/lib/crypto/doc/src/engine_keys.xml b/lib/crypto/doc/src/engine_keys.xml index feeb353d1e..f78bb81bba 100644 --- a/lib/crypto/doc/src/engine_keys.xml +++ b/lib/crypto/doc/src/engine_keys.xml @@ -40,7 +40,7 @@ </p> <p> An engine could among other tasks provide a storage for - private or public keys. Such a storage could be made safer than the normal file system. Thoose techniques are not + private or public keys. Such a storage could be made safer than the normal file system. Those techniques are not described in this User's Guide. Here we concentrate on how to use private or public keys stored in such an engine. </p> @@ -51,7 +51,7 @@ <p> OTP/Crypto requires that the user provides two or three items of information about the key. The application used by the user is usually on a higher level, for example in - <seealso marker="ssl:ssl#key_option_def">SSL</seealso>. If using + <seealso marker="ssl:ssl#type-key">SSL</seealso>. If using the crypto application directly, it is required that: </p> <list> diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 72cb9aabfd..fe8390c5b8 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -31,9 +31,10 @@ -export([cmac/3, cmac/4]). -export([poly1305/2]). -export([exor/2, strong_rand_bytes/1, mod_pow/3]). --export([rand_seed/0, rand_seed_alg/1]). --export([rand_seed_s/0, rand_seed_alg_s/1]). +-export([rand_seed/0, rand_seed_alg/1, rand_seed_alg/2]). +-export([rand_seed_s/0, rand_seed_alg_s/1, rand_seed_alg_s/2]). -export([rand_plugin_next/1]). +-export([rand_plugin_aes_next/1, rand_plugin_aes_jump/1]). -export([rand_plugin_uniform/1]). -export([rand_plugin_uniform/2]). -export([rand_cache_plugin_next/1]). @@ -46,6 +47,19 @@ -export([privkey_to_pubkey/2]). -export([ec_curve/1, ec_curves/0]). -export([rand_seed/1]). + +%% Experiment +-export([crypto_init/4, + crypto_update/2, crypto_update/3, + %% Emulates old api: + crypto_stream_init/2, crypto_stream_init/3, + crypto_stream_encrypt/2, + crypto_stream_decrypt/2, + crypto_block_encrypt/3, crypto_block_encrypt/4, + crypto_block_decrypt/3, crypto_block_decrypt/4 + ]). + + %% Engine -export([ engine_get_all_methods/0, @@ -88,11 +102,14 @@ -export_type([ stream_state/0, hmac_state/0, - hash_state/0 + hash_state/0, + crypto_state/0 ]). %% Private. For tests. --export([packed_openssl_version/4, engine_methods_convert_to_bitmask/2, get_test_engine/0]). +-export([packed_openssl_version/4, engine_methods_convert_to_bitmask/2, + get_test_engine/0]). +-export([rand_plugin_aes_jump_2pow20/1]). -deprecated({rand_uniform, 2, next_major_release}). @@ -258,17 +275,36 @@ %%% -type block_cipher_with_iv() :: cbc_cipher() | cfb_cipher() - | aes_cbc128 - | aes_cbc256 | aes_ige256 | blowfish_ofb64 - | des3_cbf % cfb misspelled - | des_ede3 | rc2_cbc . --type cbc_cipher() :: des_cbc | des3_cbc | aes_cbc | blowfish_cbc . --type aead_cipher() :: aes_gcm | aes_ccm | chacha20_poly1305 . --type cfb_cipher() :: aes_cfb128 | aes_cfb8 | blowfish_cfb64 | des3_cfb | des_cfb . +-type cbc_cipher() :: des_cbc | des_ede3_cbc + | blowfish_cbc + | aes_cbc | aes_128_cbc | aes_192_cbc | aes_256_cbc + | alias_cbc() . +-type alias_cbc() :: des3_cbc | des_ede3 + | aes_cbc128 | aes_cbc256 . + +-type aead_cipher() :: aes_gcm + | aes_128_gcm + | aes_192_gcm + | aes_256_gcm + | aes_ccm + | aes_128_ccm + | aes_192_ccm + | aes_256_ccm + | chacha20_poly1305 . + +-type cfb_cipher() :: aes_cfb8 + | aes_cfb128 + | blowfish_cfb64 + | des_cfb + | des_ede3_cfb + | alias_cfb() . +-type alias_cfb() :: des_ede3_cbf | des3_cbf + | des3_cfb . + -type block_cipher_without_iv() :: ecb_cipher() . -type ecb_cipher() :: des_ecb | blowfish_ecb | aes_ecb . @@ -284,6 +320,7 @@ -type sha1() :: sha . -type sha2() :: sha224 | sha256 | sha384 | sha512 . -type sha3() :: sha3_224 | sha3_256 | sha3_384 | sha3_512 . +-type blake2() :: blake2b | blake2s . -type compatibility_only_hash() :: md5 | md4 . @@ -326,11 +363,11 @@ stop() -> | {macs, Macs} | {curves, Curves} | {rsa_opts, RSAopts}, - Hashs :: [sha1() | sha2() | sha3() | ripemd160 | compatibility_only_hash()], + Hashs :: [sha1() | sha2() | sha3() | blake2() | ripemd160 | compatibility_only_hash()], Ciphers :: [stream_cipher() | block_cipher_with_iv() | block_cipher_without_iv() | aead_cipher() - ], + ], PKs :: [rsa | dss | ecdsa | dh | ecdh | ec_gf2m], Macs :: [hmac | cmac | poly1305], Curves :: [ec_named_curve() | edwards_curve_dh() | edwards_curve_ed()], @@ -338,7 +375,7 @@ stop() -> supports()-> {Hashs, PubKeys, Ciphers, Macs, Curves, RsaOpts} = algorithms(), [{hashs, Hashs}, - {ciphers, Ciphers}, + {ciphers, prepend_cipher_aliases(Ciphers)}, {public_keys, PubKeys}, {macs, Macs}, {curves, Curves}, @@ -364,7 +401,7 @@ enable_fips_mode(_) -> ?nif_stub. %%% %%%================================================================ --define(HASH_HASH_ALGORITHM, sha1() | sha2() | sha3() | ripemd160 | compatibility_only_hash() ). +-define(HASH_HASH_ALGORITHM, sha1() | sha2() | sha3() | blake2() | ripemd160 | compatibility_only_hash() ). -spec hash(Type, Data) -> Digest when Type :: ?HASH_HASH_ALGORITHM, Data :: iodata(), @@ -469,7 +506,7 @@ hmac_final_n(Context, HashLen) -> Data :: iodata(), Mac :: binary(). cmac(Type, Key, Data) -> - notsup_to_error(cmac_nif(Type, Key, Data)). + notsup_to_error(cmac_nif(alias(Type), Key, Data)). -spec cmac(Type, Key, Data, MacLength) -> Mac when Type :: ?CMAC_CIPHER_ALGORITHM, @@ -477,8 +514,9 @@ cmac(Type, Key, Data) -> Data :: iodata(), MacLength :: integer(), Mac :: binary(). + cmac(Type, Key, Data, MacLength) -> - erlang:binary_part(cmac(Type, Key, Data), 0, MacLength). + erlang:binary_part(cmac(alias(Type), Key, Data), 0, MacLength). %%%---- POLY1305 @@ -495,91 +533,80 @@ poly1305(Key, Data) -> %%%---- Block ciphers +%%%---------------------------------------------------------------- -spec block_encrypt(Type::block_cipher_with_iv(), Key::key()|des3_key(), Ivec::binary(), PlainText::iodata()) -> binary(); (Type::aead_cipher(), Key::iodata(), Ivec::binary(), {AAD::binary(), PlainText::iodata()}) -> {binary(), binary()}; (aes_gcm | aes_ccm, Key::iodata(), Ivec::binary(), {AAD::binary(), PlainText::iodata(), TagLength::1..16}) -> {binary(), binary()}. -block_encrypt(Type, Key, Ivec, PlainText) when Type =:= des_cbc; - Type =:= des_cfb; - Type =:= blowfish_cbc; - Type =:= blowfish_cfb64; - Type =:= blowfish_ofb64; - Type =:= aes_cbc128; - Type =:= aes_cfb8; - Type =:= aes_cfb128; - Type =:= aes_cbc256; - Type =:= aes_cbc; - Type =:= rc2_cbc -> - block_crypt_nif(Type, Key, Ivec, PlainText, true); -block_encrypt(Type, Key0, Ivec, PlainText) when Type =:= des3_cbc; - Type =:= des_ede3 -> - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbc, Key, Ivec, PlainText, true); -block_encrypt(des3_cbf, Key0, Ivec, PlainText) -> % cfb misspelled - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbf, Key, Ivec, PlainText, true); -block_encrypt(des3_cfb, Key0, Ivec, PlainText) -> + +block_encrypt(Type, Key, Ivec, Data) -> + do_block_encrypt(alias(Type), Key, Ivec, Data). + +do_block_encrypt(Type, Key0, Ivec, Data) when Type =:= des_ede3_cbc; + Type =:= des_ede3_cfb -> Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cfb, Key, Ivec, PlainText, true); -block_encrypt(aes_ige256, Key, Ivec, PlainText) -> + block_crypt_nif(Type, Key, Ivec, Data, true); + +do_block_encrypt(Type, Key, Ivec, PlainText) when Type =:= aes_ige256 -> notsup_to_error(aes_ige_crypt_nif(Key, Ivec, PlainText, true)); -block_encrypt(Type, Key, Ivec, {AAD, PlainText}) when Type =:= aes_gcm; - Type =:= aes_ccm -> - aead_encrypt(Type, Key, Ivec, AAD, PlainText); -block_encrypt(Type, Key, Ivec, {AAD, PlainText, TagLength}) when Type =:= aes_gcm; - Type =:= aes_ccm -> - aead_encrypt(Type, Key, Ivec, AAD, PlainText, TagLength); -block_encrypt(chacha20_poly1305=Type, Key, Ivec, {AAD, PlainText}) -> - aead_encrypt(Type, Key, Ivec, AAD, PlainText, 16). +do_block_encrypt(Type, Key, Ivec, {AAD, PlainText}) when Type =:= chacha20_poly1305 -> + aead_encrypt(Type, Key, Ivec, AAD, PlainText, 16); + +do_block_encrypt(Type, Key, Ivec, Data) when Type =:= aes_gcm; + Type =:= aes_ccm -> + case Data of + {AAD, PlainText} -> + aead_encrypt(Type, Key, Ivec, AAD, PlainText); + {AAD, PlainText, TagLength} -> + aead_encrypt(Type, Key, Ivec, AAD, PlainText, TagLength) + end; + +do_block_encrypt(Type, Key, Ivec, PlainText) -> + block_crypt_nif(Type, Key, Ivec, PlainText, true). + + +-spec block_encrypt(Type::block_cipher_without_iv(), Key::key(), PlainText::iodata()) -> binary(). + +block_encrypt(Type, Key, PlainText) -> + block_crypt_nif(alias(Type), Key, PlainText, true). + +%%%---------------------------------------------------------------- +%%%---------------------------------------------------------------- -spec block_decrypt(Type::block_cipher_with_iv(), Key::key()|des3_key(), Ivec::binary(), Data::iodata()) -> binary(); (Type::aead_cipher(), Key::iodata(), Ivec::binary(), {AAD::binary(), Data::iodata(), Tag::binary()}) -> binary() | error. -block_decrypt(Type, Key, Ivec, Data) when Type =:= des_cbc; - Type =:= des_cfb; - Type =:= blowfish_cbc; - Type =:= blowfish_cfb64; - Type =:= blowfish_ofb64; - Type =:= aes_cbc; - Type =:= aes_cbc128; - Type =:= aes_cfb8; - Type =:= aes_cfb128; - Type =:= aes_cbc256; - Type =:= rc2_cbc -> - block_crypt_nif(Type, Key, Ivec, Data, false); -block_decrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc; - Type =:= des_ede3 -> - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbc, Key, Ivec, Data, false); -block_decrypt(des3_cbf, Key0, Ivec, Data) -> % cfb misspelled - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, false); -block_decrypt(des3_cfb, Key0, Ivec, Data) -> + +block_decrypt(Type, Key, Ivec, Data) -> + do_block_decrypt(alias(Type), Key, Ivec, Data). + +do_block_decrypt(Type, Key0, Ivec, Data) when Type =:= des_ede3_cbc; + Type =:= des_ede3_cfb -> Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, false); -block_decrypt(aes_ige256, Key, Ivec, Data) -> + block_crypt_nif(Type, Key, Ivec, Data, false); + +do_block_decrypt(aes_ige256, Key, Ivec, Data) -> notsup_to_error(aes_ige_crypt_nif(Key, Ivec, Data, false)); -block_decrypt(Type, Key, Ivec, {AAD, Data, Tag}) when Type =:= aes_gcm; + +do_block_decrypt(Type, Key, Ivec, {AAD, Data, Tag}) when Type =:= aes_gcm; Type =:= aes_ccm; Type =:= chacha20_poly1305 -> - aead_decrypt(Type, Key, Ivec, AAD, Data, Tag). - + aead_decrypt(Type, Key, Ivec, AAD, Data, Tag); --spec block_encrypt(Type::block_cipher_without_iv(), Key::key(), PlainText::iodata()) -> binary(). +do_block_decrypt(Type, Key, Ivec, Data) -> + block_crypt_nif(Type, Key, Ivec, Data, false). -block_encrypt(Type, Key, PlainText) -> - block_crypt_nif(Type, Key, PlainText, true). -spec block_decrypt(Type::block_cipher_without_iv(), Key::key(), Data::iodata()) -> binary(). block_decrypt(Type, Key, Data) -> - block_crypt_nif(Type, Key, Data, false). - + block_crypt_nif(alias(Type), Key, Data, false). +%%%---------------------------------------------------------------- -spec next_iv(Type:: cbc_cipher(), Data) -> NextIVec when % Type :: cbc_cipher(), %des_cbc | des3_cbc | aes_cbc | aes_ige, Data :: iodata(), NextIVec :: binary(). @@ -610,18 +637,30 @@ next_iv(Type, Data, _Ivec) -> -opaque stream_state() :: {stream_cipher(), reference()}. --type stream_cipher() :: rc4 | aes_ctr | chacha20 . +-type stream_cipher() :: stream_cipher_iv() | stream_cipher_no_iv() . +-type stream_cipher_no_iv() :: rc4 . +-type stream_cipher_iv() :: aes_ctr + | aes_128_ctr + | aes_192_ctr + | aes_256_ctr + | chacha20 . --spec stream_init(Type, Key, IVec) -> State when Type :: aes_ctr | chacha20, +-spec stream_init(Type, Key, IVec) -> State when Type :: stream_cipher_iv(), Key :: iodata(), IVec :: binary(), State :: stream_state() . stream_init(aes_ctr, Key, Ivec) -> {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; +stream_init(aes_128_ctr, Key, Ivec) -> + {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; +stream_init(aes_192_ctr, Key, Ivec) -> + {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; +stream_init(aes_256_ctr, Key, Ivec) -> + {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; stream_init(chacha20, Key, Ivec) -> {chacha20, chacha20_stream_init(Key,Ivec)}. --spec stream_init(Type, Key) -> State when Type :: rc4, +-spec stream_init(Type, Key) -> State when Type :: stream_cipher_no_iv(), Key :: iodata(), State :: stream_state() . stream_init(rc4, Key) -> @@ -679,34 +718,73 @@ rand_seed_s() -> rand_seed_alg(Alg) -> rand:seed(rand_seed_alg_s(Alg)). +-spec rand_seed_alg(Alg :: atom(), Seed :: term()) -> + {rand:alg_handler(), + atom() | rand_cache_seed()}. +rand_seed_alg(Alg, Seed) -> + rand:seed(rand_seed_alg_s(Alg, Seed)). + -define(CRYPTO_CACHE_BITS, 56). +-define(CRYPTO_AES_BITS, 58). + -spec rand_seed_alg_s(Alg :: atom()) -> {rand:alg_handler(), atom() | rand_cache_seed()}. -rand_seed_alg_s(?MODULE) -> - {#{ type => ?MODULE, - bits => 64, - next => fun ?MODULE:rand_plugin_next/1, - uniform => fun ?MODULE:rand_plugin_uniform/1, - uniform_n => fun ?MODULE:rand_plugin_uniform/2}, - no_seed}; -rand_seed_alg_s(crypto_cache) -> +rand_seed_alg_s({AlgHandler, _AlgState} = State) when is_map(AlgHandler) -> + State; +rand_seed_alg_s({Alg, AlgState}) when is_atom(Alg) -> + {mk_alg_handler(Alg),AlgState}; + rand_seed_alg_s(Alg) when is_atom(Alg) -> + {mk_alg_handler(Alg),mk_alg_state(Alg)}. +%% +-spec rand_seed_alg_s(Alg :: atom(), Seed :: term()) -> + {rand:alg_handler(), + atom() | rand_cache_seed()}. +rand_seed_alg_s(Alg, Seed) when is_atom(Alg) -> + {mk_alg_handler(Alg),mk_alg_state({Alg,Seed})}. + +mk_alg_handler(?MODULE = Alg) -> + #{ type => Alg, + bits => 64, + next => fun ?MODULE:rand_plugin_next/1, + uniform => fun ?MODULE:rand_plugin_uniform/1, + uniform_n => fun ?MODULE:rand_plugin_uniform/2}; +mk_alg_handler(crypto_cache = Alg) -> + #{ type => Alg, + bits => ?CRYPTO_CACHE_BITS, + next => fun ?MODULE:rand_cache_plugin_next/1}; +mk_alg_handler(crypto_aes = Alg) -> + #{ type => Alg, + bits => ?CRYPTO_AES_BITS, + next => fun ?MODULE:rand_plugin_aes_next/1, + jump => fun ?MODULE:rand_plugin_aes_jump/1}. + +mk_alg_state(?MODULE) -> + no_seed; +mk_alg_state(crypto_cache) -> CacheBits = ?CRYPTO_CACHE_BITS, - EnvCacheSize = - application:get_env( - crypto, rand_cache_size, CacheBits * 16), % Cache 16 * 8 words - Bytes = (CacheBits + 7) div 8, + BytesPerWord = (CacheBits + 7) div 8, + GenBytes = + ((rand_cache_size() + (2*BytesPerWord - 1)) div BytesPerWord) + * BytesPerWord, + {CacheBits, GenBytes, <<>>}; +mk_alg_state({crypto_aes,Seed}) -> + %% 16 byte words (128 bit crypto blocks) + GenWords = (rand_cache_size() + 31) div 16, + Key = crypto:hash(sha256, Seed), + {F,Count} = longcount_seed(Seed), + {Key,GenWords,F,Count}. + +rand_cache_size() -> + DefaultCacheSize = 1024, CacheSize = - case ((EnvCacheSize + (Bytes - 1)) div Bytes) * Bytes of - Sz when is_integer(Sz), Bytes =< Sz -> - Sz; - _ -> - Bytes - end, - {#{ type => crypto_cache, - bits => CacheBits, - next => fun ?MODULE:rand_cache_plugin_next/1}, - {CacheBits, CacheSize, <<>>}}. + application:get_env(crypto, rand_cache_size, DefaultCacheSize), + if + is_integer(CacheSize), 0 =< CacheSize -> + CacheSize; + true -> + DefaultCacheSize + end. rand_plugin_next(Seed) -> {bytes_to_integer(strong_rand_range(1 bsl 64)), Seed}. @@ -717,12 +795,97 @@ rand_plugin_uniform(State) -> rand_plugin_uniform(Max, State) -> {bytes_to_integer(strong_rand_range(Max)) + 1, State}. -rand_cache_plugin_next({CacheBits, CacheSize, <<>>}) -> + +rand_cache_plugin_next({CacheBits, GenBytes, <<>>}) -> rand_cache_plugin_next( - {CacheBits, CacheSize, strong_rand_bytes(CacheSize)}); -rand_cache_plugin_next({CacheBits, CacheSize, Cache}) -> + {CacheBits, GenBytes, strong_rand_bytes(GenBytes)}); +rand_cache_plugin_next({CacheBits, GenBytes, Cache}) -> <<I:CacheBits, NewCache/binary>> = Cache, - {I, {CacheBits, CacheSize, NewCache}}. + {I, {CacheBits, GenBytes, NewCache}}. + + +%% Encrypt 128 bit counter values and use the 58 lowest +%% encrypted bits as random numbers. +%% +%% The 128 bit counter is handled as 4 32 bit words +%% to avoid bignums. Generate a bunch of numbers +%% at the time and cache them. +%% +-dialyzer({no_improper_lists, rand_plugin_aes_next/1}). +rand_plugin_aes_next([V|Cache]) -> + {V,Cache}; +rand_plugin_aes_next({Key,GenWords,F,Count}) -> + rand_plugin_aes_next(Key, GenWords, F, Count); +rand_plugin_aes_next({Key,GenWords,F,_JumpBase,Count}) -> + rand_plugin_aes_next(Key, GenWords, F, Count). +%% +rand_plugin_aes_next(Key, GenWords, F, Count) -> + {Cleartext,NewCount} = aes_cleartext(<<>>, F, Count, GenWords), + Encrypted = crypto:block_encrypt(aes_ecb, Key, Cleartext), + [V|Cache] = aes_cache(Encrypted, {Key,GenWords,F,Count,NewCount}), + {V,Cache}. + +%% A jump advances the counter 2^512 steps; the jump function +%% is applied to the jump base and then the number of used +%% numbers from the cache has to be wasted for the jump to be correct +%% +rand_plugin_aes_jump({#{type := crypto_aes} = Alg, Cache}) -> + {Alg,rand_plugin_aes_jump(fun longcount_jump/1, 0, Cache)}. +%% Count cached words and subtract their number from jump +-dialyzer({no_improper_lists, rand_plugin_aes_jump/3}). +rand_plugin_aes_jump(Jump, J, [_|Cache]) -> + rand_plugin_aes_jump(Jump, J + 1, Cache); +rand_plugin_aes_jump(Jump, J, {Key,GenWords,F,JumpBase, _Count}) -> + rand_plugin_aes_jump(Jump, GenWords - J, Key, GenWords, F, JumpBase); +rand_plugin_aes_jump(Jump, 0, {Key,GenWords,F,JumpBase}) -> + rand_plugin_aes_jump(Jump, 0, Key, GenWords, F, JumpBase). +%% +rand_plugin_aes_jump(Jump, Skip, Key, GenWords, F, JumpBase) -> + Count = longcount_next_count(Skip, Jump(JumpBase)), + {Key,GenWords,F,Count}. + +rand_plugin_aes_jump_2pow20(Cache) -> + rand_plugin_aes_jump(fun longcount_jump_2pow20/1, 0, Cache). + + +longcount_seed(Seed) -> + <<X:64, _:6, F:12, S2:58, S1:58, S0:58>> = + crypto:hash(sha256, [Seed,<<"Xoroshiro928">>]), + {F,rand:exro928_seed([S0,S1,S2|rand:seed58(13, X)])}. + +longcount_next_count(0, Count) -> + Count; +longcount_next_count(N, Count) -> + longcount_next_count(N - 1, rand:exro928_next_state(Count)). + +longcount_next(Count) -> + rand:exro928_next(Count). + +longcount_jump(Count) -> + rand:exro928_jump_2pow512(Count). + +longcount_jump_2pow20(Count) -> + rand:exro928_jump_2pow20(Count). + + +%% Build binary with counter values to cache +aes_cleartext(Cleartext, _F, Count, 0) -> + {Cleartext,Count}; +aes_cleartext(Cleartext, F, Count, GenWords) -> + {{S0,S1}, NewCount} = longcount_next(Count), + aes_cleartext( + <<Cleartext/binary, F:12, S1:58, S0:58>>, + F, NewCount, GenWords - 1). + +%% Parse and cache encrypted counter values aka random numbers +-dialyzer({no_improper_lists, aes_cache/2}). +aes_cache(<<>>, Cache) -> + Cache; +aes_cache( + <<_:(128 - ?CRYPTO_AES_BITS), V:?CRYPTO_AES_BITS, Encrypted/binary>>, + Cache) -> + [V|aes_cache(Encrypted, Cache)]. + strong_rand_range(Range) when is_integer(Range), Range > 0 -> BinRange = int_to_bin(Range), @@ -787,7 +950,8 @@ rand_seed_nif(_Seed) -> ?nif_stub. -type pk_sign_verify_opts() :: [ rsa_sign_verify_opt() ] . -type rsa_sign_verify_opt() :: {rsa_padding, rsa_sign_verify_padding()} - | {rsa_pss_saltlen, integer()} . + | {rsa_pss_saltlen, integer()} + | {rsa_mgf1_md, sha2()}. -type rsa_sign_verify_padding() :: rsa_pkcs1_padding | rsa_pkcs1_pss_padding | rsa_x931_padding | rsa_no_padding @@ -803,7 +967,7 @@ rand_seed_nif(_Seed) -> ?nif_stub. DigestType :: rsa_digest_type() | dss_digest_type() | ecdsa_digest_type(), - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Key :: rsa_private() | dss_private() | [ecdsa_private() | ecdsa_params()] @@ -822,7 +986,7 @@ sign(Algorithm, Type, Data, Key) -> | dss_digest_type() | ecdsa_digest_type() | none, - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Key :: rsa_private() | dss_private() | [ecdsa_private() | ecdsa_params()] @@ -851,7 +1015,7 @@ pkey_sign_nif(_Algorithm, _Type, _Digest, _Key, _Options) -> ?nif_stub. | dss_digest_type() | ecdsa_digest_type() | none, - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Signature :: binary(), Key :: rsa_public() | dss_public() @@ -869,7 +1033,7 @@ verify(Algorithm, Type, Data, Signature, Key) -> DigestType :: rsa_digest_type() | dss_digest_type() | ecdsa_digest_type(), - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Signature :: binary(), Key :: rsa_public() | dss_public() @@ -2051,3 +2215,178 @@ check_otp_test_engine(LibDir) -> {error, notexist} end end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% +%%% Experimental NG +%%% + +%%% -> {ok,State::ref()} | {error,Reason} + +-opaque crypto_state() :: reference() | {any(),any(),any(),any()}. + + +%%%---------------------------------------------------------------- +%%% +%%% Create and initialize a new state for encryption or decryption +%%% + +-spec crypto_init(Cipher, Key, IV, EncryptFlag) -> {ok,State} | {error,term()} | undefined + when Cipher :: stream_cipher() + | block_cipher_with_iv() + | block_cipher_without_iv() , + Key :: iodata(), + IV :: binary(), + EncryptFlag :: boolean() | undefined, + State :: crypto_state() . + +crypto_init(Cipher, Key, IV, EncryptFlag) when is_atom(Cipher), + is_binary(Key), + is_binary(IV), + is_atom(EncryptFlag) -> + case ng_crypto_init_nif(alias(Cipher), Key, IV, EncryptFlag) of + {error,Error} -> + {error,Error}; + undefined -> % For compatibility function crypto_stream_init/3 + undefined; + Ref when is_reference(Ref) -> + {ok,Ref}; + State when is_tuple(State), + size(State)==4 -> + {ok,State} % compatibility with old cryptolibs < 1.0.1 + end. + + +%%%---------------------------------------------------------------- +%%% +%%% Encrypt/decrypt a sequence of bytes. The sum of the sizes +%%% of all blocks must be an integer multiple of the crypto's +%%% blocksize. +%%% + +-spec crypto_update(State, Data) -> {ok,Result} | {error,term()} + when State :: crypto_state(), + Data :: iodata(), + Result :: binary() | {crypto_state(),binary()}. +crypto_update(State, Data) -> + mk_ret(ng_crypto_update_nif(State, Data)). + +%%%---------------------------------------------------------------- +%%% +%%% Encrypt/decrypt a sequence of bytes but change the IV first. +%%% Not applicable for all modes. +%%% + +-spec crypto_update(State, Data, IV) -> {ok,Result} | {error,term()} + when State :: crypto_state(), + Data :: iodata(), + IV :: binary(), + Result :: binary() | {crypto_state(),binary()}. +crypto_update(State, Data, IV) -> + mk_ret(ng_crypto_update_nif(State, Data, IV)). + +%%%---------------------------------------------------------------- +%%% Helpers +mk_ret(R) -> mk_ret(R, []). + +mk_ret({error,Error}, _) -> + {error,Error}; +mk_ret(Bin, Acc) when is_binary(Bin) -> + {ok, iolist_to_binary(lists:reverse([Bin|Acc]))}; +mk_ret({State1,Bin}, Acc) when is_tuple(State1), + size(State1) == 4, + is_binary(Bin) -> + %% compatibility with old cryptolibs < 1.0.1 + {ok, {State1, iolist_to_binary(lists:reverse([Bin|Acc]))}}. + +%%%---------------------------------------------------------------- +%%% NIFs +ng_crypto_init_nif(_Cipher, _Key, _IVec, _EncryptFlg) -> ?nif_stub. +ng_crypto_update_nif(_State, _Data) -> ?nif_stub. +ng_crypto_update_nif(_State, _Data, _IV) -> ?nif_stub. + +%%%================================================================ +%%% Compatibility functions to be called by "old" api functions. + +%%%-------------------------------- +%%%---- block encrypt/decrypt +crypto_block_encrypt(Cipher, Key, Data) -> crypto_block_encrypt(Cipher, Key, <<>>, Data). +crypto_block_decrypt(Cipher, Key, Data) -> crypto_block_decrypt(Cipher, Key, <<>>, Data). + +crypto_block_encrypt(Cipher, Key, Ivec, Data) -> crypto_block(Cipher, Key, Ivec, Data, true). +crypto_block_decrypt(Cipher, Key, Ivec, Data) -> crypto_block(Cipher, Key, Ivec, Data, false). + +%% AEAD: use old funcs + +%%%---- helper +crypto_block(Cipher, Key, IV, Data, EncryptFlag) -> + case crypto_init(Cipher, iolist_to_binary(Key), iolist_to_binary(IV), EncryptFlag) of + {ok, Ref} -> + case crypto_update(Ref, Data) of + {ok, {_,Bin}} when is_binary(Bin) -> Bin; + {ok, Bin} when is_binary(Bin) -> Bin; + {error,_} -> error(badarg) + end; + + {error,_} -> error(badarg) + end. + +%%%-------------------------------- +%%%---- stream init, encrypt/decrypt + +crypto_stream_init(Cipher, Key) -> + crypto_stream_init(Cipher, Key, <<>>). + +crypto_stream_init(Cipher, Key0, IV0) -> + Key = iolist_to_binary(Key0), + IV = iolist_to_binary(IV0), + %% First check the argumensts: + case crypto_init(Cipher, Key, IV, undefined) of + undefined -> + {Cipher, {Key, IV}}; + {error,_} -> + {error,badarg} + end. + +crypto_stream_encrypt(State, PlainText) -> + crypto_stream_emulate(State, PlainText, true). + +crypto_stream_decrypt(State, CryptoText) -> + crypto_stream_emulate(State, CryptoText, false). + + +%%%---- helper +crypto_stream_emulate({Cipher,{Key,IV}}, Data, EncryptFlag) -> + case crypto_init(Cipher, Key, IV, EncryptFlag) of + {ok,State} -> + crypto_stream_emulate({Cipher,State}, Data, EncryptFlag); + {error,_} -> + error(badarg) + end; +crypto_stream_emulate({Cipher,State}, Data, _) -> + case crypto_update(State, Data) of + {ok, {State1,Bin}} when is_binary(Bin) -> {{Cipher,State1},Bin}; + {ok,Bin} when is_binary(Bin) -> {{Cipher,State},Bin}; + {error,_} -> error(badarg) + end. + + +%%%================================================================ + +prepend_cipher_aliases(L) -> + [des3_cbc, des_ede3, des_ede3_cbf, des3_cbf, des3_cfb, aes_cbc128, aes_cbc256 | L]. + + +%%%---- des_ede3_cbc +alias(des3_cbc) -> des_ede3_cbc; +alias(des_ede3) -> des_ede3_cbc; +%%%---- des_ede3_cfb +alias(des_ede3_cbf) -> des_ede3_cfb; +alias(des3_cbf) -> des_ede3_cfb; +alias(des3_cfb) -> des_ede3_cfb; +%%%---- aes_*_cbc +alias(aes_cbc128) -> aes_128_cbc; +alias(aes_cbc256) -> aes_256_cbc; + +alias(Alg) -> Alg. diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile index e046a25338..988d95a8bc 100644 --- a/lib/crypto/test/Makefile +++ b/lib/crypto/test/Makefile @@ -6,7 +6,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # ---------------------------------------------------- MODULES = \ - blowfish_SUITE \ + crypto_bench_SUITE \ crypto_SUITE \ engine_SUITE @@ -77,7 +77,7 @@ release_spec: release_tests_spec: $(TEST_TARGET) $(INSTALL_DIR) "$(RELSYSDIR)" - $(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) crypto.spec crypto_bench.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" @tar cfh - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) chmod -R u+w "$(RELSYSDIR)" diff --git a/lib/crypto/test/blowfish_SUITE.erl b/lib/crypto/test/blowfish_SUITE.erl deleted file mode 100644 index a931ebb47e..0000000000 --- a/lib/crypto/test/blowfish_SUITE.erl +++ /dev/null @@ -1,300 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009-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% -%% - -%% --module(blowfish_SUITE). - -%% Note: This directive should only be used in test suites. --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(TIMEOUT, 120000). % 2 min - --define(KEY, to_bin("0123456789ABCDEFF0E1D2C3B4A59687")). --define(IVEC, to_bin("FEDCBA9876543210")). -%% "7654321 Now is the time for " (includes trailing '\0') --define(DATA, to_bin("37363534333231204E6F77206973207468652074696D6520666F722000")). --define(DATA_PADDED, to_bin("37363534333231204E6F77206973207468652074696D6520666F722000000000")). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - case catch crypto:start() of - ok -> - catch ct:comment("~s",[element(3,hd(crypto:info_lib()))]), - catch ct:log("crypto:info_lib() -> ~p~n" - "crypto:supports() -> ~p~n" - "crypto:version() -> ~p~n" - ,[crypto:info_lib(), crypto:supports(), crypto:version()]), - Config; - _Else -> - {skip,"Could not start crypto!"} - end. - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(_Config) -> - crypto:stop(). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> -[{group, fips}, - {group, non_fips}]. - -groups() -> - [{fips, [], [no_ecb, no_cbc, no_cfb64, no_ofb64]}, - {non_fips, [], [ecb, cbc, cfb64, ofb64]}]. - -init_per_group(fips, Config) -> - case crypto:info_fips() of - enabled -> - Config; - not_enabled -> - case crypto:enable_fips_mode(true) of - true -> - enabled = crypto:info_fips(), - Config; - false -> - {skip, "Failed to enable FIPS mode"} - end; - not_supported -> - {skip, "FIPS mode not supported"} - end; -init_per_group(non_fips, Config) -> - case crypto:info_fips() of - enabled -> - true = crypto:enable_fips_mode(false), - not_enabled = crypto:info_fips(), - Config; - _NotEnabled -> - Config - end; -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases start here. -%%-------------------------------------------------------------------- - -ecb_test(KeyBytes, ClearBytes, CipherBytes) -> - {Key, Clear, Cipher} = - {to_bin(KeyBytes), to_bin(ClearBytes), to_bin(CipherBytes)}, - ?line m(crypto:block_encrypt(blowfish_ecb, Key, Clear), Cipher), - true. - -ecb(doc) -> - "Test that ECB mode is OK"; -ecb(suite) -> - []; -ecb(Config) when is_list(Config) -> - true = ecb_test("0000000000000000", "0000000000000000", "4EF997456198DD78"), - true = ecb_test("FFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFF", "51866FD5B85ECB8A"), - true = ecb_test("3000000000000000", "1000000000000001", "7D856F9A613063F2"), - true = ecb_test("1111111111111111", "1111111111111111", "2466DD878B963C9D"), - true = ecb_test("0123456789ABCDEF", "1111111111111111", "61F9C3802281B096"), - true = ecb_test("1111111111111111", "0123456789ABCDEF", "7D0CC630AFDA1EC7"), - true = ecb_test("0000000000000000", "0000000000000000", "4EF997456198DD78"), - true = ecb_test("FEDCBA9876543210", "0123456789ABCDEF", "0ACEAB0FC6A0A28D"), - true = ecb_test("7CA110454A1A6E57", "01A1D6D039776742", "59C68245EB05282B"), - true = ecb_test("0131D9619DC1376E", "5CD54CA83DEF57DA", "B1B8CC0B250F09A0"), - true = ecb_test("07A1133E4A0B2686", "0248D43806F67172", "1730E5778BEA1DA4"), - true = ecb_test("3849674C2602319E", "51454B582DDF440A", "A25E7856CF2651EB"), - true = ecb_test("04B915BA43FEB5B6", "42FD443059577FA2", "353882B109CE8F1A"), - true = ecb_test("0113B970FD34F2CE", "059B5E0851CF143A", "48F4D0884C379918"), - true = ecb_test("0170F175468FB5E6", "0756D8E0774761D2", "432193B78951FC98"), - true = ecb_test("43297FAD38E373FE", "762514B829BF486A", "13F04154D69D1AE5"), - true = ecb_test("07A7137045DA2A16", "3BDD119049372802", "2EEDDA93FFD39C79"), - true = ecb_test("04689104C2FD3B2F", "26955F6835AF609A", "D887E0393C2DA6E3"), - true = ecb_test("37D06BB516CB7546", "164D5E404F275232", "5F99D04F5B163969"), - true = ecb_test("1F08260D1AC2465E", "6B056E18759F5CCA", "4A057A3B24D3977B"), - true = ecb_test("584023641ABA6176", "004BD6EF09176062", "452031C1E4FADA8E"), - true = ecb_test("025816164629B007", "480D39006EE762F2", "7555AE39F59B87BD"), - true = ecb_test("49793EBC79B3258F", "437540C8698F3CFA", "53C55F9CB49FC019"), - true = ecb_test("4FB05E1515AB73A7", "072D43A077075292", "7A8E7BFA937E89A3"), - true = ecb_test("49E95D6D4CA229BF", "02FE55778117F12A", "CF9C5D7A4986ADB5"), - true = ecb_test("018310DC409B26D6", "1D9D5C5018F728C2", "D1ABB290658BC778"), - true = ecb_test("1C587F1C13924FEF", "305532286D6F295A", "55CB3774D13EF201"), - true = ecb_test("0101010101010101", "0123456789ABCDEF", "FA34EC4847B268B2"), - true = ecb_test("1F1F1F1F0E0E0E0E", "0123456789ABCDEF", "A790795108EA3CAE"), - true = ecb_test("E0FEE0FEF1FEF1FE", "0123456789ABCDEF", "C39E072D9FAC631D"), - true = ecb_test("0000000000000000", "FFFFFFFFFFFFFFFF", "014933E0CDAFF6E4"), - true = ecb_test("FFFFFFFFFFFFFFFF", "0000000000000000", "F21E9A77B71C49BC"), - true = ecb_test("0123456789ABCDEF", "0000000000000000", "245946885754369A"), - true = ecb_test("FEDCBA9876543210", "FFFFFFFFFFFFFFFF", "6B5C5A9C5D9E0A5A"), - ok. - -cbc(doc) -> - "Test that CBC mode is OK"; -cbc(suite) -> - []; -cbc(Config) when is_list(Config) -> - true = crypto:block_encrypt(blowfish_cbc, ?KEY, ?IVEC, ?DATA_PADDED) =:= - to_bin("6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC"), - ok. - -cfb64(doc) -> - "Test that CFB64 mode is OK"; -cfb64(suite) -> - []; -cfb64(Config) when is_list(Config) -> - true = crypto:block_encrypt(blowfish_cfb64, ?KEY, ?IVEC, ?DATA) =:= - to_bin("E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3"), - ok. - -ofb64(doc) -> - "Test that OFB64 mode is OK"; -ofb64(suite) -> - []; -ofb64(Config) when is_list(Config) -> - true = crypto:block_encrypt(blowfish_ofb64, ?KEY, ?IVEC, ?DATA) =:= - to_bin("E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA"), - ok. - -no_ecb(doc) -> - "Test that ECB mode is disabled"; -no_ecb(suite) -> - []; -no_ecb(Config) when is_list(Config) -> - notsup(fun crypto:block_encrypt/3, - [blowfish_ecb, - to_bin("0000000000000000"), - to_bin("FFFFFFFFFFFFFFFF")]). - -no_cbc(doc) -> - "Test that CBC mode is disabled"; -no_cbc(suite) -> - []; -no_cbc(Config) when is_list(Config) -> - notsup(fun crypto:block_encrypt/4, - [blowfish_cbc, ?KEY, ?IVEC, ?DATA_PADDED]). - -no_cfb64(doc) -> - "Test that CFB64 mode is disabled"; -no_cfb64(suite) -> - []; -no_cfb64(Config) when is_list(Config) -> - notsup(fun crypto:block_encrypt/4, - [blowfish_cfb64, ?KEY, ?IVEC, ?DATA]), - ok. - -no_ofb64(doc) -> - "Test that OFB64 mode is disabled"; -no_ofb64(suite) -> - []; -no_ofb64(Config) when is_list(Config) -> - notsup(fun crypto:block_encrypt/4, - [blowfish_ofb64, ?KEY, ?IVEC, ?DATA]). - -%% Helper functions - -%% Assert function fails with notsup error -notsup(Fun, Args) -> - ok = try - {error, {return, apply(Fun, Args)}} - catch - error:notsup -> - ok; - Class:Error -> - {error, {Class, Error}} - end. - - -%% Convert a hexadecimal string to a binary. --spec(to_bin(L::string()) -> binary()). -to_bin(L) -> - to_bin(L, []). - -%% @spec dehex(char()) -> integer() -%% @doc Convert a hex digit to its integer value. --spec(dehex(char()) -> integer()). -dehex(C) when C >= $0, C =< $9 -> - C - $0; -dehex(C) when C >= $a, C =< $f -> - C - $a + 10; -dehex(C) when C >= $A, C =< $F -> - C - $A + 10. - --spec(to_bin(L::string(), list()) -> binary()). -to_bin([], Acc) -> - iolist_to_binary(lists:reverse(Acc)); -to_bin([C1, C2 | Rest], Acc) -> - to_bin(Rest, [(dehex(C1) bsl 4) bor dehex(C2) | Acc]). - -m(X,X) -> ok. diff --git a/lib/crypto/test/crypto.spec b/lib/crypto/test/crypto.spec index cc09970cb3..4a95275687 100644 --- a/lib/crypto/test/crypto.spec +++ b/lib/crypto/test/crypto.spec @@ -1 +1,6 @@ {suites,"../crypto_test",all}. + +{skip_suites, "../crypto_test", [crypto_bench_SUITE + ], + "Benchmarks run separately"}. + diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 6c6188f775..ab6d88deb2 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -56,6 +56,8 @@ groups() -> {group, sha3_256}, {group, sha3_384}, {group, sha3_512}, + {group, blake2b}, + {group, blake2s}, {group, rsa}, {group, dss}, {group, ecdsa}, @@ -99,6 +101,8 @@ groups() -> {group, rsa}, {group, dss}, {group, ecdsa}, + {group, no_ed25519}, + {group, no_ed448}, {group, dh}, {group, ecdh}, {group, no_srp}, @@ -113,8 +117,8 @@ groups() -> {group, no_blowfish_cfb64}, {group, no_blowfish_ofb64}, {group, aes_cbc128}, - {group, aes_cfb8}, - {group, aes_cfb128}, + {group, no_aes_cfb8}, + {group, no_aes_cfb128}, {group, aes_cbc256}, {group, no_aes_ige256}, {group, no_rc2_cbc}, @@ -137,6 +141,8 @@ groups() -> {sha3_256, [], [hash, hmac]}, {sha3_384, [], [hash, hmac]}, {sha3_512, [], [hash, hmac]}, + {blake2b, [], [hash, hmac]}, + {blake2s, [], [hash, hmac]}, {rsa, [], [sign_verify, public_encrypt, private_encrypt, @@ -156,7 +162,7 @@ groups() -> ]}, {dh, [], [generate_compute, compute_bug]}, - {ecdh, [], [generate_all_supported, compute, generate]}, + {ecdh, [], [use_all_elliptic_curves, compute, generate]}, {srp, [], [generate_compute]}, {des_cbc, [], [block]}, {des_cfb, [], [block]}, @@ -183,8 +189,16 @@ groups() -> {chacha20, [], [stream]}, {poly1305, [], [poly1305]}, {aes_cbc, [], [block]}, + {no_aes_cfb8,[], [no_support, no_block]}, + {no_aes_cfb128,[], [no_support, no_block]}, {no_md4, [], [no_support, no_hash]}, {no_md5, [], [no_support, no_hash, no_hmac]}, + {no_ed25519, [], [no_support, no_sign_verify + %% Does not work yet: ,public_encrypt, private_encrypt + ]}, + {no_ed448, [], [no_support, no_sign_verify + %% Does not work yet: ,public_encrypt, private_encrypt + ]}, {no_ripemd160, [], [no_support, no_hash]}, {no_srp, [], [no_support, no_generate_compute]}, {no_des_cbc, [], [no_support, no_block]}, @@ -251,7 +265,7 @@ init_per_group(fips, Config) -> enabled = crypto:info_fips(), FIPSConfig; false -> - {skip, "Failed to enable FIPS mode"} + {fail, "Failed to enable FIPS mode"} end; not_supported -> {skip, "FIPS mode not supported"} @@ -401,17 +415,6 @@ block() -> block(Config) when is_list(Config) -> Fips = proplists:get_bool(fips, Config), Type = ?config(type, Config), - %% See comment about EVP_CIPHER_CTX_set_key_length in - %% block_crypt_nif in crypto.c. - case {Fips, Type} of - {true, aes_cfb8} -> - throw({skip, "Cannot test aes_cfb8 in FIPS mode because of key length issue"}); - {true, aes_cfb128} -> - throw({skip, "Cannot test aes_cfb128 in FIPS mode because of key length issue"}); - _ -> - ok - end, - Blocks = lazy_eval(proplists:get_value(block, Config)), lists:foreach(fun block_cipher/1, Blocks), lists:foreach(fun block_cipher/1, block_iolistify(Blocks)), @@ -500,6 +503,13 @@ sign_verify(Config) when is_list(Config) -> SignVerify = proplists:get_value(sign_verify, Config), lists:foreach(fun do_sign_verify/1, SignVerify). +%%-------------------------------------------------------------------- +no_sign_verify() -> + [{doc, "Test disabled sign/verify digital signatures"}]. +no_sign_verify(Config) when is_list(Config) -> + [SignVerifyHd|_] = proplists:get_value(sign_verify, Config), + notsup(fun do_sign_verify/1, [SignVerifyHd]). + %%-------------------------------------------------------------------- public_encrypt() -> [{doc, "Test public_encrypt/decrypt "}]. @@ -563,32 +573,43 @@ compute(Config) when is_list(Config) -> Gen = proplists:get_value(compute, Config), lists:foreach(fun do_compute/1, Gen). %%-------------------------------------------------------------------- -generate_all_supported() -> - [{doc, " Test that all curves from crypto:ec_curves/0 returns two binaries"}]. -generate_all_supported(_Config) -> +use_all_elliptic_curves() -> + [{doc, " Test that all curves from crypto:ec_curves/0"}]. +use_all_elliptic_curves(_Config) -> + Msg = <<"hello world!">>, + Sups = crypto:supports(), + Curves = proplists:get_value(curves, Sups), + Hashs = proplists:get_value(hashs, Sups), + ct:log("Lib: ~p~nFIPS: ~p~nCurves:~n~p~nHashs: ~p", [crypto:info_lib(), + crypto:info_fips(), + Curves, + Hashs]), Results = - [try - crypto:generate_key(ecdh, C) - of - {B1,B2} when is_binary(B1) and is_binary(B2) -> - %% That is, seems like it works as expected. - {ok,C}; - Err -> - ct:log("ERROR: Curve ~p generated ~p", [C,Err]), - {error,{C,Err}} - catch - Cls:Err:Stack -> - ct:log("ERROR: Curve ~p exception ~p:~p~n~p", [C,Cls,Err,Stack]), - {error,{C,{Cls,Err}}} - end - || C <- crypto:ec_curves(), - not lists:member(C, [ed25519, ed448]) + [{{Curve,Hash}, + try + {Pub,Priv} = crypto:generate_key(ecdh, Curve), + true = is_binary(Pub), + true = is_binary(Priv), + Sig = crypto:sign(ecdsa, Hash, Msg, [Priv, Curve]), + crypto:verify(ecdsa, Hash, Msg, Sig, [Pub, Curve]) + catch + C:E -> + {C,E} + end} + || Curve <- Curves -- [ed25519, ed448, x25519, x448, ipsec3, ipsec4], + Hash <- Hashs -- [md4, md5, ripemd160, sha3_224, sha3_256, sha3_384, sha3_512, blake2b, blake2s] ], - OK = [C || {ok,C} <- Results], - ct:log("Ok (len=~p): ~p", [length(OK), OK]), - false = lists:any(fun({error,_}) -> true; - (_) -> false - end, Results). + Fails = + lists:filter(fun({_,true}) -> false; + (_) -> true + end, Results), + case Fails of + [] -> + ok; + _ -> + ct:log("Fails:~n~p",[Fails]), + ct:fail("Bad curve(s)",[]) + end. %%-------------------------------------------------------------------- generate() -> @@ -970,7 +991,7 @@ do_sign_verify({Type, Hash, Public, Private, Msg, Options}) -> error:notsup when NotSupLow == true, is_integer(LibVer), LibVer < 16#10001000 -> - %% Thoose opts where introduced in 1.0.1 + %% Those opts where introduced in 1.0.1 ct:log("notsup but OK in old cryptolib crypto:sign(~p, ~p, ..., ..., ..., ~p)", [Type,Hash,Options]), true; @@ -1427,6 +1448,12 @@ group_config(sha3_384 = Type, Config) -> group_config(sha3_512 = Type, Config) -> {Msgs,Digests} = sha3_test_vectors(Type), [{hash, {Type, Msgs, Digests}}, {hmac, hmac_sha3(Type)} | Config]; +group_config(blake2b = Type, Config) -> + {Msgs, Digests} = blake2_test_vectors(Type), + [{hash, {Type, Msgs, Digests}}, {hmac, blake2_hmac(Type)} | Config]; +group_config(blake2s = Type, Config) -> + {Msgs, Digests} = blake2_test_vectors(Type), + [{hash, {Type, Msgs, Digests}}, {hmac, blake2_hmac(Type)} | Config]; group_config(rsa, Config) -> Msg = rsa_plain(), Public = rsa_public(), @@ -1693,6 +1720,71 @@ rfc_1321_md5_digests() -> hexstr2bin("d174ab98d277d9f5a5611c2c9f419d9f"), hexstr2bin("57edf4a22be3c955ac49da2e2107b67a")]. + +%% BLAKE2 re-use SHA3 test vectors. +blake2_test_vectors(blake2b) -> + {sha3_msgs(), + [ <<186,128,165,63,152,28,77,13,106,39,151,182,159,18,246,233,76,33,47,20,104,90,196,183,75,18,187,111,219,255,162,209,125,135,197,57,42,171,121,45,194,82,213,222,69,51,204,149,24,211,138,168,219,241,146,90,185,35,134,237,212,0,153,35>> + , <<120,106,2,247,66,1,89,3,198,198,253,133,37,82,210,114,145,47,71,64,225,88,71,97,138,134,226,23,247,31,84,25,210,94,16,49,175,238,88,83,19,137,100,68,147,78,176,75,144,58,104,91,20,72,183,85,213,111,112,26,254,155,226,206>> + , <<114,133,255,62,139,215,104,214,155,230,43,59,241,135,101,163,37,145,127,169,116,74,194,245,130,162,8,80,188,43,17,65,237,27,62,69,40,89,90,204,144,119,43,223,45,55,220,138,71,19,11,68,243,58,2,232,115,14,90,216,225,102,232,136>> + , <<206,116,26,197,147,15,227,70,129,17,117,197,34,123,183,191,205,71,244,38,18,250,228,108,8,9,81,79,158,14,58,17,238,23,115,40,113,71,205,234,238,223,245,7,9,170,113,99,65,254,101,36,15,74,214,119,125,107,250,249,114,110,94,82>> + , <<152,251,62,251,114,6,253,25,235,246,155,111,49,44,247,182,78,59,148,219,225,161,113,7,145,57,117,167,147,241,119,225,208,119,96,157,127,186,54,60,187,160,13,5,247,170,78,79,168,113,93,100,40,16,76,10,117,100,59,15,243,253,62,175>> + ]}; +blake2_test_vectors(blake2s) -> + {sha3_msgs(), + [ <<80,140,94,140,50,124,20,226,225,167,43,163,78,235,69,47,55,69,139,32,158,214,58,41,77,153,155,76,134,103,89,130>> + , <<105,33,122,48,121,144,128,148,225,17,33,208,66,53,74,124,31,85,182,72,44,161,165,30,27,37,13,253,30,208,238,249>> + , <<111,77,245,17,106,111,51,46,218,177,217,225,14,232,125,246,85,123,234,182,37,157,118,99,243,188,213,114,44,19,241,137>> + , <<53,141,210,237,7,128,212,5,78,118,203,111,58,91,206,40,65,232,226,245,71,67,29,77,9,219,33,182,109,148,31,199>> + , <<190,192,192,230,205,229,182,122,203,115,184,31,121,166,122,64,121,174,28,96,218,201,210,102,26,241,142,159,139,80,223,165>> + ]}. + +blake2_hmac(Type) -> + {Ks, Ds, Hs} = lists:unzip3( + [ {hexstr2bin(K), hexstr2bin(D), H} + || {{K, D}, H} <- lists:zip(blake2_hmac_key_data(), blake2_hmac_hmac(Type)) ]), + {Type, Ks, Ds, Hs}. + +blake2_hmac_key_data() -> + [ {"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b 0b0b0b0b", + "4869205468657265"} + , {"4a656665", + "7768617420646f2079612077616e7420 666f72206e6f7468696e673f"} + , {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaa", + "dddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddd dddd"} + , {"0102030405060708090a0b0c0d0e0f10 111213141516171819", + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd cdcd"} + , {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa", + "54657374205573696e67204c61726765 72205468616e20426c6f636b2d53697a 65204b6579202d2048617368204b6579 204669727374"} + , {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa", + "54657374205573696e67204c61726765 72205468616e20426c6f636b2d53697a 65204b6579202d2048617368204b6579 204669727374"} + , {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa", + "54686973206973206120746573742075 73696e672061206c6172676572207468 616e20626c6f636b2d73697a65206b65 7920616e642061206c61726765722074 68616e20626c6f636b2d73697a652064 6174612e20546865206b6579206e6565 647320746f2062652068617368656420 6265666f7265206265696e6720757365 642062792074686520484d414320616c 676f726974686d2e"} + , {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaa", + "54686973206973206120746573742075 73696e672061206c6172676572207468 616e20626c6f636b2d73697a65206b65 7920616e642061206c61726765722074 68616e20626c6f636b2d73697a652064 6174612e20546865206b6579206e6565 647320746f2062652068617368656420 6265666f7265206265696e6720757365 642062792074686520484d414320616c 676f726974686d2e"} + ]. + +blake2_hmac_hmac(blake2b) -> + [ <<53,138,106,24,73,36,137,79,195,75,238,86,128,238,223,87,216,74,55,187,56,131,47,40,142,59,39,220,99,169,140,200,201,30,118,218,71,107,80,139,198,178,212,8,162,72,133,116,82,144,110,74,32,180,140,107,75,85,210,223,15,225,221,36>> + , <<111,248,132,248,221,194,166,88,107,60,152,164,205,110,189,241,78,193,2,4,182,113,0,115,235,88,101,173,227,122,38,67,184,128,124,19,53,209,7,236,219,159,254,174,182,130,140,70,37,186,23,44,102,55,158,252,210,34,194,222,17,114,122,180>> + , <<244,59,198,44,122,153,53,60,59,44,96,232,239,36,251,189,66,233,84,120,102,220,156,91,228,237,198,244,167,212,188,10,198,32,194,198,0,52,208,64,240,219,175,134,249,233,205,120,145,160,149,89,94,237,85,226,169,150,33,95,12,21,192,24>> + , <<229,219,182,222,47,238,66,161,202,160,110,78,123,132,206,64,143,250,92,74,157,226,99,46,202,118,156,222,136,117,1,76,114,208,114,15,234,245,63,118,230,161,128,53,127,82,141,123,244,132,250,58,20,232,204,31,15,59,173,167,23,180,52,145>> + , <<165,75,41,67,178,162,2,39,212,28,164,108,9,69,175,9,188,31,174,251,47,73,137,76,35,174,188,85,127,183,156,72,137,220,167,68,8,220,134,80,134,102,122,237,238,74,49,133,197,58,73,200,11,129,76,76,88,19,234,12,139,56,168,248>> + , <<180,214,140,139,182,82,151,170,52,132,168,110,29,51,183,138,70,159,33,234,170,158,212,218,159,236,145,218,71,23,34,61,44,15,163,134,170,47,209,241,255,207,89,23,178,103,84,96,53,237,48,238,164,178,19,162,133,148,211,211,169,179,140,170>> + , <<171,52,121,128,166,75,94,130,93,209,14,125,50,253,67,160,26,142,109,234,38,122,185,173,125,145,53,36,82,102,24,146,83,17,175,188,176,196,149,25,203,235,221,112,149,64,168,215,37,251,145,26,194,174,233,178,163,170,67,215,150,18,51,147>> + , <<97,220,242,140,166,12,169,92,130,89,147,39,171,215,169,161,152,111,242,219,211,199,73,69,198,227,35,186,203,76,159,26,94,103,82,93,20,186,141,98,36,177,98,229,102,23,21,37,83,3,69,169,178,86,8,178,125,251,163,180,146,115,213,6>> + ]; +blake2_hmac_hmac(blake2s) -> + [ <<101,168,183,197,204,145,54,212,36,232,44,55,226,112,126,116,233,19,192,101,91,153,199,95,64,237,243,135,69,58,50,96>> + , <<144,182,40,30,47,48,56,201,5,106,240,180,167,231,99,202,230,254,93,158,180,56,106,14,201,82,55,137,12,16,79,240>> + , <<252,196,245,149,41,80,46,52,195,216,218,63,253,171,130,150,106,44,182,55,255,94,155,215,1,19,92,46,148,105,231,144>> + , <<70,68,52,220,190,206,9,93,69,106,29,98,214,236,86,248,152,230,37,163,158,92,82,189,249,77,175,17,27,173,131,170>> + , <<210,61,121,57,79,83,213,54,160,150,230,81,68,71,238,170,187,5,222,208,27,227,44,25,55,218,106,143,113,3,188,78>> + , <<92,76,83,46,110,69,89,83,133,78,21,16,149,38,110,224,127,213,88,129,190,223,139,57,8,217,95,13,190,54,159,234>> + , <<203,96,246,167,145,241,64,191,138,162,229,31,243,88,205,178,204,92,3,51,4,91,127,183,122,186,122,179,176,207,178,55>> + , <<190,53,233,217,99,171,215,108,1,184,171,181,22,36,240,209,16,96,16,92,213,22,16,58,114,241,117,214,211,189,30,202>> + ]. + %%% https://www.di-mgt.com.au/sha_testvectors.html sha3_msgs() -> ["abc", diff --git a/lib/crypto/test/crypto_bench.spec b/lib/crypto/test/crypto_bench.spec new file mode 100644 index 0000000000..b9a26d94db --- /dev/null +++ b/lib/crypto/test/crypto_bench.spec @@ -0,0 +1,3 @@ +{suites, "../crypto_test", [ + crypto_bench_SUITE + ]}. diff --git a/lib/crypto/test/crypto_bench_SUITE.erl b/lib/crypto/test/crypto_bench_SUITE.erl new file mode 100644 index 0000000000..c66a27f0c8 --- /dev/null +++ b/lib/crypto/test/crypto_bench_SUITE.erl @@ -0,0 +1,400 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-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% +%% + +%% +-module(crypto_bench_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct_event.hrl"). +-include_lib("common_test/include/ct.hrl"). + +suite() -> [%%{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}, + {timetrap,{minutes,2}} + ]. + +all() -> + [ + {group, textblock_256} + ]. + +groups() -> + [ + {textblock_256, [], [ + {group, ciphers_128}, + {group, ciphers_256} + ]}, + + {ciphers_128, [{repeat, 5}], [ + block, + stream + ]}, + + {ciphers_256, [{repeat, 5}], [ + block, + stream, + chacha + ]} + ]. + +%%%---------------------------------------------------------------- +%%% +init_per_suite(Config0) -> + try crypto:start() of + _ -> + [{_,_,Info}] = crypto:info_lib(), + ct:comment("~s",[Info]), + ct:pal("Crypto version: ~p~n~n~p",[Info,crypto:supports()]), + Config1 = measure_openssl_aes_cbc([128,256], Config0), + calibrate([{sec_goal,10} | Config1]) + + catch _:_ -> + {fail, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + application:stop(crypto). + +%%%---------------------------------------------------------------- +%%% +init_per_group(Group, Config) -> + case atom_to_list(Group) of + "ciphers_"++KeySizeStr -> + KeySize = list_to_integer(KeySizeStr), + [{key_size,KeySize} | Config]; + + "textblock_"++BlockSizeStr -> + BlockSize = list_to_integer(BlockSizeStr), + [{block_size,BlockSize} | Config]; + + _ -> + Config + end. + +end_per_group(_Group, Config) -> + Config. + + +measure_openssl_aes_cbc(KeySizes, Config) -> + BLno_acc = [baseline(aes_cbc, KeySize, false) || KeySize <- KeySizes], + ct:pal("Non-accelerated baseline encryption time [µs/block]:~n~p", [BLno_acc]), + BLacc = [baseline(aes_cbc, KeySize, true) || KeySize <- KeySizes], + ct:pal("Possibly accelerated baseline encryption time [µs/block]:~n~p", [BLacc]), + [{acc,BLacc}, + {no_acc,BLno_acc} | Config]. + +calibrate(Config) -> + Secs = proplists:get_value(sec_goal, Config, 10), + {_,Empty} = data(empty, 0, 0), + {Ne,Te} = run1(Secs*3000, Empty), + report(["Overhead"], Te/Ne), + [{overhead,Te/Ne} | Config]. + +%%%================================================================ +%%% +%%% +block(Config) -> + run_cryptos([aes_cbc, aes_gcm, aes_ccm], + Config). + +stream(Config) -> + run_cryptos([aes_ctr], + Config). + +chacha(Config) -> + run_cryptos([chacha20, chacha20_poly1305], + Config). + + +%%%================================================================ +%%% +%%% + +run_cryptos(Cryptos, Config) -> + KeySize = proplists:get_value(key_size, Config), + BlockSize = proplists:get_value(block_size, Config), + MilliSecGoal = 1000*proplists:get_value(sec_goal,Config), + OverHead = proplists:get_value(overhead, Config, 0), + [try + TimePerOpBrutto = run(Crypto,KeySize,BlockSize,MilliSecGoal), + %% ct:pal("Brutto: ~p Overhead: ~p (~.2f %) Netto: ~p", + %% [TimePerOpBrutto, OverHead, 100*OverHead/TimePerOpBrutto,TimePerOpBrutto - OverHead]), + TimePerOpBrutto - OverHead + of + TimePerOp -> % µs + %% First, Report speed of encrypting blocks of 1000. [blocks/sec] + ReportUnit = 1000, + Label = [fmt(Crypto)," key:",KeySize," block:",BlockSize], + report(Label, + (BlockSize/ReportUnit)*1000000/TimePerOp + ), + + EffCrypto = case Crypto of + X -> X + end, + %% Percent of accelerated speed + case find_value([acc,{EffCrypto,KeySize},BlockSize], Config) of + undefined -> + ok; + TimePerOpBaseAcc -> + report(["Percent of acc OpenSSL "|Label], + 100*TimePerOpBaseAcc/TimePerOp % Percent of base *speed* + ) + end, + + %% Percent of non-accelerated speed + case find_value([no_acc,{EffCrypto,KeySize},BlockSize], Config) of + undefined -> + ok; + TimePerOpBaseNoAcc -> + report(["Percent of noacc OpenSSL "|Label], + 100*TimePerOpBaseNoAcc/TimePerOp % Percent of base *speed* + ) + end + catch + _:_ -> + ct:pal("~p unsupported",[{Crypto,KeySize,BlockSize}]) + end + || Crypto <- Cryptos, + supported(Crypto) + ]. + + +run(Crypto, KeySize, BlockSize, MilliSecGoal) -> + {_Type, Funs} = data(Crypto, KeySize, BlockSize), + {Nc,Tc} = run1(MilliSecGoal, Funs), + Tc/Nc. + +fmt(X) -> X. + + +find_value(KeyPath, PropList, Default) -> + try find_value(KeyPath, PropList) + of + undefined -> Default + catch + error:function_clause -> Default + end. + +find_value(KeyPath, PropList) -> + lists:foldl(fun(K, L) when is_list(L) -> proplists:get_value(K,L); + (_, _) -> undefined + end, PropList, KeyPath). + +%%%================================================================ +%%% +%%% +funs({block, {Type, Key, IV, Block}}) -> + {fun() -> ok end, + fun(_) -> crypto:block_encrypt(Type, Key, IV, Block) end, + fun(_) -> ok end}; + +funs({stream, {Type, Key, IV, Block}}) -> + {fun() -> {crypto:stream_init(Type, Key, IV),ok} end, + fun({Ctx,_}) -> crypto:stream_encrypt(Ctx, Block) end, + fun(_) -> ok end}. + + +data(aes_cbc, KeySize, BlockSize) -> + Type = case KeySize of + 128 -> aes_cbc128; + 256 -> aes_cbc256 + end, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({block, {Type, Key, IV, Block}})}; + +data(aes_gcm, KeySize, BlockSize) -> + Type = aes_gcm, + Key = mk_bin(KeySize div 8), + IV = mk_bin(12), + Block = mk_bin(BlockSize), + AAD = <<01,02,03,04>>, + {Type, funs({block, {Type, Key, IV, {AAD,Block,16}}})}; + +data(aes_ccm, KeySize, BlockSize) -> + Type = aes_ccm, + Key = mk_bin(KeySize div 8), + IV = mk_bin(12), + Block = mk_bin(BlockSize), + AAD = <<01,02,03,04>>, + {Type, funs({block, {Type, Key, IV, {AAD,Block,12}}})}; + +data(aes_ctr, KeySize, BlockSize) -> + Type = aes_ctr, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({stream, {Type, Key, IV, Block}})}; + +data(chacha20_poly1305, 256=KeySize, BlockSize) -> + Type = chacha20_poly1305, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + AAD = <<01,02,03,04>>, + Block = mk_bin(BlockSize), + {Type, funs({block, {Type, Key, IV, {AAD,Block}}})}; + +data(chacha20, 256=KeySize, BlockSize) -> + Type = chacha20, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({stream, {Type, Key, IV, Block}})}; + +data(empty, 0, 0) -> + {undefined, + {fun() -> ok end, + fun(X) -> X end, + fun(_) -> ok end}}. + +%%%================================================================ +%%% +%%% +run1(MilliSecGoal, Funs) -> + Parent = self(), + Pid = spawn(fun() -> + {Fi,Fu,Ff} = Funs, + Ctx0 = Fi(), + erlang:garbage_collect(), + T0 = start_time(), + {N,Ctx} = loop(Fu, Ctx0, 0), + T = elapsed_time(T0), + Ff(Ctx), + Parent ! {result,N,microseconds(T)} + end), + Pid ! go, + receive + after MilliSecGoal -> + Pid ! stop + end, + receive + {result,N,MicroSecs} -> + {N,MicroSecs} + end. + + +loop(F, Ctx, N) -> + receive + stop -> + {N, Ctx} + after 0 -> + loop(F, F(Ctx), N+1) + end. + +%%%---------------------------------------------------------------- +report(LabelList, Value) -> + Label = report_chars(lists:concat(LabelList)), + ct:pal("ct_event:notify ~p: ~p", [Label, Value]), + ct_event:notify( + #event{name = benchmark_data, + data = [{name, Label}, + {value,Value}]}). + +report_chars(Cs) -> + [case C of + $- -> $_; + _ -> C + end || C <- Cs]. + +%%%---------------------------------------------------------------- +supported(Algorithm) -> + lists:member(Algorithm, + [A || {_,As} <- crypto:supports(), A <- As] + ). + +%%%---------------------------------------------------------------- +start_time() -> + erlang:system_time(). + +elapsed_time(StartTime) -> + erlang:system_time() - StartTime. + +microseconds(Time) -> + erlang:convert_time_unit(Time, native, microsecond). + +%%%---------------------------------------------------------------- + +%% Example output: +%% +DT:aes-128-cbc:3:16 +%% +R:135704772:aes-128-cbc:2.980000 +%% +DT:aes-128-cbc:3:64 +%% +R:36835089:aes-128-cbc:3.000000 +%% +DT:aes-128-cbc:3:256 +%% +R:9398616:aes-128-cbc:3.000000 +%% +DT:aes-128-cbc:3:1024 +%% +R:2355683:aes-128-cbc:2.990000 +%% +DT:aes-128-cbc:3:8192 +%% +R:294508:aes-128-cbc:2.990000 +%% +H:16:64:256:1024:8192 +%% +F:22:aes-128-cbc:728616225.50:785815232.00:802015232.00:806762338.46:806892821.40 + +baseline(Crypto, KeySize, EVP) -> + Spec= + case {Crypto,KeySize} of + {aes_cbc, 128} -> "aes-128-cbc"; + {aes_cbc, 256} -> "aes-256-cbc" + end, + {{Crypto,KeySize}, baseline(Spec, EVP)}. + +baseline(Spec, EVP) -> + Cmd = + case EVP of + true -> "openssl speed -mr -evp " ++ Spec; + false-> "openssl speed -mr " ++ Spec + end, + get_base_values(string:tokens(os:cmd(Cmd),"\n"), Spec, []). + + +get_base_values(["+DT:"++Sdt, + "+R:"++Sr + |T], Crypto, Acc) -> + [Crypto0,_GoalSecs0,BlockSize0] = string:tokens(Sdt, ":"), + [Nblocks0,Crypto0,RealSecs0] = string:tokens(Sr, ":"), + Crypto = fix_possible_space_bug(Crypto0), + RealSecs = list_to_float(RealSecs0), + BlockSize = list_to_integer(BlockSize0), + Nblocks = list_to_integer(Nblocks0), + get_base_values(T, Crypto, [{BlockSize, 1000000*RealSecs/Nblocks} | Acc]); + +get_base_values([_|T], Crypto, Acc) -> + get_base_values(T, Crypto, Acc); + +get_base_values([], _, Acc) -> + lists:sort(Acc). + +fix_possible_space_bug(S) -> lists:concat(lists:join("-",string:tokens(S,"- "))). + +%%%---------------------------------------------------------------- +mk_bin(Size) when Size =< 256 -> + list_to_binary(lists:seq(0,Size-1)); + +mk_bin(Size) when 1024 =< Size -> + B = mk_bin(Size div 4), + Brest = mk_bin(Size rem 4), + <<B/binary, B/binary, B/binary, B/binary, Brest/binary>>; + +mk_bin(Size) when 256 < Size -> + B = mk_bin(Size div 2), + Brest = mk_bin(Size rem 2), + <<B/binary, B/binary, Brest/binary>>. + diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl index 8a45fc9076..3416fbd78d 100644 --- a/lib/crypto/test/engine_SUITE.erl +++ b/lib/crypto/test/engine_SUITE.erl @@ -345,13 +345,13 @@ engine_list(Config) when is_list(Config) -> {skip, "OTP Test engine not found"}; {ok, Engine} -> try - EngineList0 = crypto:engine_list(), case crypto:engine_load(<<"dynamic">>, [{<<"SO_PATH">>, Engine}, <<"LOAD">>], []) of {ok, E} -> EngineList0 = crypto:engine_list(), + false = lists:member(<<"MD5">>, EngineList0), ok = crypto:engine_add(E), [<<"MD5">>] = lists:subtract(crypto:engine_list(), EngineList0), ok = crypto:engine_remove(E), @@ -674,7 +674,7 @@ ensure_load(Config) when is_list(Config) -> end. %%%---------------------------------------------------------------- -%%% Pub/priv key storage tests. Thoose are for testing the crypto.erl +%%% Pub/priv key storage tests. Those are for testing the crypto.erl %%% support for using priv/pub keys stored in an engine. sign_verify_rsa(Config) -> |