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
45unsigned 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
50unsigned 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
55unsigned char aes_plaintx[AES_PLAINTX_LEN] = "Single block msg";
56
57#define AES_CIPHER_LEN 64
58unsigned 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
64static int
65wait_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 */
96static int
97test_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 */
126static int
127test_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 */
149static int
150test_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 */
181static int
182test_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. */
262static int
263test_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
282static int
283test_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
345static int
346test_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 */
364static int
365set_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
381static int
382test_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
393static int
394test_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
425int
426main(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