diff options
author | Sverker Eriksson <[email protected]> | 2010-04-28 14:08:55 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2010-04-28 14:08:55 +0000 |
commit | 98226b13ccabb8e968b33a649aa175a7c48b0d3b (patch) | |
tree | af799d50426f0e516e7eba276dbd8ac3cf03050d /lib/crypto/c_src/crypto.c | |
parent | df7bf3f81ef645f24110c5e4cdeaf78dca44f810 (diff) | |
download | otp-98226b13ccabb8e968b33a649aa175a7c48b0d3b.tar.gz otp-98226b13ccabb8e968b33a649aa175a7c48b0d3b.tar.bz2 otp-98226b13ccabb8e968b33a649aa175a7c48b0d3b.zip |
OTP-8333 Crypto using NIF's
crypto application changed to use NIFs instead of driver.
Diffstat (limited to 'lib/crypto/c_src/crypto.c')
-rw-r--r-- | lib/crypto/c_src/crypto.c | 1531 |
1 files changed, 1531 insertions, 0 deletions
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); +} + |