Home | History | Annotate | Line # | Download | only in opencrypto
      1 /*	$NetBSD: h_ioctl.c,v 1.6 2023/08/05 13:29:57 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2017 Internet Initiative Japan Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <err.h>
     30 #include <errno.h>
     31 #include <fcntl.h>
     32 #include <poll.h>
     33 #include <stdio.h>
     34 #include <string.h>
     35 #include <unistd.h>
     36 
     37 #include <sys/errno.h>
     38 #include <sys/ioctl.h>
     39 #include <sys/sysctl.h>
     40 
     41 #include <crypto/cryptodev.h>
     42 
     43 /* copy from h_aescbc.c */
     44 #define AES_KEY_LEN 16
     45 unsigned char aes_key[AES_KEY_LEN] =
     46 { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
     47   0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06, };
     48 
     49 #define AES_IV_LEN 16
     50 unsigned char aes_iv[AES_IV_LEN] =
     51 { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
     52   0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41, };
     53 
     54 #define AES_PLAINTX_LEN 64
     55 unsigned char aes_plaintx[AES_PLAINTX_LEN] = "Single block msg";
     56 
     57 #define AES_CIPHER_LEN 64
     58 unsigned char aes_cipher[AES_CIPHER_LEN] =
     59 { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
     60   0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a, };
     61 
     62 #define COUNT 2
     63 
     64 static int
     65 wait_for_read(int fd)
     66 {
     67 	struct pollfd pfd = { .fd = fd, .events = POLLIN };
     68 	int nfd;
     69 
     70 	nfd = poll(&pfd, 1, 5000);
     71 	if (nfd == -1) {
     72 		warn("failed: poll");
     73 		return -1;
     74 	}
     75 	if (nfd == 0) {
     76 		warnx("failed: timeout");
     77 		errno = ETIMEDOUT;
     78 		return -1;
     79 	}
     80 	if (nfd != 1 || (pfd.revents & POLLIN) == 0) {
     81 		warnx("failed: invalid poll: %d", nfd);
     82 		errno = EIO;
     83 		return -1;
     84 	}
     85 	return 0;
     86 }
     87 
     88 /*
     89  * CRIOGET is deprecated.
     90  */
     91 
     92 /*
     93  * CIOCNGSESSION
     94  * Hmm, who uses? (1)
     95  */
     96 static int
     97 test_ngsession(int fd)
     98 {
     99 	int ret;
    100 	struct crypt_sgop sg;
    101 	struct session_n_op css[COUNT];
    102 
    103 	for (size_t i = 0; i < COUNT; i++) {
    104 		struct session_n_op *cs = &css[i];
    105 
    106 		memset(cs, 0, sizeof(*cs));
    107 		cs->cipher = CRYPTO_AES_CBC;
    108 		cs->keylen = AES_KEY_LEN;
    109 		cs->key = __UNCONST(&aes_key);
    110 	}
    111 	memset(&sg, 0, sizeof(sg));
    112 	sg.count = COUNT;
    113 	sg.sessions = css;
    114 
    115 	ret = ioctl(fd, CIOCNGSESSION, &sg);
    116 	if (ret < 0)
    117 		warn("failed: CIOCNGSESSION");
    118 
    119 	return ret;
    120 }
    121 
    122 /*
    123  * CIOCNFSESSION
    124  * Hmm, who uses? (2)
    125  */
    126 static int
    127 test_nfsession(int fd)
    128 {
    129 	int ret;
    130 	struct crypt_sfop sf;
    131 	u_int32_t sids[COUNT];
    132 
    133 	memset(sids, 0, sizeof(sids));
    134 	memset(&sf, 0, sizeof(sf));
    135 	sf.count = COUNT;
    136 	sf.sesid = sids;
    137 
    138 	ret = ioctl(fd, CIOCNFSESSION, &sf);
    139 	if (ret < 0)
    140 		warn("failed: CIOCNFSESSION");
    141 
    142 	return ret;
    143 }
    144 
    145 /*
    146  * CIOCNCRYPTM
    147  * Hmm, who uses? (3)
    148  */
    149 static int
    150 test_ncryptm(int fd)
    151 {
    152 	int ret;
    153 	struct crypt_mop mop;
    154 	struct crypt_n_op css[COUNT];
    155 
    156 	for (size_t i = 0; i < COUNT; i++) {
    157 		struct crypt_n_op *cs;
    158 		cs = &css[i];
    159 
    160 		memset(cs, 0, sizeof(*cs));
    161 		cs->ses = 0; /* session id */
    162 		cs->op = COP_ENCRYPT;
    163 		/* XXX */
    164 	}
    165 
    166 	memset(&mop, 0, sizeof(mop));
    167 	mop.count = COUNT;
    168 	mop.reqs = css;
    169 
    170 	ret = ioctl(fd, CIOCNCRYPTM, &mop);
    171 	if (ret < 0)
    172 		warn("failed: CIOCNCRYPTM");
    173 
    174 	return ret;
    175 }
    176 
    177 /*
    178  * CIOCNCRYPTRETM
    179  * Hmm, who uses? (4)
    180  */
    181 static int
    182 test_ncryptretm(int fd)
    183 {
    184 	int ret;
    185 	struct session_op cs;
    186 
    187 	struct crypt_mop mop;
    188 	struct crypt_n_op cnos[COUNT];
    189 	unsigned char cno_dst[COUNT][AES_CIPHER_LEN];
    190 	struct cryptret cret;
    191 	struct crypt_result crs[COUNT];
    192 
    193 	memset(&cs, 0, sizeof(cs));
    194 	cs.cipher = CRYPTO_AES_CBC;
    195 	cs.keylen = AES_KEY_LEN;
    196 	cs.key = __UNCONST(&aes_key);
    197 	ret = ioctl(fd, CIOCGSESSION, &cs);
    198 	if (ret < 0) {
    199 		warn("failed: CIOCGSESSION");
    200 		return ret;
    201 	}
    202 
    203 	for (size_t i = 0; i < COUNT; i++) {
    204 		struct crypt_n_op *cno = &cnos[i];
    205 
    206 		memset(cno, 0, sizeof(*cno));
    207 		cno->ses = cs.ses;
    208 		cno->op = COP_ENCRYPT;
    209 		cno->len = AES_PLAINTX_LEN;
    210 		cno->src = aes_plaintx;
    211 		cno->dst_len = AES_CIPHER_LEN;
    212 		cno->dst = cno_dst[i];
    213 	}
    214 
    215 	memset(&mop, 0, sizeof(mop));
    216 	mop.count = COUNT;
    217 	mop.reqs = cnos;
    218 	ret = ioctl(fd, CIOCNCRYPTM, &mop);
    219 	if (ret < 0) {
    220 		warn("failed: CIOCNCRYPTM");
    221 		return ret;
    222 	}
    223 
    224 	for (size_t i = 0; i < COUNT; i++) {
    225 		struct crypt_result *cr = &crs[i];
    226 
    227 		memset(cr, 0, sizeof(*cr));
    228 		cr->reqid = cnos[i].reqid;
    229 	}
    230 
    231 	memset(&cret, 0, sizeof(cret));
    232 	cret.count = COUNT;
    233 	cret.results = crs;
    234 	ret = ioctl(fd, CIOCNCRYPTRETM, &cret);
    235 	if (ret < 0) {
    236 		if (errno != EINPROGRESS) {
    237 			warn("failed: CIOCNCRYPTRETM");
    238 			return ret;
    239 		}
    240 
    241 		ret = wait_for_read(fd);
    242 		if (ret < 0)
    243 			return ret;
    244 
    245 		cret.count = COUNT;
    246 		cret.results = crs;
    247 		ret = ioctl(fd, CIOCNCRYPTRETM, &cret);
    248 		if (ret < 0) {
    249 			warn("failed: CIOCNCRYPTRET");
    250 			return ret;
    251 		}
    252 	}
    253 
    254 	return ret;
    255 }
    256 
    257 /*
    258  * CIOCNCRYPTRET
    259  * Hmm, who uses? (5)
    260  */
    261 /* test when it does not request yet. */
    262 static int
    263 test_ncryptret_noent(int fd)
    264 {
    265 	int ret;
    266 	struct crypt_result cr;
    267 
    268 	memset(&cr, 0, sizeof(cr));
    269 
    270 	ret = ioctl(fd, CIOCNCRYPTRET, &cr);
    271 	if (ret == 0) {
    272 		warn("failed: CIOCNCRYPTRET unexpected success when no entry");
    273 		ret = -1;
    274 	} else if (errno == EINPROGRESS) {
    275 		/* expected fail */
    276 		ret = 0;
    277 	}
    278 
    279 	return ret;
    280 }
    281 
    282 static int
    283 test_ncryptret_ent(int fd)
    284 {
    285 	int ret;
    286 	struct session_op cs;
    287 
    288 	struct crypt_mop mop;
    289 	struct crypt_n_op cno;
    290 	unsigned char cno_dst[AES_CIPHER_LEN];
    291 
    292 	struct crypt_result cr;
    293 
    294 	memset(&cs, 0, sizeof(cs));
    295 	cs.cipher = CRYPTO_AES_CBC;
    296 	cs.keylen = AES_KEY_LEN;
    297 	cs.key = __UNCONST(&aes_key);
    298 	ret = ioctl(fd, CIOCGSESSION, &cs);
    299 	if (ret < 0) {
    300 		warn("failed: CIOCGSESSION");
    301 		return ret;
    302 	}
    303 
    304 	memset(&cno, 0, sizeof(cno));
    305 	cno.ses = cs.ses;
    306 	cno.op = COP_ENCRYPT;
    307 	cno.len = AES_PLAINTX_LEN;
    308 	cno.src = aes_plaintx;
    309 	cno.dst_len = AES_CIPHER_LEN;
    310 	cno.dst = cno_dst;
    311 
    312 	memset(&mop, 0, sizeof(mop));
    313 	mop.count = 1;
    314 	mop.reqs = &cno;
    315 	ret = ioctl(fd, CIOCNCRYPTM, &mop);
    316 	if (ret < 0) {
    317 		warn("failed: CIOCNCRYPTM");
    318 		return ret;
    319 	}
    320 
    321 	memset(&cr, 0, sizeof(cr));
    322 	cr.reqid = cno.reqid;
    323 
    324 	ret = ioctl(fd, CIOCNCRYPTRET, &cr);
    325 	if (ret < 0) {
    326 		if (errno != EINPROGRESS) {
    327 			warn("failed: CIOCNCRYPTRET");
    328 			return ret;
    329 		}
    330 
    331 		ret = wait_for_read(fd);
    332 		if (ret < 0)
    333 			return ret;
    334 		ret = ioctl(fd, CIOCNCRYPTRET, &cr);
    335 		if (ret < 0) {
    336 			warn("failed: CIOCNCRYPTRET");
    337 			return ret;
    338 		}
    339 		return 0;
    340 	}
    341 
    342 	return ret;
    343 }
    344 
    345 static int
    346 test_ncryptret(int fd)
    347 {
    348 	int ret;
    349 
    350 	ret = test_ncryptret_noent(fd);
    351 	if (ret < 0)
    352 		return ret;
    353 
    354 	ret = test_ncryptret_ent(fd);
    355 	if (ret < 0)
    356 		return ret;
    357 
    358 	return ret;
    359 }
    360 
    361 /*
    362  * CIOCASYMFEAT
    363  */
    364 static int
    365 set_userasymcrypto(int new, int *old)
    366 {
    367 	int ret;
    368 
    369 	ret = sysctlbyname("kern.userasymcrypto", NULL, NULL, &new, sizeof(new));
    370 	if (ret < 0) {
    371 		warn("failed: kern.userasymcrypto=%d", new);
    372 		return ret;
    373 	}
    374 
    375 	if (old != NULL)
    376 		*old = new;
    377 
    378 	return ret;
    379 }
    380 
    381 static int
    382 test_asymfeat_each(int fd, u_int32_t *asymfeat, int userasym)
    383 {
    384 	int ret;
    385 
    386 	ret = ioctl(fd, CIOCASYMFEAT, asymfeat);
    387 	if (ret < 0)
    388 		warn("failed: CIOCASYMFEAT when userasym=%d", userasym);
    389 
    390 	return ret;
    391 }
    392 
    393 static int
    394 test_asymfeat(int fd)
    395 {
    396 	int ret, new, orig;
    397 	u_int32_t asymfeat = 0;
    398 
    399 	/* test for kern.userasymcrypto=1 */
    400 	new = 1;
    401 	ret = set_userasymcrypto(new, &orig);
    402 	if (ret < 0)
    403 		return ret;
    404 	ret = test_asymfeat_each(fd, &asymfeat, new);
    405 	if (ret < 0)
    406 		return ret;
    407 
    408 	/* test for kern.userasymcrypto=0 */
    409 	new = 0;
    410 	ret = set_userasymcrypto(new, NULL);
    411 	if (ret < 0)
    412 		return ret;
    413 	ret = test_asymfeat_each(fd, &asymfeat, new);
    414 	if (ret < 0)
    415 		return ret;
    416 
    417 	/* cleanup */
    418 	ret = set_userasymcrypto(orig, NULL);
    419 	if (ret < 0)
    420 		warnx("failed: cleanup kern.userasymcrypto");
    421 
    422 	return ret;
    423 }
    424 
    425 int
    426 main(void)
    427 {
    428 	int fd, ret;
    429 
    430 	fd = open("/dev/crypto", O_RDWR, 0);
    431 	if (fd < 0)
    432 		err(1, "open");
    433 
    434 	ret = test_ngsession(fd);
    435 	if (ret < 0)
    436 		err(1, "test_ngsession");
    437 
    438 	ret = test_nfsession(fd);
    439 	if (ret < 0)
    440 		err(1, "test_ngsession");
    441 
    442 	ret = test_ncryptm(fd);
    443 	if (ret < 0)
    444 		err(1, "test_ncryptm");
    445 
    446 	test_ncryptretm(fd);
    447 	if (ret < 0)
    448 		err(1, "test_ncryptretm");
    449 
    450 	ret = test_ncryptret(fd);
    451 	if (ret < 0)
    452 		err(1, "test_ncryptret");
    453 
    454 	if (getuid() == 0) {
    455 		ret = test_asymfeat(fd);
    456 		if (ret < 0)
    457 			err(1, "test_asymfeat");
    458 	}
    459 
    460 	return 0;
    461 }
    462