diff options
Diffstat (limited to 'lib/crypto/c_src/crypto_drv.c')
-rw-r--r-- | lib/crypto/c_src/crypto_drv.c | 1721 |
1 files changed, 1721 insertions, 0 deletions
diff --git a/lib/crypto/c_src/crypto_drv.c b/lib/crypto/c_src/crypto_drv.c new file mode 100644 index 0000000000..241c4ec733 --- /dev/null +++ b/lib/crypto/c_src/crypto_drv.c @@ -0,0 +1,1721 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-2009. 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_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 46 + +#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_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); +} |