]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
af_alg: Crypto wrappers for Linux kernel crypto (AF_ALG)
authorJouni Malinen <j@w1.fi>
Sun, 26 Feb 2017 23:10:02 +0000 (01:10 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 28 Feb 2017 09:24:15 +0000 (11:24 +0200)
CONFIG_TLS=linux can now be used to select the crypto implementation
that uses the user space socket interface (AF_ALG) for the Linux kernel
crypto implementation. This commit includes some of the cipher, hash,
and HMAC functions. The functions that are not available through AF_ALG
(e.g., the actual TLS implementation) use the internal implementation
(CONFIG_TLS=internal).

Signed-off-by: Jouni Malinen <j@w1.fi>
hostapd/Makefile
hostapd/defconfig
src/crypto/crypto_linux.c [new file with mode: 0644]
wpa_supplicant/Makefile
wpa_supplicant/defconfig

index ea9234bad0103524b14aa96ddcb4211ce814b9e4..bc56c4cf20555874ab0d7c74b8fb123ea44f6ed0 100644 (file)
@@ -732,6 +732,47 @@ CONFIG_INTERNAL_RC4=y
 endif
 endif
 
+ifeq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/crypto_linux.o
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/crypto_internal-rsa.o
+OBJS += ../src/crypto/tls_internal.o
+OBJS += ../src/tls/tlsv1_common.o
+OBJS += ../src/tls/tlsv1_record.o
+OBJS += ../src/tls/tlsv1_cred.o
+OBJS += ../src/tls/tlsv1_server.o
+OBJS += ../src/tls/tlsv1_server_write.o
+OBJS += ../src/tls/tlsv1_server_read.o
+OBJS += ../src/tls/asn1.o
+OBJS += ../src/tls/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+CFLAGS += -DCONFIG_TLS_INTERNAL
+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DLTM_FAST
+endif
+CONFIG_INTERNAL_DH_GROUP5=y
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_internal.o
+OBJS += ../src/crypto/sha1-internal.o
+endif
+endif
+
 ifeq ($(CONFIG_TLS), none)
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_none.o
@@ -781,20 +822,26 @@ ifdef NEED_AES_ENCBLOCK
 AESOBJS += ../src/crypto/aes-encblock.o
 endif
 ifdef NEED_AES_OMAC1
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
+endif
 ifdef NEED_AES_UNWRAP
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 NEED_AES_DEC=y
 AESOBJS += ../src/crypto/aes-unwrap.o
 endif
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_DEC=y
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-cbc.o
 endif
 endif
+endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += ../src/crypto/aes-internal-dec.o
@@ -806,8 +853,10 @@ endif
 
 ifdef NEED_SHA1
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 SHA1OBJS += ../src/crypto/sha1.o
 endif
+endif
 SHA1OBJS += ../src/crypto/sha1-prf.o
 ifdef CONFIG_INTERNAL_SHA1
 SHA1OBJS += ../src/crypto/sha1-internal.o
@@ -831,8 +880,10 @@ OBJS += $(SHA1OBJS)
 endif
 
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/md5.o
 endif
+endif
 
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
@@ -868,8 +919,10 @@ endif
 ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/sha256.o
 endif
+endif
 OBJS += ../src/crypto/sha256-prf.o
 ifdef CONFIG_INTERNAL_SHA256
 OBJS += ../src/crypto/sha256-internal.o
@@ -884,8 +937,10 @@ endif
 ifdef NEED_SHA384
 CFLAGS += -DCONFIG_SHA384
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/sha384.o
 endif
+endif
 OBJS += ../src/crypto/sha384-prf.o
 endif
 
@@ -923,9 +978,11 @@ HOBJS += ../src/crypto/random.o
 HOBJS += ../src/utils/eloop.o
 HOBJS += $(SHA1OBJS)
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 HOBJS += ../src/crypto/md5.o
 endif
 endif
+endif
 
 ifdef CONFIG_RADIUS_SERVER
 CFLAGS += -DRADIUS_SERVER
@@ -1124,6 +1181,9 @@ ifdef CONFIG_INTERNAL_AES
 HOBJS += ../src/crypto/aes-internal.o
 HOBJS += ../src/crypto/aes-internal-enc.o
 endif
+ifeq ($(CONFIG_TLS), linux)
+HOBJS += ../src/crypto/crypto_linux.o
+endif
 
 nt_password_hash: $(NOBJS)
        $(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
index 9ade580a3d8411c6c66bb61311edee3ca484e1d6..e92c0ed002f165e773a0a21d33957a326b70a267 100644 (file)
@@ -265,6 +265,7 @@ CONFIG_IPV6=y
 # openssl = OpenSSL (default)
 # gnutls = GnuTLS
 # internal = Internal TLSv1 implementation (experimental)
+# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
 # none = Empty template
 #CONFIG_TLS=openssl
 
diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c
new file mode 100644 (file)
index 0000000..8099193
--- /dev/null
@@ -0,0 +1,1006 @@
+/*
+ * Crypto wrapper for Linux kernel AF_ALG
+ * Copyright (c) 2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <linux/if_alg.h>
+
+#include "common.h"
+#include "crypto.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "aes.h"
+
+
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif /* SOL_ALG */
+
+
+static int linux_af_alg_socket(const char *type, const char *name)
+{
+       struct sockaddr_alg sa;
+       int s;
+
+       if (TEST_FAIL())
+               return -1;
+
+       s = socket(AF_ALG, SOCK_SEQPACKET, 0);
+       if (s < 0) {
+               wpa_printf(MSG_ERROR, "%s: Failed to open AF_ALG socket: %s",
+                          __func__, strerror(errno));
+               return -1;
+       }
+
+       os_memset(&sa, 0, sizeof(sa));
+       sa.salg_family = AF_ALG;
+       os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type));
+       os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_type));
+       if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+               wpa_printf(MSG_ERROR,
+                          "%s: Failed to bind AF_ALG socket(%s,%s): %s",
+                          __func__, type, name, strerror(errno));
+               close(s);
+               return -1;
+       }
+
+       return s;
+}
+
+
+static int linux_af_alg_hash_vector(const char *alg, const u8 *key,
+                                   size_t key_len, size_t num_elem,
+                                   const u8 *addr[], const size_t *len,
+                                   u8 *mac, size_t mac_len)
+{
+       int s, t;
+       size_t i;
+       ssize_t res;
+       int ret = -1;
+
+       s = linux_af_alg_socket("hash", alg);
+       if (s < 0)
+               return -1;
+
+       if (key && setsockopt(s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+               wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+                          __func__, strerror(errno));
+               close(s);
+               return -1;
+       }
+
+       t = accept(s, NULL, NULL);
+       if (t < 0) {
+               wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+                          __func__, strerror(errno));
+               close(s);
+               return -1;
+       }
+
+       for (i = 0; i < num_elem; i++) {
+               res = send(t, addr[i], len[i], i + 1 < num_elem ? MSG_MORE : 0);
+               if (res < 0) {
+                       wpa_printf(MSG_ERROR,
+                                  "%s: send on AF_ALG socket failed: %s",
+                                  __func__, strerror(errno));
+                       goto fail;
+               }
+               if ((size_t) res < len[i]) {
+                       wpa_printf(MSG_ERROR,
+                                  "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
+                                  __func__, (int) res, (int) len[i]);
+                       goto fail;
+               }
+       }
+
+       res = recv(t, mac, mac_len, 0);
+       if (res < 0) {
+               wpa_printf(MSG_ERROR,
+                          "%s: recv on AF_ALG socket failed: %s",
+                          __func__, strerror(errno));
+               goto fail;
+       }
+       if ((size_t) res < mac_len) {
+               wpa_printf(MSG_ERROR,
+                          "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
+                          __func__, (int) res, (int) mac_len);
+               goto fail;
+       }
+
+       ret = 0;
+fail:
+       close(t);
+       close(s);
+
+       return ret;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return linux_af_alg_hash_vector("md4", NULL, 0, num_elem, addr, len,
+                                       mac, 16);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return linux_af_alg_hash_vector("md5", NULL, 0, num_elem, addr, len,
+                                       mac, MD5_MAC_LEN);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+               u8 *mac)
+{
+       return linux_af_alg_hash_vector("sha1", NULL, 0, num_elem, addr, len,
+                                       mac, SHA1_MAC_LEN);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+                 u8 *mac)
+{
+       return linux_af_alg_hash_vector("sha256", NULL, 0, num_elem, addr, len,
+                                       mac, SHA256_MAC_LEN);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+                 u8 *mac)
+{
+       return linux_af_alg_hash_vector("sha384", NULL, 0, num_elem, addr, len,
+                                       mac, SHA384_MAC_LEN);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+                 u8 *mac)
+{
+       return linux_af_alg_hash_vector("sha512", NULL, 0, num_elem, addr, len,
+                                       mac, 64);
+}
+
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+                   const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return linux_af_alg_hash_vector("hmac(md5)", key, key_len, num_elem,
+                                       addr, len, mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+            u8 *mac)
+{
+       return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+                    const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return linux_af_alg_hash_vector("hmac(sha1)", key, key_len, num_elem,
+                                       addr, len, mac, SHA1_MAC_LEN);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+             u8 *mac)
+{
+       return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+                      const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return linux_af_alg_hash_vector("hmac(sha256)", key, key_len, num_elem,
+                                       addr, len, mac, SHA256_MAC_LEN);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+               size_t data_len, u8 *mac)
+{
+       return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+                      const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return linux_af_alg_hash_vector("hmac(sha384)", key, key_len, num_elem,
+                                       addr, len, mac, SHA384_MAC_LEN);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+               size_t data_len, u8 *mac)
+{
+       return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+struct crypto_hash {
+       int s;
+       int t;
+       size_t mac_len;
+       int failed;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+                                     size_t key_len)
+{
+       struct crypto_hash *ctx;
+       const char *name;
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (!ctx)
+               return NULL;
+
+       switch (alg) {
+       case CRYPTO_HASH_ALG_MD5:
+               name = "md5";
+               ctx->mac_len = MD5_MAC_LEN;
+               break;
+       case CRYPTO_HASH_ALG_SHA1:
+               name = "sha1";
+               ctx->mac_len = SHA1_MAC_LEN;
+               break;
+       case CRYPTO_HASH_ALG_HMAC_MD5:
+               name = "hmac(md5)";
+               ctx->mac_len = MD5_MAC_LEN;
+               break;
+       case CRYPTO_HASH_ALG_HMAC_SHA1:
+               name = "hmac(sha1)";
+               ctx->mac_len = SHA1_MAC_LEN;
+               break;
+       case CRYPTO_HASH_ALG_SHA256:
+               name = "sha256";
+               ctx->mac_len = SHA256_MAC_LEN;
+               break;
+       case CRYPTO_HASH_ALG_HMAC_SHA256:
+               name = "hmac(sha256)";
+               ctx->mac_len = SHA256_MAC_LEN;
+               break;
+       case CRYPTO_HASH_ALG_SHA384:
+               name = "sha384";
+               ctx->mac_len = SHA384_MAC_LEN;
+               break;
+       case CRYPTO_HASH_ALG_SHA512:
+               name = "sha512";
+               ctx->mac_len = 64;
+               break;
+       default:
+               os_free(ctx);
+               return NULL;
+       }
+
+       ctx->s = linux_af_alg_socket("hash", name);
+       if (ctx->s < 0) {
+               os_free(ctx);
+               return NULL;
+       }
+
+       if (key && key_len &&
+           setsockopt(ctx->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+               wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+                          __func__, strerror(errno));
+               close(ctx->s);
+               os_free(ctx);
+               return NULL;
+       }
+
+       ctx->t = accept(ctx->s, NULL, NULL);
+       if (ctx->t < 0) {
+               wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+                          __func__, strerror(errno));
+               close(ctx->s);
+               os_free(ctx);
+               return NULL;
+       }
+
+       return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+       ssize_t res;
+
+       if (!ctx)
+               return;
+
+       res = send(ctx->t, data, len, MSG_MORE);
+       if (res < 0) {
+               wpa_printf(MSG_ERROR,
+                          "%s: send on AF_ALG socket failed: %s",
+                          __func__, strerror(errno));
+               ctx->failed = 1;
+               return;
+       }
+       if ((size_t) res < len) {
+               wpa_printf(MSG_ERROR,
+                          "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
+                          __func__, (int) res, (int) len);
+               ctx->failed = 1;
+               return;
+       }
+}
+
+
+static void crypto_hash_deinit(struct crypto_hash *ctx)
+{
+       close(ctx->s);
+       close(ctx->t);
+       os_free(ctx);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+       ssize_t res;
+
+       if (!ctx)
+               return -2;
+
+       if (!mac || !len) {
+               crypto_hash_deinit(ctx);
+               return 0;
+       }
+
+       if (ctx->failed) {
+               crypto_hash_deinit(ctx);
+               return -2;
+       }
+
+       if (*len < ctx->mac_len) {
+               crypto_hash_deinit(ctx);
+               *len = ctx->mac_len;
+               return -1;
+       }
+       *len = ctx->mac_len;
+
+       res = recv(ctx->t, mac, ctx->mac_len, 0);
+       if (res < 0) {
+               wpa_printf(MSG_ERROR,
+                          "%s: recv on AF_ALG socket failed: %s",
+                          __func__, strerror(errno));
+               crypto_hash_deinit(ctx);
+               return -2;
+       }
+       if ((size_t) res < ctx->mac_len) {
+               wpa_printf(MSG_ERROR,
+                          "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
+                          __func__, (int) res, (int) ctx->mac_len);
+               crypto_hash_deinit(ctx);
+               return -2;
+       }
+
+       crypto_hash_deinit(ctx);
+       return 0;
+}
+
+
+struct linux_af_alg_skcipher {
+       int s;
+       int t;
+};
+
+
+static void linux_af_alg_skcipher_deinit(struct linux_af_alg_skcipher *skcipher)
+{
+       if (!skcipher)
+               return;
+       if (skcipher->s >= 0)
+               close(skcipher->s);
+       if (skcipher->t >= 0)
+               close(skcipher->t);
+       os_free(skcipher);
+}
+
+
+static struct linux_af_alg_skcipher *
+linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len)
+{
+       struct linux_af_alg_skcipher *skcipher;
+
+       skcipher = os_zalloc(sizeof(*skcipher));
+       if (!skcipher)
+               goto fail;
+       skcipher->t = -1;
+
+       skcipher->s = linux_af_alg_socket("skcipher", alg);
+       if (skcipher->s < 0)
+               goto fail;
+
+       if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+               wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+                          __func__, strerror(errno));
+               goto fail;
+       }
+
+       skcipher->t = accept(skcipher->s, NULL, NULL);
+       if (skcipher->t < 0) {
+               wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+                          __func__, strerror(errno));
+               goto fail;
+       }
+
+       return skcipher;
+fail:
+       linux_af_alg_skcipher_deinit(skcipher);
+       return NULL;
+}
+
+
+static int linux_af_alg_skcipher_oper(struct linux_af_alg_skcipher *skcipher,
+                                     int enc, const u8 *in, u8 *out)
+{
+       char buf[CMSG_SPACE(sizeof(u32))];
+       struct iovec io[1];
+       struct msghdr msg;
+       struct cmsghdr *hdr;
+       ssize_t ret;
+       u32 *op;
+
+       io[0].iov_base = (void *) in;
+       io[0].iov_len = AES_BLOCK_SIZE;
+       os_memset(&msg, 0, sizeof(msg));
+       os_memset(buf, 0, sizeof(buf));
+       msg.msg_control = buf;
+       msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+       msg.msg_iov = io;
+       msg.msg_iovlen = 1;
+       hdr = CMSG_FIRSTHDR(&msg);
+       hdr->cmsg_level = SOL_ALG;
+       hdr->cmsg_type = ALG_SET_OP;
+       hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+       op = (u32 *) CMSG_DATA(hdr);
+       *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+
+       ret = sendmsg(skcipher->t, &msg, 0);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+                          __func__, strerror(errno));
+               return -1;
+       }
+
+       ret = read(skcipher->t, out, AES_BLOCK_SIZE);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: read failed: %s",
+                          __func__, strerror(errno));
+               return -1;
+       }
+       if (ret < AES_BLOCK_SIZE) {
+               wpa_printf(MSG_ERROR,
+                          "%s: read did not return full data (%d/%d)",
+                          __func__, (int) ret, AES_BLOCK_SIZE);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+       return linux_af_alg_skcipher("ecb(aes)", key, len);
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+       struct linux_af_alg_skcipher *skcipher = ctx;
+
+       return linux_af_alg_skcipher_oper(skcipher, 1, plain, crypt);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+       linux_af_alg_skcipher_deinit(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+       return linux_af_alg_skcipher("ecb(aes)", key, len);
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+       struct linux_af_alg_skcipher *skcipher = ctx;
+
+       return linux_af_alg_skcipher_oper(skcipher, 0, crypt, plain);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+       linux_af_alg_skcipher_deinit(ctx);
+}
+
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+            u8 *data, size_t data_len)
+{
+       struct linux_af_alg_skcipher *skcipher;
+       u8 *skip_buf;
+       char buf[CMSG_SPACE(sizeof(u32))];
+       struct iovec io[2];
+       struct msghdr msg;
+       struct cmsghdr *hdr;
+       ssize_t ret;
+       u32 *op;
+
+       skip_buf = os_zalloc(skip + 1);
+       if (!skip_buf)
+               return -1;
+       skcipher = linux_af_alg_skcipher("ecb(arc4)", key, keylen);
+       if (!skcipher) {
+               os_free(skip_buf);
+               return -1;
+       }
+
+       io[0].iov_base = skip_buf;
+       io[0].iov_len = skip;
+       io[1].iov_base = data;
+       io[1].iov_len = data_len;
+       os_memset(&msg, 0, sizeof(msg));
+       os_memset(buf, 0, sizeof(buf));
+       msg.msg_control = buf;
+       msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+       msg.msg_iov = io;
+       msg.msg_iovlen = 2;
+       hdr = CMSG_FIRSTHDR(&msg);
+       hdr->cmsg_level = SOL_ALG;
+       hdr->cmsg_type = ALG_SET_OP;
+       hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+       op = (u32 *) CMSG_DATA(hdr);
+       *op = ALG_OP_ENCRYPT;
+
+       ret = sendmsg(skcipher->t, &msg, 0);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+                          __func__, strerror(errno));
+               os_free(skip_buf);
+               linux_af_alg_skcipher_deinit(skcipher);
+               return -1;
+       }
+       os_free(skip_buf);
+
+       msg.msg_control = NULL;
+       msg.msg_controllen = 0;
+       ret = recvmsg(skcipher->t, &msg, 0);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
+                          __func__, strerror(errno));
+               linux_af_alg_skcipher_deinit(skcipher);
+               return -1;
+       }
+       linux_af_alg_skcipher_deinit(skcipher);
+
+       if ((size_t) ret < skip + data_len) {
+               wpa_printf(MSG_ERROR,
+                          "%s: recvmsg did not return full data (%d/%d)",
+                          __func__, (int) ret, (int) (skip + data_len));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+       u8 pkey[8], next, tmp;
+       int i;
+       struct linux_af_alg_skcipher *skcipher;
+       char buf[CMSG_SPACE(sizeof(u32))];
+       struct iovec io[1];
+       struct msghdr msg;
+       struct cmsghdr *hdr;
+       ssize_t ret;
+       u32 *op;
+       int res = -1;
+
+       /* Add parity bits to the key */
+       next = 0;
+       for (i = 0; i < 7; i++) {
+               tmp = key[i];
+               pkey[i] = (tmp >> i) | next | 1;
+               next = tmp << (7 - i);
+       }
+       pkey[i] = next | 1;
+
+       skcipher = linux_af_alg_skcipher("ecb(des)", pkey, sizeof(pkey));
+       if (!skcipher)
+               goto fail;
+
+       io[0].iov_base = (void *) clear;
+       io[0].iov_len = 8;
+       os_memset(&msg, 0, sizeof(msg));
+       os_memset(buf, 0, sizeof(buf));
+       msg.msg_control = buf;
+       msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+       msg.msg_iov = io;
+       msg.msg_iovlen = 1;
+       hdr = CMSG_FIRSTHDR(&msg);
+       hdr->cmsg_level = SOL_ALG;
+       hdr->cmsg_type = ALG_SET_OP;
+       hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+       op = (u32 *) CMSG_DATA(hdr);
+       *op = ALG_OP_ENCRYPT;
+
+       ret = sendmsg(skcipher->t, &msg, 0);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+                          __func__, strerror(errno));
+               goto fail;
+       }
+
+       ret = read(skcipher->t, cypher, 8);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: read failed: %s",
+                          __func__, strerror(errno));
+               goto fail;
+       }
+       if (ret < 8) {
+               wpa_printf(MSG_ERROR,
+                          "%s: read did not return full data (%d/8)",
+                          __func__, (int) ret);
+               goto fail;
+       }
+
+       res = 0;
+fail:
+       linux_af_alg_skcipher_deinit(skcipher);
+       return res;
+}
+
+
+static int aes_128_cbc_oper(const u8 *key, int enc, const u8 *iv,
+                           u8 *data, size_t data_len)
+{
+       struct linux_af_alg_skcipher *skcipher;
+       char buf[100];
+       struct iovec io[1];
+       struct msghdr msg;
+       struct cmsghdr *hdr;
+       ssize_t ret;
+       u32 *op;
+       struct af_alg_iv *alg_iv;
+       size_t iv_len = AES_BLOCK_SIZE;
+
+       skcipher = linux_af_alg_skcipher("cbc(aes)", key, 16);
+       if (!skcipher)
+               return -1;
+
+       io[0].iov_base = (void *) data;
+       io[0].iov_len = data_len;
+       os_memset(&msg, 0, sizeof(msg));
+       os_memset(buf, 0, sizeof(buf));
+       msg.msg_control = buf;
+       msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
+               CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+       msg.msg_iov = io;
+       msg.msg_iovlen = 1;
+
+       hdr = CMSG_FIRSTHDR(&msg);
+       hdr->cmsg_level = SOL_ALG;
+       hdr->cmsg_type = ALG_SET_OP;
+       hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+       op = (u32 *) CMSG_DATA(hdr);
+       *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+
+       hdr = CMSG_NXTHDR(&msg, hdr);
+       hdr->cmsg_level = SOL_ALG;
+       hdr->cmsg_type = ALG_SET_IV;
+       hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+       alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+       alg_iv->ivlen = iv_len;
+       os_memcpy(alg_iv->iv, iv, iv_len);
+
+       ret = sendmsg(skcipher->t, &msg, 0);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+                          __func__, strerror(errno));
+               linux_af_alg_skcipher_deinit(skcipher);
+               return -1;
+       }
+
+       ret = recvmsg(skcipher->t, &msg, 0);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
+                          __func__, strerror(errno));
+               linux_af_alg_skcipher_deinit(skcipher);
+               return -1;
+       }
+       if ((size_t) ret < data_len) {
+               wpa_printf(MSG_ERROR,
+                          "%s: recvmsg not return full data (%d/%d)",
+                          __func__, (int) ret, (int) data_len);
+               linux_af_alg_skcipher_deinit(skcipher);
+               return -1;
+       }
+
+       linux_af_alg_skcipher_deinit(skcipher);
+       return 0;
+}
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+       return aes_128_cbc_oper(key, 1, iv, data, data_len);
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+       return aes_128_cbc_oper(key, 0, iv, data, data_len);
+}
+
+
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+                    const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return linux_af_alg_hash_vector("cmac(aes)", key, key_len, num_elem,
+                                       addr, len, mac, AES_BLOCK_SIZE);
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+                        const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+       return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+       return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+              u8 *plain)
+{
+       struct linux_af_alg_skcipher *skcipher;
+       char buf[100];
+       struct iovec io[1];
+       struct msghdr msg;
+       struct cmsghdr *hdr;
+       ssize_t ret;
+       u32 *op;
+       struct af_alg_iv *alg_iv;
+       size_t iv_len = 8;
+
+       skcipher = linux_af_alg_skcipher("kw(aes)", kek, kek_len);
+       if (!skcipher)
+               return -1;
+
+       io[0].iov_base = (void *) (cipher + iv_len);
+       io[0].iov_len = n * 8;
+       os_memset(&msg, 0, sizeof(msg));
+       os_memset(buf, 0, sizeof(buf));
+       msg.msg_control = buf;
+       msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
+               CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+       msg.msg_iov = io;
+       msg.msg_iovlen = 1;
+
+       hdr = CMSG_FIRSTHDR(&msg);
+       hdr->cmsg_level = SOL_ALG;
+       hdr->cmsg_type = ALG_SET_OP;
+       hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+       op = (u32 *) CMSG_DATA(hdr);
+       *op = ALG_OP_DECRYPT;
+
+       hdr = CMSG_NXTHDR(&msg, hdr);
+       hdr->cmsg_level = SOL_ALG;
+       hdr->cmsg_type = ALG_SET_IV;
+       hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+       alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+       alg_iv->ivlen = iv_len;
+       os_memcpy(alg_iv->iv, cipher, iv_len);
+
+       ret = sendmsg(skcipher->t, &msg, 0);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+                          __func__, strerror(errno));
+               return -1;
+       }
+
+       ret = read(skcipher->t, plain, n * 8);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: read failed: %s",
+                          __func__, strerror(errno));
+               linux_af_alg_skcipher_deinit(skcipher);
+               return -1;
+       }
+       if (ret < n * 8) {
+               wpa_printf(MSG_ERROR,
+                          "%s: read not return full data (%d/%d)",
+                          __func__, (int) ret, n * 8);
+               linux_af_alg_skcipher_deinit(skcipher);
+               return -1;
+       }
+
+       linux_af_alg_skcipher_deinit(skcipher);
+       return 0;
+}
+
+
+struct crypto_cipher {
+       struct linux_af_alg_skcipher *skcipher;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+                                         const u8 *iv, const u8 *key,
+                                         size_t key_len)
+{
+       struct crypto_cipher *ctx;
+       const char *name;
+       struct af_alg_iv *alg_iv;
+       size_t iv_len = 0;
+       char buf[100];
+       struct msghdr msg;
+       struct cmsghdr *hdr;
+       ssize_t ret;
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (!ctx)
+               return NULL;
+
+       switch (alg) {
+       case CRYPTO_CIPHER_ALG_RC4:
+               name = "ecb(arc4)";
+               break;
+       case CRYPTO_CIPHER_ALG_AES:
+               name = "cbc(aes)";
+               iv_len = AES_BLOCK_SIZE;
+               break;
+       case CRYPTO_CIPHER_ALG_3DES:
+               name = "cbc(des3_ede)";
+               iv_len = 8;
+               break;
+       case CRYPTO_CIPHER_ALG_DES:
+               name = "cbc(des)";
+               iv_len = 8;
+               break;
+       default:
+               os_free(ctx);
+               return NULL;
+       }
+
+       ctx->skcipher = linux_af_alg_skcipher(name, key, key_len);
+       if (!ctx->skcipher) {
+               os_free(ctx);
+               return NULL;
+       }
+
+       if (iv && iv_len) {
+               os_memset(&msg, 0, sizeof(msg));
+               os_memset(buf, 0, sizeof(buf));
+               msg.msg_control = buf;
+               msg.msg_controllen = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+               hdr = CMSG_FIRSTHDR(&msg);
+               hdr->cmsg_level = SOL_ALG;
+               hdr->cmsg_type = ALG_SET_IV;
+               hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+               alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+               alg_iv->ivlen = iv_len;
+               os_memcpy(alg_iv->iv, iv, iv_len);
+
+               ret = sendmsg(ctx->skcipher->t, &msg, 0);
+               if (ret < 0) {
+                       wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+                                  __func__, strerror(errno));
+                       linux_af_alg_skcipher_deinit(ctx->skcipher);
+                       os_free(ctx);
+                       return NULL;
+               }
+       }
+
+       return ctx;
+}
+
+
+static int crypto_cipher_oper(struct crypto_cipher *ctx, u32 type, const u8 *in,
+                             u8 *out, size_t len)
+{
+       char buf[CMSG_SPACE(sizeof(u32))];
+       struct iovec io[1];
+       struct msghdr msg;
+       struct cmsghdr *hdr;
+       ssize_t ret;
+       u32 *op;
+
+       io[0].iov_base = (void *) in;
+       io[0].iov_len = len;
+       os_memset(&msg, 0, sizeof(msg));
+       os_memset(buf, 0, sizeof(buf));
+       msg.msg_control = buf;
+       msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+       msg.msg_iov = io;
+       msg.msg_iovlen = 1;
+       hdr = CMSG_FIRSTHDR(&msg);
+       hdr->cmsg_level = SOL_ALG;
+       hdr->cmsg_type = ALG_SET_OP;
+       hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+       op = (u32 *) CMSG_DATA(hdr);
+       *op = type;
+
+       ret = sendmsg(ctx->skcipher->t, &msg, 0);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+                          __func__, strerror(errno));
+               return -1;
+       }
+
+       ret = read(ctx->skcipher->t, out, len);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: read failed: %s",
+                          __func__, strerror(errno));
+               return -1;
+       }
+       if (ret < (ssize_t) len) {
+               wpa_printf(MSG_ERROR,
+                          "%s: read did not return full data (%d/%d)",
+                          __func__, (int) ret, (int) len);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+                         u8 *crypt, size_t len)
+{
+       return crypto_cipher_oper(ctx, ALG_OP_ENCRYPT, plain, crypt, len);
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+                         u8 *plain, size_t len)
+{
+       return crypto_cipher_oper(ctx, ALG_OP_DECRYPT, crypt, plain, len);
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+       if (ctx) {
+               linux_af_alg_skcipher_deinit(ctx->skcipher);
+               os_free(ctx);
+       }
+}
+
+
+int crypto_global_init(void)
+{
+       return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
index ce0a0c951399397beb5c65a172fc6ea203faa6b0..5b207f489edd8e706e028725cb0541908434081e 100644 (file)
@@ -1143,6 +1143,48 @@ CONFIG_INTERNAL_RC4=y
 endif
 endif
 
+ifeq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/crypto_linux.o
+OBJS_p += ../src/crypto/crypto_linux.o
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/crypto_internal-rsa.o
+OBJS += ../src/crypto/tls_internal.o
+OBJS += ../src/tls/tlsv1_common.o
+OBJS += ../src/tls/tlsv1_record.o
+OBJS += ../src/tls/tlsv1_cred.o
+OBJS += ../src/tls/tlsv1_client.o
+OBJS += ../src/tls/tlsv1_client_write.o
+OBJS += ../src/tls/tlsv1_client_read.o
+OBJS += ../src/tls/tlsv1_client_ocsp.o
+OBJS += ../src/tls/asn1.o
+OBJS += ../src/tls/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DLTM_FAST
+endif
+CONFIG_INTERNAL_DH_GROUP5=y
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_internal.o
+OBJS += ../src/crypto/sha1-internal.o
+endif
+endif
+
 ifeq ($(CONFIG_TLS), none)
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_none.o
@@ -1197,8 +1239,10 @@ NEED_INTERNAL_AES_WRAP=y
 endif
 
 ifdef NEED_INTERNAL_AES_WRAP
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-unwrap.o
 endif
+endif
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
@@ -1220,9 +1264,11 @@ NEED_AES_ENC=y
 ifdef CONFIG_OPENSSL_CMAC
 CFLAGS += -DCONFIG_OPENSSL_CMAC
 else
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
 endif
+endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
 ifdef NEED_INTERNAL_AES_WRAP
@@ -1232,9 +1278,11 @@ endif
 ifdef NEED_AES_CBC
 NEED_AES_ENC=y
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 AESOBJS += ../src/crypto/aes-cbc.o
 endif
 endif
+endif
 ifdef NEED_AES_ENC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += ../src/crypto/aes-internal-enc.o
@@ -1246,8 +1294,10 @@ endif
 
 ifdef NEED_SHA1
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 SHA1OBJS += ../src/crypto/sha1.o
 endif
+endif
 SHA1OBJS += ../src/crypto/sha1-prf.o
 ifdef CONFIG_INTERNAL_SHA1
 SHA1OBJS += ../src/crypto/sha1-internal.o
@@ -1272,9 +1322,11 @@ endif
 
 ifndef CONFIG_FIPS
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 MD5OBJS += ../src/crypto/md5.o
 endif
 endif
+endif
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += ../src/crypto/md5-internal.o
@@ -1313,8 +1365,10 @@ SHA256OBJS = # none by default
 ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 SHA256OBJS += ../src/crypto/sha256.o
 endif
+endif
 SHA256OBJS += ../src/crypto/sha256-prf.o
 ifdef CONFIG_INTERNAL_SHA256
 SHA256OBJS += ../src/crypto/sha256-internal.o
@@ -1338,8 +1392,10 @@ OBJS += $(SHA256OBJS)
 endif
 ifdef NEED_SHA384
 ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
 OBJS += ../src/crypto/sha384.o
 endif
+endif
 CFLAGS += -DCONFIG_SHA384
 OBJS += ../src/crypto/sha384-prf.o
 endif
index 1e37e27f4b2c2cd8904a6b2e7d4f726ba958d41d..8b1ff753433b74e1cd41a7d1abe398a9351173da 100644 (file)
@@ -299,6 +299,7 @@ CONFIG_PEERKEY=y
 # openssl = OpenSSL (default)
 # gnutls = GnuTLS
 # internal = Internal TLSv1 implementation (experimental)
+# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
 # none = Empty template
 #CONFIG_TLS=openssl