/* * 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); if (TEST_FAIL()) return -1; 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) { }