#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <crypto/cryptodev.h>
#define AES_KEY_LEN 16
unsigned char aes_key[AES_KEY_LEN] =
{ 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06, };
#define AES_IV_LEN 16
unsigned char aes_iv[AES_IV_LEN] =
{ 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41, };
#define AES_PLAINTX_LEN 64
unsigned char aes_plaintx[AES_PLAINTX_LEN] = "Single block msg";
#define AES_CIPHER_LEN 64
unsigned char aes_cipher[AES_CIPHER_LEN] =
{ 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a, };
#define COUNT 2
static int
wait_for_read(int fd)
{
struct pollfd pfd = { .fd = fd, .events = POLLIN };
int nfd;
nfd = poll(&pfd, 1, 5000);
if (nfd == -1) {
warn("failed: poll");
return -1;
}
if (nfd == 0) {
warnx("failed: timeout");
errno = ETIMEDOUT;
return -1;
}
if (nfd != 1 || (pfd.revents & POLLIN) == 0) {
warnx("failed: invalid poll: %d", nfd);
errno = EIO;
return -1;
}
return 0;
}
static int
test_ngsession(int fd)
{
int ret;
struct crypt_sgop sg;
struct session_n_op css[COUNT];
for (size_t i = 0; i < COUNT; i++) {
struct session_n_op *cs = &css[i];
memset(cs, 0, sizeof(*cs));
cs->cipher = CRYPTO_AES_CBC;
cs->keylen = AES_KEY_LEN;
cs->key = __UNCONST(&aes_key);
}
memset(&sg, 0, sizeof(sg));
sg.count = COUNT;
sg.sessions = css;
ret = ioctl(fd, CIOCNGSESSION, &sg);
if (ret < 0)
warn("failed: CIOCNGSESSION");
return ret;
}
static int
test_nfsession(int fd)
{
int ret;
struct crypt_sfop sf;
u_int32_t sids[COUNT];
memset(sids, 0, sizeof(sids));
memset(&sf, 0, sizeof(sf));
sf.count = COUNT;
sf.sesid = sids;
ret = ioctl(fd, CIOCNFSESSION, &sf);
if (ret < 0)
warn("failed: CIOCNFSESSION");
return ret;
}
static int
test_ncryptm(int fd)
{
int ret;
struct crypt_mop mop;
struct crypt_n_op css[COUNT];
for (size_t i = 0; i < COUNT; i++) {
struct crypt_n_op *cs;
cs = &css[i];
memset(cs, 0, sizeof(*cs));
cs->ses = 0;
cs->op = COP_ENCRYPT;
}
memset(&mop, 0, sizeof(mop));
mop.count = COUNT;
mop.reqs = css;
ret = ioctl(fd, CIOCNCRYPTM, &mop);
if (ret < 0)
warn("failed: CIOCNCRYPTM");
return ret;
}
static int
test_ncryptretm(int fd)
{
int ret;
struct session_op cs;
struct crypt_mop mop;
struct crypt_n_op cnos[COUNT];
unsigned char cno_dst[COUNT][AES_CIPHER_LEN];
struct cryptret cret;
struct crypt_result crs[COUNT];
memset(&cs, 0, sizeof(cs));
cs.cipher = CRYPTO_AES_CBC;
cs.keylen = AES_KEY_LEN;
cs.key = __UNCONST(&aes_key);
ret = ioctl(fd, CIOCGSESSION, &cs);
if (ret < 0) {
warn("failed: CIOCGSESSION");
return ret;
}
for (size_t i = 0; i < COUNT; i++) {
struct crypt_n_op *cno = &cnos[i];
memset(cno, 0, sizeof(*cno));
cno->ses = cs.ses;
cno->op = COP_ENCRYPT;
cno->len = AES_PLAINTX_LEN;
cno->src = aes_plaintx;
cno->dst_len = AES_CIPHER_LEN;
cno->dst = cno_dst[i];
}
memset(&mop, 0, sizeof(mop));
mop.count = COUNT;
mop.reqs = cnos;
ret = ioctl(fd, CIOCNCRYPTM, &mop);
if (ret < 0) {
warn("failed: CIOCNCRYPTM");
return ret;
}
for (size_t i = 0; i < COUNT; i++) {
struct crypt_result *cr = &crs[i];
memset(cr, 0, sizeof(*cr));
cr->reqid = cnos[i].reqid;
}
memset(&cret, 0, sizeof(cret));
cret.count = COUNT;
cret.results = crs;
ret = ioctl(fd, CIOCNCRYPTRETM, &cret);
if (ret < 0) {
if (errno != EINPROGRESS) {
warn("failed: CIOCNCRYPTRETM");
return ret;
}
ret = wait_for_read(fd);
if (ret < 0)
return ret;
cret.count = COUNT;
cret.results = crs;
ret = ioctl(fd, CIOCNCRYPTRETM, &cret);
if (ret < 0) {
warn("failed: CIOCNCRYPTRET");
return ret;
}
}
return ret;
}
static int
test_ncryptret_noent(int fd)
{
int ret;
struct crypt_result cr;
memset(&cr, 0, sizeof(cr));
ret = ioctl(fd, CIOCNCRYPTRET, &cr);
if (ret == 0) {
warn("failed: CIOCNCRYPTRET unexpected success when no entry");
ret = -1;
} else if (errno == EINPROGRESS) {
ret = 0;
}
return ret;
}
static int
test_ncryptret_ent(int fd)
{
int ret;
struct session_op cs;
struct crypt_mop mop;
struct crypt_n_op cno;
unsigned char cno_dst[AES_CIPHER_LEN];
struct crypt_result cr;
memset(&cs, 0, sizeof(cs));
cs.cipher = CRYPTO_AES_CBC;
cs.keylen = AES_KEY_LEN;
cs.key = __UNCONST(&aes_key);
ret = ioctl(fd, CIOCGSESSION, &cs);
if (ret < 0) {
warn("failed: CIOCGSESSION");
return ret;
}
memset(&cno, 0, sizeof(cno));
cno.ses = cs.ses;
cno.op = COP_ENCRYPT;
cno.len = AES_PLAINTX_LEN;
cno.src = aes_plaintx;
cno.dst_len = AES_CIPHER_LEN;
cno.dst = cno_dst;
memset(&mop, 0, sizeof(mop));
mop.count = 1;
mop.reqs = &cno;
ret = ioctl(fd, CIOCNCRYPTM, &mop);
if (ret < 0) {
warn("failed: CIOCNCRYPTM");
return ret;
}
memset(&cr, 0, sizeof(cr));
cr.reqid = cno.reqid;
ret = ioctl(fd, CIOCNCRYPTRET, &cr);
if (ret < 0) {
if (errno != EINPROGRESS) {
warn("failed: CIOCNCRYPTRET");
return ret;
}
ret = wait_for_read(fd);
if (ret < 0)
return ret;
ret = ioctl(fd, CIOCNCRYPTRET, &cr);
if (ret < 0) {
warn("failed: CIOCNCRYPTRET");
return ret;
}
return 0;
}
return ret;
}
static int
test_ncryptret(int fd)
{
int ret;
ret = test_ncryptret_noent(fd);
if (ret < 0)
return ret;
ret = test_ncryptret_ent(fd);
if (ret < 0)
return ret;
return ret;
}
static int
set_userasymcrypto(int new, int *old)
{
int ret;
ret = sysctlbyname("kern.userasymcrypto", NULL, NULL, &new, sizeof(new));
if (ret < 0) {
warn("failed: kern.userasymcrypto=%d", new);
return ret;
}
if (old != NULL)
*old = new;
return ret;
}
static int
test_asymfeat_each(int fd, u_int32_t *asymfeat, int userasym)
{
int ret;
ret = ioctl(fd, CIOCASYMFEAT, asymfeat);
if (ret < 0)
warn("failed: CIOCASYMFEAT when userasym=%d", userasym);
return ret;
}
static int
test_asymfeat(int fd)
{
int ret, new, orig;
u_int32_t asymfeat = 0;
new = 1;
ret = set_userasymcrypto(new, &orig);
if (ret < 0)
return ret;
ret = test_asymfeat_each(fd, &asymfeat, new);
if (ret < 0)
return ret;
new = 0;
ret = set_userasymcrypto(new, NULL);
if (ret < 0)
return ret;
ret = test_asymfeat_each(fd, &asymfeat, new);
if (ret < 0)
return ret;
ret = set_userasymcrypto(orig, NULL);
if (ret < 0)
warnx("failed: cleanup kern.userasymcrypto");
return ret;
}
int
main(void)
{
int fd, ret;
fd = open("/dev/crypto", O_RDWR, 0);
if (fd < 0)
err(1, "open");
ret = test_ngsession(fd);
if (ret < 0)
err(1, "test_ngsession");
ret = test_nfsession(fd);
if (ret < 0)
err(1, "test_ngsession");
ret = test_ncryptm(fd);
if (ret < 0)
err(1, "test_ncryptm");
test_ncryptretm(fd);
if (ret < 0)
err(1, "test_ncryptretm");
ret = test_ncryptret(fd);
if (ret < 0)
err(1, "test_ncryptret");
if (getuid() == 0) {
ret = test_asymfeat(fd);
if (ret < 0)
err(1, "test_asymfeat");
}
return 0;
}