summaryrefslogtreecommitdiff
path: root/openssl1.0.0/engines/devcrypto/e_devcrypto.c
diff options
context:
space:
mode:
authorIgor Pashev <igor.pashev@nexenta.com>2012-10-31 11:24:45 +0400
committerIgor Pashev <igor.pashev@nexenta.com>2012-10-31 11:26:05 +0400
commitfbcb6172ef51ac8dc40486bed6006e912f561982 (patch)
tree6f4e162b84c3d8406621507284ee917194f5e5cc /openssl1.0.0/engines/devcrypto/e_devcrypto.c
parentee259ee9e85b5627e825aa4da9aaa59fa147c87d (diff)
downloadcibs-pkgs-fbcb6172ef51ac8dc40486bed6006e912f561982.tar.gz
OpenSSL 1.0.x
Diffstat (limited to 'openssl1.0.0/engines/devcrypto/e_devcrypto.c')
-rw-r--r--openssl1.0.0/engines/devcrypto/e_devcrypto.c1311
1 files changed, 1311 insertions, 0 deletions
diff --git a/openssl1.0.0/engines/devcrypto/e_devcrypto.c b/openssl1.0.0/engines/devcrypto/e_devcrypto.c
new file mode 100644
index 0000000..67bd085
--- /dev/null
+++ b/openssl1.0.0/engines/devcrypto/e_devcrypto.c
@@ -0,0 +1,1311 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <errno.h>
+#include <cryptoutil.h>
+#include <sys/crypto/ioctl.h>
+#include <sys/crypto/api.h>
+#include <openssl/bio.h>
+#include <openssl/aes.h>
+#include <openssl/engine.h>
+#include <security/cryptoki.h>
+
+#define DEVCRYPTO_LIB_NAME "devcrypto engine"
+#include "e_devcrypto_err.c"
+
+/* DEVCRYPTO CONTEXT */
+typedef struct devcrypto_ctx {
+ uint_t session_id;
+} devcrypto_ctx_t;
+
+/* Index for the supported ciphers */
+typedef enum {
+ DEV_DES_CBC,
+ DEV_DES3_CBC,
+ DEV_DES_ECB,
+ DEV_DES3_ECB,
+ DEV_RC4,
+ DEV_AES_128_CBC,
+ DEV_AES_192_CBC,
+ DEV_AES_256_CBC,
+ DEV_AES_128_ECB,
+ DEV_AES_192_ECB,
+ DEV_AES_256_ECB,
+ DEV_BLOWFISH_CBC,
+#ifdef SOLARIS_AES_CTR
+ DEV_AES_128_CTR,
+ DEV_AES_192_CTR,
+ DEV_AES_256_CTR,
+#endif /* SOLARIS_AES_CTR */
+ DEV_CIPHER_MAX
+} DEV_CIPHER_ID;
+
+typedef struct devcrypto_cipher {
+ DEV_CIPHER_ID id;
+ int nid;
+ int iv_len;
+ int min_key_len;
+ int max_key_len;
+ CK_KEY_TYPE key_type;
+ CK_MECHANISM_TYPE mech_type;
+ unsigned long flags;
+ crypto_mech_type_t pn_internal_number;
+} devcrypto_cipher_t;
+
+
+/* Constants used when creating the ENGINE */
+static const char *ENGINE_DEVCRYPTO_ID = "devcrypto";
+static const char *ENGINE_DEVCRYPTO_NAME = "/dev/crypto engine support";
+static const char *CRYPTO_DEVICE = "/dev/crypto";
+
+/* static variables */
+static int kernel_fd = -1;
+static int kernel_fd_ref = 0;
+static int slot_count = 0;
+static CK_SLOT_ID *kernel_provider_id = NULL;
+static int cipher_count = 0;
+static int *cipher_nids = NULL;
+pthread_mutex_t *kernel_fd_lock;
+
+#ifdef SOLARIS_AES_CTR
+/*
+ * NIDs for AES counter mode. They will be defined during the engine
+ * initialization.
+ */
+static int NID_aes_128_ctr = NID_undef;
+static int NID_aes_192_ctr = NID_undef;
+static int NID_aes_256_ctr = NID_undef;
+#endif /* SOLARIS_AES_CTR */
+
+/*
+ * Cipher Table for all supported symmetric ciphers.
+ */
+static devcrypto_cipher_t cipher_table[] = {
+ /* id, nid, iv_len, min_, max_key_len, */
+ /* key_type, mech_type, flags, pn_internal_number */
+ { DEV_DES_CBC, NID_des_cbc, 8, 8, 8,
+ CKK_DES, CKM_DES_CBC, 0, CRYPTO_MECH_INVALID},
+ { DEV_DES3_CBC, NID_des_ede3_cbc, 8, 24, 24,
+ CKK_DES3, CKM_DES3_CBC, 0, CRYPTO_MECH_INVALID},
+ { DEV_DES_ECB, NID_des_ecb, 0, 8, 8,
+ CKK_DES, CKM_DES_ECB, 0, CRYPTO_MECH_INVALID},
+ { DEV_DES3_ECB, NID_des_ede3_ecb, 0, 24, 24,
+ CKK_DES3, CKM_DES3_ECB, 0, CRYPTO_MECH_INVALID},
+ { DEV_RC4, NID_rc4, 0, 16, 256,
+ CKK_RC4, CKM_RC4, 0, CRYPTO_MECH_INVALID},
+ { DEV_AES_128_CBC, NID_aes_128_cbc, 16, 16, 16,
+ CKK_AES, CKM_AES_CBC, 0, CRYPTO_MECH_INVALID},
+ { DEV_AES_192_CBC, NID_aes_192_cbc, 16, 24, 24,
+ CKK_AES, CKM_AES_CBC, 0, CRYPTO_MECH_INVALID},
+ { DEV_AES_256_CBC, NID_aes_256_cbc, 16, 32, 32,
+ CKK_AES, CKM_AES_CBC, 0, CRYPTO_MECH_INVALID},
+ { DEV_AES_128_ECB, NID_aes_128_ecb, 0, 16, 16,
+ CKK_AES, CKM_AES_ECB, 0, CRYPTO_MECH_INVALID},
+ { DEV_AES_192_ECB, NID_aes_192_ecb, 0, 24, 24,
+ CKK_AES, CKM_AES_ECB, 0, CRYPTO_MECH_INVALID},
+ { DEV_AES_256_ECB, NID_aes_256_ecb, 0, 32, 32,
+ CKK_AES, CKM_AES_ECB, 0, CRYPTO_MECH_INVALID},
+ { DEV_BLOWFISH_CBC, NID_bf_cbc, 8, 16, 16,
+ CKK_BLOWFISH, CKM_BLOWFISH_CBC, 0, CRYPTO_MECH_INVALID},
+#ifdef SOLARIS_AES_CTR
+ /*
+ * For the following 3 AES counter mode entries, we don't know the
+ * NIDs until the engine is initialized
+ */
+ { DEV_AES_128_CTR, NID_undef, 16, 16, 16,
+ CKK_AES, CKM_AES_CTR, EVP_CIPH_NO_PADDING,
+ CRYPTO_MECH_INVALID},
+ { DEV_AES_192_CTR, NID_undef, 16, 24, 24,
+ CKK_AES, CKM_AES_CTR, EVP_CIPH_NO_PADDING,
+ CRYPTO_MECH_INVALID},
+ { DEV_AES_256_CTR, NID_undef, 16, 32, 32,
+ CKK_AES, CKM_AES_CTR, EVP_CIPH_NO_PADDING,
+ CRYPTO_MECH_INVALID},
+#endif /* SOLARIS_AES_CTR */
+ };
+
+
+/* Formal declaration for functions in EVP_CIPHER structure */
+static int devcrypto_cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+ const unsigned char *iv, int enc);
+static int devcrypto_cipher_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl);
+static int devcrypto_cipher_cleanup(EVP_CIPHER_CTX *ctx);
+
+/*
+ * Cipher Algorithms
+ *
+ * OpenSSL's libcrypto EVP stuff. This is how this engine gets wired to EVP.
+ * EVP_CIPHER is defined in evp.h. To maintain binary compatibility the
+ * definition cannot be modified.
+ * Stuff specific to the devcrypto engine is kept in devcrypto_ctx_t, which is
+ * pointed to by cipher_data or md_data.
+ *
+ * Fields: nid, block_size, key_len, iv_len, flags,
+ * init(), do_cipher(), cleanup(),
+ * ctx_size,
+ * set_asn1_parameters(), get_asn1_parameters(), ctrl(), app_data
+ */
+static const EVP_CIPHER dev_des_cbc = {
+ NID_des_cbc,
+ 8, 8, 8,
+ EVP_CIPH_CBC_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ EVP_CIPHER_set_asn1_iv,
+ EVP_CIPHER_get_asn1_iv,
+ NULL
+};
+
+static const EVP_CIPHER dev_3des_cbc = {
+ NID_des_ede3_cbc,
+ 8, 24, 8,
+ EVP_CIPH_CBC_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ EVP_CIPHER_set_asn1_iv,
+ EVP_CIPHER_get_asn1_iv,
+ NULL
+};
+
+/*
+ * ECB modes don't use an Initial Vector, therefore set_asn1_parameters and
+ * get_asn1_parameters fields are set to NULL.
+ */
+static const EVP_CIPHER dev_des_ecb = {
+ NID_des_ecb,
+ 8, 8, 8,
+ EVP_CIPH_ECB_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ NULL,
+ NULL,
+ NULL
+};
+
+static const EVP_CIPHER dev_3des_ecb = {
+ NID_des_ede3_ecb,
+ 8, 24, 8,
+ EVP_CIPH_ECB_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ NULL,
+ NULL,
+ NULL
+};
+
+static const EVP_CIPHER dev_rc4 = {
+ NID_rc4,
+ 1, 16, 0,
+ EVP_CIPH_VARIABLE_LENGTH,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ NULL,
+ NULL,
+ NULL
+};
+
+static const EVP_CIPHER dev_aes_128_cbc = {
+ NID_aes_128_cbc,
+ 16, 16, 16,
+ EVP_CIPH_CBC_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ EVP_CIPHER_set_asn1_iv,
+ EVP_CIPHER_get_asn1_iv,
+ NULL
+};
+
+static const EVP_CIPHER dev_aes_192_cbc = {
+ NID_aes_192_cbc,
+ 16, 24, 16,
+ EVP_CIPH_CBC_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ EVP_CIPHER_set_asn1_iv,
+ EVP_CIPHER_get_asn1_iv,
+ NULL
+};
+
+static const EVP_CIPHER dev_aes_256_cbc = {
+ NID_aes_256_cbc,
+ 16, 32, 16,
+ EVP_CIPH_CBC_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ EVP_CIPHER_set_asn1_iv,
+ EVP_CIPHER_get_asn1_iv,
+ NULL
+};
+
+
+/*
+ * ECB modes don't use IV, therefore set_asn1_parameters and
+ * get_asn1_parameters are set to NULL.
+ */
+static const EVP_CIPHER dev_aes_128_ecb = {
+ NID_aes_128_ecb,
+ 16, 16, 0,
+ EVP_CIPH_ECB_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ NULL,
+ NULL,
+ NULL
+};
+
+static const EVP_CIPHER dev_aes_192_ecb = {
+ NID_aes_192_ecb,
+ 16, 24, 0,
+ EVP_CIPH_ECB_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ NULL,
+ NULL,
+ NULL
+};
+
+static const EVP_CIPHER dev_aes_256_ecb = {
+ NID_aes_256_ecb,
+ 16, 32, 0,
+ EVP_CIPH_ECB_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ NULL,
+ NULL,
+ NULL
+};
+
+static const EVP_CIPHER dev_bf_cbc = {
+ NID_bf_cbc,
+ 8, 16, 8,
+ EVP_CIPH_VARIABLE_LENGTH,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ EVP_CIPHER_set_asn1_iv,
+ EVP_CIPHER_get_asn1_iv,
+ NULL
+};
+
+#ifdef SOLARIS_AES_CTR
+
+/*
+ * NID_undef's will be changed for AES counter mode, as soon they are created.
+ */
+static EVP_CIPHER dev_aes_128_ctr = {
+ NID_undef,
+ 16, 16, 16,
+ EVP_CIPH_CBC_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ EVP_CIPHER_set_asn1_iv,
+ EVP_CIPHER_get_asn1_iv,
+ NULL
+};
+
+static EVP_CIPHER dev_aes_192_ctr = {
+ NID_undef,
+ 16, 24, 16,
+ EVP_CIPH_CBC_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ EVP_CIPHER_set_asn1_iv,
+ EVP_CIPHER_get_asn1_iv,
+ NULL
+};
+
+static EVP_CIPHER dev_aes_256_ctr = {
+ NID_undef,
+ 16, 32, 16,
+ EVP_CIPH_CBC_MODE,
+ devcrypto_cipher_init,
+ devcrypto_cipher_do_cipher,
+ devcrypto_cipher_cleanup,
+ sizeof (devcrypto_ctx_t),
+ EVP_CIPHER_set_asn1_iv,
+ EVP_CIPHER_get_asn1_iv,
+ NULL
+};
+
+#endif /* SOLARIS_AES_CTR */
+
+
+/*
+ * This function creates a new NID.
+ */
+static int
+devcrypto_add_NID(char *sn, char *ln)
+{
+ ASN1_OBJECT *o;
+ int nid;
+
+ if ((o = ASN1_OBJECT_create(OBJ_new_nid(1), (unsigned char *)"",
+ 1, sn, ln)) == NULL) {
+ return (0);
+ }
+
+ nid = OBJ_add_object(o); /* will return NID_undef on error */
+ ASN1_OBJECT_free(o);
+ return (nid);
+}
+
+#ifdef SOLARIS_AES_CTR
+/*
+ * This function creates new NIDs for AES counter mode algorithms.
+ * Note that OpenSSL doesn't support them now so we have to help
+ * ourselves here.
+ */
+static int
+devcrypto_add_aes_ctr_NIDs(void)
+{
+ if (NID_aes_256_ctr != NID_undef) /* already set */
+ return (1);
+
+ NID_aes_128_ctr = devcrypto_add_NID("AES-128-CTR", "aes-128-ctr");
+ if (NID_aes_128_ctr == NID_undef)
+ goto failed;
+ cipher_table[DEV_AES_128_CTR].nid =
+ dev_aes_128_ctr.nid = NID_aes_128_ctr;
+
+ NID_aes_192_ctr = devcrypto_add_NID("AES-192-CTR", "aes-192-ctr");
+ if (NID_aes_192_ctr == NID_undef)
+ goto failed;
+ cipher_table[DEV_AES_192_CTR].nid =
+ dev_aes_192_ctr.nid = NID_aes_192_ctr;
+
+ NID_aes_256_ctr = devcrypto_add_NID("AES-256-CTR", "aes-256-ctr");
+ if (NID_aes_256_ctr == NID_undef)
+ goto failed;
+ cipher_table[DEV_AES_256_CTR].nid =
+ dev_aes_256_ctr.nid = NID_aes_256_ctr;
+
+ return (1);
+
+failed:
+ return (0);
+}
+
+
+static void
+devcrypto_free_aes_ctr_NIDs(void)
+{
+ ASN1_OBJECT *ob = NULL;
+
+ if (NID_aes_128_ctr != NID_undef) {
+ ob = OBJ_nid2obj(NID_aes_128_ctr);
+ if (ob != NULL)
+ ASN1_OBJECT_free(ob);
+ }
+
+ if (NID_aes_192_ctr != NID_undef) {
+ ob = OBJ_nid2obj(NID_aes_192_ctr);
+ if (ob != NULL)
+ ASN1_OBJECT_free(ob);
+ }
+
+ if (NID_aes_256_ctr != NID_undef) {
+ ob = OBJ_nid2obj(NID_aes_256_ctr);
+ if (ob != NULL)
+ ASN1_OBJECT_free(ob);
+ }
+}
+
+#endif /* SOLARIS_AES_CTR */
+
+/*
+ * Open the /dev/crypto device
+ */
+static int
+devcrypto_open(void)
+{
+ int fd = -1;
+
+ if (kernel_fd != -1) { /* already open */
+ (void) pthread_mutex_lock(kernel_fd_lock);
+ kernel_fd_ref++;
+ (void) pthread_mutex_unlock(kernel_fd_lock);
+ return (1);
+ }
+
+ (void) pthread_mutex_lock(kernel_fd_lock);
+ fd = open(CRYPTO_DEVICE, O_RDWR);
+ if (fd == -1) {
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "libdevcrypto: open /dev/crypto failed, errno=%x\n",
+ errno);
+#endif
+ (void) pthread_mutex_unlock(kernel_fd_lock);
+ return (0);
+ }
+
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto: failed to fcntl\n");
+#endif
+ (void) close(fd);
+ (void) pthread_mutex_unlock(kernel_fd_lock);
+ return (0);
+ }
+
+ kernel_fd = fd;
+ kernel_fd_ref++;
+ (void) pthread_mutex_unlock(kernel_fd_lock);
+ return (1);
+}
+
+
+/*
+ * This function gets the total number of hardware providers presented in
+ * the system first. If there is any hardware providers, then it will get
+ * the kernel provider id for each hardware slot also.
+ */
+static int
+devcrypto_get_slot_info(void)
+{
+ crypto_get_provider_list_t *pl = NULL;
+ int ret = 1;
+ int r;
+ int i;
+
+ /* Already have the information */
+ if (kernel_provider_id != NULL)
+ return (1);
+
+ /* Find out how many hardware slots are presented. */
+ pl = OPENSSL_malloc(sizeof (crypto_get_provider_list_t));
+ if (pl == NULL)
+ return (0);
+
+ pl->pl_count = 0;
+ while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_LIST, pl)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ if (r < 0 || pl->pl_return_value != CRYPTO_SUCCESS) {
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto:CRYPTO_GET_PROVIDER_LIST:"
+ "ret (r) = 0x%x, (rv) = 0x%x\n", r, pl->pl_return_value);
+#endif /* DEBUG */
+ ret = 0;
+ goto out;
+ }
+
+ slot_count = pl->pl_count;
+ if (slot_count == 0) {
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto: no hw providers\n");
+#endif /* DEBUG */
+ ret = 0;
+ goto out;
+ }
+
+ /* Get the provider ID for each slot from kernel and save it */
+ kernel_provider_id = OPENSSL_malloc(sizeof (CK_SLOT_ID) * slot_count);
+ if (kernel_provider_id == NULL) {
+ ret = 0;
+ goto out;
+ }
+
+ (void) OPENSSL_free(pl);
+ pl = OPENSSL_malloc(slot_count * sizeof (crypto_get_provider_list_t));
+ if (pl == NULL) {
+ ret = 0;
+ goto out;
+ }
+
+ pl->pl_count = slot_count;
+ while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_LIST, pl)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ if (r < 0 || (pl->pl_return_value != CRYPTO_SUCCESS)) {
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto:CRYPTO_GET_PROVIDER_LIST:"
+ "ret (r) = 0x%x, (rv) = 0x%x\n", r, pl->pl_return_value);
+#endif /* DEBUG */
+ ret = 0;
+ goto out;
+ }
+
+ for (i = 0; i < slot_count; i++) {
+ kernel_provider_id[i] = pl->pl_list[i].pe_provider_id;
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto: i = %d, "
+ "kernel_provider_id = %d\n", i, kernel_provider_id[i]);
+#endif /* DEBUG */
+ }
+
+out:
+ if (pl != NULL)
+ (void) OPENSSL_free(pl);
+
+ if (ret == 0 && kernel_provider_id != NULL) {
+ (void) OPENSSL_free(kernel_provider_id);
+ kernel_provider_id = NULL;
+ }
+
+ return (ret);
+}
+
+
+/*
+ * This function checks if the "nid" is already in the nid list.
+ */
+static int
+nid_in_list(int nid, int *nid_list, int count)
+{
+ int i = 0;
+
+ if (nid_list == NULL || count <= 0)
+ return (0);
+
+ while (i < count) {
+ if (nid == nid_list[i])
+ break;
+ i++;
+ }
+ return (i < count ? 1 : 0);
+}
+
+/*
+ * This function is to get all the ciphers supported by hardware providers.
+ * If this function is successfully completed, then the following 2 global
+ * variables will be set.
+ * cipher_count - the number of ciphers found in all hardware providers.
+ * cipher_nids - the nid list for all the ciphers.
+ */
+static int
+devcrypto_get_hw_ciphers(void)
+{
+ crypto_get_provider_mechanism_info_t mechinfo;
+ int max_cipher_count;
+ int *tmp_nids = NULL;
+ const char *mech_string;
+ int r;
+ int i, j;
+
+ if (slot_count <= 0) /* no hardware provider */
+ return (0);
+
+ max_cipher_count = slot_count * DEV_CIPHER_MAX + 1;
+ tmp_nids = OPENSSL_malloc(max_cipher_count * sizeof (int));
+ if (tmp_nids == NULL) {
+ /* not enough memory */
+ goto failed;
+ }
+
+ for (i = 0; i < slot_count; i++) {
+ mechinfo.mi_provider_id = kernel_provider_id[i];
+ for (j = 0; j < DEV_CIPHER_MAX; j++) {
+ mech_string =
+ pkcs11_mech2str(cipher_table[j].mech_type);
+ if (mech_string == NULL) {
+ continue; /* shouldn't happen; skip it */
+ }
+
+ (void) strlcpy(mechinfo.mi_mechanism_name,
+ mech_string, CRYPTO_MAX_MECH_NAME);
+ while ((r = ioctl(kernel_fd,
+ CRYPTO_GET_PROVIDER_MECHANISM_INFO,
+ &mechinfo)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ if (r < 0) {
+ goto failed;
+ }
+
+ if (mechinfo.mi_return_value == CRYPTO_SUCCESS) {
+ /*
+ * Found this mechanism in hardware providers.
+ * If it is not in the nid list yet, add it.
+ */
+ if (!nid_in_list(cipher_table[j].nid,
+ tmp_nids, cipher_count)) {
+ tmp_nids[cipher_count] =
+ cipher_table[j].nid;
+ cipher_count++;
+ }
+ }
+ }
+ }
+
+ if (cipher_count > 0) {
+ cipher_nids = tmp_nids;
+ }
+
+ return (1);
+
+failed:
+ if (r < 0 || cipher_count == 0) {
+ if (tmp_nids != NULL)
+ OPENSSL_free(tmp_nids);
+ }
+ return (0);
+}
+
+/*
+ * Registered by the ENGINE when used to find out how to deal with
+ * a particular NID in the ENGINE. This says what we'll do at the
+ * top level - note, that list is restricted by what we answer with.
+ */
+static int
+devcrypto_get_all_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
+ const int **nids, int nid)
+{
+ if (!cipher) {
+ *nids = (cipher_count > 0) ? cipher_nids : NULL;
+ return (cipher_count);
+ }
+
+ switch (nid) {
+ case NID_des_cbc:
+ *cipher = &dev_des_cbc;
+ break;
+ case NID_des_ede3_cbc:
+ *cipher = &dev_3des_cbc;
+ break;
+ case NID_des_ecb:
+ *cipher = &dev_des_ecb;
+ break;
+ case NID_des_ede3_ecb:
+ *cipher = &dev_3des_ecb;
+ break;
+ case NID_rc4:
+ *cipher = &dev_rc4;
+ break;
+ case NID_aes_128_cbc:
+ *cipher = &dev_aes_128_cbc;
+ break;
+ case NID_aes_192_cbc:
+ *cipher = &dev_aes_192_cbc;
+ break;
+ case NID_aes_256_cbc:
+ *cipher = &dev_aes_256_cbc;
+ break;
+ case NID_aes_128_ecb:
+ *cipher = &dev_aes_128_ecb;
+ break;
+ case NID_aes_192_ecb:
+ *cipher = &dev_aes_192_ecb;
+ break;
+ case NID_aes_256_ecb:
+ *cipher = &dev_aes_256_ecb;
+ break;
+ case NID_bf_cbc:
+ *cipher = &dev_bf_cbc;
+ break;
+ default:
+#ifdef SOLARIS_AES_CTR
+ /*
+ * We cannot put the NIDs for AES counter mode in separated
+ * cases as above because they are not constants.
+ */
+ if (nid == NID_aes_128_ctr)
+ *cipher = &dev_aes_128_ctr;
+ else if (nid == NID_aes_192_ctr)
+ *cipher = &dev_aes_192_ctr;
+ else if (nid == NID_aes_256_ctr)
+ *cipher = &dev_aes_256_ctr;
+ else
+ *cipher = NULL;
+#endif /* SOLARIS_AES_CTR */
+ break;
+ }
+
+ return (*cipher != NULL);
+}
+
+
+static int
+get_cipher_id_by_nid(int nid)
+{
+ int i;
+
+ for (i = 0; i < DEV_CIPHER_MAX; i++)
+ if (cipher_table[i].nid == nid)
+ return (cipher_table[i].id);
+ return (-1);
+}
+
+
+static int
+get_slotid_by_mechanism(const char *mech_string, CK_SLOT_ID *slot_id)
+{
+ crypto_get_provider_mechanism_info_t mechanism_info;
+ uint_t rv;
+ int r;
+ int i = 0;
+
+ (void) strlcpy(mechanism_info.mi_mechanism_name, mech_string,
+ CRYPTO_MAX_MECH_NAME);
+ while (i < slot_count) {
+ mechanism_info.mi_provider_id = kernel_provider_id[i];
+ while ((r = ioctl(kernel_fd,
+ CRYPTO_GET_PROVIDER_MECHANISM_INFO,
+ &mechanism_info)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ if (r < 0) {
+ return (0); /* ioctl function failed */
+ }
+ rv = mechanism_info.mi_return_value;
+ if (rv == 0) { /* found it */
+ *slot_id = kernel_provider_id[i];
+ return (1);
+ }
+ i++;
+ }
+
+ return (0);
+}
+
+
+static int
+devcrypto_cipher_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+ const unsigned char *iv, int enc)
+{
+ devcrypto_ctx_t *devc_ctx = ctx->cipher_data;
+ crypto_encrypt_init_t encrypt_init;
+ crypto_decrypt_init_t decrypt_init;
+ crypto_open_session_t session;
+ crypto_get_mechanism_number_t get_number;
+ CK_AES_CTR_PARAMS aes_ctr_params;
+ devcrypto_cipher_t *the_cipher;
+ const char *mech_string;
+ CK_SLOT_ID slot_id;
+ int index;
+ int r;
+ uint_t rv = 0;
+
+ if (key == NULL) {
+ DEVCRYPTOerr(DEVC_F_CIPHER_INIT, DEVC_R_CIPHER_KEY);
+ return (0);
+ }
+
+ /* get the cipher entry index in cipher_table from nid */
+ index = get_cipher_id_by_nid(ctx->cipher->nid);
+ if (index < 0 || index >= DEV_CIPHER_MAX) {
+ DEVCRYPTOerr(DEVC_F_CIPHER_INIT, DEVC_R_CIPHER_NID);
+ return (0);
+ }
+ the_cipher = &cipher_table[index];
+
+ /* check key size */
+ if (ctx->cipher->iv_len < the_cipher->iv_len ||
+ ctx->key_len < the_cipher->min_key_len ||
+ ctx->key_len > the_cipher->max_key_len) {
+ DEVCRYPTOerr(DEVC_F_CIPHER_INIT, DEVC_R_KEY_OR_IV_LEN_PROBLEM);
+ return (0);
+ }
+
+ /* Set cipher flags, if any */
+ ctx->flags |= the_cipher->flags;
+
+ /* get the mechanism string */
+ mech_string = pkcs11_mech2str(the_cipher->mech_type);
+ if (mech_string == NULL) {
+ DEVCRYPTOerr(DEVC_F_CIPHER_INIT, DEVC_R_MECH_STRING);
+ return (0);
+ }
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto: mech_string=%s\n", mech_string);
+#endif
+
+ /* Find the slot that supports this mechanism */
+ if (!get_slotid_by_mechanism(mech_string, &slot_id)) {
+ DEVCRYPTOerr(DEVC_F_CIPHER_INIT, DEVC_R_FIND_SLOT_BY_MECH);
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "libdevcrypto: failed to find a slot with %s\n",
+ mech_string);
+#endif
+ return (0);
+ }
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto: found a slot with %s, "
+ "slot_id = %d\n", mech_string, slot_id);
+#endif
+
+ /* Open a session on this slot */
+ session.os_provider_id = slot_id;
+ session.os_flags = CKF_RW_SESSION | CKF_SERIAL_SESSION;
+ while ((r = ioctl(kernel_fd, CRYPTO_OPEN_SESSION, &session)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ rv = session.os_return_value;
+ if (r || rv) {
+ DEVCRYPTOerr(DEVC_F_CIPHER_INIT, DEVC_R_OPEN_SESSION);
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "libdevcrypto:cipher_init:failed to open a session\n");
+#endif /* DEBUG */
+ goto failed;
+ }
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto:cipher_init: open session = %d\n",
+ session.os_session);
+#endif /* DEBUG */
+
+ /* save the session_id */
+ devc_ctx->session_id = session.os_session;
+
+ /*
+ * Get the kernel mechanism number for this mechanism, if it has not
+ * been retrieved yet.
+ */
+ if (the_cipher->pn_internal_number == CRYPTO_MECH_INVALID) {
+ get_number.pn_mechanism_string = (char *)mech_string;
+ get_number.pn_mechanism_len = strlen(mech_string) + 1;
+ while ((r = ioctl(kernel_fd, CRYPTO_GET_MECHANISM_NUMBER,
+ &get_number)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ rv = get_number.pn_return_value;
+ if (r || rv) {
+ DEVCRYPTOerr(DEVC_F_CIPHER_INIT,
+ DEVC_R_GET_MECHANISM_NUMBER);
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto:cipher_init: "
+ "failed to get the kernel mech number.\n");
+#endif /* DEBUG */
+ goto failed;
+ }
+
+ the_cipher->pn_internal_number = get_number.pn_internal_number;
+ }
+
+ /* Crypto Init */
+ if (ctx->encrypt) {
+ encrypt_init.ei_session = session.os_session;
+ encrypt_init.ei_key.ck_format = CRYPTO_KEY_RAW;
+ encrypt_init.ei_key.ck_obj_id = 0;
+ encrypt_init.ei_key.ck_data = (void *) key;
+ encrypt_init.ei_key.ck_length = ctx->key_len * 8;
+ encrypt_init.ei_mech.cm_type = the_cipher->pn_internal_number;
+
+ if (ctx->cipher->nid == NID_aes_128_ctr ||
+ ctx->cipher->nid == NID_aes_192_ctr ||
+ ctx->cipher->nid == NID_aes_256_ctr) {
+ encrypt_init.ei_mech.cm_param =
+ (void *) (&aes_ctr_params);
+ encrypt_init.ei_mech.cm_param_len =
+ sizeof (aes_ctr_params);
+
+ aes_ctr_params.ulCounterBits = AES_BLOCK_SIZE * 8;
+ OPENSSL_assert(ctx->cipher->iv_len == AES_BLOCK_SIZE);
+ (void) memcpy(aes_ctr_params.cb, ctx->iv,
+ AES_BLOCK_SIZE);
+ } else {
+ if (the_cipher->iv_len > 0) {
+ encrypt_init.ei_mech.cm_param =
+ (char *)ctx->iv;
+ encrypt_init.ei_mech.cm_param_len =
+ ctx->cipher->iv_len;
+ } else {
+ encrypt_init.ei_mech.cm_param = NULL;
+ encrypt_init.ei_mech.cm_param_len = 0;
+ }
+ }
+
+ while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT_INIT,
+ &encrypt_init)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ rv = encrypt_init.ei_return_value;
+
+ } else {
+ decrypt_init.di_session = session.os_session;
+ decrypt_init.di_key.ck_format = CRYPTO_KEY_RAW;
+ decrypt_init.di_key.ck_obj_id = 0;
+ decrypt_init.di_key.ck_data = (void *) key;
+ decrypt_init.di_key.ck_length = ctx->key_len * 8;
+ decrypt_init.di_mech.cm_type = the_cipher->pn_internal_number;
+
+ if (ctx->cipher->nid == NID_aes_128_ctr ||
+ ctx->cipher->nid == NID_aes_192_ctr ||
+ ctx->cipher->nid == NID_aes_256_ctr) {
+ decrypt_init.di_mech.cm_param =
+ (void *)(&aes_ctr_params);
+ decrypt_init.di_mech.cm_param_len =
+ sizeof (aes_ctr_params);
+ aes_ctr_params.ulCounterBits = AES_BLOCK_SIZE * 8;
+ OPENSSL_assert(ctx->cipher->iv_len == AES_BLOCK_SIZE);
+ (void) memcpy(aes_ctr_params.cb, ctx->iv,
+ AES_BLOCK_SIZE);
+ } else {
+ if (the_cipher->iv_len > 0) {
+ decrypt_init.di_mech.cm_param =
+ (char *)ctx->iv;
+ decrypt_init.di_mech.cm_param_len =
+ ctx->cipher->iv_len;
+ } else {
+ decrypt_init.di_mech.cm_param = NULL;
+ decrypt_init.di_mech.cm_param_len = 0;
+ }
+ }
+
+ while ((r = ioctl(kernel_fd, CRYPTO_DECRYPT_INIT,
+ &decrypt_init)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ rv = decrypt_init.di_return_value;
+ }
+
+failed:
+ if (r || rv) {
+ if (ctx->encrypt)
+ DEVCRYPTOerr(DEVC_F_CIPHER_INIT, DEVC_R_ENCRYPT_INIT);
+ else
+ DEVCRYPTOerr(DEVC_F_CIPHER_INIT, DEVC_R_DECRYPT_INIT);
+
+ return (0);
+ }
+
+ return (1);
+}
+
+
+/*
+ * ENCRYPT_UPDATE or DECRYPT_UPDATE
+ */
+static int
+devcrypto_cipher_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ crypto_encrypt_update_t encrypt_update;
+ crypto_decrypt_update_t decrypt_update;
+ devcrypto_ctx_t *devc_ctx = ctx->cipher_data;
+ int r = 0, rv = 0;
+
+ if (ctx->encrypt) {
+ encrypt_update.eu_session = devc_ctx->session_id;
+ encrypt_update.eu_databuf = (char *)in;
+ encrypt_update.eu_datalen = inl;
+ encrypt_update.eu_encrbuf = (char *)out;
+ encrypt_update.eu_encrlen = inl;
+
+ while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT_UPDATE,
+ &encrypt_update)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ rv = encrypt_update.eu_return_value;
+
+ } else { /* decrypt */
+ decrypt_update.du_session = devc_ctx->session_id;
+ decrypt_update.du_encrbuf = (char *)in;
+ decrypt_update.du_encrlen = inl;
+ decrypt_update.du_databuf = (char *)out;
+ decrypt_update.du_datalen = inl;
+
+ while ((r = ioctl(kernel_fd, CRYPTO_DECRYPT_UPDATE,
+ &decrypt_update)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ rv = decrypt_update.du_return_value;
+ }
+
+ if (r || rv) {
+ if (ctx->encrypt)
+ DEVCRYPTOerr(DEVC_F_CIPHER_DO_CIPHER,
+ DEVC_R_ENCRYPT_UPDATE);
+ else
+ DEVCRYPTOerr(DEVC_F_CIPHER_DO_CIPHER,
+ DEVC_R_DECRYPT_UPDATE);
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto:crypto_do ret (r) = 0x%x,"
+ "crypto ret (rv) = 0x%x,", r, rv);
+#endif /* DEBUG */
+ return (0);
+ }
+
+ return (1);
+}
+
+
+/*
+ * ENCRYPT_FINAL or DECRYPT_FINAL
+ */
+static int
+devcrypto_cipher_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ crypto_encrypt_final_t encrypt_final;
+ crypto_decrypt_final_t decrypt_final;
+ crypto_close_session_t session;
+ devcrypto_ctx_t *devc_ctx = ctx->cipher_data;
+ char buf[EVP_MAX_BLOCK_LENGTH];
+ int r;
+ uint_t rv = 0;
+ int ret = 1;
+
+ if (ctx->encrypt) {
+ encrypt_final.ef_session = devc_ctx->session_id;
+ encrypt_final.ef_encrbuf = buf;
+ encrypt_final.ef_encrlen = sizeof (buf);
+ while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT_FINAL,
+ &encrypt_final)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ rv = encrypt_final.ef_return_value;
+
+ } else {
+ decrypt_final.df_session = devc_ctx->session_id;
+ decrypt_final.df_databuf = buf;
+ decrypt_final.df_datalen = sizeof (buf);
+ while ((r = ioctl(kernel_fd, CRYPTO_DECRYPT_FINAL,
+ &decrypt_final)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+ rv = decrypt_final.df_return_value;
+ }
+
+#ifdef DEBUG
+ if (ctx->encrypt)
+ (void) fprintf(stderr, "libdevcrypto:CRYPTO_ENCRYPT_FINAL "
+ "ret (r) = 0x%x, (rv) = 0x%x\n", r, rv);
+ else
+ (void) fprintf(stderr, "libdevcrypto:CRYPTO_DECRYPT_FINAL "
+ "ret (r) = 0x%x, (rv) = 0x%x\n", r, rv);
+#endif /* DEBUG */
+
+ if (r || rv) {
+ if (ctx->encrypt)
+ DEVCRYPTOerr(DEVC_F_CIPHER_CLEANUP,
+ DEVC_R_ENCRYPT_FINAL);
+ else
+ DEVCRYPTOerr(DEVC_F_CIPHER_CLEANUP,
+ DEVC_R_DECRYPT_FINAL);
+ ret = 0;
+ }
+
+ /* close the session */
+ session.cs_session = devc_ctx->session_id;
+ while ((r = ioctl(kernel_fd, CRYPTO_CLOSE_SESSION, &session)) < 0) {
+ if (errno != EINTR)
+ break;
+ }
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto:CRYPTO_CLOSE_SESSION, "
+ "session id = %d ret (r) = 0x%x, crypto ret (rv) = 0x%x\n",
+ devc_ctx->session_id, r, rv);
+#endif /* DEBUG */
+
+ if (r || rv) {
+ DEVCRYPTOerr(DEVC_F_CIPHER_CLEANUP, DEVC_R_CLOSE_SESSION);
+ ret = 0;
+ }
+
+ return (ret);
+}
+
+static void
+devcrypto_cleanup(void)
+{
+ if (kernel_fd == -1)
+ return;
+
+ (void) pthread_mutex_lock(kernel_fd_lock);
+ kernel_fd_ref--;
+ (void) pthread_mutex_unlock(kernel_fd_lock);
+
+ if (kernel_fd_ref == 0) {
+ (void) pthread_mutex_lock(kernel_fd_lock);
+ (void) close(kernel_fd);
+ kernel_fd = -1;
+ if (kernel_provider_id != NULL) {
+ OPENSSL_free(kernel_provider_id);
+ kernel_provider_id = NULL;
+ }
+ if (cipher_nids != NULL) {
+ OPENSSL_free(cipher_nids);
+ cipher_nids = NULL;
+ }
+ devcrypto_free_aes_ctr_NIDs();
+ (void) pthread_mutex_unlock(kernel_fd_lock);
+ (void) pthread_mutex_destroy(kernel_fd_lock);
+ OPENSSL_free(kernel_fd_lock);
+ kernel_fd_lock = NULL;
+ }
+}
+
+static int
+devcrypto_destroy(ENGINE *e)
+{
+ ERR_unload_devcrypto_strings();
+ return (1);
+}
+
+static int
+devcrypto_finish(ENGINE *e)
+{
+ devcrypto_cleanup();
+ return (1);
+}
+
+/*
+ * Set up the engine info and get the /dev/crypto engine ready.
+ */
+static int
+devcrypto_bind(ENGINE *e)
+{
+#ifdef DEBUG
+ int i;
+#endif
+
+#ifdef SOLARIS_AES_CTR
+ /* Get the NIDs for AES counter mode algorithms first. */
+ if (devcrypto_add_aes_ctr_NIDs() == 0) {
+ return (0);
+ }
+#endif /* SOLARIS_AES_CTR */
+
+ /* Create a lock for the devcrypto device file descriptor */
+ if (kernel_fd_lock == NULL) {
+ kernel_fd_lock = OPENSSL_malloc(sizeof (pthread_mutex_t));
+ if (kernel_fd_lock == NULL) {
+ devcrypto_free_aes_ctr_NIDs();
+ return (0);
+ }
+
+ if (pthread_mutex_init(kernel_fd_lock, NULL) != 0) {
+ devcrypto_free_aes_ctr_NIDs();
+ OPENSSL_free(kernel_fd_lock);
+ kernel_fd_lock = NULL;
+ return (0);
+ }
+ }
+
+ /* Open the /dev/crypto device */
+ if (devcrypto_open() == 0) {
+ devcrypto_free_aes_ctr_NIDs();
+ pthread_mutex_destroy(kernel_fd_lock);
+ OPENSSL_free(kernel_fd_lock);
+ kernel_fd_lock = NULL;
+ return (0);
+ }
+
+ /* Get all hardware providers' information */
+ if (devcrypto_get_slot_info() == 0) {
+ goto failed;
+ }
+
+ if (devcrypto_get_hw_ciphers() == 0) {
+ goto failed;
+ }
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "cipher_count = %d\n", cipher_count);
+ for (i = 0; i < cipher_count; i++) {
+ (void) fprintf(stderr,
+ "cipher_nids[i] = %d\n", cipher_nids[i]);
+ }
+#endif /* DEBUG */
+
+ if (!ENGINE_set_id(e, ENGINE_DEVCRYPTO_ID) ||
+ !ENGINE_set_name(e, ENGINE_DEVCRYPTO_NAME) ||
+ !ENGINE_set_ciphers(e, devcrypto_get_all_ciphers) ||
+ !ENGINE_set_destroy_function(e, devcrypto_destroy) ||
+ !ENGINE_set_finish_function(e, devcrypto_finish)) {
+ goto failed;
+ }
+
+ /* Set up the devcrypto error handling */
+ ERR_load_devcrypto_strings();
+ return (1);
+
+failed:
+ devcrypto_cleanup();
+ return (0);
+}
+
+
+static int
+bind_helper(ENGINE *e, const char *id)
+{
+ if (id != NULL && (strcmp(id, ENGINE_DEVCRYPTO_ID) != 0)) {
+#ifdef DEBUG
+ (void) fprintf(stderr, "libdevcrypto - bad engine id\n");
+#endif /* DEBUG */
+ return (0);
+ }
+ if (!devcrypto_bind(e)) {
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "libdevcrypto - failed to bind engine\n");
+#endif /* DEBUG */
+ return (0);
+ }
+
+ return (1);
+}
+
+IMPLEMENT_DYNAMIC_CHECK_FN()
+IMPLEMENT_DYNAMIC_BIND_FN(bind_helper)