#ifdef __linux
#include "linux-crypto.h"
struct linux_crypto {
int sockfd;
int cryptofd;
struct kcapi_handle *handle;
};
int32_t _linux_crypto_send_crypt(struct linux_crypto *ctx,
struct iovec *iov, uint32_t iovlen,
uint32_t enc, const uint8_t *iv)
{
int32_t ret;
char buffer_static[80] = { 0 };
char *buffer_p = buffer_static;
char *buffer_alloc = NULL;
struct cmsghdr *header = NULL;
uint32_t *type = NULL;
struct msghdr msg;
struct af_alg_iv *alg_iv = NULL;
uint32_t iv_msg_size = CMSG_SPACE(sizeof(*alg_iv) + 16);
uint32_t bufferlen = CMSG_SPACE(sizeof(*type)) +
iv_msg_size;
memset(&msg, 0, sizeof(msg));
if (RIST_UNLIKELY(bufferlen > sizeof(buffer_static))) {
buffer_alloc = calloc(1, bufferlen);
if (!buffer_alloc)
return -ENOMEM;
buffer_p = buffer_alloc;
}
msg.msg_control = buffer_p;
msg.msg_controllen = bufferlen;
msg.msg_iov = iov;
msg.msg_iovlen = iovlen;
header = CMSG_FIRSTHDR(&msg);
if (RIST_UNLIKELY(!header)) {
ret = -EFAULT;
goto out;
}
header->cmsg_level = SOL_ALG;
header->cmsg_type = ALG_SET_OP;
header->cmsg_len = CMSG_LEN(sizeof(*type));
type = (void*)CMSG_DATA(header);
*type = enc;
header = CMSG_NXTHDR(&msg, header);
if (RIST_UNLIKELY(!header)) {
ret = -EFAULT;
goto out;
}
header->cmsg_level = SOL_ALG;
header->cmsg_type = ALG_SET_IV;
header->cmsg_len = iv_msg_size;
alg_iv = (void*)CMSG_DATA(header);
alg_iv->ivlen = 16;
memcpy(alg_iv->iv, iv, 16);
ret = (int32_t)sendmsg(ctx->cryptofd, &msg, 0);
if (RIST_UNLIKELY(ret < 0))
ret = -errno;
out:
if (buffer_alloc)
free(buffer_alloc);
return ret;
}
int32_t _linux_crypto_read(struct linux_crypto *ctx,
uint8_t *out, uint32_t outlen)
{
int ret;
int32_t totallen = 0;
if (RIST_LIKELY(outlen)) {
do {
ret = (int32_t)read(ctx->cryptofd, out, outlen);
if (ret > 0) {
out += ret;
outlen -= ret;
totallen += ret;
}
} while ((ret > 0 || errno == EINTR) && outlen);
if (RIST_UNLIKELY(ret < 0))
return -errno;
}
return totallen;
}
int32_t _linux_crypto_process(struct linux_crypto *ctx,
const uint8_t *in, uint32_t inlen,
uint8_t *out, uint32_t outlen,
int access,const uint8_t *iv, int enc)
{
RIST_MARK_UNUSED(access);
int32_t totallen = 0;
int32_t ret;
struct iovec iov;
if (RIST_UNLIKELY(!ctx->cryptofd)) {
return -1;
}
while (inlen && outlen) {
uint32_t inprocess = inlen;
uint32_t outprocess = outlen;
iov.iov_base = (void*)(uintptr_t)in;
iov.iov_len = inprocess;
ret = _linux_crypto_send_crypt(ctx, &iov, 1, enc, iv);
if (RIST_UNLIKELY(0 > ret)) {
return ret;
}
ret = _linux_crypto_read(ctx, out, outprocess);
if (RIST_UNLIKELY(ret < 0))
return ret;
totallen += inprocess;
in += inprocess;
inlen -= inprocess;
out += ret;
outlen -= ret;
}
return totallen;
}
int linux_crypto_init(struct linux_crypto **_ctx) {
struct linux_crypto *ctx = calloc(1, sizeof(*ctx));
int ret;
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "skcipher",
.salg_name = "ctr(aes)"
};
ctx->sockfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (ctx->sockfd == -1) {
fprintf(stderr, "Failed to set up socket!\n");
free(ctx);
return -1;
}
ret = bind(ctx->sockfd, (struct sockaddr *)&sa, sizeof(sa));
if (ret == -1) {
fprintf(stderr, "Failed to bind to socket!\n");
free(ctx);
return ret;
}
*_ctx = ctx;
return 0;
}
void linux_crypto_free(struct linux_crypto **ctx) {
if (!ctx || !*ctx) {
return;
}
if ((*ctx)->cryptofd)
close((*ctx)->cryptofd);
if ((*ctx)->sockfd)
close((*ctx)->sockfd);
free(*ctx);
*ctx = NULL;
}
int linux_crypto_set_key(const uint8_t *key, int keylen, struct linux_crypto *ctx) {
int ret;
if (ctx->cryptofd) {
close(ctx->cryptofd);
ctx->cryptofd = 0;
}
ret = setsockopt(ctx->sockfd, SOL_ALG, ALG_SET_KEY, key, keylen);
if (ret < 0) {
fprintf(stderr, "Errno is %d\n", -errno);
fprintf(stderr, "Failed to set key!\n");
return -1;
}
ret = accept(ctx->sockfd, NULL, 0);
if (ret == -1) {
fprintf(stderr, "Failed to accept socket!\n");
return ret;
}
ctx->cryptofd = ret;
return 0;
}
int linux_crypto_decrypt(uint8_t inbuf[], uint8_t outbuf[], int buflen, uint8_t iv[], struct linux_crypto *ctx) {
return _linux_crypto_process(ctx, inbuf, buflen, outbuf, buflen, 0, iv, 0);
}
int linux_crypto_encrypt(uint8_t inbuf[], uint8_t outbuf[], int buflen, uint8_t iv[], struct linux_crypto *ctx) {
return _linux_crypto_process(ctx, inbuf, buflen, outbuf, buflen, 1, iv, 0);
}
#endif