blob: 564201a95fc5df0e995bda25751b67e250631b11 [file] [edit]
// SPDX-License-Identifier: GPL-2.0-or-later
/* AF_ALG hash test
*
* Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <getopt.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <linux/if_alg.h>
#define OSERROR(X, Y) do { if ((long)(X) == -1) { perror(Y); exit(1); } } while(0)
#define min(x, y) ((x) < (y) ? (x) : (y))
static unsigned char buffer[4096 * 32] __attribute__((aligned(4096)));
static unsigned char iv[16];
static unsigned char key[16];
static const struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "skcipher",
.salg_name = "cbc(aes)",
};
static __attribute__((noreturn))
void format(void)
{
fprintf(stderr, "alg-send [-s] [-n<size>] <file>|-\n");
exit(2);
}
static void algif_add_set_op(struct msghdr *msg, unsigned int op)
{
struct cmsghdr *__cmsg;
__cmsg = msg->msg_control + msg->msg_controllen;
__cmsg->cmsg_len = CMSG_LEN(sizeof(unsigned int));
__cmsg->cmsg_level = SOL_ALG;
__cmsg->cmsg_type = ALG_SET_OP;
*(unsigned int *)CMSG_DATA(__cmsg) = op;
msg->msg_controllen += CMSG_ALIGN(__cmsg->cmsg_len);
}
static void algif_add_set_iv(struct msghdr *msg, const void *iv, size_t ivlen)
{
struct af_alg_iv *ivbuf;
struct cmsghdr *__cmsg;
printf("%zx\n", msg->msg_controllen);
__cmsg = msg->msg_control + msg->msg_controllen;
__cmsg->cmsg_len = CMSG_LEN(sizeof(*ivbuf) + ivlen);
__cmsg->cmsg_level = SOL_ALG;
__cmsg->cmsg_type = ALG_SET_IV;
ivbuf = (struct af_alg_iv *)CMSG_DATA(__cmsg);
ivbuf->ivlen = ivlen;
memcpy(ivbuf->iv, iv, ivlen);
msg->msg_controllen += CMSG_ALIGN(__cmsg->cmsg_len);
}
int main(int argc, char *argv[])
{
struct msghdr msg;
struct stat st;
const char *filename;
unsigned char ctrl[4096];
ssize_t r, w, o, ret;
size_t size = LONG_MAX, total = 0, i, out = 160;
char *end;
bool use_sendfile = false, all = true;
int opt, alg, sock, fd = 0;
while ((opt = getopt(argc, argv, "n:s")) != EOF) {
switch (opt) {
case 'n':
size = strtoul(optarg, &end, 0);
switch (*end) {
case 'K':
case 'k':
size *= 1024;
break;
case 'M':
case 'm':
size *= 1024 * 1024;
break;
}
all = false;
break;
case 's':
use_sendfile = true;
break;
default:
format();
}
}
argc -= optind;
argv += optind;
if (argc != 1)
format();
filename = argv[0];
alg = socket(AF_ALG, SOCK_SEQPACKET, 0);
OSERROR(alg, "AF_ALG");
OSERROR(bind(alg, (struct sockaddr *)&sa, sizeof(sa)), "bind");
OSERROR(setsockopt(alg, SOL_ALG, ALG_SET_KEY, key, sizeof(key)), "ALG_SET_KEY");
sock = accept(alg, NULL, 0);
OSERROR(sock, "accept");
if (strcmp(filename, "-") != 0) {
fd = open(filename, O_RDONLY);
OSERROR(fd, filename);
OSERROR(fstat(fd, &st), filename);
size = st.st_size;
} else {
OSERROR(fstat(fd, &st), argv[2]);
}
memset(&msg, 0, sizeof(msg));
msg.msg_control = ctrl;
algif_add_set_op(&msg, ALG_OP_ENCRYPT);
algif_add_set_iv(&msg, iv, sizeof(iv));
OSERROR(sendmsg(sock, &msg, MSG_MORE), "sock/sendmsg");
if (!use_sendfile) {
bool more = false;
while (size) {
r = read(fd, buffer, sizeof(buffer));
OSERROR(r, filename);
if (r == 0)
break;
size -= r;
o = 0;
do {
more = size > 0;
w = send(sock, buffer + o, r - o,
more ? MSG_MORE : 0);
OSERROR(w, "sock/send");
total += w;
o += w;
} while (o < r);
}
if (more)
send(sock, NULL, 0, 0);
} else if (S_ISFIFO(st.st_mode)) {
do {
r = splice(fd, NULL, sock, NULL, size,
size > 0 ? SPLICE_F_MORE : 0);
OSERROR(r, "sock/splice");
size -= r;
total += r;
} while (r > 0 && size > 0);
if (size && !all) {
fprintf(stderr, "Short splice\n");
exit(1);
}
} else {
r = sendfile(sock, fd, NULL, size);
OSERROR(r, "sock/sendfile");
if (r != size) {
fprintf(stderr, "Short sendfile\n");
exit(1);
}
total = r;
}
while (total > 0) {
ret = read(sock, buffer, min(sizeof(buffer), total));
OSERROR(ret, "sock/read");
if (ret == 0)
break;
total -= ret;
if (out > 0) {
ret = min(out, ret);
out -= ret;
for (i = 0; i < ret; i++)
printf("%02x", (unsigned char)buffer[i]);
}
printf("...\n");
}
OSERROR(close(sock), "sock/close");
OSERROR(close(alg), "alg/close");
OSERROR(close(fd), "close");
return 0;
}