From 206516e8c2bdd12651438a0c5c355e4bce343611 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 27 Feb 2017 01:10:02 +0200 Subject: [PATCH] af_alg: Crypto wrappers for Linux kernel crypto (AF_ALG) 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 --- hostapd/Makefile | 60 +++ hostapd/defconfig | 1 + src/crypto/crypto_linux.c | 1006 +++++++++++++++++++++++++++++++++++++ wpa_supplicant/Makefile | 56 +++ wpa_supplicant/defconfig | 1 + 5 files changed, 1124 insertions(+) create mode 100644 src/crypto/crypto_linux.c diff --git a/hostapd/Makefile b/hostapd/Makefile index ea9234bad..bc56c4cf2 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -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) diff --git a/hostapd/defconfig b/hostapd/defconfig index 9ade580a3..e92c0ed00 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -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 index 000000000..8099193bf --- /dev/null +++ b/src/crypto/crypto_linux.c @@ -0,0 +1,1006 @@ +/* + * Crypto wrapper for Linux kernel AF_ALG + * Copyright (c) 2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#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) +{ +} diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index ce0a0c951..5b207f489 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -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 diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 1e37e27f4..8b1ff7534 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -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