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