diff options
Diffstat (limited to 'lib')
26 files changed, 2892 insertions, 2718 deletions
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index 18040a3b26..e728db18eb 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -68,13 +68,13 @@ RELSYSDIR = $(RELEASE_PATH)/lib/crypto-$(VSN) # ---------------------------------------------------- # Misc Macros # ---------------------------------------------------- -OBJS = $(OBJDIR)/crypto_drv.o -DRV_MAKEFILE = $(PRIVDIR)/Makefile +OBJS = $(OBJDIR)/crypto.o +NIF_MAKEFILE = $(PRIVDIR)/Makefile ifeq ($(findstring win32,$(TARGET)), win32) -DYN_DRIVER = $(LIBDIR)/crypto_drv.dll +NIF_LIB = $(LIBDIR)/crypto.dll else -DYN_DRIVER = $(LIBDIR)/crypto_drv.so +NIF_LIB = $(LIBDIR)/crypto.so endif ifeq ($(HOST_OS),) @@ -94,7 +94,7 @@ endif # Targets # ---------------------------------------------------- -debug opt valgrind: $(OBJDIR) $(LIBDIR) $(DYN_DRIVER) +debug opt valgrind: $(OBJDIR) $(LIBDIR) $(NIF_LIB) $(OBJDIR): -@mkdir -p $(OBJDIR) @@ -106,16 +106,16 @@ $(OBJDIR)/%.o: %.c $(INSTALL_DIR) $(OBJDIR) $(CC) -c -o $@ $(ALL_CFLAGS) $< -$(LIBDIR)/crypto_drv.so: $(OBJS) +$(LIBDIR)/crypto.so: $(OBJS) $(INSTALL_DIR) $(LIBDIR) $(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(CRYPTO_LINK_LIB) -$(LIBDIR)/crypto_drv.dll: $(OBJS) +$(LIBDIR)/crypto.dll: $(OBJS) $(INSTALL_DIR) $(LIBDIR) $(LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(OBJS) -llibeay32 clean: - rm -f $(DYN_DRIVER) $(OBJS) + rm -f $(NIF_LIB) $(OBJS) rm -f core *~ docs: @@ -128,9 +128,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt $(INSTALL_DIR) $(RELSYSDIR)/priv/obj $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_DATA) $(DRV_MAKEFILE) $(RELSYSDIR)/priv/obj + $(INSTALL_DATA) $(NIF_MAKEFILE) $(RELSYSDIR)/priv/obj $(INSTALL_PROGRAM) $(OBJS) $(RELSYSDIR)/priv/obj - $(INSTALL_PROGRAM) $(DYN_DRIVER) $(RELSYSDIR)/priv/lib + $(INSTALL_PROGRAM) $(NIF_LIB) $(RELSYSDIR)/priv/lib release_docs_spec: diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c new file mode 100644 index 0000000000..a71df1d7fd --- /dev/null +++ b/lib/crypto/c_src/crypto.c @@ -0,0 +1,1531 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * Purpose: Dynamically loadable NIF library for cryptography. + * 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> +#include <openssl/des.h> +/* #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/bn.h> +#include <openssl/objects.h> +#include <openssl/rc4.h> +#include <openssl/rc2.h> +#include <openssl/blowfish.h> +#include <openssl/rand.h> + +#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) \ + ((void) ((VALGRIND_CHECK_MEM_IS_DEFINED(ptr,size) == 0) ? 1 : \ + (fprintf(stderr,"\r\n####### VALGRIND_ASSSERT(%p,%d) failed at %s:%d\r\n",\ + (ptr),(size), __FILE__, __LINE__), abort(), 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);\ +} + +/* NIF interface declarations */ +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); +static int reload(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 md5(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM md5_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM md5_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM md5_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM md4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM md4_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM md4_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM md4_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM md5_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM des_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM des_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM rand_bytes_3(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 dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM rsa_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM aes_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM rc4_encrypt(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 rc2_40_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM rsa_private_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM dh_check(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 bf_cfb64_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM bf_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM bf_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + + +/* openssl callbacks */ +#ifdef OPENSSL_THREADS +static void locking_function(int mode, int n, const char *file, int line); +static unsigned long id_function(void); +static struct CRYPTO_dynlock_value* dyn_create_function(const char *file, + int line); +static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value* ptr, + const char *file, int line); +static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr, + const char *file, int line); +#endif /* OPENSSL_THREADS */ + +/* helpers */ +static void hmac_md5(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf); +static void hmac_sha1(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf); + +static int library_refc = 0; /* number of users of this dynamic library */ + +static ErlNifFunc nif_funcs[] = { + {"info_lib", 0, info_lib}, + {"md5", 1, md5}, + {"md5_init", 0, md5_init}, + {"md5_update", 2, md5_update}, + {"md5_final", 1, md5_final}, + {"sha", 1, sha}, + {"sha_init", 0, sha_init}, + {"sha_update", 2, sha_update}, + {"sha_final", 1, sha_final}, + {"md4", 1, md4}, + {"md4_init", 0, md4_init}, + {"md4_update", 2, md4_update}, + {"md4_final", 1, md4_final}, + {"md5_mac_n", 3, md5_mac_n}, + {"sha_mac_n", 3, sha_mac_n}, + {"des_cbc_crypt", 4, des_cbc_crypt}, + {"des_ecb_crypt", 3, des_ecb_crypt}, + {"des_ede3_cbc_crypt", 6, des_ede3_cbc_crypt}, + {"aes_cfb_128_crypt", 4, aes_cfb_128_crypt}, + {"rand_bytes", 1, rand_bytes_1}, + {"rand_bytes", 3, rand_bytes_3}, + {"rand_uniform_nif", 2, rand_uniform_nif}, + {"mod_exp_nif", 3, mod_exp_nif}, + {"dss_verify", 3, dss_verify}, + {"rsa_verify", 4, rsa_verify}, + {"aes_cbc_crypt", 4, aes_cbc_crypt}, + {"exor", 2, exor}, + {"rc4_encrypt", 2, rc4_encrypt}, + {"rc4_set_key", 1, rc4_set_key}, + {"rc4_encrypt_with_state", 2, rc4_encrypt_with_state}, + {"rc2_40_cbc_crypt", 4, rc2_40_cbc_crypt}, + {"rsa_sign_nif", 3, rsa_sign_nif}, + {"dss_sign_nif", 2, dss_sign_nif}, + {"rsa_public_crypt", 4, rsa_public_crypt}, + {"rsa_private_crypt", 4, rsa_private_crypt}, + {"dh_generate_parameters_nif", 2, dh_generate_parameters_nif}, + {"dh_check", 1, dh_check}, + {"dh_generate_key_nif", 2, dh_generate_key_nif}, + {"dh_compute_key_nif", 3, dh_compute_key_nif}, + {"bf_cfb64_crypt", 4, bf_cfb64_crypt}, + {"bf_cbc_crypt", 4, bf_cbc_crypt}, + {"bf_ecb_crypt", 3, bf_ecb_crypt}, + {"blowfish_ofb64_encrypt", 3, blowfish_ofb64_encrypt} +}; + +ERL_NIF_INIT(crypto,nif_funcs,load,reload,upgrade,unload) + + +#define MD5_CTX_LEN (sizeof(MD5_CTX)) +#define MD5_LEN 16 +#define MD5_LEN_96 12 +#define MD4_CTX_LEN (sizeof(MD4_CTX)) +#define MD4_LEN 16 +#define SHA_CTX_LEN (sizeof(SHA_CTX)) +#define SHA_LEN 20 +#define SHA_LEN_96 12 +#define HMAC_INT_LEN 64 + +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c + + +static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */ +static ERL_NIF_TERM atom_true; +static ERL_NIF_TERM atom_false; +static ERL_NIF_TERM atom_sha; +static ERL_NIF_TERM atom_md5; +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_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 int is_ok_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info) +{ + int i; + return enif_get_int(env,load_info,&i) && i == 101; +} +static void* crypto_alloc(size_t size) +{ + return enif_alloc(NULL, size); +} +static void* crypto_realloc(void* ptr, size_t size) +{ + return enif_realloc(NULL, ptr, size); +} +static void crypto_free(void* ptr) +{ + enif_free(NULL, ptr); +} + +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + ErlNifSysInfo sys_info; + CRYPTO_set_mem_functions(crypto_alloc, crypto_realloc, crypto_free); + + if (!is_ok_load_info(env, load_info)) { + return -1; + } + +#ifdef OPENSSL_THREADS + enif_system_info(&sys_info, sizeof(sys_info)); + + if (sys_info.scheduler_threads > 1) { + int i; + lock_vec = enif_alloc(env,CRYPTO_num_locks()*sizeof(*lock_vec)); + if (lock_vec==NULL) return -1; + memset(lock_vec,0,CRYPTO_num_locks()*sizeof(*lock_vec)); + + for (i=CRYPTO_num_locks()-1; i>=0; --i) { + lock_vec[i] = enif_rwlock_create("crypto_stat"); + if (lock_vec[i]==NULL) return -1; + } + CRYPTO_set_locking_callback(locking_function); + CRYPTO_set_id_callback(id_function); + CRYPTO_set_dynlock_create_callback(dyn_create_function); + CRYPTO_set_dynlock_lock_callback(dyn_lock_function); + CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function); + } + /* else no need for locks */ +#endif /* OPENSSL_THREADS */ + + atom_true = enif_make_atom(env,"true"); + atom_false = enif_make_atom(env,"false"); + atom_sha = enif_make_atom(env,"sha"); + atom_md5 = enif_make_atom(env,"md5"); + 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_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"); + + *priv_data = NULL; + library_refc++; + return 0; +} + +static int reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + if (*priv_data != NULL) { + return -1; /* Don't know how to do that */ + } + if (library_refc == 0) { + /* No support for real library upgrade. The tricky thing is to know + when to (re)set the callbacks for allocation and locking. */ + return -2; + } + if (!is_ok_load_info(env, load_info)) { + return -1; + } + return 0; +} + +static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, + ERL_NIF_TERM load_info) +{ + int i; + if (*old_priv_data != NULL) { + return -1; /* Don't know how to do that */ + } + i = reload(env,priv_data,load_info); + if (i != 0) { + return i; + } + library_refc++; + return 0; +} + +static void unload(ErlNifEnv* env, void* priv_data) +{ + if (--library_refc <= 0) { + CRYPTO_cleanup_all_ex_data(); + + if (lock_vec != NULL) { + int i; + for (i=CRYPTO_num_locks()-1; i>=0; --i) { + if (lock_vec[i] != NULL) { + enif_rwlock_destroy(lock_vec[i]); + } + } + enif_free(env,lock_vec); + } + } + /*else NIF library still used by other (new) module code */ +} + +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; + + 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, SSLeay()), + ver_term)); +} + +static ERL_NIF_TERM md5(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data) */ + ErlNifBinary ibin; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) { + return enif_make_badarg(env); + } + MD5((unsigned char *) ibin.data, ibin.size, + enif_make_new_binary(env,MD5_LEN, &ret)); + return ret; +} +static ERL_NIF_TERM md5_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ + ERL_NIF_TERM ret; + MD5_Init((MD5_CTX *) enif_make_new_binary(env, MD5_CTX_LEN, &ret)); + return ret; +} +static ERL_NIF_TERM md5_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + MD5_CTX* new_ctx; + ErlNifBinary ctx_bin, data_bin; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) + || ctx_bin.size != MD5_CTX_LEN + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + new_ctx = (MD5_CTX*) enif_make_new_binary(env,MD5_CTX_LEN, &ret); + memcpy(new_ctx, ctx_bin.data, MD5_CTX_LEN); + MD5_Update(new_ctx, data_bin.data, data_bin.size); + return ret; +} +static ERL_NIF_TERM md5_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) */ + ErlNifBinary ctx_bin; + MD5_CTX ctx_clone; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != MD5_CTX_LEN) { + return enif_make_badarg(env); + } + memcpy(&ctx_clone, ctx_bin.data, MD5_CTX_LEN); /* writable */ + MD5_Final(enif_make_new_binary(env, MD5_LEN, &ret), &ctx_clone); + return ret; +} + +static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data) */ + ErlNifBinary ibin; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) { + return enif_make_badarg(env); + } + SHA1((unsigned char *) ibin.data, ibin.size, + enif_make_new_binary(env,SHA_LEN, &ret)); + return ret; +} +static ERL_NIF_TERM sha_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ + ERL_NIF_TERM ret; + SHA1_Init((SHA_CTX *) enif_make_new_binary(env, SHA_CTX_LEN, &ret)); + return ret; +} +static ERL_NIF_TERM sha_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + SHA_CTX* new_ctx; + ErlNifBinary ctx_bin, data_bin; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != SHA_CTX_LEN + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + new_ctx = (SHA_CTX*) enif_make_new_binary(env,SHA_CTX_LEN, &ret); + memcpy(new_ctx, ctx_bin.data, SHA_CTX_LEN); + SHA1_Update(new_ctx, data_bin.data, data_bin.size); + return ret; +} +static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) */ + ErlNifBinary ctx_bin; + SHA_CTX ctx_clone; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != SHA_CTX_LEN) { + return enif_make_badarg(env); + } + memcpy(&ctx_clone, ctx_bin.data, SHA_CTX_LEN); /* writable */ + SHA1_Final(enif_make_new_binary(env, SHA_LEN, &ret), &ctx_clone); + return ret; +} + +static ERL_NIF_TERM md4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data) */ + ErlNifBinary ibin; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) { + return enif_make_badarg(env); + } + MD4((unsigned char *) ibin.data, ibin.size, + enif_make_new_binary(env,MD4_LEN, &ret)); + return ret; +} +static ERL_NIF_TERM md4_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ + ERL_NIF_TERM ret; + MD4_Init((MD4_CTX *) enif_make_new_binary(env, MD4_CTX_LEN, &ret)); + return ret; +} +static ERL_NIF_TERM md4_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + MD4_CTX* new_ctx; + ErlNifBinary ctx_bin, data_bin; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != MD4_CTX_LEN + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + new_ctx = (MD4_CTX*) enif_make_new_binary(env,MD4_CTX_LEN, &ret); + memcpy(new_ctx, ctx_bin.data, MD4_CTX_LEN); + MD4_Update(new_ctx, data_bin.data, data_bin.size); + return ret; +} +static ERL_NIF_TERM md4_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) */ + ErlNifBinary ctx_bin; + MD4_CTX ctx_clone; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != MD4_CTX_LEN) { + return enif_make_badarg(env); + } + memcpy(&ctx_clone, ctx_bin.data, MD4_CTX_LEN); /* writable */ + MD4_Final(enif_make_new_binary(env, MD4_LEN, &ret), &ctx_clone); + return ret; +} + +static ERL_NIF_TERM md5_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, MacSize) */ + unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + ErlNifBinary key, data; + unsigned mac_sz; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > MD5_LEN) { + return enif_make_badarg(env); + } + hmac_md5(key.data, key.size, data.data, data.size, hmacbuf); + memcpy(enif_make_new_binary(env, mac_sz, &ret), hmacbuf, mac_sz); + return ret; +} + +static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, MacSize) */ + unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + ErlNifBinary key, data; + unsigned mac_sz; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA_LEN) { + return enif_make_badarg(env); + } + hmac_sha1(key.data, key.size, data.data, data.size, hmacbuf); + memcpy(enif_make_new_binary(env, mac_sz, &ret), + hmacbuf, mac_sz); + return ret; +} + +static ERL_NIF_TERM des_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Ivec, Text, IsEncrypt) */ + ErlNifBinary key, ivec, text; + DES_key_schedule schedule; + DES_cblock ivec_clone; /* writable copy */ + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 8 + || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 8 + || !enif_inspect_iolist_as_binary(env, argv[2], &text) + || text.size % 8 != 0) { + return enif_make_badarg(env); + } + memcpy(&ivec_clone, ivec.data, 8); + DES_set_key((const_DES_cblock*)key.data, &schedule); + DES_ncbc_encrypt(text.data, enif_make_new_binary(env, text.size, &ret), + text.size, &schedule, &ivec_clone, (argv[3] == atom_true)); + return ret; +} + +static ERL_NIF_TERM des_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Text/Cipher, IsEncrypt) */ + ErlNifBinary key, text; + DES_key_schedule schedule; + ERL_NIF_TERM ret; + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 8 || + !enif_inspect_iolist_as_binary(env, argv[1], &text) || text.size != 8) { + return enif_make_badarg(env); + } + DES_set_key((const_DES_cblock*)key.data, &schedule); + DES_ecb_encrypt((const_DES_cblock*)text.data, + (DES_cblock*)enif_make_new_binary(env, 8, &ret), + &schedule, (argv[2] == atom_true)); + return ret; +} + +static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key1, Key2, Key3, IVec, Text/Cipher, IsEncrypt) */ + ErlNifBinary key1, key2, key3, ivec, text; + DES_key_schedule schedule1, schedule2, schedule3; + DES_cblock ivec_clone; /* writable copy */ + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key1) || key1.size != 8 + || !enif_inspect_iolist_as_binary(env, argv[1], &key2) || key2.size != 8 + || !enif_inspect_iolist_as_binary(env, argv[2], &key3) || key3.size != 8 + || !enif_inspect_binary(env, argv[3], &ivec) || ivec.size != 8 + || !enif_inspect_iolist_as_binary(env, argv[4], &text) + || text.size % 8 != 0) { + return enif_make_badarg(env); + } + + memcpy(&ivec_clone, ivec.data, 8); + DES_set_key((const_DES_cblock*)key1.data, &schedule1); + DES_set_key((const_DES_cblock*)key2.data, &schedule2); + DES_set_key((const_DES_cblock*)key3.data, &schedule3); + DES_ede3_cbc_encrypt(text.data, enif_make_new_binary(env,text.size,&ret), + text.size, &schedule1, &schedule2, &schedule3, + &ivec_clone, (argv[5] == atom_true)); + return ret; +} + +static ERL_NIF_TERM aes_cfb_128_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; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 16 + || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16 + || !enif_inspect_iolist_as_binary(env, argv[2], &text) + || text.size % 16 != 0) { + return enif_make_badarg(env); + } + + memcpy(ivec_clone, ivec.data, 16); + AES_set_encrypt_key(key.data, 128, &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)); + return ret; +} + +static ERL_NIF_TERM rand_bytes_1(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); + RAND_pseudo_bytes(data, bytes); + ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes); + return ret; +} +static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Bytes, TopMask, BottomMask) */ + unsigned bytes; + unsigned char* data; + unsigned top_mask, bot_mask; + ERL_NIF_TERM ret; + if (!enif_get_uint(env, argv[0], &bytes) + || !enif_get_uint(env, argv[1], &top_mask) + || !enif_get_uint(env, argv[2], &bot_mask)) { + return enif_make_badarg(env); + } + data = enif_make_new_binary(env, bytes, &ret); + RAND_pseudo_bytes(data, bytes); + ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes); + if (bytes > 0) { + data[bytes-1] |= top_mask; + data[0] |= bot_mask; + } + 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 ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Lo,Hi) */ + BIGNUM *bn_from, *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)) { + 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) */ + BIGNUM *bn_base=NULL, *bn_exponent=NULL, *bn_modulo, *bn_result; + BN_CTX *bn_ctx; + unsigned char* ptr; + unsigned dlen; + ERL_NIF_TERM ret; + + if (!get_bn_from_mpint(env, argv[0], &bn_base) + || !get_bn_from_mpint(env, argv[1], &bn_exponent) + || !get_bn_from_mpint(env, argv[2], &bn_modulo)) { + + if (bn_base) BN_free(bn_base); + if (bn_exponent) BN_free(bn_exponent); + 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); + ptr = enif_make_new_binary(env, dlen+4, &ret); + put_int32(ptr, dlen); + BN_bn2bin(bn_result, ptr+4); + BN_free(bn_result); + BN_CTX_free(bn_ctx); + BN_free(bn_modulo); + BN_free(bn_exponent); + BN_free(bn_base); + return ret; +} + +static int inspect_mpint(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifBinary* bin) +{ + return enif_inspect_binary(env, term, bin) && + bin->size >= 4 && get_int32(bin->data) == bin->size-4; +} + +static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data,Signature,Key=[P, Q, G, Y]) */ + ErlNifBinary data_bin, sign_bin; + BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_y; + unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + ERL_NIF_TERM head, tail; + DSA *dsa; + int i; + + if (!inspect_mpint(env,argv[0],&data_bin) + || !inspect_mpint(env,argv[1],&sign_bin) + || !enif_get_list_cell(env, argv[2], &head, &tail) + || !get_bn_from_mpint(env, head, &dsa_p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &dsa_q) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &dsa_g) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &dsa_y) + || !enif_is_empty_list(env,tail)) { + + return enif_make_badarg(env); + } + SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); + + dsa = DSA_new(); + dsa->p = dsa_p; + dsa->q = dsa_q; + dsa->g = dsa_g; + dsa->priv_key = NULL; + dsa->pub_key = dsa_y; + i = DSA_verify(0, hmacbuf, SHA_DIGEST_LENGTH, + sign_bin.data+4, sign_bin.size-4, dsa); + DSA_free(dsa); + return(i > 0) ? atom_true : atom_false; +} + +static ERL_NIF_TERM rsa_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Data, Signature, Key=[E,N]) */ + ErlNifBinary data_bin, sign_bin; + unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + ERL_NIF_TERM head, tail, ret; + int i, is_sha; + RSA* rsa = RSA_new(); + + if (argv[0] == atom_sha) is_sha = 1; + else if (argv[0] == atom_md5) is_sha = 0; + else goto badarg; + + if (!inspect_mpint(env, argv[1], &data_bin) + || !inspect_mpint(env, argv[2], &sign_bin) + || !enif_get_list_cell(env, argv[3], &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->e) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->n) + || !enif_is_empty_list(env, tail)) { + badarg: + ret = enif_make_badarg(env); + } + else { + if (is_sha) { + SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); + i = RSA_verify(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH, + sign_bin.data+4, sign_bin.size-4, rsa); + } + else { + MD5(data_bin.data+4, data_bin.size-4, hmacbuf); + i = RSA_verify(NID_md5, hmacbuf, MD5_DIGEST_LENGTH, + sign_bin.data+4, sign_bin.size-4, rsa); + } + ret = (i==1 ? atom_true : atom_false); + } + RSA_free(rsa); + return ret; +} + +static ERL_NIF_TERM aes_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec, Data, IsEncrypt) */ + ErlNifBinary key_bin, ivec_bin, data_bin; + AES_KEY aes_key; + unsigned char ivec[16]; + int i; + unsigned char* ret_ptr; + ERL_NIF_TERM ret; + + 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 != 16 + || !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, 16); /* writable copy */ + AES_cbc_encrypt(data_bin.data, ret_ptr, data_bin.size, &aes_key, ivec, i); + return ret; +} + +static ERL_NIF_TERM 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]; + } + return ret; +} + +static ERL_NIF_TERM rc4_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data) */ + ErlNifBinary key, data; + RC4_KEY rc4_key; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env,argv[0], &key) + || !enif_inspect_iolist_as_binary(env,argv[1], &data)) { + return enif_make_badarg(env); + } + RC4_set_key(&rc4_key, key.size, key.data); + RC4(&rc4_key, data.size, data.data, + enif_make_new_binary(env, data.size, &ret)); + return ret; +} + +static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key) */ + ErlNifBinary key; + ERL_NIF_TERM ret; + + 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; +} + +static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (State, Data) */ + + ErlNifBinary state, data; + RC4_KEY* rc4_key; + ERL_NIF_TERM new_state, new_data; + + 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)); + + return enif_make_tuple2(env,argv[0],new_data); +} + +static ERL_NIF_TERM rc2_40_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key,IVec,Data,IsEncrypt) */ + ErlNifBinary key_bin, ivec_bin, data_bin; + RC2_KEY rc2_key; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) + || key_bin.size != 5 + || !enif_inspect_binary(env, argv[1], &ivec_bin) + || ivec_bin.size != 8 + || !enif_inspect_iolist_as_binary(env, argv[2], &data_bin)) { + + return enif_make_badarg(env); + } + + RC2_set_key(&rc2_key, 5, key_bin.data, 40); + RC2_cbc_encrypt(data_bin.data, + enif_make_new_binary(env, data_bin.size, &ret), + data_bin.size, &rc2_key, + ivec_bin.data, + (argv[3] == atom_true)); + + return ret; +} + +static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type,Data,Key=[E,N,D]) */ + ErlNifBinary data_bin, ret_bin; + ERL_NIF_TERM head, tail; + unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + unsigned rsa_s_len; + RSA *rsa = RSA_new(); + int i, is_sha; + + if (argv[0] == atom_sha) is_sha = 1; + else if (argv[0] == atom_md5) is_sha = 0; + else goto badarg; + + if (!inspect_mpint(env,argv[1],&data_bin) + || !enif_get_list_cell(env, argv[2], &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->e) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->n) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->d) + || !enif_is_empty_list(env,tail)) { + badarg: + RSA_free(rsa); + return enif_make_badarg(env); + } + enif_alloc_binary(env, RSA_size(rsa), &ret_bin); + if (is_sha) { + SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); + ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, SHA_DIGEST_LENGTH); + i = RSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH, + ret_bin.data, &rsa_s_len, rsa); + } + else { + MD5(data_bin.data+4, data_bin.size-4, hmacbuf); + ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, MD5_DIGEST_LENGTH); + i = RSA_sign(NID_md5, hmacbuf,MD5_DIGEST_LENGTH, + ret_bin.data, &rsa_s_len, rsa); + } + RSA_free(rsa); + if (i) { + ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, rsa_s_len); + if (rsa_s_len != data_bin.size) { + enif_realloc_binary(env, &ret_bin, rsa_s_len); + ERL_VALGRIND_ASSERT_MEM_DEFINED(ret_bin.data, rsa_s_len); + } + return enif_make_binary(env,&ret_bin); + } + else { + enif_release_binary(env, &ret_bin); + return atom_error; + } +} + +static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data,Key=[P,Q,G,PrivKey]) */ + ErlNifBinary data_bin, ret_bin; + ERL_NIF_TERM head, tail; + unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + unsigned int dsa_s_len; + DSA* dsa = DSA_new(); + int i; + + dsa->pub_key = NULL; + if (!inspect_mpint(env, argv[0], &data_bin) + || !enif_get_list_cell(env, argv[1], &head, &tail) + || !get_bn_from_mpint(env, head, &dsa->p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &dsa->q) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &dsa->g) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &dsa->priv_key) + || !enif_is_empty_list(env,tail)) { + + DSA_free(dsa); + return enif_make_badarg(env); + } + + SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); + + enif_alloc_binary(env, DSA_size(dsa), &ret_bin); + i = DSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH, + ret_bin.data, &dsa_s_len, dsa); + DSA_free(dsa); + if (i) { + if (dsa_s_len != ret_bin.size) { + enif_realloc_binary(env, &ret_bin, dsa_s_len); + } + return enif_make_binary(env, &ret_bin); + } + else { + return atom_error; + } +} + +static int rsa_pad(ERL_NIF_TERM term, int* padding) +{ + if (term == atom_rsa_pkcs1_padding) { + *padding = RSA_PKCS1_PADDING; + } + else if (term == atom_rsa_pkcs1_oaep_padding) { + *padding = RSA_PKCS1_OAEP_PADDING; + } + else if (term == atom_rsa_no_padding) { + *padding = RSA_NO_PADDING; + } + else { + return 0; + } + return 1; +} + +static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data, PublKey=[E,N], Padding, IsEncrypt) */ + ErlNifBinary data_bin, ret_bin; + ERL_NIF_TERM head, tail; + int padding, i; + RSA* rsa = RSA_new(); + + if (!enif_inspect_binary(env, argv[0], &data_bin) + || !enif_get_list_cell(env, argv[1], &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->e) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->n) + || !enif_is_empty_list(env,tail) + || !rsa_pad(argv[2], &padding)) { + + RSA_free(rsa); + return enif_make_badarg(env); + } + + enif_alloc_binary(env, RSA_size(rsa), &ret_bin); + + if (argv[3] == atom_true) { + ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,data_len); + i = RSA_public_encrypt(data_bin.size, data_bin.data, + ret_bin.data, rsa, padding); + if (i > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, i); + } + } + else { + i = RSA_public_decrypt(data_bin.size, data_bin.data, + ret_bin.data, rsa, padding); + if (i > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, i); + enif_realloc_binary(env, &ret_bin, i); + } + } + RSA_free(rsa); + if (i > 0) { + return enif_make_binary(env,&ret_bin); + } + else { + return atom_error; + } +} + +static ERL_NIF_TERM rsa_private_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data, PublKey=[E,N,D], Padding, IsEncrypt) */ + ErlNifBinary data_bin, ret_bin; + ERL_NIF_TERM head, tail; + int padding, i; + RSA* rsa = RSA_new(); + + if (!enif_inspect_binary(env, argv[0], &data_bin) + || !enif_get_list_cell(env, argv[1], &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->e) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->n) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->d) + || !enif_is_empty_list(env,tail) + || !rsa_pad(argv[2], &padding)) { + + RSA_free(rsa); + return enif_make_badarg(env); + } + + enif_alloc_binary(env, RSA_size(rsa), &ret_bin); + + if (argv[3] == atom_true) { + ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,data_len); + i = RSA_private_encrypt(data_bin.size, data_bin.data, + ret_bin.data, rsa, padding); + if (i > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, i); + } + } + else { + i = RSA_private_decrypt(data_bin.size, data_bin.data, + ret_bin.data, rsa, padding); + if (i > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, i); + enif_realloc_binary(env, &ret_bin, i); + } + } + RSA_free(rsa); + if (i > 0) { + return enif_make_binary(env,&ret_bin); + } + else { + return atom_error; + } +} + +static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (PrimeLen, Generator) */ + int prime_len, generator; + DH* dh_params; + int p_len, g_len; + unsigned char *p_ptr, *g_ptr; + ERL_NIF_TERM ret_p, ret_g; + + if (!enif_get_int(env, argv[0], &prime_len) + || !enif_get_int(env, argv[1], &generator)) { + + return enif_make_badarg(env); + } + dh_params = DH_generate_parameters(prime_len, generator, NULL, NULL); + if (dh_params == NULL) { + return atom_error; + } + p_len = BN_num_bytes(dh_params->p); + g_len = BN_num_bytes(dh_params->g); + p_ptr = enif_make_new_binary(env, p_len+4, &ret_p); + g_ptr = enif_make_new_binary(env, g_len+4, &ret_g); + put_int32(p_ptr, p_len); + put_int32(g_ptr, g_len); + BN_bn2bin(dh_params->p, p_ptr+4); + BN_bn2bin(dh_params->g, g_ptr+4); + ERL_VALGRIND_MAKE_MEM_DEFINED(p_ptr+4, p_len); + ERL_VALGRIND_MAKE_MEM_DEFINED(g_ptr+4, g_len); + DH_free(dh_params); + return enif_make_list2(env, ret_p, ret_g); +} + +static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* ([PrimeLen, Generator]) */ + DH* dh_params = DH_new(); + int i; + ERL_NIF_TERM ret, head, tail; + + if (!enif_get_list_cell(env, argv[0], &head, &tail) + || !get_bn_from_mpint(env, head, &dh_params->p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &dh_params->g) + || !enif_is_empty_list(env,tail)) { + + DH_free(dh_params); + return enif_make_badarg(env); + } + if (DH_check(dh_params, &i)) { + if (i == 0) ret = atom_ok; + else if (i & DH_CHECK_P_NOT_PRIME) ret = atom_not_prime; + else if (i & DH_CHECK_P_NOT_SAFE_PRIME) ret = atom_not_strong_prime; + else if (i & DH_UNABLE_TO_CHECK_GENERATOR) ret = atom_unable_to_check_generator; + else if (i & DH_NOT_SUITABLE_GENERATOR) ret = atom_not_suitable_generator; + else ret = enif_make_tuple2(env, atom_unknown, enif_make_uint(env, i)); + } + else { /* Check Failed */ + ret = enif_make_tuple2(env, atom_error, atom_check_failed); + } + DH_free(dh_params); + return ret; +} + +static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (PrivKey, DHParams=[P,G]) */ + DH* dh_params = DH_new(); + int pub_len, prv_len; + unsigned char *pub_ptr, *prv_ptr; + ERL_NIF_TERM ret, ret_pub, ret_prv, head, tail; + + if (!(get_bn_from_mpint(env, argv[0], &dh_params->priv_key) + || argv[0] == atom_undefined) + || !enif_get_list_cell(env, argv[1], &head, &tail) + || !get_bn_from_mpint(env, head, &dh_params->p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &dh_params->g) + || !enif_is_empty_list(env, tail)) { + + return enif_make_badarg(env); + } + + if (DH_generate_key(dh_params)) { + pub_len = BN_num_bytes(dh_params->pub_key); + prv_len = BN_num_bytes(dh_params->priv_key); + pub_ptr = enif_make_new_binary(env, pub_len+4, &ret_pub); + prv_ptr = enif_make_new_binary(env, prv_len+4, &ret_prv); + put_int32(pub_ptr, pub_len); + put_int32(prv_ptr, prv_len); + BN_bn2bin(dh_params->pub_key, pub_ptr+4); + BN_bn2bin(dh_params->priv_key, prv_ptr+4); + ERL_VALGRIND_MAKE_MEM_DEFINED(pub_ptr+4, pub_len); + ERL_VALGRIND_MAKE_MEM_DEFINED(prv_ptr+4, prv_len); + ret = enif_make_tuple2(env, ret_pub, ret_prv); + } + else { + ret = atom_error; + } + DH_free(dh_params); + return ret; +} + +static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */ + DH* dh_params = DH_new(); + BIGNUM* pubkey; + int i; + ErlNifBinary ret_bin; + ERL_NIF_TERM ret, head, tail; + + if (!get_bn_from_mpint(env, argv[0], &pubkey) + || !get_bn_from_mpint(env, argv[1], &dh_params->priv_key) + || !enif_get_list_cell(env, argv[2], &head, &tail) + || !get_bn_from_mpint(env, head, &dh_params->p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &dh_params->g) + || !enif_is_empty_list(env, tail)) { + + ret = enif_make_badarg(env); + } + else { + enif_alloc_binary(env, DH_size(dh_params), &ret_bin); + i = DH_compute_key(ret_bin.data, pubkey, dh_params); + if (i > 0) { + if (i != ret_bin.size) { + enif_realloc_binary(env, &ret_bin, i); + } + ret = enif_make_binary(env, &ret_bin); + } + else { + ret = atom_error; + } + } + DH_free(dh_params); + return ret; +} + +static ERL_NIF_TERM bf_cfb64_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Ivec, Data, IsEncrypt) */ + ErlNifBinary key_bin, ivec_bin, data_bin; + BF_KEY bf_key; /* blowfish key 8 */ + unsigned char bf_tkey[8]; /* blowfish ivec */ + int bf_n = 0; /* blowfish ivec pos */ + 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 != 8 + || !enif_inspect_iolist_as_binary(env, argv[2], &data_bin)) { + return enif_make_badarg(env); + } + + BF_set_key(&bf_key, key_bin.size, key_bin.data); + memcpy(bf_tkey, ivec_bin.data, 8); + BF_cfb64_encrypt(data_bin.data, enif_make_new_binary(env,data_bin.size,&ret), + data_bin.size, &bf_key, bf_tkey, &bf_n, + (argv[3] == atom_true ? BF_ENCRYPT : BF_DECRYPT)); + return ret; +} + +static ERL_NIF_TERM bf_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Ivec, Data, IsEncrypt) */ + ErlNifBinary key_bin, ivec_bin, data_bin; + BF_KEY bf_key; /* blowfish key 8 */ + unsigned char bf_tkey[8]; /* blowfish ivec */ + 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 != 8 + || !enif_inspect_iolist_as_binary(env, argv[2], &data_bin) + || data_bin.size % 8 != 0) { + return enif_make_badarg(env); + } + + BF_set_key(&bf_key, key_bin.size, key_bin.data); + memcpy(bf_tkey, ivec_bin.data, 8); + BF_cbc_encrypt(data_bin.data, enif_make_new_binary(env,data_bin.size,&ret), + data_bin.size, &bf_key, bf_tkey, + (argv[3] == atom_true ? BF_ENCRYPT : BF_DECRYPT)); + return ret; +} + +static ERL_NIF_TERM bf_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, IsEncrypt) */ + ErlNifBinary key_bin, data_bin; + BF_KEY bf_key; /* blowfish key 8 */ + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin) + || data_bin.size < 8) { + return enif_make_badarg(env); + } + BF_set_key(&bf_key, key_bin.size, key_bin.data); + BF_ecb_encrypt(data_bin.data, enif_make_new_binary(env,data_bin.size,&ret), + &bf_key, (argv[2] == atom_true ? BF_ENCRYPT : BF_DECRYPT)); + return ret; +} + +static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec, Data) */ + ErlNifBinary key_bin, ivec_bin, data_bin; + BF_KEY bf_key; /* blowfish key 8 */ + unsigned char bf_tkey[8]; /* blowfish ivec */ + int bf_n = 0; /* blowfish ivec pos */ + 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 != 8 + || !enif_inspect_iolist_as_binary(env, argv[2], &data_bin)) { + return enif_make_badarg(env); + } + + BF_set_key(&bf_key, key_bin.size, key_bin.data); + memcpy(bf_tkey, ivec_bin.data, 8); + BF_ofb64_encrypt(data_bin.data, enif_make_new_binary(env,data_bin.size,&ret), + data_bin.size, &bf_key, bf_tkey, &bf_n); + return ret; +} + + + +#ifdef OPENSSL_THREADS /* vvvvvvvvvvvvvvv OPENSSL_THREADS vvvvvvvvvvvvvvvv */ + +static INLINE void locking(int mode, ErlNifRWLock* lock) +{ + switch (mode) { + case CRYPTO_LOCK|CRYPTO_READ: + enif_rwlock_rlock(lock); + break; + case CRYPTO_LOCK|CRYPTO_WRITE: + enif_rwlock_rwlock(lock); + break; + case CRYPTO_UNLOCK|CRYPTO_READ: + enif_rwlock_runlock(lock); + break; + case CRYPTO_UNLOCK|CRYPTO_WRITE: + enif_rwlock_rwunlock(lock); + break; + default: + ASSERT(!"Invalid lock mode"); + } +} + +/* Callback from openssl for static locking + */ +static void locking_function(int mode, int n, const char *file, int line) +{ + ASSERT(n>=0 && n<CRYPTO_num_locks()); + + locking(mode, lock_vec[n]); +} + +/* Callback from openssl for thread id + */ +static unsigned long id_function(void) +{ + return(unsigned long) enif_thread_self(); +} + +/* Callbacks for dynamic locking, not used by current openssl version (0.9.8) + */ +static struct CRYPTO_dynlock_value* dyn_create_function(const char *file, int line) { + return(struct CRYPTO_dynlock_value*) enif_rwlock_create("crypto_dyn"); +} +static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value* ptr,const char *file, int line) +{ + locking(mode, (ErlNifRWLock*)ptr); +} +static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr, const char *file, int line) +{ + enif_rwlock_destroy((ErlNifRWLock*)ptr); +} + +#endif /* ^^^^^^^^^^^^^^^^^^^^^^ OPENSSL_THREADS ^^^^^^^^^^^^^^^^^^^^^^ */ + +/* HMAC */ + +static void hmac_md5(unsigned char *key, int klen, unsigned char *dbuf, int dlen, + unsigned char *hmacbuf) +{ + MD5_CTX ctx; + char ipad[HMAC_INT_LEN]; + char opad[HMAC_INT_LEN]; + unsigned char nkey[MD5_LEN]; + int i; + + /* Change key if longer than 64 bytes */ + if (klen > HMAC_INT_LEN) { + MD5(key, klen, nkey); + key = nkey; + klen = MD5_LEN; + } + + memset(ipad, '\0', sizeof(ipad)); + memset(opad, '\0', sizeof(opad)); + memcpy(ipad, key, klen); + memcpy(opad, key, klen); + + for (i = 0; i < HMAC_INT_LEN; i++) { + ipad[i] ^= HMAC_IPAD; + opad[i] ^= HMAC_OPAD; + } + + /* inner MD5 */ + MD5_Init(&ctx); + MD5_Update(&ctx, ipad, HMAC_INT_LEN); + MD5_Update(&ctx, dbuf, dlen); + MD5_Final((unsigned char *) hmacbuf, &ctx); + /* outer MD5 */ + MD5_Init(&ctx); + MD5_Update(&ctx, opad, HMAC_INT_LEN); + MD5_Update(&ctx, hmacbuf, MD5_LEN); + MD5_Final((unsigned char *) hmacbuf, &ctx); +} + +static void hmac_sha1(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf) +{ + SHA_CTX ctx; + char ipad[HMAC_INT_LEN]; + char opad[HMAC_INT_LEN]; + unsigned char nkey[SHA_LEN]; + int i; + + /* Change key if longer than 64 bytes */ + if (klen > HMAC_INT_LEN) { + SHA1(key, klen, nkey); + key = nkey; + klen = SHA_LEN; + } + + memset(ipad, '\0', sizeof(ipad)); + memset(opad, '\0', sizeof(opad)); + memcpy(ipad, key, klen); + memcpy(opad, key, klen); + + for (i = 0; i < HMAC_INT_LEN; i++) { + ipad[i] ^= HMAC_IPAD; + opad[i] ^= HMAC_OPAD; + } + + /* inner SHA */ + SHA1_Init(&ctx); + SHA1_Update(&ctx, ipad, HMAC_INT_LEN); + SHA1_Update(&ctx, dbuf, dlen); + SHA1_Final((unsigned char *) hmacbuf, &ctx); + /* outer SHA */ + SHA1_Init(&ctx); + SHA1_Update(&ctx, opad, HMAC_INT_LEN); + SHA1_Update(&ctx, hmacbuf, SHA_LEN); + SHA1_Final((unsigned char *) hmacbuf, &ctx); +} + diff --git a/lib/crypto/c_src/crypto_drv.c b/lib/crypto/c_src/crypto_drv.c deleted file mode 100644 index 8c1356c4cf..0000000000 --- a/lib/crypto/c_src/crypto_drv.c +++ /dev/null @@ -1,1817 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 1999-2010. All Rights Reserved. - * - * The contents of this file are subject to the Erlang Public License, - * Version 1.1, (the "License"); you may not use this file except in - * compliance with the License. You should have received a copy of the - * Erlang Public License along with this software. If not, it can be - * retrieved online at http://www.erlang.org/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and limitations - * under the License. - * - * %CopyrightEnd% - */ - -/* - * Purpose: Dynamically loadable driver for cryptography libraries. - * Based on OpenSSL. - */ - -#ifdef __WIN32__ -#include <windows.h> -#endif - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include "erl_driver.h" - -#define OPENSSL_THREAD_DEFINES -#include <openssl/opensslconf.h> -#ifndef OPENSSL_THREADS -# ifdef __GNUC__ -# warning No thread support by openssl. Driver will use coarse grain locking. -# endif -#endif - -#include <openssl/crypto.h> -#include <openssl/des.h> -/* #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/bn.h> -#include <openssl/objects.h> -#include <openssl/rc4.h> -#include <openssl/rc2.h> -#include <openssl/blowfish.h> -#include <openssl/rand.h> - -#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 escapes the driver. -*/ -# define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size) \ - VALGRIND_MAKE_MEM_DEFINED(ptr,size) - -# define ERL_VALGRIND_ASSERT_MEM_DEFINED(ptr,size) \ - ((void) ((VALGRIND_CHECK_MEM_IS_DEFINED(ptr,size) == 0) ? 1 : \ - (fprintf(stderr,"\r\n####### VALGRIND_ASSSERT(%p,%d) failed at %s:%d\r\n",\ - (ptr),(size), __FILE__, __LINE__), abort(), 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);\ -} - -/* Driver interface declarations */ -static int init(void); -static void finish(void); -static ErlDrvData start(ErlDrvPort port, char *command); -static void stop(ErlDrvData drv_data); -static int crypto_control(ErlDrvData drv_data, unsigned int command, char *buf, - int len, char **rbuf, int rlen); - -/* openssl callbacks */ -#ifdef OPENSSL_THREADS -static void locking_function(int mode, int n, const char *file, int line); -static unsigned long id_function(void); -static struct CRYPTO_dynlock_value* dyn_create_function(const char *file, - int line); -static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value* ptr, - const char *file, int line); -static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr, - const char *file, int line); -#endif /* OPENSSL_THREADS */ - -/* helpers */ -static void hmac_md5(char *key, int klen, char *dbuf, int dlen, - char *hmacbuf); -static void hmac_sha1(char *key, int klen, char *dbuf, int dlen, - char *hmacbuf); - -static ErlDrvEntry crypto_driver_entry = { - init, - start, - stop, - NULL, /* output */ - NULL, /* ready_input */ - NULL, /* ready_output */ - "crypto_drv", - finish, - NULL, /* handle */ - crypto_control, - NULL, /* timeout */ - NULL, /* outputv */ - - NULL, /* ready_async */ - NULL, /* flush */ - NULL, /* call */ - NULL, /* event */ - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, -#ifdef OPENSSL_THREADS - ERL_DRV_FLAG_USE_PORT_LOCKING, -#else - 0, -#endif - NULL, /* handle2 */ - NULL /* process_exit */ -}; - - -/* Keep the following definitions in alignment with the FUNC_LIST - * in crypto.erl. - */ - -#define DRV_INFO 0 -#define DRV_MD5 1 -#define DRV_MD5_INIT 2 -#define DRV_MD5_UPDATE 3 -#define DRV_MD5_FINAL 4 -#define DRV_SHA 5 -#define DRV_SHA_INIT 6 -#define DRV_SHA_UPDATE 7 -#define DRV_SHA_FINAL 8 -#define DRV_MD5_MAC 9 -#define DRV_MD5_MAC_96 10 -#define DRV_SHA_MAC 11 -#define DRV_SHA_MAC_96 12 -#define DRV_CBC_DES_ENCRYPT 13 -#define DRV_CBC_DES_DECRYPT 14 -#define DRV_EDE3_CBC_DES_ENCRYPT 15 -#define DRV_EDE3_CBC_DES_DECRYPT 16 -#define DRV_AES_CFB_128_ENCRYPT 17 -#define DRV_AES_CFB_128_DECRYPT 18 -#define DRV_RAND_BYTES 19 -#define DRV_RAND_UNIFORM 20 -#define DRV_MOD_EXP 21 -#define DRV_DSS_VERIFY 22 -#define DRV_RSA_VERIFY_SHA 23 -/* #define DRV_RSA_VERIFY_MD5 35 */ -#define DRV_CBC_AES128_ENCRYPT 24 -#define DRV_CBC_AES128_DECRYPT 25 -#define DRV_XOR 26 -#define DRV_RC4_ENCRYPT 27 /* no decrypt needed; symmetric */ -#define DRV_RC4_SETKEY 28 -#define DRV_RC4_ENCRYPT_WITH_STATE 29 -#define DRV_CBC_RC2_40_ENCRYPT 30 -#define DRV_CBC_RC2_40_DECRYPT 31 -#define DRV_CBC_AES256_ENCRYPT 32 -#define DRV_CBC_AES256_DECRYPT 33 -#define DRV_INFO_LIB 34 -/* #define DRV_RSA_VERIFY_SHA 23 */ -#define DRV_RSA_VERIFY_MD5 35 -#define DRV_RSA_SIGN_SHA 36 -#define DRV_RSA_SIGN_MD5 37 -#define DRV_DSS_SIGN 38 -#define DRV_RSA_PUBLIC_ENCRYPT 39 -#define DRV_RSA_PRIVATE_DECRYPT 40 -#define DRV_RSA_PRIVATE_ENCRYPT 41 -#define DRV_RSA_PUBLIC_DECRYPT 42 -#define DRV_DH_GENERATE_PARAMS 43 -#define DRV_DH_CHECK 44 -#define DRV_DH_GENERATE_KEY 45 -#define DRV_DH_COMPUTE_KEY 46 -#define DRV_MD4 47 -#define DRV_MD4_INIT 48 -#define DRV_MD4_UPDATE 49 -#define DRV_MD4_FINAL 50 - -#define SSL_VERSION_0_9_8 0 -#if SSL_VERSION_0_9_8 -#define DRV_SHA256 51 -#define DRV_SHA256_INIT 52 -#define DRV_SHA256_UPDATE 53 -#define DRV_SHA256_FINAL 54 -#define DRV_SHA512 55 -#define DRV_SHA512_INIT 56 -#define DRV_SHA512_UPDATE 57 -#define DRV_SHA512_FINAL 58 -#endif - -#define DRV_BF_CFB64_ENCRYPT 59 -#define DRV_BF_CFB64_DECRYPT 60 -#define DRV_BF_ECB_ENCRYPT 61 -#define DRV_BF_ECB_DECRYPT 62 -#define DRV_BF_OFB64_ENCRYPT 63 -#define DRV_BF_CBC_ENCRYPT 64 -#define DRV_BF_CBC_DECRYPT 65 - -#define DRV_ECB_DES_ENCRYPT 66 -#define DRV_ECB_DES_DECRYPT 67 - -/* #define DRV_CBC_IDEA_ENCRYPT 34 */ -/* #define DRV_CBC_IDEA_DECRYPT 35 */ - -/* Not DRV_DH_GENERATE_PARAMS DRV_DH_CHECK - * Calc RSA_VERIFY_* and RSA_SIGN once */ -#define NUM_CRYPTO_FUNCS 48 - -#define MD5_CTX_LEN (sizeof(MD5_CTX)) -#define MD5_LEN 16 -#define MD5_LEN_96 12 -#define MD4_CTX_LEN (sizeof(MD4_CTX)) -#define MD4_LEN 16 -#define SHA_CTX_LEN (sizeof(SHA_CTX)) -#define SHA_LEN 20 -#define SHA_LEN_96 12 -#define HMAC_INT_LEN 64 - -#define HMAC_IPAD 0x36 -#define HMAC_OPAD 0x5c - -#if SSL_VERSION_0_9_8 -#define SHA256_CTX_LEN (sizeof(SHA256_CTX)) -#define SHA256_LEN 32 - -#define SHA512_CTX_LEN (sizeof(SHA512_CTX)) -#define SHA512_LEN 64 -#endif - -/* INITIALIZATION AFTER LOADING */ - -/* - * This is the init function called after this driver has been loaded. - * It must *not* be declared static. Must return the address to - * the driver entry. - */ - -#if !defined(__WIN32__) -DRIVER_INIT(crypto_drv); -#endif - -DRIVER_INIT(crypto_drv) -{ - return &crypto_driver_entry; -} - -static ErlDrvRWLock** lock_vec = NULL; /* Static locks used by openssl */ - -/* DRIVER INTERFACE */ - -static int init(void) -{ - ErlDrvSysInfo sys_info; - int i; - - CRYPTO_set_mem_functions(driver_alloc, driver_realloc, driver_free); - -#ifdef OPENSSL_THREADS - driver_system_info(&sys_info, sizeof(sys_info)); - - if(sys_info.scheduler_threads > 1) { - lock_vec = driver_alloc(CRYPTO_num_locks()*sizeof(*lock_vec)); - if (lock_vec==NULL) return -1; - memset(lock_vec,0,CRYPTO_num_locks()*sizeof(*lock_vec)); - - for(i=CRYPTO_num_locks()-1; i>=0; --i) { - lock_vec[i] = erl_drv_rwlock_create("crypto_drv_stat"); - if (lock_vec[i]==NULL) return -1; - } - CRYPTO_set_locking_callback(locking_function); - CRYPTO_set_id_callback(id_function); - CRYPTO_set_dynlock_create_callback(dyn_create_function); - CRYPTO_set_dynlock_lock_callback(dyn_lock_function); - CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function); - } - /* else no need for locks */ -#endif /* OPENSSL_THREADS */ - - return 0; -} - -static void finish(void) -{ - /* Moved here from crypto_control() as it's not thread safe */ - CRYPTO_cleanup_all_ex_data(); - - if(lock_vec != NULL) { - int i; - for(i=CRYPTO_num_locks()-1; i>=0; --i) { - if (lock_vec[i] != NULL) { - erl_drv_rwlock_destroy(lock_vec[i]); - } - } - driver_free(lock_vec); - } -} - -static ErlDrvData start(ErlDrvPort port, char *command) -{ - set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); - return 0; /* not used */ -} - -static void stop(ErlDrvData drv_data) -{ - return; -} - -/* Helper functions for 'crypto_control' -*/ -static INLINE unsigned char* return_binary(char **rbuf, int rlen, int len) -{ - if (len <= rlen) { - return (unsigned char *) *rbuf; - } - else { - ErlDrvBinary* bin; - *rbuf = (char*) (bin = driver_alloc_binary(len)); - return (bin==NULL) ? NULL : (unsigned char *) bin->orig_bytes; - } -} - -static INLINE unsigned char* return_binary_shrink(char **rbuf, int rlen, unsigned char* data, int len) -{ - if ((char *) data == *rbuf) { /* default buffer */ - ASSERT(len <= rlen); - return (unsigned char *) data; - } - else { - ErlDrvBinary* bin = (ErlDrvBinary*) *rbuf; - *rbuf = (char*) (bin=driver_realloc_binary(bin, len)); - return (bin==NULL) ? NULL : (unsigned char *) bin->orig_bytes; - } -} - -/* Nowadays (R13) it does matter what value control returns - * as it may return data in default buffer. - */ -static int crypto_control(ErlDrvData drv_data, unsigned int command, char *buf, - int len, char **rbuf, int rlen) -{ - int klen, dlen, macsize, from_len, to_len, i; - int base_len, exponent_len, modulo_len; - int data_len, dsa_p_len, dsa_q_len; - int dsa_s_len, dsa_g_len, dsa_y_len; - int rsa_e_len, rsa_n_len, rsa_d_len, padding; - int or_mask; - int prime_len, generator; - int privkey_len, pubkey_len, dh_p_len, dh_g_len; - unsigned int rsa_s_len, j; - char *key, *key2, *dbuf; - unsigned char *p; - const_DES_cblock *des_key, *des_key2, *des_key3; - const unsigned char *des_dbuf; - BIGNUM *bn_from, *bn_to, *bn_rand, *bn_result; - BIGNUM *bn_base, *bn_exponent, *bn_modulo; - BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_y; - BIGNUM *rsa_n, *rsa_e, *rsa_d; - BIGNUM *dh_p, *dh_g, *privkey, *pubkey; - DES_cblock *des_ivec; - unsigned char* bin; - DES_key_schedule schedule, schedule2, schedule3; - DH *dh_params; -/* IDEA_KEY_SCHEDULE idea, idea2; */ - char hmacbuf[SHA_DIGEST_LENGTH]; - unsigned char *rsa_s, *dsa_s; - /* char hmacbuf[SHA_LEN]; */ -#if SSL_VERSION_0_9_8 - SHA256_CTX sha256_ctx; - SHA512_CTX sha512_ctx; -#endif - MD5_CTX md5_ctx; - MD4_CTX md4_ctx; - SHA_CTX sha_ctx; - int new_ivlen = 0; - BN_CTX *bn_ctx; - DSA *dsa; - RSA *rsa; - AES_KEY aes_key; - RC4_KEY rc4_key; - RC2_KEY rc2_key; - - switch(command) { - - case DRV_INFO: - bin = return_binary(rbuf,rlen,NUM_CRYPTO_FUNCS); - if (bin==NULL) return -1; - - for (i = 0; i < NUM_CRYPTO_FUNCS; i++) { - bin[i] = i + 1; - } - return NUM_CRYPTO_FUNCS; - - case DRV_MD5: - bin = return_binary(rbuf,rlen,MD5_LEN); - if (bin==NULL) return -1; - MD5((unsigned char *) buf, len, bin); - return MD5_LEN; - - case DRV_MD5_INIT: - bin = return_binary(rbuf,rlen,MD5_CTX_LEN); - if (bin==NULL) return -1; - MD5_Init((MD5_CTX *)bin); - return MD5_CTX_LEN; - - case DRV_MD5_UPDATE: - if (len < MD5_CTX_LEN) - return -1; - bin = return_binary(rbuf,rlen,MD5_CTX_LEN); - if (bin==NULL) return -1; - memcpy(bin, buf, MD5_CTX_LEN); - MD5_Update((MD5_CTX *)bin, buf + MD5_CTX_LEN, - len - MD5_CTX_LEN); - return MD5_CTX_LEN; - - case DRV_MD5_FINAL: - if (len != MD5_CTX_LEN) - return -1; - memcpy(&md5_ctx, buf, MD5_CTX_LEN); /* XXX Use buf only? */ - bin = return_binary(rbuf,rlen,MD5_LEN); - if (bin==NULL) return -1; - MD5_Final(bin, &md5_ctx); - return MD5_LEN; - - case DRV_SHA: - bin = return_binary(rbuf,rlen,SHA_LEN); - if (bin==NULL) return -1; - SHA1((unsigned char *) buf, len, bin); - return SHA_LEN; - - case DRV_SHA_INIT: - bin = return_binary(rbuf,rlen,SHA_CTX_LEN); - if (bin==NULL) return -1; - SHA1_Init((SHA_CTX*)bin); - return SHA_CTX_LEN; - - case DRV_SHA_UPDATE: - if (len < SHA_CTX_LEN) - return -1; - bin = return_binary(rbuf,rlen,SHA_CTX_LEN); - if (bin==NULL) return -1; - memcpy(bin, buf, SHA_CTX_LEN); - SHA1_Update((SHA_CTX*)bin, buf + SHA_CTX_LEN, len - SHA_CTX_LEN); - return SHA_CTX_LEN; - - case DRV_SHA_FINAL: - if (len != SHA_CTX_LEN) - return -1; - memcpy(&sha_ctx, buf, SHA_CTX_LEN); /* XXX Use buf only? */ - bin = return_binary(rbuf,rlen,SHA_LEN); - if (bin==NULL) return -1; - SHA1_Final(bin, &sha_ctx); - return SHA_LEN; - - case DRV_MD5_MAC: - case DRV_MD5_MAC_96: - /* buf = klen[4] key data */ - klen = get_int32(buf); - key = buf + 4; - dlen = len - klen - 4; - dbuf = key + klen; - hmac_md5(key, klen, dbuf, dlen, hmacbuf); - macsize = (command == DRV_MD5_MAC) ? MD5_LEN : MD5_LEN_96; - bin = return_binary(rbuf,rlen,macsize); - if (bin==NULL) return -1; - memcpy(bin, hmacbuf, macsize); - return macsize; - - case DRV_SHA_MAC: - case DRV_SHA_MAC_96: - /* buf = klen[4] key data */ - klen = get_int32(buf); - key = buf + 4; - dlen = len - klen - 4; - dbuf = key + klen; - hmac_sha1(key, klen, dbuf, dlen, hmacbuf); - macsize = (command == DRV_SHA_MAC) ? SHA_LEN : SHA_LEN_96; - bin = return_binary(rbuf,rlen,macsize); - if (bin==NULL) return -1; - memcpy(bin, (unsigned char *) hmacbuf, macsize); - return macsize; - - case DRV_CBC_DES_ENCRYPT: - case DRV_CBC_DES_DECRYPT: - /* buf = key[8] ivec[8] data */ - dlen = len - 16; - if (dlen < 0) - return -1; - if (dlen % 8 != 0) - return -1; - des_key = (const_DES_cblock*) buf; - des_ivec = (DES_cblock*)(buf + 8); - des_dbuf = (unsigned char *) (buf + 16); - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - DES_set_key(des_key, &schedule); - DES_ncbc_encrypt(des_dbuf, bin, dlen, &schedule, des_ivec, - (command == DRV_CBC_DES_ENCRYPT)); - return dlen; - - case DRV_ECB_DES_ENCRYPT: - case DRV_ECB_DES_DECRYPT: - /* buf = key[8] data */ - dlen = len - 8; - if (dlen != 8) - return -1; - des_key = (const_DES_cblock*) buf; - des_dbuf = (unsigned char *) (buf + 8); - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - DES_set_key(des_key, &schedule); - DES_ecb_encrypt((const_DES_cblock*) des_dbuf, (DES_cblock*) bin, &schedule, - (command == DRV_ECB_DES_ENCRYPT)); - return dlen; - - case DRV_BF_ECB_ENCRYPT: - case DRV_BF_ECB_DECRYPT: - { - /* buf = klen[4] key data */ - int bf_direction; - const unsigned char *ukey; - const unsigned char *bf_dbuf; /* blowfish input data */ - BF_KEY bf_key; /* blowfish key 8 */ - - klen = get_int32(buf); - ukey = (unsigned char *) buf + 4; - bf_dbuf = ukey + klen; - dlen = len - 4 - klen; - if (dlen < 0) return -1; - BF_set_key(&bf_key, klen, ukey); - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - bf_direction = command == DRV_BF_ECB_ENCRYPT ? BF_ENCRYPT : BF_DECRYPT; - BF_ecb_encrypt(bf_dbuf, bin, &bf_key, bf_direction); - return dlen; - } - - case DRV_BF_CBC_ENCRYPT: - case DRV_BF_CBC_DECRYPT: - { - /* buf = klen[4] key ivec[8] data */ - unsigned char *ukey; - unsigned char* ivec; - unsigned char bf_tkey[8]; /* blowfish ivec */ - int bf_direction; - const unsigned char *bf_dbuf; /* blowfish input data */ - BF_KEY bf_key; /* blowfish key 8 */ - - klen = get_int32(buf); - ukey = (unsigned char *)buf + 4; - ivec = ukey + klen; - bf_dbuf = ivec + 8; - dlen = len - 4 - klen - 8; - if (dlen < 0) return -1; - BF_set_key(&bf_key, klen, ukey); - memcpy(bf_tkey, ivec, 8); - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - bf_direction = command == DRV_BF_CBC_ENCRYPT ? BF_ENCRYPT : BF_DECRYPT; - BF_cbc_encrypt(bf_dbuf, bin, dlen, &bf_key, bf_tkey, bf_direction); - return dlen; - } - - case DRV_BF_OFB64_ENCRYPT: - { - /* buf = klen[4] key ivec[8] data */ - unsigned char *ukey; - unsigned char* ivec; - unsigned char bf_tkey[8]; /* blowfish ivec */ - int bf_n; /* blowfish ivec pos */ - const unsigned char *bf_dbuf; /* blowfish input data */ - BF_KEY bf_key; /* blowfish key 8 */ - - klen = get_int32(buf); - ukey = (unsigned char *)buf + 4; - ivec = ukey + klen; - bf_dbuf = ivec + 8; - dlen = len - 4 - klen - 8; - if (dlen < 0) return -1; - BF_set_key(&bf_key, klen, ukey); - memcpy(bf_tkey, ivec, 8); - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - bf_n = 0; - BF_ofb64_encrypt(bf_dbuf, bin, dlen, &bf_key, bf_tkey, &bf_n); - return dlen; - } - - case DRV_BF_CFB64_ENCRYPT: - case DRV_BF_CFB64_DECRYPT: - { - /* buf = klen[4] key ivec[8] data */ - unsigned char* ivec; - unsigned char bf_tkey[8]; /* blowfish ivec */ - int bf_n; /* blowfish ivec pos */ - int bf_direction; - const unsigned char *bf_dbuf; /* blowfish input data */ - BF_KEY bf_key; /* blowfish key 8 */ - - klen = get_int32(buf); - key = buf + 4; - ivec = (unsigned char *) (key + klen); - bf_dbuf = ivec + 8; - dlen = len - 4 - klen - 8; - if (dlen < 0) return -1; - BF_set_key(&bf_key, klen, (unsigned char *) key); - memcpy(bf_tkey, ivec, 8); - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - bf_direction = command == DRV_BF_CFB64_ENCRYPT ? BF_ENCRYPT : BF_DECRYPT; - bf_n = 0; - BF_cfb64_encrypt(bf_dbuf, bin, dlen, &bf_key, bf_tkey, &bf_n, bf_direction); - return dlen; - } - -/* case DRV_CBC_IDEA_ENCRYPT: */ -/* case DRV_CBC_IDEA_DECRYPT: */ - /* buf = key[16] ivec[8] data */ -/* dlen = len - 24; */ -/* if (dlen < 0) */ -/* return -1; */ -/* if (dlen % 8 != 0) */ -/* return -1; */ -/* bin = return_binary(rbuf,rlen,dlen); */ -/* idea_set_encrypt_key(buf, &idea); */ -/* if (command == DRV_CBC_IDEA_DECRYPT) { */ -/* idea_set_decrypt_key(&idea, &idea2); */ -/* memcpy(&idea, &idea2, sizeof(idea)); */ -/* } */ -/* idea_cbc_encrypt(buf + 24, bin, dlen, &idea, buf + 8, */ -/* (command == DRV_CBC_IDEA_ENCRYPT)); */ -/* return dlen; */ - - case DRV_CBC_RC2_40_ENCRYPT: - case DRV_CBC_RC2_40_DECRYPT: - /* buf = key[5] ivec[8] data */ - dlen = len - 13; - if (dlen < 0) - return -1; - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - RC2_set_key(&rc2_key, 5, (unsigned char *) buf, 40); - RC2_cbc_encrypt((unsigned char *) (buf + 13), bin, dlen, &rc2_key, - (unsigned char *) (buf + 5), - (command == DRV_CBC_RC2_40_ENCRYPT)); - return dlen; - - case DRV_EDE3_CBC_DES_ENCRYPT: - case DRV_EDE3_CBC_DES_DECRYPT: - dlen = len - 32; - if (dlen < 0) - return -1; - des_key = (const_DES_cblock*) buf; - des_key2 = (const_DES_cblock*) (buf + 8); - des_key3 = (const_DES_cblock*) (buf + 16); - des_ivec = (DES_cblock*) (buf + 24); - des_dbuf = (unsigned char *) (buf + 32); - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - DES_set_key(des_key, &schedule); - DES_set_key(des_key2, &schedule2); - DES_set_key(des_key3, &schedule3); - DES_ede3_cbc_encrypt(des_dbuf, bin, dlen, &schedule, - &schedule2, &schedule3, des_ivec, - (command == DRV_EDE3_CBC_DES_ENCRYPT)); - return dlen; - - case DRV_AES_CFB_128_ENCRYPT: - case DRV_AES_CFB_128_DECRYPT: - /* buf = key[16] ivec[16] data */ - dlen = len - 32; - if (dlen < 0) - return -1; - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - AES_set_encrypt_key((unsigned char *) buf, 128, &aes_key); - AES_cfb128_encrypt((unsigned char *) (buf+32), bin, dlen, &aes_key, - (unsigned char *) (buf+16), &new_ivlen, - (command == DRV_AES_CFB_128_ENCRYPT)); - return dlen; - - case DRV_RC4_ENCRYPT: - /* buf = klen[4] key data */ - klen = get_int32(buf); - key = buf + 4; - dlen = len - klen - 4; - dbuf = key + klen; - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - RC4_set_key(&rc4_key, klen, (unsigned char *) key); - RC4(&rc4_key, dlen, (unsigned char *) dbuf, bin); - return dlen; - - case DRV_RC4_SETKEY: - /* buf = key */ - dlen = sizeof(rc4_key); - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - RC4_set_key(&rc4_key, len, (unsigned char *) buf); - memcpy(bin, &rc4_key, dlen); - return dlen; - - case DRV_RC4_ENCRYPT_WITH_STATE: - /* buf = statelength[4] state data, return statelength[4] state data */ - klen = get_int32(buf); - key = buf + 4; - dlen = len - klen - 4; - dbuf = key + klen; - bin = return_binary(rbuf,rlen,len); - if (bin==NULL) return -1; - memcpy(&rc4_key, key, klen); - RC4(&rc4_key, dlen, (unsigned char *) dbuf, bin + klen + 4); - memcpy(bin, buf, 4); - memcpy(bin + 4, &rc4_key, klen); - return len; - - case DRV_RAND_BYTES: - /* buf = <<rlen:32/integer,topmask:8/integer,bottommask:8/integer>> */ - - if (len != 6) - return -1; - dlen = get_int32(buf); - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - RAND_pseudo_bytes(bin,dlen); - ERL_VALGRIND_MAKE_MEM_DEFINED(bin, dlen); - or_mask = ((unsigned char*)buf)[4]; - bin[dlen-1] |= or_mask; /* topmask */ - or_mask = ((unsigned char*)buf)[5]; - bin[0] |= or_mask; /* bottommask */ - return dlen; - - case DRV_RAND_UNIFORM: - /* buf = <<from_len:32/integer,bn_from:from_len/binary, * - * to_len:32/integer,bn_to:to_len/binary>> */ - if (len < 8) - return -1; - from_len = get_int32(buf); - if (len < (8 + from_len)) - return -1; - to_len = get_int32(buf + 4 + from_len); - if (len != (8 + from_len + to_len)) - return -1; - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf, 4 + from_len + 4 + to_len); - bn_from = BN_new(); - BN_bin2bn((unsigned char *)(buf + 4), from_len, bn_from); - bn_rand = BN_new(); - BN_bin2bn((unsigned char *)(buf + 8 + from_len), to_len, bn_rand); - 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); - bin = return_binary(rbuf,rlen,dlen + 4); - if (bin==NULL) return -1; - put_int32(bin, dlen); - BN_bn2bin(bn_rand,(unsigned char*)(bin + 4)); - ERL_VALGRIND_MAKE_MEM_DEFINED(bin+4, dlen); - BN_free(bn_rand); - BN_free(bn_from); - BN_free(bn_to); - return dlen + 4; - - case DRV_MOD_EXP: - /* buf = <<base_len:32/integer,base/binary, * - * exponent_len:32/integer,exponent/binary, * - * modulo_len:32/integer, modulo/binary>> */ - if (len < 12) - return -1; - base_len = get_int32(buf); - if (len < (12 + base_len)) - return -1; - exponent_len = get_int32(buf + 4 + base_len); - if (len < (12 + base_len + exponent_len)) - return -1; - modulo_len = get_int32(buf + 8 + base_len + exponent_len); - if (len != (12 + base_len + exponent_len + modulo_len)) - return -1; - bn_base = BN_new(); - BN_bin2bn((unsigned char *)(buf + 4), - base_len, bn_base); - bn_exponent = BN_new(); - BN_bin2bn((unsigned char *)(buf + 8 + base_len), - exponent_len, bn_exponent); - bn_modulo = BN_new(); - BN_bin2bn((unsigned char *)(buf + 12 + base_len + exponent_len), - modulo_len, bn_modulo); - 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); - bin = return_binary(rbuf,rlen,dlen + 4); - if (bin==NULL) return -1; - put_int32(bin, dlen); - BN_bn2bin(bn_result,(unsigned char*)(bin + 4)); - BN_free(bn_result); - BN_free(bn_modulo); - BN_free(bn_exponent); - BN_free(bn_base); - BN_CTX_free(bn_ctx); - return dlen + 4; - - case DRV_DSS_VERIFY: - /* buf = <<data_len:32/integer, data:data_len/binary, - * dsa_s_len:32/integer, dsa_s:dsa_s_len/binary, - * dsa_p_len:32/integer, dsa_p:dsa_p_len/binary, - * dsa_q_len:32/integer, dsa_q:dsa_q_len/binary, - * dsa_g_len:32/integer, dsa_g:dsa_g_len/binary, - * dsa_y_len:32/integer, dsa_y:dsa_y_len/binary>> */ - i = 0; - j = 0; - if (len < 24) - return -1; - data_len = get_int32(buf + i + j); - j += data_len; i += 4; - if (len < (24 + j)) - return -1; - dsa_s_len = get_int32(buf + i + j); - j += dsa_s_len; i += 4; - if (len < (24 + j)) - return -1; - dsa_p_len = get_int32(buf + i + j); - j += dsa_p_len; i += 4; - if (len < (24 + j)) - return -1; - dsa_q_len = get_int32(buf + i + j); - j += dsa_q_len; i += 4; - if (len < (24 + j)) - return -1; - dsa_g_len = get_int32(buf + i + j); - j += dsa_g_len; i += 4; - if (len < (24 + j)) - return -1; - dsa_y_len = get_int32(buf + i + j); - j += dsa_y_len; - if (len != (24 + j)) - return -1; - i = 4; - SHA1((unsigned char *) (buf + i), data_len, (unsigned char *) hmacbuf); - i += data_len + 4; - dsa_s = (unsigned char *)(buf + i); - i += (dsa_s_len + 4); - dsa_p = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dsa_p_len, dsa_p); - i += (dsa_p_len + 4); - dsa_q = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dsa_q_len, dsa_q); - i += (dsa_q_len + 4); - dsa_g = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dsa_g_len, dsa_g); - i += (dsa_g_len + 4); - dsa_y = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dsa_y_len, dsa_y); - dsa = DSA_new(); - dsa->p = dsa_p; - dsa->q = dsa_q; - dsa->g = dsa_g; - dsa->priv_key = NULL; - dsa->pub_key = dsa_y; - i = DSA_verify(0, (unsigned char *) hmacbuf, SHA_DIGEST_LENGTH, - dsa_s, dsa_s_len, dsa); - bin = return_binary(rbuf,rlen,1); - if (bin==NULL) return -1; - - DSA_free(dsa); - bin[0] = (i > 0) ? 1 : 0; - return 1; - - case DRV_DSS_SIGN: - /* buf = <<data_len:32/integer, data:data_len/binary, - * dsa_p_len:32/integer, dsa_p:dsa_p_len/binary, - * dsa_q_len:32/integer, dsa_q:dsa_q_len/binary, - * dsa_g_len:32/integer, dsa_g:dsa_g_len/binary, - * dsa_y_len:32/integer, dsa_y:dsa_y_len/binary, - * dsa_x_len:32/integer, dsa_s:dsa_x_len/binary>> */ - i = 0; - j = 0; - if (len < 20) - return -1; - data_len = get_int32(buf + i + j); - j += data_len; i += 4; - if (len < (20 + j)) - return -1; - dsa_p_len = get_int32(buf + i + j); - j += dsa_p_len; i += 4; - if (len < (20 + j)) - return -1; - dsa_q_len = get_int32(buf + i + j); - j += dsa_q_len; i += 4; - if (len < (20 + j)) - return -1; - dsa_g_len = get_int32(buf + i + j); - j += dsa_g_len; i += 4; - if (len < (20 + j)) - return -1; - dsa_y_len = get_int32(buf + i + j); - j += dsa_y_len; - if (len < (20 + j)) - return -1; - if (len != (20 + j)) - return -1; - - i = 4; - SHA1((unsigned char *) (buf + i), data_len, (unsigned char *) hmacbuf); - i += data_len + 4; - dsa_p = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dsa_p_len, dsa_p); - i += (dsa_p_len + 4); - dsa_q = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dsa_q_len, dsa_q); - i += (dsa_q_len + 4); - dsa_g = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dsa_g_len, dsa_g); - i += (dsa_g_len + 4); - dsa_y = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dsa_y_len, dsa_y); - /* i += (dsa_y_len + 4); */ - - dsa = DSA_new(); - dsa->p = dsa_p; - dsa->q = dsa_q; - dsa->g = dsa_g; - dsa->priv_key = dsa_y; - dsa->pub_key = NULL; - dlen = DSA_size(dsa); - bin = return_binary(rbuf,rlen, dlen+1); - if (bin==NULL) return -1; - i = DSA_sign(NID_sha1, - (unsigned char *) hmacbuf,SHA_DIGEST_LENGTH, - (unsigned char *) &bin[1], - (unsigned int *) &dsa_s_len, dsa); - DSA_free(dsa); - if (i) { - if (dsa_s_len != dlen) { - bin = return_binary_shrink(rbuf,rlen,bin,dsa_s_len+1); - } - bin[0] = 1; - return dsa_s_len + 1; - } - else { - bin[0] = 0; - return 1; - } - - case DRV_RSA_VERIFY_MD5: - case DRV_RSA_VERIFY_SHA: - /* buf = <<data_len:32/integer, data:data_len/binary, - * rsa_s_len:32/integer, rsa_s:rsa_s_len/binary, - * rsa_e_len:32/integer, rsa_e:rsa_e_len/binary, - * rsa_n_len:32/integer, rsa_n:rsa_n_len/binary>> */ - i = 0; - j = 0; - if (len < 16) - return -1; - data_len = get_int32(buf + i + j); - j += data_len; i += 4; - if (len < (16 + j)) - return -1; - rsa_s_len = get_int32(buf + i + j); - j += rsa_s_len; i += 4; - if (len < (16 + j)) - return -1; - rsa_e_len = get_int32(buf + i + j); - j += rsa_e_len; i += 4; - if (len < (16 + j)) - return -1; - rsa_n_len = get_int32(buf + i + j); - j += rsa_n_len; i += 4; - if (len != (16 + j)) - return -1; - i = 4; - i += (data_len + 4); - rsa_s = (unsigned char *)(buf + i); - i += (rsa_s_len + 4); - rsa_e = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), rsa_e_len, rsa_e); - i += (rsa_e_len + 4); - rsa_n = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), rsa_n_len, rsa_n); - rsa = RSA_new(); - rsa->n = rsa_n; - rsa->e = rsa_e; - i = 4; - if(command == DRV_RSA_VERIFY_SHA) { - SHA1((unsigned char *) (buf + i), data_len, - (unsigned char *) hmacbuf); - i = RSA_verify(NID_sha1, (unsigned char *) hmacbuf, SHA_DIGEST_LENGTH, - rsa_s, rsa_s_len, rsa); - } else { - MD5((unsigned char *) (buf + i), data_len, (unsigned char *) hmacbuf); - i = RSA_verify(NID_md5, (unsigned char *) hmacbuf, MD5_DIGEST_LENGTH, - rsa_s, rsa_s_len, rsa); - } - - bin = return_binary(rbuf,rlen,1); - if (bin==NULL) return -1; - bin[0] = (char)(i & 0xff); - RSA_free(rsa); - return 1; - - case DRV_RSA_SIGN_MD5: - case DRV_RSA_SIGN_SHA: - /* buf = <<data_len:32/integer, data:data_len/binary, - * rsa_e_len:32/integer, rsa_e:rsa_e_len/binary, - * rsa_n_len:32/integer, rsa_n:rsa_n_len/binary, - * rsa_d_len:32/integer, rsa_d:rsa_d_len/binary>> */ - - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf,len); - - i = 0; - j = 0; - - if (len < 16) - return -1; - data_len = get_int32(buf + i + j); - j += data_len; i += 4; - if (len < (16 + j)) - return -1; - rsa_e_len = get_int32(buf + i + j); - j += rsa_e_len; i += 4; - if (len < (16 + j)) - return -1; - rsa_n_len = get_int32(buf + i + j); - j += rsa_n_len; i += 4; - if (len < (16 + j)) - return -1; - rsa_d_len = get_int32(buf + i + j); - j += rsa_d_len; i += 4; - if (len != (16 + j)) - return -1; - - i = 4; - i += (data_len + 4); - rsa_e = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), rsa_e_len, rsa_e); - i += (rsa_e_len + 4); - rsa_n = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), rsa_n_len, rsa_n); - i += (rsa_n_len + 4); - rsa_d = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), rsa_d_len, rsa_d); - i += (rsa_d_len + 4); - - rsa = RSA_new(); - rsa->e = rsa_e; - rsa->n = rsa_n; - rsa->d = rsa_d; - - dlen = RSA_size(rsa); - bin = return_binary(rbuf,rlen,dlen+1); - if (bin==NULL) return -1; - i = 4; - if (command == DRV_RSA_SIGN_MD5) { - MD5((unsigned char *) (buf + i), data_len, (unsigned char *) hmacbuf); - ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, MD5_DIGEST_LENGTH); - i = RSA_sign(NID_md5, - (unsigned char *) hmacbuf,MD5_DIGEST_LENGTH, - (unsigned char *) &bin[1], - &rsa_s_len, rsa); - } else { - SHA1((unsigned char *) (buf + i), data_len, - (unsigned char *) hmacbuf); - ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, SHA_DIGEST_LENGTH); - i = RSA_sign(NID_sha1, - (unsigned char *) hmacbuf,SHA_DIGEST_LENGTH, - (unsigned char *) &bin[1], - &rsa_s_len, rsa); - } - RSA_free(rsa); - if (i) { - ERL_VALGRIND_MAKE_MEM_DEFINED(bin+1, rsa_s_len); - if (rsa_s_len != dlen) { - bin = return_binary_shrink(rbuf,rlen,bin,rsa_s_len+1); - ERL_VALGRIND_ASSERT_MEM_DEFINED(bin+1, rsa_s_len); - } - bin[0] = 1; - return rsa_s_len + 1; - } - else { - bin[0] = 0; - return 1; - } - - case DRV_RSA_PRIVATE_DECRYPT: - case DRV_RSA_PRIVATE_ENCRYPT: - /* buf = <<data_len:32/integer, data:data_len/binary, - * rsa_e_len:32/integer, rsa_e:rsa_e_len/binary, - * rsa_n_len:32/integer, rsa_n:rsa_n_len/binary, - * rsa_d_len:32/integer, rsa_d:rsa_d_len/binary, - * pad:8/integer >> */ - - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf,len); - i = 0; - j = 0; - - if (len < 17) - return -1; - data_len = get_int32(buf + i + j); - j += data_len; i += 4; - if (len < (17 + j)) - return -1; - rsa_e_len = get_int32(buf + i + j); - j += rsa_e_len; i += 4; - if (len < (17 + j)) - return -1; - rsa_n_len = get_int32(buf + i + j); - j += rsa_n_len; i += 4; - if (len < (17 + j)) - return -1; - rsa_d_len = get_int32(buf + i + j); - j += rsa_d_len; i += 4; - padding = *(unsigned char *) (buf+i+j); - if (len != (17 + j)) - return -1; - - i = 4; - i += (data_len + 4); - rsa_e = BN_new(); - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,rsa_e_len); - BN_bin2bn((unsigned char *)(buf + i), rsa_e_len, rsa_e); - i += (rsa_e_len + 4); - rsa_n = BN_new(); - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,rsa_n_len); - BN_bin2bn((unsigned char *)(buf + i), rsa_n_len, rsa_n); - i += (rsa_n_len + 4); - rsa_d = BN_new(); - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,rsa_d_len); - BN_bin2bn((unsigned char *)(buf + i), rsa_d_len, rsa_d); - i += (rsa_d_len + 4); - - switch(padding) { - case 0: - padding = RSA_NO_PADDING; - break; - case 1: - padding = RSA_PKCS1_PADDING; - break; - case 2: - padding = RSA_PKCS1_OAEP_PADDING; - break; - case 3: - padding = RSA_SSLV23_PADDING; - break; - default: - return -1; - } - - rsa = RSA_new(); - rsa->e = rsa_e; - rsa->n = rsa_n; - rsa->d = rsa_d; - - dlen = RSA_size(rsa) + 1; - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - i = 4; - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,data_len); - if(command == DRV_RSA_PRIVATE_DECRYPT) { - i = RSA_private_decrypt(data_len, (unsigned char *) (buf+i), - (unsigned char *) &bin[1], - rsa, padding); - if(i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(&bin[1],i); - bin = return_binary_shrink(rbuf,rlen, bin, i+1); - if (bin==NULL) return -1; - } - } else { - i = RSA_private_encrypt(data_len, (unsigned char *) (buf+i), - (unsigned char *) &bin[1], - rsa, padding); - if(i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(&bin[1],i); - } - } - RSA_free(rsa); - if(i > 0) { - bin[0] = 1; - return i + 1; - } else { - bin[0] = 0; - return 1; - } - break; - - case DRV_RSA_PUBLIC_ENCRYPT: - case DRV_RSA_PUBLIC_DECRYPT: - /* buf = <<data_len:32/integer, data:data_len/binary, - * rsa_e_len:32/integer, rsa_e:rsa_e_len/binary, - * rsa_n_len:32/integer, rsa_n:rsa_n_len/binary, - * pad:8/integer >> */ - - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf,len); - i = 0; - j = 0; - - if (len < 13) - return -1; - data_len = get_int32(buf + i + j); - j += data_len; i += 4; - if (len < (13 + j)) - return -1; - rsa_e_len = get_int32(buf + i + j); - j += rsa_e_len; i += 4; - if (len < (13 + j)) - return -1; - rsa_n_len = get_int32(buf + i + j); - j += rsa_n_len; i += 4; - if (len < (13 + j)) - return -1; - padding = *(unsigned char *) (buf + i + j); - if (len != (13 + j)) - return -1; - - i = 4; - i += (data_len + 4); - rsa_e = BN_new(); - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,rsa_e_len); - BN_bin2bn((unsigned char *)(buf + i), rsa_e_len, rsa_e); - i += (rsa_e_len + 4); - rsa_n = BN_new(); - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,rsa_n_len); - BN_bin2bn((unsigned char *)(buf + i), rsa_n_len, rsa_n); - i += (rsa_n_len + 4); - - switch(padding) { - case 0: - padding = RSA_NO_PADDING; - break; - case 1: - padding = RSA_PKCS1_PADDING; - break; - case 2: - padding = RSA_PKCS1_OAEP_PADDING; - break; - case 3: - padding = RSA_SSLV23_PADDING; - break; - default: - return -1; - } - - rsa = RSA_new(); - rsa->e = rsa_e; - rsa->n = rsa_n; - - dlen = RSA_size(rsa) + 1; - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - i = 4; - if(command == DRV_RSA_PUBLIC_ENCRYPT) { - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,data_len); - i = RSA_public_encrypt(data_len, (unsigned char *) (buf+i), - (unsigned char *) &bin[1], - rsa, padding); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(bin+1, i); - } - } else { - i = RSA_public_decrypt(data_len, (unsigned char *) (buf+i), - (unsigned char *) &bin[1], - rsa, padding); - if(i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(bin+1, i); - bin = return_binary_shrink(rbuf,rlen,bin, i+1); - if (bin==NULL) return -1; - } - } - - RSA_free(rsa); - if(i > 0) { - bin[0] = 1; - return i + 1; - } else { -/* ERR_load_crypto_strings(); */ -/* fprintf(stderr, "%d: %s \r\n", __LINE__, ERR_reason_error_string(ERR_get_error())); */ - bin[0] = 0; - return 1; - } - break; - - case DRV_CBC_AES128_ENCRYPT: - case DRV_CBC_AES256_ENCRYPT: - case DRV_CBC_AES128_DECRYPT: - case DRV_CBC_AES256_DECRYPT: - /* buf = key[klen] ivec[klen] data */ - if (command == DRV_CBC_AES256_ENCRYPT || command == DRV_CBC_AES256_DECRYPT) - klen = 32; - else - klen = 16; - dlen = len - klen - 16; - if (dlen < 0) - return -1; - if (dlen % 16 != 0) - return -1; - if (command == DRV_CBC_AES128_ENCRYPT || command == DRV_CBC_AES256_ENCRYPT) { - i = AES_ENCRYPT; - AES_set_encrypt_key((unsigned char *) buf, klen*8, &aes_key); - } else { - i = AES_DECRYPT; - AES_set_decrypt_key((unsigned char *) buf, klen*8, &aes_key); - } - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - AES_cbc_encrypt((unsigned char *) (buf + klen+16), - (unsigned char *) bin, - dlen, - &aes_key, - (unsigned char *) (buf + klen), - i); - return dlen; - -/* case DRV_CBC_AES128_DECRYPT: */ -/* case DRV_CBC_AES256_DECRYPT: */ -/* /\* buf = key[klen] ivec[16] data *\/ */ -/* if (command == DRV_CBC_AES256_DECRYPT) */ -/* klen = 32; */ -/* else */ -/* klen = 16; */ -/* dlen = len - klen - 16; */ -/* if (dlen < 0) */ -/* return -1; */ -/* *rbuf = (char *)(bin = driver_alloc_binary(dlen)); */ -/* AES_set_decrypt_key((unsigned char *) buf, klen*8, &aes_key); */ -/* AES_cbc_encrypt((unsigned char *) (buf + klen+16), */ -/* (unsigned char *) bin->orig_bytes, */ -/* dlen, */ -/* &aes_key, */ -/* (unsigned char *) (buf + klen), */ -/* AES_DECRYPT); */ -/* return dlen; */ -/* break; */ - - case DRV_XOR: - /* buf = data1, data2 with same size */ - dlen = len / 2; - if (len != dlen * 2) - return -1; - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - p = bin, - dbuf = buf + dlen; - for (key = buf, key2 = dbuf; key != dbuf; ++key, ++key2, ++p) - *p = *key ^ *key2; - return dlen; - - case DRV_DH_GENERATE_PARAMS: - /* buf = <<PrimeLen:32 Generator:32>> */ - if (len != 8) - return -1; - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf,len); - prime_len = get_int32(buf); - generator = get_int32(buf+4); - dh_params = DH_generate_parameters(prime_len, generator, NULL, NULL); - - if(dh_params) { - dh_p_len = BN_num_bytes(dh_params->p); - dh_g_len = BN_num_bytes(dh_params->g); - dlen = 1 + 4 + 4 + dh_g_len + dh_p_len; - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - bin[0] = 1; - put_int32(bin+1, dh_p_len); - BN_bn2bin(dh_params->p, bin+5); - ERL_VALGRIND_MAKE_MEM_DEFINED(bin+5,dh_p_len); - put_int32(bin+5+dh_p_len, dh_g_len); - BN_bn2bin(dh_params->g, bin+5+dh_p_len+4); - ERL_VALGRIND_MAKE_MEM_DEFINED(bin+5+dh_p_len+4,dh_g_len); - } else { - dlen = 1; - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - bin[0] = 0; - } - DH_free(dh_params); - return dlen; - - case DRV_DH_CHECK: - /* buf = <<dh_p_len:32/integer, dh_p:dh_p_len/binary, - * dh_g_len:32/integer, dh_g:dh_g_len/binary>> */ - i = 0; - j = 0; - if(len < 8) return -1; - dh_p_len = get_int32(buf + i + j); - j += dh_p_len; i += 4; - if (len < (8 + j)) return -1; - dh_g_len = get_int32(buf + i + j); - j += dh_g_len; i += 4; - if(len != (8+j)) return -1; - i=4; - dh_p = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dh_p_len, dh_p); - i += (dh_p_len + 4); - dh_g = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dh_g_len, dh_g); - /* i += (dsa_g_len + 4); */ - - dh_params = DH_new(); - dh_params->p = dh_p; - dh_params->g = dh_g; - - i=0; - bin = return_binary(rbuf,rlen,4); - if (bin==NULL) return -1; - if(DH_check(dh_params, &i)) { - put_int32(bin, i); - } else { - /* Check Failed */ - put_int32(bin, -1); - } - DH_free(dh_params); - return 4; - - case DRV_DH_GENERATE_KEY: - /* buf = <<key_len:32, key:key_len/binary, * - * dh_p_len:32/integer, dh_p:dh_p_len/binary, * - * dh_g_len:32/integer, dh_g:dh_g_len/binary>> */ - ERL_VALGRIND_ASSERT_MEM_DEFINED(buf,len); - i = 0; - j = 0; - if(len < 12) return -1; - base_len = get_int32(buf + i + j); - j += base_len; i += 4; - if (len < (12 + j)) return -1; - dh_p_len = get_int32(buf + i + j); - j += dh_p_len; i += 4; - if (len < (12 + j)) return -1; - dh_g_len = get_int32(buf + i + j); - j += dh_g_len; i += 4; - if(len != (12 + j)) return -1; - i=4; - i += (base_len + 4); - dh_p = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dh_p_len, dh_p); - i += (dh_p_len + 4); - dh_g = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dh_g_len, dh_g); - /* i += (dsa_g_len + 4); */ - - dh_params = DH_new(); - dh_params->p = dh_p; - dh_params->g = dh_g; - if(base_len > 0) { - dh_params->priv_key = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), base_len, - dh_params->priv_key); - } - i=0; - if(DH_generate_key(dh_params)) { - privkey_len = BN_num_bytes(dh_params->priv_key); - pubkey_len = BN_num_bytes(dh_params->pub_key); - dlen = 1 + 4 + 4 + pubkey_len + privkey_len; - bin = return_binary(rbuf,rlen, dlen); - if (bin==NULL) return -1; - bin[0] = 1; - put_int32(bin+1, pubkey_len); - BN_bn2bin(dh_params->pub_key, bin+5); - ERL_VALGRIND_MAKE_MEM_DEFINED(bin+5, pubkey_len); - put_int32(bin+5+pubkey_len, privkey_len); - BN_bn2bin(dh_params->priv_key, bin+5+pubkey_len+4); - ERL_VALGRIND_MAKE_MEM_DEFINED(bin+5+pubkey_len+4, privkey_len); - } else { - dlen = 1; - bin = return_binary(rbuf,rlen,dlen); - if (bin==NULL) return -1; - bin[0] = 0; - } - DH_free(dh_params); - return dlen; - - case DRV_DH_COMPUTE_KEY: - /* buf = <<pubkey_len:32, pubkey:pubkey_len/binary, * - * privkey_len:32, privkey:privkey_len/binary, * - * dh_p_len:32/integer, dh_p:dh_p_len/binary, * - * dh_g_len:32/integer, dh_g:dh_g_len/binary>> */ - i = 0; - j = 0; - if(len < 16) return -1; - pubkey_len = get_int32(buf + i + j); - j += pubkey_len; i += 4; - if (len < (16 + j)) return -1; - privkey_len = get_int32(buf + i + j); - j += privkey_len; i += 4; - if (len < (16 + j)) return -1; - dh_p_len = get_int32(buf + i + j); - j += dh_p_len; i += 4; - if (len < (16 + j)) return -1; - dh_g_len = get_int32(buf + i + j); - j += dh_g_len; i += 4; - if(len != (16 + j)) return -1; - i=4; - pubkey = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), pubkey_len, pubkey); - i += (pubkey_len + 4); - privkey = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), privkey_len, privkey); - i += (privkey_len + 4); - dh_p = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dh_p_len, dh_p); - i += (dh_p_len + 4); - dh_g = BN_new(); - BN_bin2bn((unsigned char *)(buf + i), dh_g_len, dh_g); - /* i += (dsa_g_len + 4); */ - - dh_params = DH_new(); - dh_params->p = dh_p; - dh_params->g = dh_g; - dh_params->priv_key = privkey; - - klen = DH_size(dh_params); - bin = return_binary(rbuf,rlen,1+klen); - if (bin==NULL) return -1; - i = DH_compute_key(&bin[1], pubkey, dh_params); - DH_free(dh_params); - if (i > 0) { - if (i != klen) { - bin = return_binary_shrink(rbuf,rlen,bin,1+i); - } - bin[0] = 1; - return i + 1; - } - else { - bin[0] = 0; - return 1; - } - - case DRV_MD4: - bin = return_binary(rbuf,rlen,MD4_LEN); - MD4((unsigned char *)buf, len, (unsigned char *)bin); - return MD4_LEN; - - case DRV_MD4_INIT: - bin = return_binary(rbuf,rlen,MD4_CTX_LEN); - MD4_Init((MD4_CTX *) bin); - return MD4_CTX_LEN; - - case DRV_MD4_UPDATE: - if (len < MD4_CTX_LEN) - return -1; - bin = return_binary(rbuf,rlen,MD4_CTX_LEN); - memcpy(bin, buf, MD4_CTX_LEN); - MD4_Update((MD4_CTX *) bin, buf + MD4_CTX_LEN, len - MD4_CTX_LEN); - return MD4_CTX_LEN; - - case DRV_MD4_FINAL: - if (len != MD4_CTX_LEN) - return -1; - memcpy(&md4_ctx, buf, MD4_CTX_LEN); /* XXX Use buf only? */ - bin = return_binary(rbuf,rlen,MD4_LEN); - MD4_Final((unsigned char *)bin, &md4_ctx); - return MD4_LEN; - -#if SSL_VERSION_0_9_8 - case DRV_SHA256: - bin = return_binary(rbuf,rlen,SHA256_LEN); - SHA256(buf, len, bin); - return SHA256_LEN; - - case DRV_SHA256_INIT: - bin = return_binary(rbuf,rlen,SHA256_CTX_LEN); - SHA256_Init((SHA256_CTX *)bin); - return SHA256_CTX_LEN; - - case DRV_SHA256_UPDATE: - if (len < SHA256_CTX_LEN) - return -1; - bin = return_binary(rbuf,rlen,SHA256_CTX_LEN); - memcpy(bin, buf, SHA256_CTX_LEN); - SHA256_Update((SHA256_CTX *)bin, buf + SHA256_CTX_LEN, - len - SHA256_CTX_LEN); - return SHA256_CTX_LEN; - - case DRV_SHA256_FINAL: - if (len != SHA256_CTX_LEN) - return -1; - memcpy(&sha256_ctx, buf, SHA256_CTX_LEN); /* XXX Use buf only? */ - bin = return_binary(rbuf,rlen,SHA256_LEN); - SHA256_Final(bin, &sha256_ctx); - return SHA256_LEN; - - case DRV_SHA512: - bin = return_binary(rbuf,rlen,SHA512_LEN); - SHA512(buf, len, bin); - return SHA512_LEN; - - case DRV_SHA512_INIT: - bin = return_binary(rbuf,rlen,SHA512_CTX_LEN); - SHA512_Init((SHA512_CTX *)bin); - return SHA512_CTX_LEN; - - case DRV_SHA512_UPDATE: - if (len < SHA512_CTX_LEN) - return -1; - bin = return_binary(rbuf,rlen,SHA512_CTX_LEN); - memcpy(bin, buf, SHA512_CTX_LEN); - SHA512_Update((SHA512_CTX *)bin, buf + SHA512_CTX_LEN, - len - SHA512_CTX_LEN); - return SHA512_CTX_LEN; - - case DRV_SHA512_FINAL: - if (len != SHA512_CTX_LEN) - return -1; - memcpy(&sha512_ctx, buf, SHA512_CTX_LEN); /* XXX Use buf only? */ - bin = return_binary(rbuf,rlen,SHA512_LEN)); - SHA512_Final(bin, &sha512_ctx); - return SHA512_LEN; -#endif - - case DRV_INFO_LIB: - {/* <<DrvVer:8, NameSize:8, Name:NameSize/binary, VerNum:32, VerStr/binary>> */ - static const char libname[] = "OpenSSL"; - unsigned name_sz = strlen(libname); - const char* ver = SSLeay_version(SSLEAY_VERSION); - unsigned ver_sz = strlen(ver); - dlen = 1+1+name_sz+4+ver_sz; - bin = return_binary(rbuf, rlen, dlen); - if (bin==NULL) return -1; - p = bin; - *p++ = 0; /* "driver version" for future use */ - *p++ = name_sz; - memcpy(p, libname, name_sz); - p += name_sz; - put_int32(p,SSLeay()); /* OPENSSL_VERSION_NUMBER */ - p += 4; - memcpy(p, ver, ver_sz); - } - return dlen; - - default: - break; - } - return -1; -} - - -#ifdef OPENSSL_THREADS /* vvvvvvvvvvvvvvv OPENSSL_THREADS vvvvvvvvvvvvvvvv */ - -static INLINE void locking(int mode, ErlDrvRWLock* lock) -{ - switch(mode) { - case CRYPTO_LOCK|CRYPTO_READ: - erl_drv_rwlock_rlock(lock); - break; - case CRYPTO_LOCK|CRYPTO_WRITE: - erl_drv_rwlock_rwlock(lock); - break; - case CRYPTO_UNLOCK|CRYPTO_READ: - erl_drv_rwlock_runlock(lock); - break; - case CRYPTO_UNLOCK|CRYPTO_WRITE: - erl_drv_rwlock_rwunlock(lock); - break; - default: - ASSERT(!"Invalid lock mode"); - } -} - -/* Callback from openssl for static locking - */ -static void locking_function(int mode, int n, const char *file, int line) -{ - ASSERT(n>=0 && n<CRYPTO_num_locks()); - - locking(mode, lock_vec[n]); -} - -/* Callback from openssl for thread id - */ -static unsigned long id_function(void) -{ - return (unsigned long) erl_drv_thread_self(); -} - -/* Callbacks for dynamic locking, not used by current openssl version (0.9.8) - */ -static struct CRYPTO_dynlock_value* dyn_create_function(const char *file, int line) -{ - return (struct CRYPTO_dynlock_value*) erl_drv_rwlock_create("crypto_drv_dyn"); -} -static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value* ptr,const char *file, int line) -{ - locking(mode, (ErlDrvRWLock*)ptr); -} -static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr, const char *file, int line) -{ - erl_drv_rwlock_destroy((ErlDrvRWLock*)ptr); -} - -#endif /* ^^^^^^^^^^^^^^^^^^^^^^ OPENSSL_THREADS ^^^^^^^^^^^^^^^^^^^^^^ */ - -/* HMAC */ - -static void hmac_md5(char *key, int klen, char *dbuf, int dlen, char *hmacbuf) -{ - MD5_CTX ctx; - char ipad[HMAC_INT_LEN]; - char opad[HMAC_INT_LEN]; - unsigned char nkey[MD5_LEN]; - int i; - - /* Change key if longer than 64 bytes */ - if (klen > HMAC_INT_LEN) { - MD5_CTX kctx; - - MD5_Init(&kctx); - MD5_Update(&kctx, key, klen); - MD5_Final(nkey, &kctx); - key = (char *) nkey; - klen = MD5_LEN; - } - - memset(ipad, '\0', sizeof(ipad)); - memset(opad, '\0', sizeof(opad)); - memcpy(ipad, key, klen); - memcpy(opad, key, klen); - - for (i = 0; i < HMAC_INT_LEN; i++) { - ipad[i] ^= HMAC_IPAD; - opad[i] ^= HMAC_OPAD; - } - - /* inner MD5 */ - MD5_Init(&ctx); - MD5_Update(&ctx, ipad, HMAC_INT_LEN); - MD5_Update(&ctx, dbuf, dlen); - MD5_Final((unsigned char *) hmacbuf, &ctx); - /* outer MD5 */ - MD5_Init(&ctx); - MD5_Update(&ctx, opad, HMAC_INT_LEN); - MD5_Update(&ctx, hmacbuf, MD5_LEN); - MD5_Final((unsigned char *) hmacbuf, &ctx); -} - -static void hmac_sha1(char *key, int klen, char *dbuf, int dlen, - char *hmacbuf) -{ - SHA_CTX ctx; - char ipad[HMAC_INT_LEN]; - char opad[HMAC_INT_LEN]; - unsigned char nkey[SHA_LEN]; - int i; - - /* Change key if longer than 64 bytes */ - if (klen > HMAC_INT_LEN) { - SHA_CTX kctx; - - SHA1_Init(&kctx); - SHA1_Update(&kctx, key, klen); - SHA1_Final(nkey, &kctx); - key = (char *) nkey; - klen = SHA_LEN; - } - - memset(ipad, '\0', sizeof(ipad)); - memset(opad, '\0', sizeof(opad)); - memcpy(ipad, key, klen); - memcpy(opad, key, klen); - - for (i = 0; i < HMAC_INT_LEN; i++) { - ipad[i] ^= HMAC_IPAD; - opad[i] ^= HMAC_OPAD; - } - - /* inner SHA */ - SHA1_Init(&ctx); - SHA1_Update(&ctx, ipad, HMAC_INT_LEN); - SHA1_Update(&ctx, dbuf, dlen); - SHA1_Final((unsigned char *) hmacbuf, &ctx); - /* outer SHA */ - SHA1_Init(&ctx); - SHA1_Update(&ctx, opad, HMAC_INT_LEN); - SHA1_Update(&ctx, hmacbuf, SHA_LEN); - SHA1_Final((unsigned char *) hmacbuf, &ctx); -} diff --git a/lib/crypto/priv/Makefile b/lib/crypto/priv/Makefile index b8acdacc00..0989f14c94 100644 --- a/lib/crypto/priv/Makefile +++ b/lib/crypto/priv/Makefile @@ -18,21 +18,21 @@ # ---------------------------------------------------- # THIS MAKEFILE SERVES AS AN EXAMPLE OF -# HOW TO RELINK THE CRYPTO DRIVER +# HOW TO RELINK THE CRYPTO NIF LIBRARY # ---------------------------------------------------- # ---------------------------------------------------- -# Variables for linking a .so driver on unix. +# Variables for linking a .so library on unix. # Note: These may differ between systems. # ---------------------------------------------------- SO_LD = gcc SO_LDFLAGS = -G SO_SSL_LIBDIR = /usr/local/lib -SO_DRIVER = $(LIBDIR)/$(DRIVER_NAME).so +SO_NIFLIB = $(LIBDIR)/$(LIB_NAME).so # ---------------------------------------------------- -# Variables for linking a win32 .dll driver. +# Variables for linking a win32 .dll library. # Note: These may differ between systems. # ---------------------------------------------------- @@ -42,9 +42,9 @@ DLL_LIBDIR = "c:\\OpenSSL\\lib\\VC" DLL_LIBS = libeay32.lib MSVCRT.LIB kernel32.lib \ advapi32.lib gdi32.lib user32.lib \ comctl32.lib comdlg32.lib shell32.lib -DLL_DRIVER = $(LIBDIR)/$(DRIVER_NAME).dll -DLL_EXP = $(LIBDIR)/$(DRIVER_NAME).exp -DLL_LIB = $(LIBDIR)/$(DRIVER_NAME).lib +DLL_NIFLIB = $(LIBDIR)/$(LIB_NAME).dll +DLL_EXP = $(LIBDIR)/$(LIB_NAME).exp +DLL_LIB = $(LIBDIR)/$(LIB_NAME).lib # ---------------------------------------------------- # Common variables @@ -52,27 +52,27 @@ DLL_LIB = $(LIBDIR)/$(DRIVER_NAME).lib OBJDIR = ./ LIBDIR = ../lib -DRIVER_NAME = crypto_drv -OBJS = $(OBJDIR)/crypto_drv.o +LIB_NAME = crypto +OBJS = $(OBJDIR)/crypto.o # ---------------------------------------------------- # Targets # ---------------------------------------------------- -$(SO_DRIVER): $(OBJS) +$(SO_NIFLIB): $(OBJS) $(SO_LD) $(SO_LDFLAGS) -L$(SO_SSL_LIBDIR) -Wl,-R$(SO_SSL_LIBDIR) \ -o $@ $^ -lcrypto -$(DLL_DRIVER): $(OBJS) +$(DLL_NIFLIB): $(OBJS) $(DLL_LD) $(DLL_LDFLAGS) -out:$@ -libpath:$(DLL_LIBDIR) $(OBJS) \ $(DLL_LIBS) -so: $(SO_DRIVER) +so: $(SO_NIFLIB) -dll: $(DLL_DRIVER) +dll: $(DLL_NIFLIB) clean: - rm -f $(SO_DRIVER) $(DLL_DRIVER) + rm -f $(SO_NIFLIB) $(DLL_NIFLIB) rm -f $(DLL_EXP) $(DLL_LIB) rm -f core *~ diff --git a/lib/crypto/src/Makefile b/lib/crypto/src/Makefile index 51092d16a5..0e886ce8bf 100644 --- a/lib/crypto/src/Makefile +++ b/lib/crypto/src/Makefile @@ -1,19 +1,19 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 1999-2009. All Rights Reserved. -# +# +# Copyright Ericsson AB 1999-2010. All Rights Reserved. +# # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in # compliance with the License. You should have received a copy of the # Erlang Public License along with this software. If not, it can be # retrieved online at http://www.erlang.org/. -# +# # Software distributed under the License is distributed on an "AS IS" # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See # the License for the specific language governing rights and limitations # under the License. -# +# # %CopyrightEnd% # include $(ERL_TOP)/make/target.mk @@ -57,7 +57,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += +warn_obsolete_guard +ERL_COMPILE_FLAGS += +warn_obsolete_guard -DCRYPTO_VSN=\"$(VSN)\" # ---------------------------------------------------- # Targets diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 8cb750759f..5b1ce96caf 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -21,7 +21,7 @@ -module(crypto). --export([start/0, stop/0, info/0, info_lib/0]). +-export([start/0, stop/0, info/0, info_lib/0, version/0]). -export([md4/1, md4_init/0, md4_update/2, md4_final/1]). -export([md5/1, md5_init/0, md5_update/2, md5_final/1]). -export([sha/1, sha_init/0, sha_update/2, sha_final/1]). @@ -54,82 +54,6 @@ -export([dh_generate_parameters/2, dh_check/1]). %% Testing see below --define(INFO, 0). --define(MD5, 1). --define(MD5_INIT, 2). --define(MD5_UPDATE, 3). --define(MD5_FINAL, 4). --define(SHA, 5). --define(SHA_INIT, 6). --define(SHA_UPDATE, 7). --define(SHA_FINAL, 8). --define(MD5_MAC, 9). --define(MD5_MAC_96, 10). --define(SHA_MAC, 11). --define(SHA_MAC_96, 12). --define(DES_CBC_ENCRYPT, 13). --define(DES_CBC_DECRYPT, 14). --define(DES_EDE3_CBC_ENCRYPT, 15). --define(DES_EDE3_CBC_DECRYPT, 16). --define(AES_CFB_128_ENCRYPT, 17). --define(AES_CFB_128_DECRYPT, 18). --define(RAND_BYTES, 19). --define(RAND_UNIFORM, 20). --define(MOD_EXP, 21). --define(DSS_VERIFY, 22). --define(RSA_VERIFY_SHA, 23). -%-define(RSA_VERIFY_MD5, 35). --define(AES_CBC_128_ENCRYPT, 24). --define(AES_CBC_128_DECRYPT, 25). --define(XOR, 26). --define(RC4_ENCRYPT, 27). --define(RC4_SET_KEY, 28). --define(RC4_ENCRYPT_WITH_STATE, 29). --define(RC2_40_CBC_ENCRYPT, 30). --define(RC2_40_CBC_DECRYPT, 31). --define(AES_CBC_256_ENCRYPT, 32). --define(AES_CBC_256_DECRYPT, 33). --define(INFO_LIB,34). -%-define(RSA_VERIFY_SHA, 23). --define(RSA_VERIFY_MD5, 35). --define(RSA_SIGN_SHA, 36). --define(RSA_SIGN_MD5, 37). --define(DSS_SIGN, 38). --define(RSA_PUBLIC_ENCRYPT, 39). --define(RSA_PRIVATE_DECRYPT, 40). --define(RSA_PRIVATE_ENCRYPT, 41). --define(RSA_PUBLIC_DECRYPT, 42). --define(DH_GENERATE_PARAMS, 43). --define(DH_CHECK, 44). --define(DH_GENERATE_KEY, 45). --define(DH_COMPUTE_KEY, 46). --define(MD4, 47). --define(MD4_INIT, 48). --define(MD4_UPDATE, 49). --define(MD4_FINAL, 50). - -%% -define(SHA256, 51). -%% -define(SHA256_INIT, 52). -%% -define(SHA256_UPDATE, 53). -%% -define(SHA256_FINAL, 54). -%% -define(SHA512, 55). -%% -define(SHA512_INIT, 56). -%% -define(SHA512_UPDATE, 57). -%% -define(SHA512_FINAL, 58). - --define(BF_CFB64_ENCRYPT, 59). --define(BF_CFB64_DECRYPT, 60). --define(BF_ECB_ENCRYPT, 61). --define(BF_ECB_DECRYPT, 62). --define(BF_OFB64_ENCRYPT, 63). --define(BF_CBC_ENCRYPT, 64). --define(BF_CBC_DECRYPT, 65). - --define(DES_ECB_ENCRYPT, 66). --define(DES_ECB_DECRYPT, 67). - -%% -define(IDEA_CBC_ENCRYPT, 34). -%% -define(IDEA_CBC_DECRYPT, 35). -define(FUNC_LIST, [md4, md4_init, md4_update, md4_final, md5, md5_init, md5_update, md5_final, @@ -158,6 +82,44 @@ aes_cbc_256_encrypt, aes_cbc_256_decrypt, info_lib]). +-define(nif_stub,nif_stub_error(?LINE)). + +-on_load(on_load/0). + +-define(CRYPTO_NIF_VSN,101). + +on_load() -> + LibName = "crypto", + PrivDir = code:priv_dir(crypto), + Lib1 = filename:join([PrivDir, "lib", LibName]), + Status = case erlang:load_nif(Lib1, ?CRYPTO_NIF_VSN) of + ok -> ok; + {error, {load_failed, _}}=Error1 -> + LibDir2 = + filename:join([PrivDir, "lib", + erlang:system_info(system_architecture)]), + Candidate = + filelib:wildcard(filename:join([LibDir2,LibName ++ "*" ])), + case Candidate of + [] -> Error1; + _ -> + Lib2 = filename:join([LibDir2, LibName]), + erlang:load_nif(Lib2, ?CRYPTO_NIF_VSN) + end; + Error1 -> Error1 + end, + case Status of + ok -> ok; + {error, {E, Str}} -> + error_logger:error_msg("Unable to load crypto library. Failed with error:~n\"~p, ~s\"~n" + "OpenSSL might not be installed on this system.~n",[E,Str]), + Status + end. + + +nif_stub_error(Line) -> + erlang:error({nif_not_loaded,module,?MODULE,line,Line}). + start() -> application:start(crypto). @@ -165,15 +127,15 @@ stop() -> application:stop(crypto). info() -> - lists:map(fun(I) -> - lists:nth(I, ?FUNC_LIST) - end, binary_to_list(control(?INFO, []))). + ?FUNC_LIST. -info_lib() -> - <<_DrvVer:8, NameSize:8, Name:NameSize/binary, - VerNum:32, VerStr/binary>> = control(?INFO_LIB,[]), - [{Name,VerNum,VerStr}]. +info_lib() -> ?nif_stub. +%% Crypto app version history: +%% (no version): Driver implementation +%% 2.0 : NIF implementation, requires OTP R14 +version() -> ?CRYPTO_VSN. + %% Below Key and Data are binaries or IO-lists. IVec is a binary. %% Output is always a binary. Context is a binary. @@ -184,73 +146,27 @@ info_lib() -> %% %% MD5 %% -md5(Data) -> - control(?MD5, Data). - -md5_init() -> - control(?MD5_INIT, []). - -md5_update(Context, Data) -> - control(?MD5_UPDATE, [Context, Data]). - -md5_final(Context) -> - control(?MD5_FINAL, Context). +md5(_Data) -> ?nif_stub. +md5_init() -> ?nif_stub. +md5_update(_Context, _Data) -> ?nif_stub. +md5_final(_Context) -> ?nif_stub. %% %% MD4 %% -md4(Data) -> - control(?MD4, Data). - -md4_init() -> - control(?MD4_INIT, []). - -md4_update(Context, Data) -> - control(?MD4_UPDATE, [Context, Data]). - -md4_final(Context) -> - control(?MD4_FINAL, Context). +md4(_Data) -> ?nif_stub. +md4_init() -> ?nif_stub. +md4_update(_Context, _Data) -> ?nif_stub. +md4_final(_Context) -> ?nif_stub. %% %% SHA %% -sha(Data) -> - control(?SHA, Data). - -sha_init() -> - control(?SHA_INIT, []). - -sha_update(Context, Data) -> - control(?SHA_UPDATE, [Context, Data]). - -sha_final(Context) -> - control(?SHA_FINAL, Context). +sha(_Data) -> ?nif_stub. +sha_init() -> ?nif_stub. +sha_update(_Context, _Data) -> ?nif_stub. +sha_final(_Context) -> ?nif_stub. -%% sha256 and sha512 requires openssl-0.9.8 removed for now - -%% sha256(Data) -> -%% control(?SHA256, Data). - -%% sha256_init() -> -%% control(?SHA256_INIT, []). - -%% sha256_update(Context, Data) -> -%% control(?SHA256_UPDATE, [Context, Data]). - -%% sha256_final(Context) -> -%% control(?SHA256_FINAL, Context). - -%% sha512(Data) -> -%% control(?SHA512, Data). - -%% sha512_init() -> -%% control(?SHA512_INIT, []). - -%% sha512_update(Context, Data) -> -%% control(?SHA512_UPDATE, [Context, Data]). - -%% sha512_final(Context) -> -%% control(?SHA512_FINAL, Context). %% %% MESSAGE AUTHENTICATION CODES @@ -260,20 +176,24 @@ sha_final(Context) -> %% MD5_MAC %% md5_mac(Key, Data) -> - control_bin(?MD5_MAC, Key, Data). + md5_mac_n(Key,Data,16). md5_mac_96(Key, Data) -> - control_bin(?MD5_MAC_96, Key, Data). + md5_mac_n(Key,Data,12). +md5_mac_n(_Key,_Data,_MacSz) -> ?nif_stub. + %% %% SHA_MAC %% sha_mac(Key, Data) -> - control_bin(?SHA_MAC, Key, Data). + sha_mac_n(Key,Data,20). sha_mac_96(Key, Data) -> - control_bin(?SHA_MAC_96, Key, Data). + sha_mac_n(Key,Data,12). +sha_mac_n(_Key,_Data,_MacSz) -> ?nif_stub. + %% %% CRYPTO FUNCTIONS %% @@ -282,10 +202,12 @@ sha_mac_96(Key, Data) -> %% DES - in cipher block chaining mode (CBC) %% des_cbc_encrypt(Key, IVec, Data) -> - control(?DES_CBC_ENCRYPT, [Key, IVec, Data]). + des_cbc_crypt(Key, IVec, Data, true). des_cbc_decrypt(Key, IVec, Data) -> - control(?DES_CBC_DECRYPT, [Key, IVec, Data]). + des_cbc_crypt(Key, IVec, Data, false). + +des_cbc_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub. %% %% dec_cbc_ivec(Data) -> binary() @@ -303,10 +225,10 @@ des_cbc_ivec(Data) when is_list(Data) -> %% DES - in electronic codebook mode (ECB) %% des_ecb_encrypt(Key, Data) -> - control(?DES_ECB_ENCRYPT, [Key, Data]). - + des_ecb_crypt(Key, Data, true). des_ecb_decrypt(Key, Data) -> - control(?DES_ECB_DECRYPT, [Key, Data]). + des_ecb_crypt(Key, Data, false). +des_ecb_crypt(_Key, _Data, _IsEncrypt) -> ?nif_stub. %% %% DES3 - in cipher block chaining mode (CBC) @@ -314,71 +236,64 @@ des_ecb_decrypt(Key, Data) -> des3_cbc_encrypt(Key1, Key2, Key3, IVec, Data) -> des_ede3_cbc_encrypt(Key1, Key2, Key3, IVec, Data). des_ede3_cbc_encrypt(Key1, Key2, Key3, IVec, Data) -> - %%io:format("des_ede3_cbc_encrypt: size(Data)=~p\n", [size(list_to_binary([Data]))]), - control(?DES_EDE3_CBC_ENCRYPT, [Key1, Key2, Key3, IVec, Data]). + des_ede3_cbc_crypt(Key1, Key2, Key3, IVec, Data, true). des3_cbc_decrypt(Key1, Key2, Key3, IVec, Data) -> des_ede3_cbc_decrypt(Key1, Key2, Key3, IVec, Data). des_ede3_cbc_decrypt(Key1, Key2, Key3, IVec, Data) -> - control(?DES_EDE3_CBC_DECRYPT, [Key1, Key2, Key3, IVec, Data]). + des_ede3_cbc_crypt(Key1, Key2, Key3, IVec, Data, false). + +des_ede3_cbc_crypt(_Key1, _Key2, _Key3, _IVec, _Data, _IsEncrypt) -> ?nif_stub. %% %% Blowfish %% -blowfish_ecb_encrypt(Key, Data) when byte_size(Data) >= 8 -> - control_bin(?BF_ECB_ENCRYPT, Key, list_to_binary([Data])). +blowfish_ecb_encrypt(Key, Data) -> + bf_ecb_crypt(Key,Data, true). -blowfish_ecb_decrypt(Key, Data) when byte_size(Data) >= 8 -> - control_bin(?BF_ECB_DECRYPT, Key, list_to_binary([Data])). +blowfish_ecb_decrypt(Key, Data) -> + bf_ecb_crypt(Key,Data, false). -blowfish_cbc_encrypt(Key, IVec, Data) when byte_size(Data) rem 8 =:= 0 -> - control_bin(?BF_CBC_ENCRYPT, Key, list_to_binary([IVec, Data])). +bf_ecb_crypt(_Key,_Data,_IsEncrypt) -> ?nif_stub. -blowfish_cbc_decrypt(Key, IVec, Data) when byte_size(Data) rem 8 =:= 0 -> - control_bin(?BF_CBC_DECRYPT, Key, list_to_binary([IVec, Data])). +blowfish_cbc_encrypt(Key, IVec, Data) -> + bf_cbc_crypt(Key,IVec,Data,true). -blowfish_cfb64_encrypt(Key, IVec, Data) when byte_size(IVec) =:= 8 -> - control_bin(?BF_CFB64_ENCRYPT, Key, list_to_binary([IVec, Data])). +blowfish_cbc_decrypt(Key, IVec, Data) -> + bf_cbc_crypt(Key,IVec,Data,false). -blowfish_cfb64_decrypt(Key, IVec, Data) when byte_size(IVec) =:= 8 -> - control_bin(?BF_CFB64_DECRYPT, Key, list_to_binary([IVec, Data])). +bf_cbc_crypt(_Key,_IVec,_Data,_IsEncrypt) -> ?nif_stub. -blowfish_ofb64_encrypt(Key, IVec, Data) when byte_size(IVec) =:= 8 -> - control_bin(?BF_OFB64_ENCRYPT, Key, list_to_binary([IVec, Data])). +blowfish_cfb64_encrypt(Key, IVec, Data) -> + bf_cfb64_crypt(Key, IVec, Data, true). + +blowfish_cfb64_decrypt(Key, IVec, Data) -> + bf_cfb64_crypt(Key, IVec, Data, false). + +bf_cfb64_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub. + +blowfish_ofb64_encrypt(_Key, _IVec, _Data) -> ?nif_stub. %% %% AES in cipher feedback mode (CFB) %% aes_cfb_128_encrypt(Key, IVec, Data) -> - control(?AES_CFB_128_ENCRYPT, [Key, IVec, Data]). + aes_cfb_128_crypt(Key, IVec, Data, true). aes_cfb_128_decrypt(Key, IVec, Data) -> - control(?AES_CFB_128_DECRYPT, [Key, IVec, Data]). - + aes_cfb_128_crypt(Key, IVec, Data, false). -%% %% -%% %% IDEA - in cipher block chaining mode (CBC) -%% %% -%% idea_cbc_encrypt(Key, IVec, Data) -> -%% control(?IDEA_CBC_ENCRYPT, [Key, IVec, Data]). - -%% idea_cbc_decrypt(Key, IVec, Data) -> -%% control(?IDEA_CBC_DECRYPT, [Key, IVec, Data]). +aes_cfb_128_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub. %% %% RAND - pseudo random numbers using RN_ functions in crypto lib %% - -rand_bytes(Bytes) -> - rand_bytes(Bytes, 0, 0). -rand_bytes(Bytes, Topmask, Bottommask) -> - control(?RAND_BYTES,[<<Bytes:32/integer, - Topmask:8/integer, - Bottommask:8/integer>>]). +rand_bytes(_Bytes) -> ?nif_stub. +rand_bytes(_Bytes, _Topmask, _Bottommask) -> ?nif_stub. rand_uniform(From,To) when is_binary(From), is_binary(To) -> - case control(?RAND_UNIFORM,[From,To]) of + case rand_uniform_nif(From,To) of <<Len:32/integer, MSB, Rest/binary>> when MSB > 127 -> <<(Len + 1):32/integer, 0, MSB, Rest/binary>>; Whatever -> @@ -394,6 +309,8 @@ rand_uniform(From,To) when is_integer(From),is_integer(To) -> Other end. +rand_uniform_nif(_From,_To) -> ?nif_stub. + %% %% mod_exp - utility for rsa generation %% @@ -402,55 +319,51 @@ mod_exp(Base, Exponent, Modulo) erlint(mod_exp(mpint(Base), mpint(Exponent), mpint(Modulo))); mod_exp(Base, Exponent, Modulo) -> - case control(?MOD_EXP,[Base,Exponent,Modulo]) of + case mod_exp_nif(Base,Exponent,Modulo) of <<Len:32/integer, MSB, Rest/binary>> when MSB > 127 -> <<(Len + 1):32/integer, 0, MSB, Rest/binary>>; Whatever -> Whatever end. +mod_exp_nif(_Base,_Exp,_Mod) -> ?nif_stub. + %% %% DSS, RSA - verify %% %% Key = [P,Q,G,Y] P,Q,G=DSSParams Y=PublicKey -dss_verify(Data,Signature,Key) -> - control(?DSS_VERIFY, [Data,Signature,Key]) == <<1>>. +dss_verify(_Data,_Signature,_Key) -> ?nif_stub. % Key = [E,N] E=PublicExponent N=PublicModulus rsa_verify(Data,Signature,Key) -> rsa_verify(sha, Data,Signature,Key). -rsa_verify(Type,Data,Signature,Key) -> - control(rsa_verify_digest_type(Type), [Data,Signature,Key]) == <<1>>. +rsa_verify(_Type,_Data,_Signature,_Key) -> ?nif_stub. -rsa_verify_digest_type(md5) -> ?RSA_VERIFY_MD5; -rsa_verify_digest_type(sha) -> ?RSA_VERIFY_SHA; -rsa_verify_digest_type(Bad) -> erlang:error(badarg, [Bad]). %% %% DSS, RSA - sign %% %% Key = [P,Q,G,X] P,Q,G=DSSParams X=PrivateKey dss_sign(Data, Key) -> - <<Ret:8, Signature/binary>> = control(?DSS_SIGN, [Data,Key]), - case Ret of - 1 -> Signature; - 0 -> erlang:error(badkey, [Data, Key]) + case dss_sign_nif(Data,Key) of + error -> erlang:error(badkey, [Data, Key]); + Sign -> Sign end. +dss_sign_nif(_Data,_Key) -> ?nif_stub. + %% Key = [E,N,D] E=PublicExponent N=PublicModulus D=PrivateExponent rsa_sign(Data,Key) -> rsa_sign(sha, Data, Key). rsa_sign(Type, Data, Key) -> - <<Ret:8, Signature/binary>> = control(rsa_sign_digest_type(Type), [Data,Key]), - case Ret of - 1 -> Signature; - 0 -> erlang:error(badkey, [Type,Data,Key]) + case rsa_sign_nif(Type,Data,Key) of + error -> erlang:error(badkey, [Type,Data,Key]); + Sign -> Sign end. -rsa_sign_digest_type(md5) -> ?RSA_SIGN_MD5; -rsa_sign_digest_type(sha) -> ?RSA_SIGN_SHA; -rsa_sign_digest_type(Bad) -> erlang:error(badarg, [Bad]). +rsa_sign_nif(_Type,_Data,_Key) -> ?nif_stub. + %% %% rsa_public_encrypt @@ -458,48 +371,39 @@ rsa_sign_digest_type(Bad) -> erlang:error(badarg, [Bad]). %% Binary, Key = [E,N] rsa_public_encrypt(BinMesg, Key, Padding) -> - Size = iolist_size(BinMesg), - <<Ret:8, Signature/binary>> = - control(?RSA_PUBLIC_ENCRYPT, [<<Size:32>>,BinMesg,Key,rsa_pad(Padding)]), - case Ret of - 1 -> Signature; - 0 -> erlang:error(encrypt_failed, [BinMesg,Key, Padding]) - end. + case rsa_public_crypt(BinMesg, Key, Padding, true) of + error -> + erlang:error(encrypt_failed, [BinMesg,Key, Padding]); + Sign -> Sign + end. + +rsa_public_crypt(_BinMsg, _Key, _Padding, _IsEncrypt) -> ?nif_stub. %% Binary, Key = [E,N,D] rsa_private_decrypt(BinMesg, Key, Padding) -> - Size = iolist_size(BinMesg), - <<Ret:8, Signature/binary>> = - control(?RSA_PRIVATE_DECRYPT, [<<Size:32>>,BinMesg,Key,rsa_pad(Padding)]), - case Ret of - 1 -> Signature; - 0 -> erlang:error(decrypt_failed, [BinMesg,Key, Padding]) - end. - -rsa_pad(rsa_pkcs1_padding) -> 1; -rsa_pad(rsa_pkcs1_oaep_padding) -> 2; -%% rsa_pad(rsa_sslv23_padding) -> 3; -rsa_pad(rsa_no_padding) -> 0; -rsa_pad(Bad) -> erlang:error(badarg, [Bad]). + case rsa_private_crypt(BinMesg, Key, Padding, false) of + error -> + erlang:error(decrypt_failed, [BinMesg,Key, Padding]); + Sign -> Sign + end. + +rsa_private_crypt(_BinMsg, _Key, _Padding, _IsEncrypt) -> ?nif_stub. + %% Binary, Key = [E,N,D] rsa_private_encrypt(BinMesg, Key, Padding) -> - Size = iolist_size(BinMesg), - <<Ret:8, Signature/binary>> = - control(?RSA_PRIVATE_ENCRYPT, [<<Size:32>>,BinMesg,Key,rsa_pad(Padding)]), - case Ret of - 1 -> Signature; - 0 -> erlang:error(encrypt_failed, [BinMesg,Key, Padding]) - end. + case rsa_private_crypt(BinMesg, Key, Padding, true) of + error -> + erlang:error(encrypt_failed, [BinMesg,Key, Padding]); + Sign -> Sign + end. %% Binary, Key = [E,N] rsa_public_decrypt(BinMesg, Key, Padding) -> - Size = iolist_size(BinMesg), - <<Ret:8, Signature/binary>> = - control(?RSA_PUBLIC_DECRYPT, [<<Size:32>>,BinMesg,Key,rsa_pad(Padding)]), - case Ret of - 1 -> Signature; - 0 -> erlang:error(decrypt_failed, [BinMesg,Key, Padding]) + case rsa_public_crypt(BinMesg, Key, Padding, false) of + error -> + erlang:error(decrypt_failed, [BinMesg,Key, Padding]); + Sign -> Sign end. %% @@ -507,16 +411,18 @@ rsa_public_decrypt(BinMesg, Key, Padding) -> %% aes_cbc_128_encrypt(Key, IVec, Data) -> - control(?AES_CBC_128_ENCRYPT, [Key, IVec, Data]). + aes_cbc_crypt(Key, IVec, Data, true). aes_cbc_128_decrypt(Key, IVec, Data) -> - control(?AES_CBC_128_DECRYPT, [Key, IVec, Data]). + aes_cbc_crypt(Key, IVec, Data, false). aes_cbc_256_encrypt(Key, IVec, Data) -> - control(?AES_CBC_256_ENCRYPT, [Key, IVec, Data]). + aes_cbc_crypt(Key, IVec, Data, true). aes_cbc_256_decrypt(Key, IVec, Data) -> - control(?AES_CBC_256_DECRYPT, [Key, IVec, Data]). + aes_cbc_crypt(Key, IVec, Data, false). + +aes_cbc_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub. %% %% aes_cbc_ivec(Data) -> binary() @@ -537,31 +443,25 @@ aes_cbc_ivec(Data) when is_list(Data) -> %% NB doesn't check that they are the same size, just concatenates %% them and sends them to the driver %% -exor(A, B) -> - control(?XOR, [A, B]). +exor(_A, _B) -> ?nif_stub. %% %% RC4 - symmetric stream cipher %% -rc4_encrypt(Key, Data) -> - control_bin(?RC4_ENCRYPT, Key, Data). - -rc4_set_key(Key) -> - control(?RC4_SET_KEY, Key). - -rc4_encrypt_with_state(State, Data) -> - <<Sz:32/integer-big-unsigned, S:Sz/binary, D/binary>> = - control_bin(?RC4_ENCRYPT_WITH_STATE, State, Data), - {S, D}. +rc4_encrypt(_Key, _Data) -> ?nif_stub. +rc4_set_key(_Key) -> ?nif_stub. +rc4_encrypt_with_state(_State, _Data) -> ?nif_stub. %% %% RC2 - 40 bits block cipher %% rc2_40_cbc_encrypt(Key, IVec, Data) -> - control(?RC2_40_CBC_ENCRYPT, [Key, IVec, Data]). + rc2_40_cbc_crypt(Key,IVec,Data,true). rc2_40_cbc_decrypt(Key, IVec, Data) -> - control(?RC2_40_CBC_DECRYPT, [Key, IVec, Data]). + rc2_40_cbc_crypt(Key,IVec,Data,false). + +rc2_40_cbc_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub. %% %% DH Diffie-Hellman functions @@ -576,87 +476,44 @@ rc2_40_cbc_decrypt(Key, IVec, Data) -> %% %% usage: dh_generate_parameters(1024, 2 or 5) -> %% [Prime=mpint(), SharedGenerator=mpint()] -dh_generate_parameters(PrimeLen, Generator) - when is_integer(PrimeLen), is_integer(Generator) -> - case control(?DH_GENERATE_PARAMS, <<PrimeLen:32, Generator:32>>) of - <<0:8, _/binary>> -> - erlang:error(generation_failed, [PrimeLen,Generator]); - <<1:8, PLen0:32, _:PLen0/binary, GLen0:32,_:GLen0/binary>> = Bin -> - PLen = PLen0+4, - GLen = GLen0+4, - <<_:8, PBin:PLen/binary,GBin:GLen/binary>> = Bin, - [PBin, GBin] - end. +dh_generate_parameters(PrimeLen, Generator) -> + case dh_generate_parameters_nif(PrimeLen, Generator) of + error -> erlang:error(generation_failed, [PrimeLen,Generator]); + Ret -> Ret + end. + +dh_generate_parameters_nif(_PrimeLen, _Generator) -> ?nif_stub. %% Checks that the DHParameters are ok. %% DHParameters = [P (Prime)= mpint(), G(Generator) = mpint()] -dh_check(DHParameters) -> - case control(?DH_CHECK, DHParameters) of - <<0:32>> -> ok; - <<_:24,_:1,_:1,_:1,1:1>> -> not_prime; - <<_:24,_:1,_:1,1:1,0:1>> -> not_strong_prime; - <<_:24,_:1,1:1,0:1,0:1>> -> unable_to_check_generator; - <<_:24,1:1,0:1,0:1,0:1>> -> not_suitable_generator; - <<16#FFFF:32>> -> {error, check_failed}; - <<X:32>> -> {unknown, X} - end. +dh_check([_Prime,_Gen]) -> ?nif_stub. %% DHParameters = [P (Prime)= mpint(), G(Generator) = mpint()] %% PrivKey = mpint() dh_generate_key(DHParameters) -> - dh_generate_key(<<0:32>>, DHParameters). + dh_generate_key(undefined, DHParameters). dh_generate_key(PrivateKey, DHParameters) -> - case control(?DH_GENERATE_KEY, [PrivateKey, DHParameters]) of - <<0:8, _/binary>> -> - erlang:error(generation_failed, [PrivateKey,DHParameters]); - Bin = <<1:8, PubLen0:32, _:PubLen0/binary, PrivLen0:32, _:PrivLen0/binary>> -> - PubLen = PubLen0+4, - PrivLen = PrivLen0+4, - <<_:8, PubBin:PubLen/binary,PrivBin:PrivLen/binary>> = Bin, - {PubBin, PrivBin} + case dh_generate_key_nif(PrivateKey, DHParameters) of + error -> erlang:error(generation_failed, [PrivateKey,DHParameters]); + Res -> Res end. +dh_generate_key_nif(_PrivateKey, _DHParameters) -> ?nif_stub. + %% DHParameters = [P (Prime)= mpint(), G(Generator) = mpint()] %% MyPrivKey, OthersPublicKey = mpint() dh_compute_key(OthersPublicKey, MyPrivateKey, DHParameters) -> - case control(?DH_COMPUTE_KEY, [OthersPublicKey, MyPrivateKey, DHParameters]) of - <<0:8, _/binary>> -> - erlang:error(computation_failed, [OthersPublicKey,MyPrivateKey,DHParameters]); - <<1:8, Binary/binary>> -> Binary + case dh_compute_key_nif(OthersPublicKey,MyPrivateKey,DHParameters) of + error -> erlang:error(computation_failed, [OthersPublicKey,MyPrivateKey,DHParameters]); + Ret -> Ret end. +dh_compute_key_nif(_OthersPublicKey, _MyPrivateKey, _DHParameters) -> ?nif_stub. + %% %% LOCAL FUNCTIONS %% -control_bin(Cmd, Key, Data) -> - Sz = iolist_size(Key), - control(Cmd, [<<Sz:32/integer-unsigned>>, Key, Data]). - -control(Cmd, Data) -> - Port = crypto_server:client_port(), - erlang:port_control(Port, Cmd, Data). - - -%% sizehdr(N) -> -%% [(N bsr 24) band 255, -%% (N bsr 16) band 255, -%% (N bsr 8) band 255, -%% N band 255]. - -%% Flat length of IOlist (or binary) -%% flen(L) when binary(L) -> -%% size(L); -%% flen(L) -> -%% flen(L, 0). - -%% flen([H| T], N) when list(H) -> -%% flen(H, flen(T, N)); -%% flen([H| T], N) when binary(H) -> -%% flen(T, N + size(H)); -%% flen([H| T], N) when integer(H), 0 =< H, H =< 255 -> -%% flen(T, N + 1); -%% flen([], N) -> -%% N. + %% large integer in a binary with 32bit length %% MP representaion (SSH2) diff --git a/lib/crypto/src/crypto_server.erl b/lib/crypto/src/crypto_server.erl index 0b1e5c9b02..89650a9f06 100644 --- a/lib/crypto/src/crypto_server.erl +++ b/lib/crypto/src/crypto_server.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% @@ -23,14 +23,12 @@ -behaviour(gen_server). --export([start_link/0,client_port/0]). +-export([start_link/0]). %% Internal exports, call-back functions. -export([init/1,handle_call/3,handle_cast/2,handle_info/2,code_change/3, terminate/2]). -%% Measurements shows that inlining port_names/0 is worth doing. --compile({inline,[{port_names,0}]}). %%% -------------------------------------------------------- %%% Interface Functions. @@ -40,66 +38,8 @@ start_link() -> gen_server:start_link({local, crypto_server}, crypto_server, [], []). init([]) -> - process_flag(trap_exit, true), - erl_ddll:start(), - PrivDir = code:priv_dir(crypto), - LibDir1 = filename:join([PrivDir, "lib"]), - {Status, LibDir} = - case erl_ddll:load_driver(LibDir1, crypto_drv) of - ok -> {ok,LibDir1}; - {error,Error1} -> - LibDir2 = - filename:join(LibDir1, - erlang:system_info(system_architecture)), - Candidate = - filelib:wildcard(filename:join([LibDir2,"crypto_drv*"])), - case Candidate of - [] -> - {{error,Error1},LibDir1}; - _ -> - case erl_ddll:load_driver(LibDir2, crypto_drv) of - ok -> - {ok,LibDir2}; - {error, Error2} -> - {{error,Error2},LibDir2} - end - end - end, - case Status of - ok -> - Cmd = "crypto_drv elibcrypto " ++ - filename:join([LibDir, "elibcrypto"]), - open_ports(Cmd,size(port_names())); - {error, E} -> - Str = erl_ddll:format_error(E), - error_logger:error_msg("Unable to load crypto_drv. Failed with error:~n\"~s\"~nOpenSSL might not be installed on this system.~n",[Str]), - {stop,nodriver} - end. - -open_ports(_,0) -> - {ok, []}; -open_ports(Cmd,N) -> - Port = open_port({spawn, Cmd}, []), - %% check that driver is loaded, linked and working - %% since crypto_drv links towards libcrypto, this is a good thing - %% since libcrypto is known to be bad with backwards compatibility - case catch port_control(Port, 0, []) of - {'EXIT', _} -> - {stop, nodriver}; - _ -> - register(element(N,port_names()), Port), - open_ports(Cmd,N-1) - end. - -port_names() -> - { crypto_drv01, crypto_drv02, crypto_drv03, crypto_drv04, - crypto_drv05, crypto_drv06, crypto_drv07, crypto_drv08, - crypto_drv09, crypto_drv10, crypto_drv11, crypto_drv12, - crypto_drv13, crypto_drv14, crypto_drv15, crypto_drv16 }. - -client_port() -> - element(erlang:system_info(scheduler_id) rem size(port_names()) + 1, - port_names()). + {ok,[]}. + %%% -------------------------------------------------------- @@ -112,11 +52,6 @@ handle_call(_, _, State) -> handle_cast(_, State) -> {noreply, State}. -handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) -> - {noreply, State}; - -handle_info({'EXIT', Port, Reason}, State) when is_port(Port) -> - {stop, {port_died, Reason}, State}; handle_info(_, State) -> {noreply, State}. @@ -124,13 +59,8 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. terminate(_Reason, _State) -> - close_ports(size(port_names())). + []. -close_ports(0) -> - ok; -close_ports(N) -> - element(N,port_names()) ! {self(), close}, - close_ports(N-1). diff --git a/lib/crypto/test/blowfish_SUITE.erl b/lib/crypto/test/blowfish_SUITE.erl index d4cc167ea9..d117e7cc3d 100644 --- a/lib/crypto/test/blowfish_SUITE.erl +++ b/lib/crypto/test/blowfish_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% @@ -116,7 +116,8 @@ all(suite) -> ecb_test(KeyBytes, ClearBytes, CipherBytes) -> {Key, Clear, Cipher} = {to_bin(KeyBytes), to_bin(ClearBytes), to_bin(CipherBytes)}, - crypto:blowfish_ecb_encrypt(Key, Clear) =:= Cipher. + ?line m(crypto:blowfish_ecb_encrypt(Key, Clear), Cipher), + true. ecb(doc) -> "Test that ECB mode is OK"; @@ -208,3 +209,5 @@ 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_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index ee0e5c0dae..08d7a0ce99 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -119,7 +119,7 @@ link_test(Config) when is_list(Config) -> link_test_1() -> ?line CryptoPriv = code:priv_dir(crypto), - ?line Wc = filename:join([CryptoPriv,"lib","crypto_drv.*"]), + ?line Wc = filename:join([CryptoPriv,"lib","crypto.*"]), ?line case filelib:wildcard(Wc) of [] -> {skip,"Didn't find the crypto driver"}; [Drv] -> link_test_2(Drv) @@ -843,16 +843,16 @@ dsa_sign_test(Config) when is_list(Config) -> ParamG = 18320614775012672475365915366944922415598782131828709277168615511695849821411624805195787607930033958243224786899641459701930253094446221381818858674389863050420226114787005820357372837321561754462061849169568607689530279303056075793886577588606958623645901271866346406773590024901668622321064384483571751669, Params = [crypto:mpint(ParamP), crypto:mpint(ParamQ), crypto:mpint(ParamG)], - ?line Sig1 = crypto:dss_sign(sized_binary(Msg), [Params, crypto:mpint(PrivKey)]), + ?line Sig1 = crypto:dss_sign(sized_binary(Msg), Params ++ [crypto:mpint(PrivKey)]), ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(Sig1), - [Params, crypto:mpint(PubKey)]), true), + Params ++ [crypto:mpint(PubKey)]), true), ?line m(crypto:dss_verify(sized_binary(one_bit_wrong(Msg)), sized_binary(Sig1), - [Params, crypto:mpint(PubKey)]), false), + Params ++ [crypto:mpint(PubKey)]), false), ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(one_bit_wrong(Sig1)), - [Params, crypto:mpint(PubKey)]), false), + Params ++ [crypto:mpint(PubKey)]), false), %%?line Bad = crypto:dss_sign(sized_binary(Msg), [Params, crypto:mpint(PubKey)]), diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index 68eecfe759..d04736fc2b 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 1.6.4 +CRYPTO_VSN = 2.0 diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 09a81122c2..3216b0c2cd 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -71,6 +71,13 @@ <p>Lev Walkin</p> </item> + <item> + <p>[httpc] - https requests with default port (443) not handled + properly. </p> + <p>Own Id: OTP-8607</p> + <p>jebu ittiachen</p> + </item> + </list> </section> diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index 5205605e0a..6deeab6948 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -432,7 +432,7 @@ handle_request(Method, Url, Options = request_options(Options0), Sync = proplists:get_value(sync, Options), Stream = proplists:get_value(stream, Options), - Host2 = header_host(Host, Port), + Host2 = header_host(Scheme, Host, Port), HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions), Receiver = proplists:get_value(receiver, Options), SocketOpts = proplists:get_value(socket_opts, Options), @@ -895,9 +895,11 @@ bad_option(Option, BadValue) -> throw({error, {bad_option, Option, BadValue}}). -header_host(Host, 80 = _Port) -> +header_host(https, Host, 443 = _Port) -> Host; -header_host(Host, Port) -> +header_host(http, Host, 80 = _Port) -> + Host; +header_host(_Scheme, Host, Port) -> Host ++ ":" ++ integer_to_list(Port). diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 7e2306d0c6..dfdfb41373 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -20,12 +20,14 @@ [ {"5.3.1", [ + {load_module, httpc, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, {update, httpc_manager, soft, soft_purge, soft_purge, []} ] }, {"5.3", [ + {load_module, httpc, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, {update, httpc_manager, soft, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []} @@ -50,12 +52,14 @@ [ {"5.3.1", [ + {load_module, httpc, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, {update, httpc_manager, soft, soft_purge, soft_purge, []} ] }, {"5.3", [ + {load_module, httpc, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, {update, httpc_manager, soft, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []} diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index ba808a2415..7776bef0a5 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -22,7 +22,7 @@ INETS_VSN = 5.3.2 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -TICKETS = OTP-8542 +TICKETS = OTP-8542 OTP-8607 TICKETS_5_3_1 = \ OTP-8508 \ diff --git a/lib/kernel/src/application_starter.erl b/lib/kernel/src/application_starter.erl index d72dca90c2..564366f304 100644 --- a/lib/kernel/src/application_starter.erl +++ b/lib/kernel/src/application_starter.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% %% ---------------------------------------------------------------------- diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl index 36f2de2af6..17dd02acd4 100644 --- a/lib/kernel/src/error_handler.erl +++ b/lib/kernel/src/error_handler.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% -module(error_handler). diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl index fc26e8dcf3..3ac35a209d 100644 --- a/lib/kernel/src/file_io_server.erl +++ b/lib/kernel/src/file_io_server.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% -module(file_io_server). diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl index 3710a00def..e78acfc7a6 100644 --- a/lib/kernel/src/heart.erl +++ b/lib/kernel/src/heart.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% -module(heart). diff --git a/lib/ssl/doc/src/new_ssl.xml b/lib/ssl/doc/src/new_ssl.xml index 08868a1b3c..ab6e112a35 100644 --- a/lib/ssl/doc/src/new_ssl.xml +++ b/lib/ssl/doc/src/new_ssl.xml @@ -581,6 +581,10 @@ end <p> Upgrades a gen_tcp, or equivalent, socket to a ssl socket e.i performs the ssl server-side handshake.</p> + <p><note>Note that the listen socket should be in {active, false} mode + before telling the client that the server is ready to upgrade + and calling this function, otherwise the upgrade may + or may not succeed depending on timing.</note></p> </desc> </func> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 3cd4c7fdbd..0ae3abfa37 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -147,7 +147,7 @@ transport_accept(ListenSocket) -> transport_accept(ListenSocket, infinity). transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts}}, - fd = new_ssl} = SslSocket, Timeout) -> + fd = new_ssl}, Timeout) -> %% The setopt could have been invoked on the listen socket %% and options should be inherited. @@ -163,8 +163,7 @@ transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts} {SslOpts, socket_options(InetValues)}, self(), CbInfo], case ssl_connection_sup:start_child(ConnArgs) of {ok, Pid} -> - CbModule:controlling_process(Socket, Pid), - {ok, SslSocket#sslsocket{pid = Pid}}; + ssl_connection:socket_control(Socket, Pid, CbModule); {error, Reason} -> {error, Reason} end; @@ -187,22 +186,9 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) -> ssl_accept(ListenSocket) -> ssl_accept(ListenSocket, infinity). -ssl_accept(#sslsocket{pid = Pid, fd = new_ssl}, Timeout) -> - gen_fsm:send_event(Pid, socket_control), - try gen_fsm:sync_send_all_state_event(Pid, started, Timeout) of - connected -> - ok; - {error, _} = Error -> - Error - catch - exit:{noproc, _} -> - {error, closed}; - exit:{timeout, _} -> - {error, timeout}; - exit:{normal, _} -> - {error, closed} - end; - +ssl_accept(#sslsocket{fd = new_ssl} = Socket, Timeout) -> + ssl_connection:handshake(Socket, Timeout); + ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) -> ssl_accept(ListenSocket, SslOptions, infinity); @@ -218,7 +204,7 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> try handle_options(SslOptions ++ InetValues, server) of {ok, #config{cb=CbInfo,ssl=SslOpts, emulated=EmOpts}} -> {ok, Port} = inet:port(Socket), - ssl_connection:accept(Port, Socket, + ssl_connection:ssl_accept(Port, Socket, {SslOpts, EmOpts}, self(), CbInfo, Timeout) catch @@ -239,7 +225,7 @@ close(Socket = #sslsocket{}) -> ssl_broker:close(Socket). %%-------------------------------------------------------------------- -%% Function: send(Socket, Data) -> ok +%% Function: send(Socket, Data) -> ok | {error, Reason} %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 8ff001b172..a406e86bbf 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -39,7 +39,8 @@ -include_lib("public_key/include/public_key.hrl"). %% Internal application API --export([send/2, send/3, recv/3, connect/7, accept/6, close/1, shutdown/2, +-export([send/2, send/3, recv/3, connect/7, ssl_accept/6, handshake/2, + socket_control/3, close/1, shutdown/2, new_user/2, get_opts/2, set_opts/2, info/1, session_info/1, peer_certificate/1, sockname/1, peername/1, renegotiation/1]). @@ -101,41 +102,70 @@ %%==================================================================== %%-------------------------------------------------------------------- -%% Function: +%% Function: send(Pid, Data) -> ok | {error, Reason} %% -%% Description: +%% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- send(Pid, Data) -> sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity). send(Pid, Data, Timeout) -> sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout). %%-------------------------------------------------------------------- -%% Function: +%% Function: recv(Socket, Length Timeout) -> {ok, Data} | {error, reason} %% -%% Description: +%% Description: Receives data when active = false %%-------------------------------------------------------------------- recv(Pid, Length, Timeout) -> sync_send_all_state_event(Pid, {recv, Length}, Timeout). %%-------------------------------------------------------------------- -%% Function: +%% Function: : connect(Host, Port, Socket, Options, +%% User, CbInfo, Timeout) -> {ok, Socket} %% -%% Description: +%% Description: Connect to a ssl server. %%-------------------------------------------------------------------- connect(Host, Port, Socket, Options, User, CbInfo, Timeout) -> start_fsm(client, Host, Port, Socket, Options, User, CbInfo, Timeout). %%-------------------------------------------------------------------- -%% Function: +%% Function: accept(Port, Socket, Opts, User, +%% CbInfo, Timeout) -> {ok, Socket} | {error, Reason} %% -%% Description: +%% Description: Performs accept on a ssl listen socket. e.i. performs +%% ssl handshake. %%-------------------------------------------------------------------- -accept(Port, Socket, Opts, User, CbInfo, Timeout) -> +ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) -> start_fsm(server, "localhost", Port, Socket, Opts, User, CbInfo, Timeout). + %%-------------------------------------------------------------------- -%% Function: +%% Function: handshake(SslSocket, Timeout) -> ok | {error, Reason} %% -%% Description: +%% Description: Starts ssl handshake. +%%-------------------------------------------------------------------- +handshake(#sslsocket{pid = Pid}, Timeout) -> + case sync_send_all_state_event(Pid, start, Timeout) of + connected -> + ok; + Error -> + Error + end. +%-------------------------------------------------------------------- +%% Function: socket_control(Pid) -> {ok, SslSocket} | {error, Reason} +%% +%% Description: Set the ssl process to own the accept socket +%%-------------------------------------------------------------------- +socket_control(Socket, Pid, CbModule) -> + case CbModule:controlling_process(Socket, Pid) of + ok -> + {ok, sslsocket(Pid)}; + {error, Reason} -> + {error, Reason} + end. + +%%-------------------------------------------------------------------- +%% Function: close() -> ok +%% +%% Description: Close a ssl connection %%-------------------------------------------------------------------- close(ConnectionPid) -> case sync_send_all_state_event(ConnectionPid, close) of @@ -146,80 +176,81 @@ close(ConnectionPid) -> end. %%-------------------------------------------------------------------- -%% Function: +%% Function: shutdown(Socket, How) -> ok | {error, Reason} %% -%% Description: +%% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- shutdown(ConnectionPid, How) -> sync_send_all_state_event(ConnectionPid, {shutdown, How}). - %%-------------------------------------------------------------------- -%% Function: +%% Function: new_user(ConnectionPid, User) -> ok | {error, Reason} %% -%% Description: +%% Description: Changes process that receives the messages when active = true +%% or once. %%-------------------------------------------------------------------- new_user(ConnectionPid, User) -> sync_send_all_state_event(ConnectionPid, {new_user, User}). %%-------------------------------------------------------------------- -%% Function: +%% Function: sockname(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason} %% -%% Description: +%% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- sockname(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, sockname). %%-------------------------------------------------------------------- -%% Function: +%% Function: peername(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason} %% -%% Description: +%% Description: Same as inet:peername/1 %%-------------------------------------------------------------------- peername(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peername). %%-------------------------------------------------------------------- -%% Function: +%% Function: get_opts(ConnectionPid, OptTags) -> {ok, Options} | {error, Reason} %% -%% Description: +%% Description: Same as inet:getopts/2 %%-------------------------------------------------------------------- get_opts({ListenSocket, {_SslOpts, SockOpts}, _}, OptTags) -> get_socket_opts(ListenSocket, OptTags, SockOpts, []); get_opts(ConnectionPid, OptTags) -> sync_send_all_state_event(ConnectionPid, {get_opts, OptTags}). %%-------------------------------------------------------------------- -%% Function: +%% Function: setopts(Socket, Options) -> ok | {error, Reason} %% -%% Description: +%% Description: Same as inet:setopts/2 %%-------------------------------------------------------------------- set_opts(ConnectionPid, Options) -> sync_send_all_state_event(ConnectionPid, {set_opts, Options}). %%-------------------------------------------------------------------- -%% Function: +%% Function: info(ConnectionPid) -> {ok, {Protocol, CipherSuite}} | +%% {error, Reason} %% -%% Description: +%% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- info(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, info). %%-------------------------------------------------------------------- -%% Function: +%% Function: session_info(ConnectionPid) -> {ok, PropList} | {error, Reason} %% -%% Description: +%% Description: Returns info about the ssl session %%-------------------------------------------------------------------- session_info(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, session_info). %%-------------------------------------------------------------------- -%% Function: +%% Function: peercert(ConnectionPid) -> {ok, Cert} | {error, Reason} %% -%% Description: +%% Description: Returns the peer cert %%-------------------------------------------------------------------- peer_certificate(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peer_certificate). %%-------------------------------------------------------------------- -%% Function: +%% Function: renegotiation(ConnectionPid) -> ok | {error, Reason} %% -%% Description: +%% Description: Starts a renegotiation of the ssl session. %%-------------------------------------------------------------------- renegotiation(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, renegotiate). @@ -269,7 +300,7 @@ init([Role, Host, Port, Socket, {SSLOpts, _} = Options, throw:Error -> {stop, Error} end. - + %%-------------------------------------------------------------------- %% Function: %% state_name(Event, State) -> {next_state, NextStateName, NextState}| @@ -282,8 +313,9 @@ init([Role, Host, Port, Socket, {SSLOpts, _} = Options, %% using gen_fsm:send_event/2, the instance of this function with the %% same name as the current state name StateName is called to handle %% the event. It is also called if a timeout occurs. +%% %%-------------------------------------------------------------------- -hello(socket_control, #state{host = Host, port = Port, role = client, +hello(start, #state{host = Host, port = Port, role = client, ssl_options = SslOpts, transport_cb = Transport, socket = Socket, connection_states = ConnectionStates} @@ -295,19 +327,22 @@ hello(socket_control, #state{host = Host, port = Port, role = client, {BinMsg, CS2, Hashes1} = encode_handshake(Hello, Version, ConnectionStates, Hashes0), Transport:send(Socket, BinMsg), - State = State0#state{connection_states = CS2, + State1 = State0#state{connection_states = CS2, negotiated_version = Version, %% Requested version session = #session{session_id = Hello#client_hello.session_id, is_resumable = false}, tls_handshake_hashes = Hashes1}, - {next_state, hello, next_record(State)}; + {Record, State} = next_record(State1), + next_state(hello, Record, State); -hello(socket_control, #state{role = server} = State) -> - {next_state, hello, next_record(State)}; +hello(start, #state{role = server} = State0) -> + {Record, State} = next_record(State0), + next_state(hello, Record, State); -hello(#hello_request{}, #state{role = client} = State) -> - {next_state, hello, State}; +hello(#hello_request{}, #state{role = client} = State0) -> + {Record, State} = next_record(State0), + next_state(hello, Record, State); hello(#server_hello{cipher_suite = CipherSuite, compression_method = Compression} = Hello, @@ -327,7 +362,7 @@ hello(#server_hello{cipher_suite = CipherSuite, PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), - State = State0#state{key_algorithm = KeyAlgorithm, + State1 = State0#state{key_algorithm = KeyAlgorithm, negotiated_version = Version, connection_states = ConnectionStates1, premaster_secret = PremasterSecret}, @@ -337,20 +372,21 @@ hello(#server_hello{cipher_suite = CipherSuite, Session = Session0#session{session_id = NewId, cipher_suite = CipherSuite, compression_method = Compression}, - {next_state, certify, - next_record(State#state{session = Session})}; + {Record, State} = next_record(State1#state{session = Session}), + next_state(certify, Record, State); false -> Session = CacheCb:lookup(Cache, {{Host, Port}, NewId}), case ssl_handshake:master_secret(Version, Session, ConnectionStates1, client) of {_, ConnectionStates2} -> - {next_state, abbreviated, - next_record(State#state{ - connection_states = ConnectionStates2, - session = Session})}; + {Record, State} = + next_record(State1#state{ + connection_states = ConnectionStates2, + session = Session}), + next_state(abbreviated, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State), - {stop, normal, State} + handle_own_alert(Alert, Version, hello, State1), + {stop, normal, State1} end end; @@ -372,50 +408,53 @@ hello(Hello = #client_hello{client_version = ClientVersion}, #alert{} = Alert -> handle_own_alert(Alert, ClientVersion, hello, State), {stop, normal, State} - end. + end; -abbreviated(socket_control, #state{role = server} = State) -> - {next_state, abbreviated, State}; -abbreviated(#hello_request{}, State) -> - {next_state, certify, State}; +hello(Msg, State) -> + handle_unexpected_message(Msg, hello, State). + +abbreviated(#hello_request{}, State0) -> + {Record, State} = next_record(State0), + next_state(hello, Record, State); abbreviated(Finished = #finished{}, #state{role = server, negotiated_version = Version, tls_handshake_hashes = Hashes, session = #session{master_secret = MasterSecret}} = - State0) -> + State) -> case ssl_handshake:verify_connection(Version, Finished, client, MasterSecret, Hashes) of - verified -> - State = ack_connection(State0), - next_state_connection(State); + verified -> + next_state_connection(abbreviated, ack_connection(State)); #alert{} = Alert -> - handle_own_alert(Alert, Version, abbreviated, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, abbreviated, State), + {stop, normal, State} end; abbreviated(Finished = #finished{}, #state{role = client, tls_handshake_hashes = Hashes0, session = #session{master_secret = MasterSecret}, - negotiated_version = Version} = State0) -> + negotiated_version = Version} = State) -> case ssl_handshake:verify_connection(Version, Finished, server, MasterSecret, Hashes0) of verified -> - {ConnectionStates, Hashes} = finalize_client_handshake(State0), - State = ack_connection(State0), - next_state_connection(State#state{tls_handshake_hashes = Hashes, - connection_states = - ConnectionStates}); + {ConnectionStates, Hashes} = finalize_client_handshake(State), + next_state_connection(abbreviated, + ack_connection(State#state{tls_handshake_hashes = Hashes, + connection_states = + ConnectionStates})); #alert{} = Alert -> - handle_own_alert(Alert, Version, abbreviated, State0), - {stop, normal, State0} - end. + handle_own_alert(Alert, Version, abbreviated, State), + {stop, normal, State} + end; + +abbreviated(Msg, State) -> + handle_unexpected_message(Msg, abbreviated, State). -certify(socket_control, #state{role = server} = State) -> - {next_state, certify, State}; -certify(#hello_request{}, State) -> - {next_state, certify, State}; +certify(#hello_request{}, State0) -> + {Record, State} = next_record(State0), + next_state(hello, Record, State); certify(#certificate{asn1_certificates = []}, #state{role = server, negotiated_version = Version, @@ -430,9 +469,9 @@ certify(#certificate{asn1_certificates = []}, #state{role = server, ssl_options = #ssl_options{verify = verify_peer, fail_if_no_peer_cert = false}} = - State) -> - {next_state, certify, - next_record(State#state{client_certificate_requested = false})}; + State0) -> + {Record, State} = next_record(State0#state{client_certificate_requested = false}), + next_state(certify, Record, State); certify(#certificate{} = Cert, #state{negotiated_version = Version, @@ -456,8 +495,9 @@ certify(#server_key_exchange{} = KeyExchangeMsg, key_algorithm = Alg} = State0) when Alg == dhe_dss; Alg == dhe_rsa ->%%Not imp:Alg == dh_anon;Alg == krb5 -> case handle_server_key(KeyExchangeMsg, State0) of - #state{} = State -> - {next_state, certify, next_record(State)}; + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, Record, State); #alert{} = Alert -> handle_own_alert(Alert, Version, certify_server_keyexchange, State0), @@ -472,10 +512,9 @@ certify(#server_key_exchange{}, handle_own_alert(Alert, Version, certify_server_key_exchange, State), {stop, normal, State}; -certify(#certificate_request{}, State) -> - NewState = State#state{client_certificate_requested = true}, - {next_state, certify, next_record(NewState)}; - +certify(#certificate_request{}, State0) -> + {Record, State} = next_record(State0#state{client_certificate_requested = true}), + next_state(certify, Record, State); %% Master secret was determined with help of server-key exchange msg certify(#server_hello_done{}, @@ -540,9 +579,10 @@ certify(#client_key_exchange{exchange_keys ConnectionStates0, server) of {MasterSecret, ConnectionStates} -> Session = Session0#session{master_secret = MasterSecret}, - State = State0#state{connection_states = ConnectionStates, + State1 = State0#state{connection_states = ConnectionStates, session = Session}, - {next_state, cipher, next_record(State)}; + {Record, State} = next_record(State1), + next_state(cipher, Record, State); #alert{} = Alert -> handle_own_alert(Alert, Version, certify_client_key_exchange, State0), @@ -574,21 +614,25 @@ certify(#client_key_exchange{exchange_keys = #client_diffie_hellman_public{ case ssl_handshake:master_secret(Version, PremasterSecret, ConnectionStates0, Role) of {MasterSecret, ConnectionStates} -> - State = State0#state{session = + State1 = State0#state{session = Session#session{master_secret = MasterSecret}, connection_states = ConnectionStates}, - {next_state, cipher, next_record(State)}; + + {Record, State} = next_record(State1), + next_state(cipher, Record, State); #alert{} = Alert -> handle_own_alert(Alert, Version, certify_client_key_exchange, State0), {stop, normal, State0} - end. + end; + +certify(Msg, State) -> + handle_unexpected_message(Msg, certify, State). -cipher(socket_control, #state{role = server} = State) -> - {next_state, cipher, State}; -cipher(#hello_request{}, State) -> - {next_state, cipher, State}; +cipher(#hello_request{}, State0) -> + {Record, State} = next_record(State0), + next_state(hello, Record, State); cipher(#certificate_verify{signature = Signature}, #state{role = server, @@ -597,15 +641,16 @@ cipher(#certificate_verify{signature = Signature}, session = #session{master_secret = MasterSecret}, key_algorithm = Algorithm, tls_handshake_hashes = Hashes - } = State) -> + } = State0) -> case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, Version, MasterSecret, Algorithm, Hashes) of valid -> - {next_state, cipher, next_record(State)}; + {Record, State} = next_record(State0), + next_state(cipher, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, cipher, State), - {stop, normal, State} + handle_own_alert(Alert, Version, cipher, State0), + {stop, normal, State0} end; cipher(#finished{} = Finished, @@ -615,53 +660,57 @@ cipher(#finished{} = Finished, role = Role, session = #session{master_secret = MasterSecret} = Session0, - tls_handshake_hashes = Hashes} = State0) -> + tls_handshake_hashes = Hashes} = State) -> case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), MasterSecret, Hashes) of verified -> - State = ack_connection(State0), Session = register_session(Role, Host, Port, Session0), case Role of client -> - next_state_connection(State#state{session = Session}); + next_state_connection(cipher, ack_connection(State#state{session = Session})); server -> {NewConnectionStates, NewHashes} = finalize_server_handshake(State#state{ session = Session}), - next_state_connection(State#state{connection_states = - NewConnectionStates, - session = Session, - tls_handshake_hashes = - NewHashes}) + next_state_connection(cipher, ack_connection(State#state{connection_states = + NewConnectionStates, + session = Session, + tls_handshake_hashes = + NewHashes})) end; #alert{} = Alert -> - handle_own_alert(Alert, Version, cipher, State0), - {stop, normal, State0} - end. + handle_own_alert(Alert, Version, cipher, State), + {stop, normal, State} + end; + +cipher(Msg, State) -> + handle_unexpected_message(Msg, cipher, State). -connection(socket_control, #state{role = server} = State) -> - {next_state, connection, State}; -connection(#hello_request{}, State = #state{host = Host, port = Port, - socket = Socket, - ssl_options = SslOpts, - negotiated_version = Version, - transport_cb = Transport, - connection_states = ConnectionStates0, - tls_handshake_hashes = Hashes0}) -> +connection(#hello_request{}, #state{host = Host, port = Port, + socket = Socket, + ssl_options = SslOpts, + negotiated_version = Version, + transport_cb = Transport, + connection_states = ConnectionStates0, + tls_handshake_hashes = Hashes0} = State0) -> + Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Hello, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinMsg), - {next_state, hello, next_record(State#state{connection_states = - ConnectionStates1, - tls_handshake_hashes = Hashes1})}; + {Record, State} = next_record(State0#state{connection_states = + ConnectionStates1, + tls_handshake_hashes = Hashes1}), + next_state(hello, Record, State); connection(#client_hello{} = Hello, #state{role = server} = State) -> - hello(Hello, State). + hello(Hello, State); +connection(Msg, State) -> + handle_unexpected_message(Msg, connection, State). %%-------------------------------------------------------------------- %% Function: %% handle_event(Event, StateName, State) -> {next_state, NextStateName, @@ -673,110 +722,8 @@ connection(#client_hello{} = Hello, #state{role = server} = State) -> %% gen_fsm:send_all_state_event/2, this function is called to handle %% the event. %%-------------------------------------------------------------------- -handle_event(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, - StateName, - State0 = #state{key_algorithm = KeyAlg, - tls_handshake_buffer = Buf0, - negotiated_version = Version}) -> - Handle = - fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) -> - %% This message should not be included in handshake - %% message hashes. Starts new handshake (renegotiation) - Hs0 = ssl_handshake:init_hashes(), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0, - renegotiation = {true, peer}}); - ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> - %% This message should not be included in handshake - %% message hashes. Already in negotiation so it will be ignored! - ?MODULE:SName(Packet, State); - ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> - Hs0 = ssl_handshake:init_hashes(), - Hs1 = ssl_handshake:update_hashes(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1, - renegotiation = {true, peer}}); - ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) -> - Hs1 = ssl_handshake:update_hashes(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1}); - (_, StopState) -> StopState - end, - try - {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version), - Start = {next_state, StateName, State0#state{tls_handshake_buffer = Buf}}, - lists:foldl(Handle, Start, Packets) - catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State0), - {stop, normal, State0} - end; - -handle_event(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, - StateName, State0) -> - case application_data(Data, State0) of - Stop = {stop,_,_} -> - Stop; - State -> - {next_state, StateName, State} - end; - -handle_event(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} = - _ChangeCipher, - StateName, - State = #state{connection_states = ConnectionStates0}) -> - ?DBG_TERM(_ChangeCipher), - ConnectionStates1 = - ssl_record:activate_pending_connection_state(ConnectionStates0, read), - {next_state, StateName, - next_record(State#state{connection_states = ConnectionStates1})}; - -handle_event(#ssl_tls{type = ?ALERT, fragment = Data}, StateName, State) -> - Alerts = decode_alerts(Data), - ?DBG_TERM(Alerts), - [alert_event(A) || A <- Alerts], - {next_state, StateName, State}; - -handle_event(#alert{level = ?FATAL} = Alert, connection, - #state{from = From, user_application = {_Mon, Pid}, - log_alert = Log, - host = Host, port = Port, session = Session, - role = Role, socket_options = Opts} = State) -> - invalidate_session(Role, Host, Port, Session), - log_alert(Log, connection, Alert), - alert_user(Opts#socket_options.active, Pid, From, Alert, Role), - {stop, normal, State}; -handle_event(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, - connection, #state{from = From, - role = Role, - user_application = {_Mon, Pid}, - socket_options = Opts} = State) -> - alert_user(Opts#socket_options.active, Pid, From, Alert, Role), - {stop, normal, State}; - -handle_event(#alert{level = ?FATAL} = Alert, StateName, - #state{from = From, host = Host, port = Port, session = Session, - log_alert = Log, role = Role} = State) -> - invalidate_session(Role, Host, Port, Session), - log_alert(Log, StateName, Alert), - alert_user(From, Alert, Role), - {stop, normal, State}; -handle_event(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, - _, #state{from = From, role = Role} = State) -> - alert_user(From, Alert, Role), - {stop, normal, State}; - -handle_event(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, - #state{log_alert = Log, renegotiation = {true, internal}} = State) -> - log_alert(Log, StateName, Alert), - {stop, normal, State}; - -handle_event(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, - #state{log_alert = Log, renegotiation = {true, From}} = State) -> - log_alert(Log, StateName, Alert), - gen_fsm:reply(From, {error, renegotiation_rejected}), - {next_state, connection, next_record(State)}; - -handle_event(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName, - #state{log_alert = Log} = State) -> - log_alert(Log, StateName, Alert), - {next_state, StateName, next_record(State)}. +handle_event(_Event, StateName, State) -> + {next_state, StateName, State}. %%-------------------------------------------------------------------- %% Function: @@ -830,27 +777,41 @@ handle_sync_event({application_data, Data}, From, StateName, #state{send_queue = Queue} = State) -> %% In renegotiation priorities handshake, send data when handshake is finished {next_state, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}}; -handle_sync_event(started, From, StateName, State) -> + +handle_sync_event(start, From, hello, State) -> + hello(start, State#state{from = From}); + +%% The two clauses below could happen if a server upgrades a socket in +%% active mode. Note that in this case we are lucky that +%% controlling_process has been evalueated before receiving handshake +%% messages from client. The server should put the socket in passive +%% mode before telling the client that it is willing to upgrade +%% and before calling ssl:ssl_accept/2. These clauses are +%% here to make sure it is the users problem and not owers if +%% they upgrade a active socket. +handle_sync_event(start, _, connection, State) -> + {reply, connected, connection, State}; +handle_sync_event(start, From, StateName, State) -> {next_state, StateName, State#state{from = From}}; -handle_sync_event(close, From, _StateName, State) -> - {stop, normal, ok, State#state{from = From}}; +handle_sync_event(close, _, _StateName, State) -> + {stop, normal, ok, State}; -handle_sync_event({shutdown, How}, From, StateName, +handle_sync_event({shutdown, How}, _, StateName, #state{transport_cb = CbModule, socket = Socket} = State) -> case CbModule:shutdown(Socket, How) of ok -> {reply, ok, StateName, State}; Error -> - {stop, normal, Error, State#state{from = From}} + {stop, normal, Error, State} end; handle_sync_event({recv, N}, From, connection = StateName, State0) -> passive_receive(State0#state{bytes_to_read = N, from = From}, StateName); %% Doing renegotiate wait with handling request until renegotiate is -%% finished. Will be handled by next_state_connection/1. +%% finished. Will be handled by next_state_connection/2. handle_sync_event({recv, N}, From, StateName, State) -> {next_state, StateName, State#state{bytes_to_read = N, from = From, recv_during_renegotiation = true}}; @@ -888,7 +849,13 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, {reply, ok, StateName, State1}; Buffer =:= <<>>, Opts1#socket_options.active =:= false -> %% Need data, set active once - {reply, ok, StateName, next_record_if_active(State1)}; + {Record, State2} = next_record_if_active(State1), + case next_state(StateName, Record, State2) of + {next_state, StateName, State} -> + {reply, ok, StateName, State}; + {stop, Reason, State} -> + {stop, Reason, State} + end; Buffer =:= <<>> -> %% Active once already set {reply, ok, StateName, State1}; @@ -896,10 +863,15 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, case application_data(<<>>, State1) of Stop = {stop,_,_} -> Stop; - State -> - {reply, ok, StateName, State} + {Record, State2} -> + case next_state(StateName, Record, State2) of + {next_state, StateName, State} -> + {reply, ok, StateName, State}; + {stop, Reason, State} -> + {stop, Reason, State} + end end - end; + end; handle_sync_event(renegotiate, From, connection, State) -> renegotiate(State#state{renegotiation = {true, From}}); @@ -939,20 +911,20 @@ handle_sync_event(peer_certificate, _, StateName, %%-------------------------------------------------------------------- %% raw data from TCP, unpack records -handle_info({Protocol, _, Data}, StateName, State = +handle_info({Protocol, _, Data}, StateName, #state{data_tag = Protocol, negotiated_version = Version, tls_record_buffer = Buf0, - tls_cipher_texts = CT0}) -> + tls_cipher_texts = CT0} = State0) -> case ssl_record:get_tls_records(Data, Buf0) of {Records, Buf1} -> CT1 = CT0 ++ Records, - {next_state, StateName, - next_record(State#state{tls_record_buffer = Buf1, - tls_cipher_texts = CT1})}; + {Record, State} = next_record(State0#state{tls_record_buffer = Buf1, + tls_cipher_texts = CT1}), + next_state(StateName, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State), - {stop, normal, State} + handle_own_alert(Alert, Version, StateName, State0), + {stop, normal, State0} end; handle_info({CloseTag, Socket}, _StateName, @@ -972,7 +944,7 @@ handle_info({CloseTag, Socket}, _StateName, alert_user(Opts#socket_options.active, Pid, From, ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), Role), {stop, normal, State}; - + handle_info({'DOWN', MonitorRef, _, _, _}, _, State = #state{user_application={MonitorRef,_Pid}}) -> {stop, normal, State}; @@ -998,12 +970,14 @@ terminate(_Reason, connection, #state{negotiated_version = Version, {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING,?CLOSE_NOTIFY), Version, ConnectionStates), Transport:send(Socket, BinAlert), + Transport:shutdown(Socket, read_write), Transport:close(Socket); terminate(_Reason, _StateName, #state{transport_cb = Transport, socket = Socket, send_queue = SendQueue, renegotiation = Renegotiate}) -> notify_senders(SendQueue), notify_renegotiater(Renegotiate), + Transport:shutdown(Socket, read_write), Transport:close(Socket). %%-------------------------------------------------------------------- @@ -1022,13 +996,16 @@ start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_} = CbInfo, case ssl_connection_sup:start_child([Role, Host, Port, Socket, Opts, User, CbInfo]) of {ok, Pid} -> - CbModule:controlling_process(Socket, Pid), - send_event(Pid, socket_control), - case sync_send_all_state_event(Pid, started, Timeout) of - connected -> - {ok, sslsocket(Pid)}; - {error, Reason} -> - {error, Reason} + case socket_control(Socket, Pid, CbModule) of + {ok, SslSocket} -> + case handshake(SslSocket, Timeout) of + ok -> + {ok, SslSocket}; + {error, Reason} -> + {error, Reason} + end; + {error, Reason} -> + {error, Reason} end; {error, Reason} -> {error, Reason} @@ -1128,13 +1105,6 @@ init_diffie_hellman(DHParamFile, server) -> ?DEFAULT_DIFFIE_HELLMAN_PARAMS end. -send_event(FsmPid, Event) -> - gen_fsm:send_event(FsmPid, Event). - - -send_all_state_event(FsmPid, Event) -> - gen_fsm:send_all_state_event(FsmPid, Event). - sync_send_all_state_event(FsmPid, Event) -> sync_send_all_state_event(FsmPid, Event, ?DEFAULT_TIMEOUT). @@ -1149,19 +1119,16 @@ sync_send_all_state_event(FsmPid, Event, Timeout) -> {error, closed} end. -%% Events: #alert{} -alert_event(Alert) -> - send_all_state_event(self(), Alert). - %% We do currently not support cipher suites that use fixed DH. %% If we want to implement that we should add a code %% here to extract DH parameters form cert. handle_peer_cert(PeerCert, PublicKeyInfo, #state{session = Session} = State0) -> - State = State0#state{session = + State1 = State0#state{session = Session#session{peer_certificate = PeerCert}, public_key_info = PublicKeyInfo}, - {next_state, certify, next_record(State)}. + {Record, State} = next_record(State1), + next_state(certify, Record, State). certify_client(#state{client_certificate_requested = true, role = client, connection_states = ConnectionStates0, @@ -1226,10 +1193,11 @@ do_server_hello(Type, #state{negotiated_version = Version, session = Session}, {ConnectionStates, Hashes} = finalize_server_handshake(State1), - Resumed = State1#state{connection_states = + Resumed0 = State1#state{connection_states = ConnectionStates, tls_handshake_hashes = Hashes}, - {next_state, abbreviated, next_record(Resumed)}; + {Record, Resumed} = next_record(Resumed0), + next_state(abbreviated, Record, Resumed); #alert{} = Alert -> handle_own_alert(Alert, Version, hello, State), {stop, normal, State} @@ -1243,12 +1211,13 @@ do_server_hello(#server_hello{cipher_suite = CipherSuite, negotiated_version = Version} = State0) -> try server_certify_and_key_exchange(State0) of #state{} = State1 -> - State = server_hello_done(State1), + State2 = server_hello_done(State1), Session = Session0#session{session_id = SessionId, cipher_suite = CipherSuite, compression_method = Compression}, - {next_state, certify, State#state{session = Session}} + {Record, State} = next_record(State2#state{session = Session}), + next_state(certify, Record, State) catch #alert{} = Alert -> handle_own_alert(Alert, Version, hello, State0), @@ -1260,15 +1229,15 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} = try do_client_certify_and_key_exchange(State0) of State1 = #state{} -> {ConnectionStates, Hashes} = finalize_client_handshake(State1), - State = State1#state{connection_states = ConnectionStates, + State2 = State1#state{connection_states = ConnectionStates, %% Reinitialize client_certificate_requested = false, tls_handshake_hashes = Hashes}, - {next_state, cipher, next_record(State)} - + {Record, State} = next_record(State2), + next_state(cipher, Record, State) catch #alert{} = Alert -> - handle_own_alert(Alert, Version, certify_foo, State0), + handle_own_alert(Alert, Version, client_certify_and_key_exchange, State0), {stop, normal, State0} end. @@ -1301,17 +1270,16 @@ server_hello_done(#state{transport_cb = Transport, socket = Socket, negotiated_version = Version, connection_states = ConnectionStates, - tls_handshake_hashes = Hashes} = State0) -> + tls_handshake_hashes = Hashes} = State) -> HelloDone = ssl_handshake:server_hello_done(), - + {BinHelloDone, NewConnectionStates, NewHashes} = encode_handshake(HelloDone, Version, ConnectionStates, Hashes), Transport:send(Socket, BinHelloDone), - State = State0#state{connection_states = NewConnectionStates, - tls_handshake_hashes = NewHashes}, - next_record(State). - + State#state{connection_states = NewConnectionStates, + tls_handshake_hashes = NewHashes}. + certify_server(#state{transport_cb = Transport, socket = Socket, negotiated_version = Version, @@ -1622,14 +1590,14 @@ decode_alerts(<<>>, Acc) -> passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> case Buffer of <<>> -> - State = next_record(State0), - {next_state, StateName, State}; + {Record, State} = next_record(State0), + next_state(StateName, Record, State); _ -> case application_data(<<>>, State0) of Stop = {stop, _, _} -> Stop; - State -> - {next_state, StateName, State} + {Record, State} -> + next_state(StateName, Record, State) end end. @@ -1654,12 +1622,12 @@ application_data(Data, #state{user_application = {_Mon, Pid}, socket_options = SocketOpt }, if - SocketOpt#socket_options.active =:= false -> - State; %% Passive mode, wait for active once or recv - Buffer =:= <<>> -> %% Active and empty, get more data - next_record(State); - true -> %% We have more data - application_data(<<>>, State) + SocketOpt#socket_options.active =:= false; Buffer =:= <<>> -> + %% Passive mode, wait for active once or recv + %% Active and empty, get more data + next_record_if_active(State); + true -> %% We have more data + application_data(<<>>, State) end; {error,_Reason} -> %% Invalid packet in packet mode deliver_packet_error(SOpts, Buffer1, Pid, From), @@ -1727,25 +1695,45 @@ deliver_app_data(SOpts = #socket_options{active=Active, packet=Type}, SO end. -format_reply(#socket_options{active=false, mode=Mode, header=Header}, Data) -> - {ok, format_reply(Mode, Header, Data)}; -format_reply(#socket_options{active=_, mode=Mode, header=Header}, Data) -> - {ssl, sslsocket(), format_reply(Mode, Header, Data)}. +format_reply(#socket_options{active = false, mode = Mode, packet = Packet, + header = Header}, Data) -> + {ok, format_reply(Mode, Packet, Header, Data)}; +format_reply(#socket_options{active = _, mode = Mode, packet = Packet, + header = Header}, Data) -> + {ssl, sslsocket(), format_reply(Mode, Packet, Header, Data)}. -deliver_packet_error(SO= #socket_options{active=Active}, Data, Pid, From) -> +deliver_packet_error(SO= #socket_options{active = Active}, Data, Pid, From) -> send_or_reply(Active, Pid, From, format_packet_error(SO, Data)). -format_packet_error(#socket_options{active=false, mode=Mode}, Data) -> - {error, {invalid_packet, format_reply(Mode, raw, Data)}}; -format_packet_error(#socket_options{active=_, mode=Mode}, Data) -> - {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, Data)}}. - -format_reply(list, _, Data) -> binary_to_list(Data); -format_reply(binary, 0, Data) -> Data; -format_reply(binary, raw, Data) -> Data; -format_reply(binary, N, Data) -> % Header mode - <<Header:N/binary, Rest/binary>> = Data, - [binary_to_list(Header), Rest]. +format_packet_error(#socket_options{active = false, mode = Mode}, Data) -> + {error, {invalid_packet, format_reply(Mode, raw, 0, Data)}}; +format_packet_error(#socket_options{active = _, mode = Mode}, Data) -> + {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, 0, Data)}}. + +format_reply(_, http, _,Data) -> Data; +format_reply(_, http_bin, _, Data) -> Data; +format_reply(_, {http, headers}, _,Data) -> Data; +format_reply(_, {http_bin, headers}, _, Data) -> Data; +format_reply(_, asn1, _,Data) -> Data; +format_reply(_, cdr, _, Data) -> Data; +format_reply(_, sunrm, _,Data) -> Data; +format_reply(_, fcgi, _, Data) -> Data; +format_reply(_, tpkt, _, Data) -> Data; +format_reply(_, line, _, Data) -> Data; +format_reply(binary, _, N, Data) when N > 0 -> % Header mode + header(N, Data); +format_reply(binary, _, _, Data) -> Data; +format_reply(list, _, _, Data) -> binary_to_list(Data). + +header(0, <<>>) -> + <<>>; +header(_, <<>>) -> + []; +header(0, Binary) -> + Binary; +header(N, Binary) -> + <<?BYTE(ByteN), NewBinary/binary>> = Binary, + [ByteN | header(N-1, NewBinary)]. %% tcp_closed send_or_reply(false, _Pid, undefined, _Data) -> @@ -1766,40 +1754,101 @@ opposite_role(server) -> send_user(Pid, Msg) -> Pid ! Msg. +next_state(Next, no_record, State) -> + {next_state, Next, State}; + +next_state(Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) -> + Alerts = decode_alerts(EncAlerts), + handle_alerts(Alerts, {next_state, Next, State}); + +next_state(StateName, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, + State0 = #state{key_algorithm = KeyAlg, + tls_handshake_buffer = Buf0, + negotiated_version = Version}) -> + Handle = + fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) -> + %% This message should not be included in handshake + %% message hashes. Starts new handshake (renegotiation) + Hs0 = ssl_handshake:init_hashes(), + ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0, + renegotiation = {true, peer}}); + ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> + %% This message should not be included in handshake + %% message hashes. Already in negotiation so it will be ignored! + ?MODULE:SName(Packet, State); + ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> + Hs0 = ssl_handshake:init_hashes(), + Hs1 = ssl_handshake:update_hashes(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1, + renegotiation = {true, peer}}); + ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) -> + Hs1 = ssl_handshake:update_hashes(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1}); + (_, StopState) -> StopState + end, + try + {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version), + Start = {next_state, StateName, State0#state{tls_handshake_buffer = Buf}}, + lists:foldl(Handle, Start, Packets) + catch throw:#alert{} = Alert -> + handle_own_alert(Alert, Version, StateName, State0), + {stop, normal, State0} + end; + +next_state(StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) -> + case application_data(Data, State0) of + Stop = {stop,_,_} -> + Stop; + {Record, State} -> + next_state(StateName, Record, State) + end; + +next_state(StateName, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} = + _ChangeCipher, + #state{connection_states = ConnectionStates0} = State0) -> + ?DBG_TERM(_ChangeCipher), + ConnectionStates1 = + ssl_record:activate_pending_connection_state(ConnectionStates0, read), + {Record, State} = next_record(State0#state{connection_states = ConnectionStates1}), + next_state(StateName, Record, State); +next_state(StateName, #ssl_tls{type = _Unknown}, State0) -> + %% Ignore unknown type + {Record, State} = next_record(State0), + next_state(StateName, Record, State). + next_record(#state{tls_cipher_texts = [], socket = Socket} = State) -> inet:setopts(Socket, [{active,once}]), - State; + {no_record, State}; next_record(#state{tls_cipher_texts = [CT | Rest], connection_states = ConnStates0} = State) -> {Plain, ConnStates} = ssl_record:decode_cipher_text(CT, ConnStates0), - gen_fsm:send_all_state_event(self(), Plain), - State#state{tls_cipher_texts = Rest, connection_states = ConnStates}. - + {Plain, State#state{tls_cipher_texts = Rest, connection_states = ConnStates}}. next_record_if_active(State = #state{socket_options = #socket_options{active = false}}) -> - State; + {no_record ,State}; next_record_if_active(State) -> next_record(State). -next_state_connection(#state{send_queue = Queue0, - negotiated_version = Version, - socket = Socket, - transport_cb = Transport, - connection_states = ConnectionStates0, - ssl_options = #ssl_options{renegotiate_at = RenegotiateAt} - } = State) -> +next_state_connection(StateName, #state{send_queue = Queue0, + negotiated_version = Version, + socket = Socket, + transport_cb = Transport, + connection_states = ConnectionStates0, + ssl_options = #ssl_options{renegotiate_at = RenegotiateAt} + } = State) -> %% Send queued up data case queue:out(Queue0) of {{value, {From, Data}}, Queue} -> case encode_data(Data, Version, ConnectionStates0, RenegotiateAt) of {Msgs, [], ConnectionStates} -> Result = Transport:send(Socket, Msgs), - gen_fsm:reply(From, Result), - next_state_connection(State#state{connection_states = ConnectionStates, - send_queue = Queue}); + gen_fsm:reply(From, Result), + next_state_connection(StateName, + State#state{connection_states = ConnectionStates, + send_queue = Queue}); %% This is unlikely to happen. User configuration of the %% undocumented test option renegotiation_at can make it more likely. {Msgs, RestData, ConnectionStates} -> @@ -1822,9 +1871,9 @@ next_state_is_connection(State = #socket_options{active = false}}) -> passive_receive(State#state{recv_during_renegotiation = false}, connection); -next_state_is_connection(State) -> - {next_state, connection, next_record_if_active(State)}. - +next_state_is_connection(State0) -> + {Record, State} = next_record_if_active(State0), + next_state(connection, Record, State). register_session(_, _, _, #session{is_resumable = true} = Session) -> Session; %% Already registered @@ -1934,10 +1983,71 @@ set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) -> set_socket_opts(Socket, [Opt | Opts], SockOpts, Other) -> set_socket_opts(Socket, Opts, SockOpts, [Opt | Other]). +handle_alerts([], Result) -> + Result; +handle_alerts(_, {stop, _, _} = Stop) -> + %% If it is a fatal alert immediately close + Stop; +handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> + handle_alerts(Alerts, handle_alert(Alert, StateName, State)). + +handle_alert(#alert{level = ?FATAL} = Alert, connection, + #state{from = From, user_application = {_Mon, Pid}, + log_alert = Log, + host = Host, port = Port, session = Session, + role = Role, socket_options = Opts} = State) -> + invalidate_session(Role, Host, Port, Session), + log_alert(Log, connection, Alert), + alert_user(Opts#socket_options.active, Pid, From, Alert, Role), + {stop, normal, State}; + +handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, + connection, #state{from = From, + role = Role, + user_application = {_Mon, Pid}, + socket_options = Opts} = State) -> + alert_user(Opts#socket_options.active, Pid, From, Alert, Role), + {stop, normal, State}; + +handle_alert(#alert{level = ?FATAL} = Alert, StateName, + #state{from = From, host = Host, port = Port, session = Session, + log_alert = Log, role = Role} = State) -> + invalidate_session(Role, Host, Port, Session), + log_alert(Log, StateName, Alert), + alert_user(From, Alert, Role), + {stop, normal, State}; +handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, + _, #state{from = From, role = Role} = State) -> + alert_user(From, Alert, Role), + {stop, normal, State}; + +handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, + #state{log_alert = Log, renegotiation = {true, internal}, from = From, + role = Role} = State) -> + log_alert(Log, StateName, Alert), + alert_user(From, Alert, Role), + {stop, normal, State}; + +handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, + #state{log_alert = Log, renegotiation = {true, From}} = State0) -> + log_alert(Log, StateName, Alert), + gen_fsm:reply(From, {error, renegotiation_rejected}), + {Record, State} = next_record(State0), + next_state(connection, Record, State); + +handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName, + #state{log_alert = Log} = State0) -> + log_alert(Log, StateName, Alert), + {Record, State} = next_record(State0), + next_state(StateName, Record, State). + alert_user(From, Alert, Role) -> alert_user(false, no_pid, From, Alert, Role). alert_user(false = Active, Pid, From, Alert, Role) -> + %% If there is an outstanding ssl_accept | recv + %% From will be defined and send_or_reply will + %% send the appropriate error message. ReasonCode = ssl_alert:reason_code(Alert, Role), send_or_reply(Active, Pid, From, {error, ReasonCode}); alert_user(Active, Pid, From, Alert, Role) -> @@ -1965,7 +2075,13 @@ handle_own_alert(Alert, Version, StateName, log_alert = Log}) -> try %% Try to tell the other side {BinMsg, _} = - encode_alert(Alert, Version, ConnectionStates), + encode_alert(Alert, Version, ConnectionStates), + %% Try to make sure alert will be sent before socket is closed + %% when process ends. This will help on some + %% linux platforms and knowingly not break anything on other + %% platforms. Other platforms will benefit from shutdown that is now + %% done before close. + inet:setopts(Socket, [{nodelay, true}]), Transport:send(Socket, BinMsg) catch _:_ -> %% Can crash if we are in a uninitialized state ignore @@ -1976,6 +2092,12 @@ handle_own_alert(Alert, Version, StateName, catch _:_ -> ok end. + +handle_unexpected_message(_Msg, StateName, #state{negotiated_version = Version} = State) -> + Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), + handle_own_alert(Alert, Version, StateName, State), + {stop, normal, State}. + make_premaster_secret({MajVer, MinVer}, Alg) when Alg == rsa; Alg == dh_dss; Alg == dh_rsa -> @@ -1996,9 +2118,12 @@ ack_connection(#state{renegotiation = {true, Initiater}} = State) ack_connection(#state{renegotiation = {true, From}} = State) -> gen_fsm:reply(From, ok), State#state{renegotiation = undefined}; -ack_connection(#state{renegotiation = {false, first}, from = From} = State) -> +ack_connection(#state{renegotiation = {false, first}, + from = From} = State) when From =/= undefined -> gen_fsm:reply(From, connected), - State#state{renegotiation = undefined}. + State#state{renegotiation = undefined}; +ack_connection(State) -> + State. renegotiate(#state{role = client} = State) -> %% Handle same way as if server requested @@ -2009,16 +2134,18 @@ renegotiate(#state{role = server, socket = Socket, transport_cb = Transport, negotiated_version = Version, - connection_states = ConnectionStates0} = State) -> + connection_states = ConnectionStates0} = State0) -> HelloRequest = ssl_handshake:hello_request(), Frag = ssl_handshake:encode_handshake(HelloRequest, Version, undefined), Hs0 = ssl_handshake:init_hashes(), {BinMsg, ConnectionStates} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), Transport:send(Socket, BinMsg), - {next_state, hello, next_record(State#state{connection_states = - ConnectionStates, - tls_handshake_hashes = Hs0})}. + {Record, State} = next_record(State0#state{connection_states = + ConnectionStates, + tls_handshake_hashes = Hs0}), + next_state(hello, Record, State). + notify_senders(SendQueue) -> lists:foreach(fun({From, _}) -> gen_fsm:reply(From, {error, closed}) diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 7f33efd7e1..3ee82d990b 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -152,6 +152,7 @@ all(doc) -> all(suite) -> [app, connection_info, controlling_process, controller_dies, + client_closes_socket, peercert, connect_dist, peername, sockname, socket_options, misc_ssl_options, versions, cipher_suites, upgrade, upgrade_with_timeout, tcp_connect, @@ -322,6 +323,10 @@ controller_dies(Config) when is_list(Config) -> Connect = fun(Pid) -> {ok, Socket} = ssl:connect(Hostname, Port, [{reuseaddr,true},{ssl_imp,new}]), + %% Make sure server finishes and verification + %% and is in coonection state before + %% killing client + test_server:sleep(?SLEEP), Pid ! {self(), connected, Socket}, receive die_nice -> normal end end, @@ -393,6 +398,36 @@ get_close(Pid, Where) -> end. %%-------------------------------------------------------------------- +client_closes_socket(doc) -> + ["Test what happens when client closes socket before handshake is compleated"]; +client_closes_socket(suite) -> []; +client_closes_socket(Config) when is_list(Config) -> + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + TcpOpts = [binary, {reuseaddr, true}], + + Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {tcp_options, TcpOpts}, + {ssl_options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Connect = fun() -> + {ok, _Socket} = rpc:call(ClientNode, gen_tcp, connect, + [Hostname, Port, TcpOpts]), + %% Make sure that ssl_accept is called before + %% client process ends and closes socket. + test_server:sleep(?SLEEP) + end, + + _Client = spawn_link(Connect), + + ssl_test_lib:check_result(Server, {error,closed}), + + ssl_test_lib:close(Server). + +%%-------------------------------------------------------------------- + peercert(doc) -> [""]; @@ -796,11 +831,12 @@ upgrade(Config) when is_list(Config) -> TcpOpts = [binary, {reuseaddr, true}], Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - upgrade_result, []}}, - {tcp_options, TcpOpts}, - {ssl_options, ServerOpts}]), + {from, self()}, + {mfa, {?MODULE, + upgrade_result, []}}, + {tcp_options, + [{active, false} | TcpOpts]}, + {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_upgrade_client([{node, ClientNode}, {port, Port}, @@ -819,6 +855,7 @@ upgrade(Config) when is_list(Config) -> ssl_test_lib:close(Client). upgrade_result(Socket) -> + ssl:setopts(Socket, [{active, true}]), ok = ssl:send(Socket, "Hello world"), %% Make sure binary is inherited from tcp socket and that we do %% not get the list default! @@ -845,7 +882,8 @@ upgrade_with_timeout(Config) when is_list(Config) -> {timeout, 5000}, {mfa, {?MODULE, upgrade_result, []}}, - {tcp_options, TcpOpts}, + {tcp_options, + [{active, false} | TcpOpts]}, {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_upgrade_client([{node, ClientNode}, @@ -1884,7 +1922,6 @@ server_require_peer_cert_fail(Config) when is_list(Config) -> Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, no_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), @@ -1892,7 +1929,6 @@ server_require_peer_cert_fail(Config) when is_list(Config) -> Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, no_result, []}}, {options, [{active, false} | BadClientOpts]}]), ssl_test_lib:check_result(Server, {error, esslaccept}, diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 1bcb9a657b..a019e660e9 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -42,7 +42,6 @@ -define(MANY, 1000). -define(SOME, 50). - %% Test server callback functions %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config @@ -144,9 +143,20 @@ all(suite) -> packet_wait_passive, packet_wait_active, packet_baddata_passive, packet_baddata_active, packet_size_passive, packet_size_active, - packet_erl_decode, + packet_cdr_decode, packet_http_decode, - packet_http_bin_decode_multi + packet_http_decode_list, + packet_http_bin_decode_multi, + packet_line_decode, + packet_asn1_decode, + packet_tpkt_decode, + %packet_fcgi_decode, + packet_sunrm_decode, + header_decode_one_byte, + header_decode_two_bytes, + header_decode_two_bytes_one_sent, + header_decode_two_bytes_two_sent + ]. %% Test cases starts here. @@ -503,7 +513,8 @@ packet_raw_active_once_many_small(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, active_once_raw, [Data, ?MANY]}}, + {mfa, {?MODULE, active_once_raw, + [Data, ?MANY]}}, {options, [{active, once}, {packet, raw} | ClientOpts]}]), @@ -535,7 +546,8 @@ packet_raw_active_once_some_big(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, active_once_raw, [Data, ?SOME]}}, + {mfa, {?MODULE, active_once_raw, + [Data, ?SOME]}}, {options, [{active, once}, {packet, raw} | ClientOpts]}]), @@ -1191,7 +1203,8 @@ packet_send_to_large(Config) when is_list(Config) -> {mfa, {?MODULE, active_packet, [Data, 1]}}, {options, [{active, true} | ClientOpts]}]), - ssl_test_lib:check_result(Server, {error, {badarg, {packet_to_large, 300, 255}}}), + ssl_test_lib:check_result(Server, {error, {badarg, + {packet_to_large, 300, 255}}}), ssl_test_lib:close(Server), ssl_test_lib:close(Client). @@ -1216,7 +1229,8 @@ packet_wait_active(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_incomplete ,[Data, ?SOME]}}, + {mfa, {?MODULE, send_incomplete, + [Data, ?SOME]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, @@ -1251,7 +1265,8 @@ packet_wait_passive(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_incomplete ,[Data, ?SOME]}}, + {mfa, {?MODULE, send_incomplete, + [Data, ?SOME]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, @@ -1293,7 +1308,8 @@ packet_baddata_active(Config) when is_list(Config) -> {packet, cdr} | ClientOpts]}]), receive - {Client, {other, {ssl_error, _Socket, {invalid_packet, _}},{error,closed},1}} -> ok; + {Client, {other, {ssl_error, _Socket, + {invalid_packet, _}},{error,closed},1}} -> ok; Unexpected -> test_server:fail({unexpected, Unexpected}) end, @@ -1338,8 +1354,11 @@ packet_baddata_passive(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- + packet_size_active(doc) -> - ["Test that if a packet of size larger than packet_size arrives error msg is sent and socket is closed"]; + ["Test that if a packet of size larger than + packet_size arrives error msg is sent and socket is closed"]; + packet_size_active(suite) -> []; @@ -1363,7 +1382,8 @@ packet_size_active(Config) when is_list(Config) -> {packet, 4}, {packet_size, 10} | ClientOpts]}]), receive - {Client, {other, {ssl_error, _Socket, {invalid_packet, _}},{error,closed},1}} -> ok; + {Client, {other, {ssl_error, _Socket, + {invalid_packet, _}},{error,closed},1}} -> ok; Unexpected -> test_server:fail({unexpected, Unexpected}) end, @@ -1371,10 +1391,11 @@ packet_size_active(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- + packet_size_passive(doc) -> - ["Test that if a packet of size larger than packet_size arrives error msg is sent and socket is closed"]; -packet_size_passive(suite) -> - []; + ["Test that if a packet of size larger + than packet_size arrives error msg is sent and socket is closed"]; +packet_size_passive(suite) -> []; packet_size_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1391,7 +1412,8 @@ packet_size_passive(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, passive_recv_packet, [Data, 1]}}, + {mfa, {?MODULE, passive_recv_packet, + [Data, 1]}}, {options, [{active, false}, {packet, 4}, {packet_size, 30} | ClientOpts]}]), @@ -1405,14 +1427,11 @@ packet_size_passive(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_erl_decode(doc) -> - ["Test that packets of sent to erlang:decode_packet works, i.e. currently" - "asn1 | cdr | sunrm | fcgi | tpkt | line | http | http_bin" - ]; -packet_erl_decode(suite) -> +packet_cdr_decode(doc) -> + ["Test setting the packet option {packet, cdr}"]; +packet_cdr_decode(suite) -> []; - -packet_erl_decode(Config) when is_list(Config) -> +packet_cdr_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1423,54 +1442,28 @@ packet_erl_decode(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_packet_decode ,[Data]}}, - {options, [{active, true}, binary, {packet, cdr}|ServerOpts]}]), + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, binary, + {packet, cdr}|ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_packet_decode, [Data]}}, - {options, [{active, true}, binary | ClientOpts]}]), + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, cdr}, + binary | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). - -server_packet_decode(Socket, CDR) -> - receive - {ssl, Socket, CDR} -> ok; - Other1 -> exit({?LINE, Other1}) - end, - ok = ssl:send(Socket, CDR), - receive - {ssl, Socket, CDR} -> ok; - Other2 -> exit({?LINE, Other2}) - end, - ok = ssl:send(Socket, CDR), - ok. - -client_packet_decode(Socket, CDR) -> - <<P1:10/binary, P2/binary>> = CDR, - ok = ssl:send(Socket, P1), - ok = ssl:send(Socket, P2), - receive - {ssl, Socket, CDR} -> ok; - Other1 -> exit({?LINE, Other1}) - end, - ssl:setopts(Socket, [{packet, cdr}]), - ok = ssl:send(Socket, CDR), - receive - {ssl, Socket, CDR} -> ok; - Other2 -> exit({?LINE, Other2}) - end, - ok. - %%-------------------------------------------------------------------- packet_http_decode(doc) -> - ["Test setting the packet option {packet, http}"]; + ["Test setting the packet option {packet, http} {mode, binary}"]; packet_http_decode(suite) -> []; @@ -1489,16 +1482,19 @@ packet_http_decode(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_http_decode, [Response]}}, - {options, [{active, true}, binary, {packet, http} | - ServerOpts]}]), + {mfa, {?MODULE, server_http_decode, + [Response]}}, + {options, [{active, true}, binary, + {packet, http} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_http_decode, [Request]}}, - {options, [{active, true}, binary, {packet, http} | + {mfa, {?MODULE, client_http_decode, + [Request]}}, + {options, [{active, true}, binary, + {packet, http} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1550,6 +1546,65 @@ client_http_decode(Socket, HttpRequest) -> ok. %%-------------------------------------------------------------------- +packet_http_decode_list(doc) -> + ["Test setting the packet option {packet, http}, {mode, list}"]; +packet_http_decode_list(suite) -> + []; +packet_http_decode_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Request = "GET / HTTP/1.1\r\n" + "host: www.example.com\r\n" + "user-agent: HttpTester\r\n" + "\r\n", + Response = "HTTP/1.1 200 OK\r\n" + "\r\n" + "Hello!", + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_http_decode, + [Response]}}, + {options, [{active, true}, binary, + {packet, http} | + ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_http_decode_list, + [Request]}}, + {options, [{active, true}, list, + {packet, http} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +client_http_decode_list(Socket, HttpRequest) -> + ok = ssl:send(Socket, HttpRequest), + receive + {ssl, Socket, {http_response, {1,1}, 200, "OK"}} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + receive + {ssl, Socket, http_eoh} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + ok = ssl:setopts(Socket, [{packet, 0}]), + receive + {ssl, Socket, "Hello!"} -> ok; + Other3 -> exit({?LINE, Other3}) + end, + ok. + +%%-------------------------------------------------------------------- packet_http_bin_decode_multi(doc) -> ["Test setting the packet option {packet, http_bin} with multiple requests"]; packet_http_bin_decode_multi(suite) -> @@ -1571,16 +1626,20 @@ packet_http_bin_decode_multi(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_http_bin_decode, [Response, NumMsgs]}}, - {options, [{active, true}, binary, {packet, http_bin} | + {mfa, {?MODULE, server_http_bin_decode, + [Response, NumMsgs]}}, + {options, [{active, true}, binary, + {packet, http_bin} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_http_bin_decode, [Request, NumMsgs]}}, - {options, [{active, true}, binary, {packet, http_bin} | + {mfa, {?MODULE, client_http_bin_decode, + [Request, NumMsgs]}}, + {options, [{active, true}, binary, + {packet, http_bin} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1635,6 +1694,344 @@ client_http_bin_decode(Socket, HttpRequest, Count) when Count > 0 -> client_http_bin_decode(Socket, HttpRequest, Count - 1); client_http_bin_decode(_, _, _) -> ok. +%%-------------------------------------------------------------------- +packet_line_decode(doc) -> + ["Test setting the packet option {packet, line}"]; +packet_line_decode(suite) -> + []; +packet_line_decode(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = list_to_binary(lists:flatten(io_lib:format("Line ends here.~n" + "Now it is a new line.~n", + []))), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_line_packet_decode, + [Data]}}, + {options, [{active, true}, binary, + {packet, line}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_line_packet_decode, + [Data]}}, + {options, [{active, true}, + {packet, line}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +server_line_packet_decode(Socket, Lines) -> + receive + {ssl, Socket, <<"Line ends here.\n">>} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + receive + {ssl, Socket, <<"Now it is a new line.\n">>} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + ok = ssl:send(Socket, Lines). + +client_line_packet_decode(Socket, Lines) -> + <<P1:10/binary, P2/binary>> = Lines, + ok = ssl:send(Socket, P1), + ok = ssl:send(Socket, P2), + receive + {ssl, Socket, <<"Line ends here.\n">>} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + receive + {ssl, Socket, <<"Now it is a new line.\n">>} -> ok; + Other2 -> exit({?LINE, Other2}) + end. + +%%-------------------------------------------------------------------- + +packet_asn1_decode(doc) -> + ["Test setting the packet option {packet, asn1}"]; +packet_asn1_decode(suite) -> + []; +packet_asn1_decode(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + File = proplists:get_value(certfile, ServerOpts), + + %% A valid asn1 BER packet (DER is stricter BER) + {ok,[{cert, Data, _}]} = public_key:pem_to_der(File), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, binary, + {packet, asn1}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, asn1}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +packet_tpkt_decode(doc) -> + ["Test setting the packet option {packet, tpkt}"]; +packet_tpkt_decode(suite) -> + []; +packet_tpkt_decode(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = list_to_binary(add_tpkt_header("TPKT data")), + + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, binary, + {packet, tpkt}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, tpkt}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +%% packet_fcgi_decode(doc) -> +%% ["Test setting the packet option {packet, fcgi}"]; +%% packet_fcgi_decode(suite) -> +%% []; +%% packet_fcgi_decode(Config) when is_list(Config) -> +%% ClientOpts = ?config(client_opts, Config), +%% ServerOpts = ?config(server_opts, Config), +%% {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + +%% Data = ... + +%% Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, +%% {from, self()}, +%% {mfa, {?MODULE, server_packet_decode, +%% [Data0, Data1]}}, +%% {options, [{active, true}, binary, +%% {packet, fcgi}|ServerOpts]}]), + +%% Port = ssl_test_lib:inet_port(Server), +%% Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, +%% {host, Hostname}, +%% {from, self()}, +%% {mfa, {?MODULE, client_packet_decode, +%% [Data0, Data1]}}, +%% {options, [{active, true}, {packet, fcgi}, +%% binary | ClientOpts]}]), + +%% ssl_test_lib:check_result(Server, ok, Client, ok), + +%% ssl_test_lib:close(Server), +%% ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +packet_sunrm_decode(doc) -> + ["Test setting the packet option {packet, sunrm}"]; +packet_sunrm_decode(suite) -> + []; +packet_sunrm_decode(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<11:32, "Hello world">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, binary, + {packet, sunrm}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, sunrm}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- + +header_decode_one_byte(doc) -> + ["Test setting the packet option {header, 1}"]; +header_decode_one_byte(suite) -> + []; +header_decode_one_byte(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<11:8, "Hello world">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode, + [Data, [11 | <<"Hello world">>]]}}, + {options, [{active, true}, binary, + {header,1}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode, + [Data, [11 | <<"Hello world">> ]]}}, + {options, [{active, true}, {header, 1}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +header_decode_two_bytes(doc) -> + ["Test setting the packet option {header, 2}"]; +header_decode_two_bytes(suite) -> + []; +header_decode_two_bytes(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<11:8, "Hello world">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode, + [Data, [11, $H | <<"ello world">> ]]}}, + {options, [{active, true}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode, + [Data, [11, $H | <<"ello world">> ]]}}, + {options, [{active, true}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +header_decode_two_bytes_two_sent(doc) -> + ["Test setting the packet option {header, 2} and sending on byte"]; +header_decode_two_bytes_two_sent(suite) -> + []; +header_decode_two_bytes_two_sent(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<"He">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode, + [Data, [$H, $e | <<>> ]]}}, + {options, [{active, true}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode, + [Data, [$H, $e | <<>> ]]}}, + {options, [{active, true}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +header_decode_two_bytes_one_sent(doc) -> + ["Test setting the packet option {header, 2} and sending on byte"]; +header_decode_two_bytes_one_sent(suite) -> + []; +header_decode_two_bytes_one_sent(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<"H">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode, + [Data, "H"]}}, + {options, [{active, true}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode, + [Data, "H"]}}, + {options, [{active, true}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- %% Internal functions @@ -1744,3 +2141,61 @@ active_packet(Socket, Data, N) -> assert_packet_opt(Socket, Type) -> {ok, [{packet, Type}]} = ssl:getopts(Socket, [packet]). + +server_packet_decode(Socket, Packet) -> + receive + {ssl, Socket, Packet} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + ok = ssl:send(Socket, Packet), + receive + {ssl, Socket, Packet} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + ok = ssl:send(Socket, Packet). + +client_packet_decode(Socket, Packet) -> + <<P1:10/binary, P2/binary>> = Packet, + ok = ssl:send(Socket, P1), + ok = ssl:send(Socket, P2), + receive + {ssl, Socket, Packet} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + ok = ssl:send(Socket, Packet), + receive + {ssl, Socket, Packet} -> ok; + Other2 -> exit({?LINE, Other2}) + end. + +server_header_decode(Socket, Packet, Result) -> + receive + {ssl, Socket, Result} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + ok = ssl:send(Socket, Packet), + receive + {ssl, Socket, Result} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + ok = ssl:send(Socket, Packet). + +client_header_decode(Socket, Packet, Result) -> + ok = ssl:send(Socket, Packet), + receive + {ssl, Socket, Result} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + ok = ssl:send(Socket, Packet), + receive + {ssl, Socket, Result} -> ok; + Other2 -> exit({?LINE, Other2}) + end. + +add_tpkt_header(Data) when is_binary(Data) -> + L = size(Data) + 4, + [3, 0, ((L) bsr 8) band 16#ff, (L) band 16#ff ,Data]; +add_tpkt_header(IOList) when is_list(IOList) -> + Binary = list_to_binary(IOList), + L = size(Binary) + 4, + [3, 0, ((L) bsr 8) band 16#ff, (L) band 16#ff , Binary]. diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 00c5350ad0..46b6eb401d 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -394,6 +394,41 @@ run_upgrade_client(Opts) -> ok = rpc:call(Node, ssl, close, [SslSocket]) end. +start_upgrade_server_error(Args) -> + Result = spawn_link(?MODULE, run_upgrade_server_error, [Args]), + receive + {listen, up} -> + Result + end. + +run_upgrade_server_error(Opts) -> + Node = proplists:get_value(node, Opts), + Port = proplists:get_value(port, Opts), + TimeOut = proplists:get_value(timeout, Opts, infinity), + TcpOptions = proplists:get_value(tcp_options, Opts), + SslOptions = proplists:get_value(ssl_options, Opts), + Pid = proplists:get_value(from, Opts), + + test_server:format("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), + {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), + Pid ! {listen, up}, + send_selected_port(Pid, Port, ListenSocket), + test_server:format("gen_tcp:accept(~p)~n", [ListenSocket]), + {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), + Error = case TimeOut of + infinity -> + test_server:format("ssl:ssl_accept(~p, ~p)~n", + [AcceptSocket, SslOptions]), + rpc:call(Node, ssl, ssl_accept, + [AcceptSocket, SslOptions]); + _ -> + test_server:format("ssl:ssl_accept(~p, ~p, ~p)~n", + [AcceptSocket, SslOptions, TimeOut]), + rpc:call(Node, ssl, ssl_accept, + [AcceptSocket, SslOptions, TimeOut]) + end, + Pid ! {self(), Error}. + start_server_error(Args) -> Result = spawn_link(?MODULE, run_server_error, [Args]), receive diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index cbf0447bf0..186bf52ff6 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -958,7 +958,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> wait_for_openssl_server(), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, server_sent_garbage, []}}, @@ -970,11 +970,22 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> test_server:sleep(?SLEEP), - Client ! server_sent_garbage, - - ssl_test_lib:check_result(Client, true), - - ssl_test_lib:close(Client), + Client0 ! server_sent_garbage, + + ssl_test_lib:check_result(Client0, true), + + ssl_test_lib:close(Client0), + + %% Make sure openssl does not hang and leave zombie process + Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result_msg, []}}, + {options, + [{versions, [tlsv1]} | ClientOpts]}]), + + ssl_test_lib:close(Client1), + %% Clean close down! close_port(OpensslPort), process_flag(trap_exit, false), @@ -1055,6 +1066,7 @@ server_sent_garbage(Socket) -> receive server_sent_garbage -> {error, closed} == ssl:send(Socket, "data") + end. wait_for_openssl_server() -> diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 337ea4b380..6db1a4b5c2 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -17,14 +17,16 @@ # %CopyrightEnd% # -SSL_VSN = 3.11 - -TICKETS = OTP-8517 \ - OTP-7046 \ - OTP-8557 \ - OTP-8560 \ - OTP-8545 \ - OTP-8554 +SSL_VSN = 3.11.1 + +TICKETS = OTP-8588 + +#TICKETS_3.11 = OTP-8517 \ +# OTP-7046 \ +# OTP-8557 \ +# OTP-8560 \ +# OTP-8545 \ +# OTP-8554 #TICKETS_3.10.9 = OTP-8510 |