blob: 17244561b372f7612faa7b63c6db29f06766762e [file] [log] [blame]
/*
* 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)
{
}