| /* |
| * 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); |
| |
| 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) |
| { |
| } |
| |
| |
| void crypto_unload(void) |
| { |
| } |