summaryrefslogtreecommitdiffstats
path: root/lib/accelerated/cryptodev.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/accelerated/cryptodev.c')
-rw-r--r--lib/accelerated/cryptodev.c517
1 files changed, 517 insertions, 0 deletions
diff --git a/lib/accelerated/cryptodev.c b/lib/accelerated/cryptodev.c
new file mode 100644
index 0000000..dca3aa8
--- /dev/null
+++ b/lib/accelerated/cryptodev.c
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2009-2010, 2012 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "errors.h"
+#include "gnutls_int.h"
+#include <gnutls/crypto.h>
+#include "errors.h"
+#include <accelerated/cryptodev.h>
+
+#ifdef ENABLE_CRYPTODEV
+
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <crypto/cryptodev.h>
+
+#ifndef CRYPTO_CIPHER_MAX_KEY_LEN
+#define CRYPTO_CIPHER_MAX_KEY_LEN 64
+#endif
+
+#ifndef EALG_MAX_BLOCK_LEN
+#define EALG_MAX_BLOCK_LEN 16
+#endif
+
+int _gnutls_cryptodev_fd = -1;
+
+static int register_mac_digest(int cfd);
+
+struct cryptodev_ctx {
+ struct session_op sess;
+ struct crypt_op cryp;
+ uint8_t iv[EALG_MAX_BLOCK_LEN];
+
+ int cfd;
+};
+
+static const int gnutls_cipher_map[] = {
+ [GNUTLS_CIPHER_AES_128_CBC] = CRYPTO_AES_CBC,
+ [GNUTLS_CIPHER_AES_192_CBC] = CRYPTO_AES_CBC,
+ [GNUTLS_CIPHER_AES_256_CBC] = CRYPTO_AES_CBC,
+ [GNUTLS_CIPHER_3DES_CBC] = CRYPTO_3DES_CBC,
+ [GNUTLS_CIPHER_CAMELLIA_128_CBC] = CRYPTO_CAMELLIA_CBC,
+ [GNUTLS_CIPHER_CAMELLIA_192_CBC] = CRYPTO_CAMELLIA_CBC,
+ [GNUTLS_CIPHER_CAMELLIA_256_CBC] = CRYPTO_CAMELLIA_CBC,
+ [GNUTLS_CIPHER_DES_CBC] = CRYPTO_DES_CBC,
+};
+
+static int
+cryptodev_cipher_init(gnutls_cipher_algorithm_t algorithm, void **_ctx,
+ int enc)
+{
+ struct cryptodev_ctx *ctx;
+ int cipher = gnutls_cipher_map[algorithm];
+
+ *_ctx = gnutls_calloc(1, sizeof(struct cryptodev_ctx));
+ if (*_ctx == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ctx = *_ctx;
+
+ ctx->cfd = _gnutls_cryptodev_fd;
+ ctx->sess.cipher = cipher;
+ ctx->cryp.iv = ctx->iv;
+
+ return 0;
+}
+
+static int
+cryptodev_cipher_setkey(void *_ctx, const void *key, size_t keysize)
+{
+ struct cryptodev_ctx *ctx = _ctx;
+
+ CHECK_AES_KEYSIZE(keysize);
+
+ ctx->sess.keylen = keysize;
+ ctx->sess.key = (void *) key;
+
+ if (ioctl(ctx->cfd, CIOCGSESSION, &ctx->sess)) {
+ gnutls_assert();
+ return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
+ }
+ ctx->cryp.ses = ctx->sess.ses;
+
+ return 0;
+}
+
+static int cryptodev_setiv(void *_ctx, const void *iv, size_t iv_size)
+{
+ struct cryptodev_ctx *ctx = _ctx;
+
+ if (iv_size > EALG_MAX_BLOCK_LEN)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ memcpy(ctx->iv, iv, iv_size);
+
+ return 0;
+}
+
+static int
+cryptodev_encrypt(void *_ctx, const void *src, size_t src_size,
+ void *dst, size_t dst_size)
+{
+ struct cryptodev_ctx *ctx = _ctx;
+ ctx->cryp.len = src_size;
+ ctx->cryp.src = (void *) src;
+ ctx->cryp.dst = dst;
+ ctx->cryp.op = COP_ENCRYPT;
+ ctx->cryp.flags = COP_FLAG_WRITE_IV;
+
+ if (unlikely(dst_size < src_size)) {
+ gnutls_assert();
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ if (ioctl(ctx->cfd, CIOCCRYPT, &ctx->cryp)) {
+ gnutls_assert();
+ return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
+ }
+
+ return 0;
+}
+
+static int
+cryptodev_decrypt(void *_ctx, const void *src, size_t src_size,
+ void *dst, size_t dst_size)
+{
+ struct cryptodev_ctx *ctx = _ctx;
+
+ ctx->cryp.len = src_size;
+ ctx->cryp.src = (void *) src;
+ ctx->cryp.dst = dst;
+ ctx->cryp.op = COP_DECRYPT;
+ ctx->cryp.flags = COP_FLAG_WRITE_IV;
+
+ if (unlikely(dst_size < src_size)) {
+ gnutls_assert();
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ if (ioctl(ctx->cfd, CIOCCRYPT, &ctx->cryp)) {
+ gnutls_assert();
+ return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
+ }
+
+ return 0;
+}
+
+static void cryptodev_deinit(void *_ctx)
+{
+ struct cryptodev_ctx *ctx = _ctx;
+
+ ioctl(ctx->cfd, CIOCFSESSION, &ctx->sess.ses);
+ gnutls_free(ctx);
+}
+
+static const gnutls_crypto_cipher_st cipher_struct = {
+ .init = cryptodev_cipher_init,
+ .setkey = cryptodev_cipher_setkey,
+ .setiv = cryptodev_setiv,
+ .encrypt = cryptodev_encrypt,
+ .decrypt = cryptodev_decrypt,
+ .deinit = cryptodev_deinit,
+};
+
+static int register_crypto(int cfd)
+{
+ struct session_op sess;
+ uint8_t fake_key[CRYPTO_CIPHER_MAX_KEY_LEN];
+ unsigned int i;
+ int ret;
+#ifdef CIOCGSESSINFO
+ struct session_info_op siop;
+#endif
+
+ memset(&sess, 0, sizeof(sess));
+
+ for (i = 0;
+ i < sizeof(gnutls_cipher_map) / sizeof(gnutls_cipher_map[0]);
+ i++) {
+ if (gnutls_cipher_map[i] == 0)
+ continue;
+
+ /* test if a cipher is supported and if yes register it */
+ sess.cipher = gnutls_cipher_map[i];
+ sess.keylen = gnutls_cipher_get_key_size(i);
+ sess.key = fake_key;
+
+ if (ioctl(cfd, CIOCGSESSION, &sess)) {
+ continue;
+ }
+#ifdef CIOCGSESSINFO
+ memset(&siop, 0, sizeof(siop));
+
+ siop.ses = sess.ses; /* do not register ciphers that are not hw accelerated */
+ if (ioctl(cfd, CIOCGSESSINFO, &siop) == 0) {
+ if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
+ ioctl(cfd, CIOCFSESSION, &sess.ses);
+ continue;
+ }
+ }
+#endif
+
+ ioctl(cfd, CIOCFSESSION, &sess.ses);
+
+ _gnutls_debug_log("/dev/crypto: registering: %s\n",
+ gnutls_cipher_get_name(i));
+ ret =
+ gnutls_crypto_single_cipher_register(i, 90,
+ &cipher_struct, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ }
+
+#ifdef CIOCAUTHCRYPT
+ return _cryptodev_register_gcm_crypto(cfd);
+#else
+ return 0;
+#endif
+}
+
+int _gnutls_cryptodev_init(void)
+{
+ int ret;
+
+ /* Open the crypto device */
+ _gnutls_cryptodev_fd = open("/dev/crypto", O_RDWR, 0);
+ if (_gnutls_cryptodev_fd < 0) {
+ gnutls_assert();
+ return GNUTLS_E_CRYPTODEV_DEVICE_ERROR;
+ }
+#ifndef CRIOGET_NOT_NEEDED
+ {
+ int cfd = -1;
+ /* Clone file descriptor */
+ if (ioctl(_gnutls_cryptodev_fd, CRIOGET, &cfd)) {
+ gnutls_assert();
+ return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
+ }
+
+ /* Set close-on-exec (not really needed here) */
+ if (fcntl(cfd, F_SETFD, 1) == -1) {
+ gnutls_assert();
+ return GNUTLS_E_CRYPTODEV_IOCTL_ERROR;
+ }
+
+ close(_gnutls_cryptodev_fd);
+ _gnutls_cryptodev_fd = cfd;
+ }
+#endif
+
+ ret = register_crypto(_gnutls_cryptodev_fd);
+ if (ret < 0)
+ gnutls_assert();
+
+ if (ret >= 0) {
+ ret = register_mac_digest(_gnutls_cryptodev_fd);
+ if (ret < 0)
+ gnutls_assert();
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ close(_gnutls_cryptodev_fd);
+ }
+
+ return ret;
+}
+
+void _gnutls_cryptodev_deinit(void)
+{
+ if (_gnutls_cryptodev_fd != -1)
+ close(_gnutls_cryptodev_fd);
+}
+
+/* MAC and digest stuff */
+
+/* if we are using linux /dev/crypto
+ */
+#if defined(COP_FLAG_UPDATE) && defined(COP_FLAG_RESET)
+
+static const int gnutls_mac_map[] = {
+ [GNUTLS_MAC_MD5] = CRYPTO_MD5_HMAC,
+ [GNUTLS_MAC_SHA1] = CRYPTO_SHA1_HMAC,
+ [GNUTLS_MAC_SHA256] = CRYPTO_SHA2_256_HMAC,
+ [GNUTLS_MAC_SHA384] = CRYPTO_SHA2_384_HMAC,
+ [GNUTLS_MAC_SHA512] = CRYPTO_SHA2_512_HMAC,
+};
+
+static int
+cryptodev_mac_fast(gnutls_mac_algorithm_t algo,
+ const void *nonce, size_t nonce_size,
+ const void *key, size_t key_size, const void *text,
+ size_t text_size, void *digest)
+{
+ struct cryptodev_ctx ctx;
+ int ret;
+
+ assert(nonce_size == 0);
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.cfd = _gnutls_cryptodev_fd;
+ ctx.sess.mac = gnutls_mac_map[algo];
+
+ ctx.sess.mackeylen = key_size;
+ ctx.sess.mackey = (void *) key;
+
+ if (ioctl(ctx.cfd, CIOCGSESSION, &ctx.sess))
+ return gnutls_assert_val(GNUTLS_E_CRYPTODEV_IOCTL_ERROR);
+
+ ctx.cryp.ses = ctx.sess.ses;
+
+ ctx.cryp.len = text_size;
+ ctx.cryp.src = (void *) text;
+ ctx.cryp.dst = NULL;
+ ctx.cryp.op = COP_ENCRYPT;
+ ctx.cryp.mac = digest;
+
+ ret = ioctl(ctx.cfd, CIOCCRYPT, &ctx.cryp);
+
+ ioctl(_gnutls_cryptodev_fd, CIOCFSESSION, &ctx.sess.ses);
+ if (ret != 0)
+ return gnutls_assert_val(GNUTLS_E_CRYPTODEV_IOCTL_ERROR);
+
+ return 0;
+}
+
+#define cryptodev_mac_deinit cryptodev_deinit
+
+static const gnutls_crypto_mac_st mac_struct = {
+ .init = NULL,
+ .setkey = NULL,
+ .setnonce = NULL,
+ .hash = NULL,
+ .output = NULL,
+ .deinit = NULL,
+ .fast = cryptodev_mac_fast
+};
+
+/* Digest algorithms */
+
+static const int gnutls_digest_map[] = {
+ [GNUTLS_DIG_MD5] = CRYPTO_MD5,
+ [GNUTLS_DIG_SHA1] = CRYPTO_SHA1,
+ [GNUTLS_DIG_SHA256] = CRYPTO_SHA2_256,
+ [GNUTLS_DIG_SHA384] = CRYPTO_SHA2_384,
+ [GNUTLS_DIG_SHA512] = CRYPTO_SHA2_512,
+};
+
+static int
+cryptodev_digest_fast(gnutls_digest_algorithm_t algo,
+ const void *text, size_t text_size, void *digest)
+{
+ struct cryptodev_ctx ctx;
+ int ret;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.cfd = _gnutls_cryptodev_fd;
+ ctx.sess.mac = gnutls_digest_map[algo];
+
+ if (ioctl(ctx.cfd, CIOCGSESSION, &ctx.sess))
+ return gnutls_assert_val(GNUTLS_E_CRYPTODEV_IOCTL_ERROR);
+
+ ctx.cryp.ses = ctx.sess.ses;
+
+ ctx.cryp.len = text_size;
+ ctx.cryp.src = (void *) text;
+ ctx.cryp.dst = NULL;
+ ctx.cryp.op = COP_ENCRYPT;
+ ctx.cryp.mac = digest;
+
+ ret = ioctl(ctx.cfd, CIOCCRYPT, &ctx.cryp);
+
+ ioctl(_gnutls_cryptodev_fd, CIOCFSESSION, &ctx.sess.ses);
+ if (ret != 0)
+ return gnutls_assert_val(GNUTLS_E_CRYPTODEV_IOCTL_ERROR);
+
+ return 0;
+}
+
+static const gnutls_crypto_digest_st digest_struct = {
+ .init = NULL,
+ .hash = NULL,
+ .output = NULL,
+ .deinit = NULL,
+ .fast = cryptodev_digest_fast
+};
+
+static int register_mac_digest(int cfd)
+{
+ struct session_op sess;
+ uint8_t fake_key[CRYPTO_CIPHER_MAX_KEY_LEN];
+ unsigned int i;
+ int ret;
+#ifdef CIOCGSESSINFO
+ struct session_info_op siop;
+#endif
+
+ memset(&sess, 0, sizeof(sess));
+ for (i = 0; i < sizeof(gnutls_mac_map) / sizeof(gnutls_mac_map[0]);
+ i++) {
+ if (gnutls_mac_map[i] == 0)
+ continue;
+
+ sess.mac = gnutls_mac_map[i];
+ sess.mackeylen = 8;
+ sess.mackey = fake_key;
+
+ if (ioctl(cfd, CIOCGSESSION, &sess)) {
+ continue;
+ }
+#ifdef CIOCGSESSINFO
+ memset(&siop, 0, sizeof(siop));
+
+ siop.ses = sess.ses; /* do not register ciphers that are not hw accelerated */
+ if (ioctl(cfd, CIOCGSESSINFO, &siop) == 0) {
+ if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
+ ioctl(cfd, CIOCFSESSION, &sess.ses);
+ continue;
+ }
+ }
+#endif
+ _gnutls_debug_log("/dev/crypto: registering: HMAC-%s\n",
+ gnutls_mac_get_name(i));
+
+ ioctl(cfd, CIOCFSESSION, &sess.ses);
+
+ ret =
+ gnutls_crypto_single_mac_register(i, 90, &mac_struct, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ memset(&sess, 0, sizeof(sess));
+ for (i = 0;
+ i < sizeof(gnutls_digest_map) / sizeof(gnutls_digest_map[0]);
+ i++) {
+ if (gnutls_digest_map[i] == 0)
+ continue;
+
+ sess.mac = gnutls_digest_map[i];
+
+ if (ioctl(cfd, CIOCGSESSION, &sess)) {
+ continue;
+ }
+#ifdef CIOCGSESSINFO
+ memset(&siop, 0, sizeof(siop));
+
+ siop.ses = sess.ses;
+ if (ioctl(cfd, CIOCGSESSINFO, &siop) == 0) {
+ if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) {
+ ioctl(cfd, CIOCFSESSION, &sess.ses);
+ continue;
+ }
+ }
+#endif
+
+ ioctl(cfd, CIOCFSESSION, &sess.ses);
+
+ _gnutls_debug_log("/dev/crypto: registering: %s\n",
+ gnutls_mac_get_name(i));
+ ret =
+ gnutls_crypto_single_digest_register(i, 90,
+ &digest_struct, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#else
+static int register_mac_digest(int cfd)
+{
+ return 0;
+}
+
+#endif /* defined(COP_FLAG_UPDATE) */
+
+#else /* ENABLE_CRYPTODEV */
+int _gnutls_cryptodev_init(void)
+{
+ return 0;
+}
+
+void _gnutls_cryptodev_deinit(void)
+{
+ return;
+}
+#endif /* ENABLE_CRYPTODEV */