xref: /openbsd/regress/sys/crypto/aes/aestest.c (revision 898184e3)
1 /*      $OpenBSD: aestest.c,v 1.1 2008/06/12 19:42:48 djm Exp $  */
2 
3 /*
4  * Copyright (c) 2002 Markus Friedl.  All rights reserved.
5  * Copyright (c) 2008 Damien Miller.  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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /*
29  * Test crypto(4) AES with test vectors provided by Dr Brian Gladman:
30  * http://fp.gladman.plus.com/AES/
31  */
32 
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/ioctl.h>
36 #include <sys/sysctl.h>
37 #include <crypto/cryptodev.h>
38 #include <err.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <ctype.h>
45 
46 static int
47 syscrypt(const unsigned char *key, size_t klen, const unsigned char *in,
48     unsigned char *out, size_t len, int do_encrypt)
49 {
50 	struct session_op session;
51 	struct crypt_op cryp;
52 	int cryptodev_fd = -1, fd = -1;
53 	u_char iv[32];
54 
55 	/*
56 	 * Kludge; the kernel doesn't support ECB encryption so we
57 	 * use a all-zero IV and encrypt a single block only, so the
58 	 * result should be the same.
59 	 */
60 	bzero(iv, sizeof(iv));
61 
62 	if ((cryptodev_fd = open("/dev/crypto", O_RDWR, 0)) < 0) {
63 		warn("/dev/crypto");
64 		goto err;
65 	}
66 	if (ioctl(cryptodev_fd, CRIOGET, &fd) == -1) {
67 		warn("CRIOGET failed");
68 		goto err;
69 	}
70 	memset(&session, 0, sizeof(session));
71 	session.cipher = CRYPTO_AES_CBC;
72 	session.key = (caddr_t) key;
73 	session.keylen = klen;
74 	if (ioctl(fd, CIOCGSESSION, &session) == -1) {
75 		warn("CIOCGSESSION");
76 		goto err;
77 	}
78 	memset(&cryp, 0, sizeof(cryp));
79 	cryp.ses = session.ses;
80 	cryp.op = do_encrypt ? COP_ENCRYPT : COP_DECRYPT;
81 	cryp.flags = 0;
82 	cryp.len = len;
83 	cryp.src = (caddr_t) in;
84 	cryp.dst = (caddr_t) out;
85 	cryp.iv = (caddr_t) iv;
86 	cryp.mac = 0;
87 	if (ioctl(fd, CIOCCRYPT, &cryp) == -1) {
88 		warn("CIOCCRYPT");
89 		goto err;
90 	}
91 	if (ioctl(fd, CIOCFSESSION, &session.ses) == -1) {
92 		warn("CIOCFSESSION");
93 		goto err;
94 	}
95 	close(fd);
96 	close(cryptodev_fd);
97 	return (0);
98 
99 err:
100 	if (fd != -1)
101 		close(fd);
102 	if (cryptodev_fd != -1)
103 		close(cryptodev_fd);
104 	return (-1);
105 }
106 
107 static int
108 getallowsoft(void)
109 {
110 	int mib[2], old;
111 	size_t olen;
112 
113 	olen = sizeof(old);
114 
115 	mib[0] = CTL_KERN;
116 	mib[1] = KERN_CRYPTODEVALLOWSOFT;
117 	if (sysctl(mib, 2, &old, &olen, NULL, 0) < 0)
118 		err(1, "sysctl failed");
119 
120 	return old;
121 }
122 
123 static void
124 setallowsoft(int new)
125 {
126 	int mib[2], old;
127 	size_t olen, nlen;
128 
129 	olen = nlen = sizeof(new);
130 
131 	mib[0] = CTL_KERN;
132 	mib[1] = KERN_CRYPTODEVALLOWSOFT;
133 
134 	if (sysctl(mib, 2, &old, &olen, &new, nlen) < 0)
135 		err(1, "sysctl failed");
136 }
137 
138 static int
139 match(unsigned char *a, unsigned char *b, size_t len)
140 {
141 	size_t i;
142 
143 	if (memcmp(a, b, len) == 0)
144 		return (1);
145 
146 	warnx("decrypt/plaintext mismatch");
147 
148 	for (i = 0; i < len; i++)
149 		printf("%2.2x", a[i]);
150 	printf("\n");
151 	for (i = 0; i < len; i++)
152 		printf("%2.2x", b[i]);
153 	printf("\n");
154 
155 	return (0);
156 }
157 
158 /*
159  * Match expected substring at start of line. If sequence is match, return
160  * a pointer to the first character in the string past the sequence and and
161  * following whitespace.
162  * returns NULL is the start of the line does not match.
163  */
164 static const char *
165 startswith(const char *line, const char *startswith)
166 {
167 	size_t len = strlen(startswith);
168 
169 	if (strncmp(line, startswith, len) != 0)
170 		return NULL;
171 	line = line + len;
172 	while (isspace(*line))
173 		line++;
174 	return line;
175 }
176 
177 /* Read a hex string and convert to bytes */
178 static void
179 parsehex(const char *hex, u_char **s, u_int *lenp)
180 {
181 	u_char *ret, v;
182 	u_int i, len;
183 	char c;
184 
185 	len = i = 0;
186 	ret = NULL;
187 	v = 0;
188 	while ((c = *(hex++)) != '\0') {
189 		if (strchr(" \t\r\n", c) != NULL)
190 			continue;
191 		if (c >= '0' && c <= '9')
192 			v |= c - '0';
193 		else if (c >= 'a' && c <= 'f')
194 			v |= 10 + (c - 'a');
195 		else if (c >= 'A' && c <= 'F')
196 			v |= 10 + c - 'A';
197 		else
198 			errx(1, "%s: invalid character \"%c\" in hex string",
199 			    __func__, c);
200 		switch (++i) {
201 		case 1:
202 			v <<= 4;
203 			break;
204 		case 2:
205 			if ((ret = realloc(ret, ++len)) == NULL)
206 				errx(1, "realloc(%u)", len);
207 			ret[len - 1] = v;
208 			v = 0;
209 			i = 0;
210 		}
211 	}
212 	if (i != 0)
213 		errx(1, "%s: odd number of characters in hex string", __func__);
214 	*lenp = len;
215 	*s = ret;
216 }
217 
218 static int
219 do_tests(const char *filename, int test_num, u_char *key, u_int keylen,
220     u_char *plaintext, u_char *ciphertext, u_int textlen)
221 {
222 	char result[32];
223 	int fail = 0;
224 
225 	/* Encrypt test */
226 	if (syscrypt(key, keylen, plaintext, result, textlen, 1) < 0) {
227 		warnx("encrypt with /dev/crypto failed");
228 		fail++;
229 	} else if (!match(result, ciphertext, textlen)) {
230 		fail++;
231 	} else
232 		printf("OK encrypt test vector %s %u\n", filename, test_num);
233 
234 	/* Decrypt test */
235 	if (syscrypt(key, keylen, ciphertext, result, textlen, 0) < 0) {
236 		warnx("decrypt with /dev/crypto failed");
237 		fail++;
238 	} else if (!match(result, plaintext, textlen)) {
239 		fail++;
240 	} else
241 		printf("OK decrypt test vector %s %u\n", filename, test_num);
242 
243 	return fail;
244 }
245 
246 static int
247 run_file(const char *filename)
248 {
249 	FILE *tv;
250 	char buf[1024], *eol;
251 	const char *cp, *errstr;
252 	int lnum = 0, fail = 0;
253 	u_char *key, *plaintext, *ciphertext;
254 	u_int keylen, textlen, tmp;
255 	int blocksize, keysize, test;
256 
257 	if ((tv = fopen(filename, "r")) == NULL)
258 		err(1, "fopen(\"%s\")", filename);
259 
260 	keylen = textlen = tmp = 0;
261 	key = ciphertext = plaintext = NULL;
262 	blocksize = keysize = test = -1;
263 	while ((fgets(buf, sizeof(buf), tv)) != NULL) {
264 		lnum++;
265 		eol = buf + strlen(buf) - 1;
266 		if (*eol != '\n')
267 			errx(1, "line %d: too long", lnum);
268 		if (eol > buf && *(eol - 1) == '\r')
269 			eol--;
270 		*eol = '\0';
271 		if ((cp = startswith(buf, "BLOCKSIZE=")) != NULL) {
272 			if (blocksize != -1)
273 				errx(1, "line %d: blocksize already set", lnum);
274 			blocksize = (int)strtonum(cp, 128, 128, &errstr);
275 			if (errstr)
276 				errx(1, "line %d: blocksize is %s: \"%s\"",
277 				    lnum, errstr, cp);
278 		} else if ((cp = startswith(buf, "KEYSIZE=")) != NULL) {
279 			if (keysize != -1)
280 				errx(1, "line %d: keysize already set", lnum);
281 			keysize = (int)strtonum(cp, 128, 256, &errstr);
282 			if (errstr)
283 				errx(1, "line %d: keysize is %s: \"%s\"",
284 				    lnum, errstr, cp);
285 			if (keysize != 128 && keysize != 256)
286 				errx(1, "line %d: XXX only 128 or 256 "
287 				    "bit keys for now (keysize = %d)",
288 				    lnum, keysize);
289 		} else if ((cp = startswith(buf, "PT=")) != NULL) {
290 			if (plaintext != NULL)
291 				free(plaintext);
292 			parsehex(cp, &plaintext, &tmp);
293 			if (tmp * 8 != (u_int)blocksize)
294 				errx(1, "line %d: plaintext len %u != "
295 				    "blocklen %d", lnum, tmp, blocksize);
296 			if (textlen != 0) {
297 				if (textlen != tmp)
298 					errx(1, "line %d: plaintext len %u != "
299 					    "ciphertext len %d", lnum, tmp,
300 					    textlen);
301 			} else
302 				textlen = tmp;
303 		} else if ((cp = startswith(buf, "CT=")) != NULL) {
304 			if (ciphertext != NULL)
305 				free(ciphertext);
306 			parsehex(cp, &ciphertext, &tmp);
307 			if (tmp * 8 != (u_int)blocksize)
308 				errx(1, "line %d: ciphertext len %u != "
309 				    "blocklen %d", lnum, tmp, blocksize);
310 			if (textlen != 0) {
311 				if (textlen != tmp)
312 					errx(1, "line %d: ciphertext len %u != "
313 					    "plaintext len %d", lnum, tmp,
314 					    textlen);
315 			} else
316 				textlen = tmp;
317 		} else if ((cp = startswith(buf, "KEY=")) != NULL) {
318 			if (key != NULL)
319 				free(key);
320 			parsehex(cp, &key, &keylen);
321 			if (keylen * 8 != (u_int)keysize)
322 				errx(1, "line %d: ciphertext len %u != "
323 				    "blocklen %d", lnum, tmp, textlen);
324 		} else if ((cp = startswith(buf, "TEST=")) != NULL) {
325 			if (plaintext == NULL || ciphertext == NULL ||
326 			    key == NULL || blocksize == -1 || keysize == -1) {
327 				if (test != -1)
328 					errx(1, "line %d: new test before "
329 					    "parameters", lnum);
330 				goto parsetest;
331 			}
332 			/* do the tests */
333 			fail += do_tests(filename, test, key, keylen,
334 			    plaintext, ciphertext, textlen);
335  parsetest:
336 			test = (int)strtonum(cp, 0, 65536, &errstr);
337 			if (errstr)
338 				errx(1, "line %d: test is %s: \"%s\"",
339 				    lnum, errstr, cp);
340 		} else {
341 			/* don't care */
342 			continue;
343 		}
344 	}
345 	fclose(tv);
346 
347 	return fail;
348 }
349 
350 int
351 main(int argc, char **argv)
352 {
353 	int allowed = 0, fail = 0, i;
354 
355 	if (argc < 2)
356 		errx(1, "usage: aestest [test-vector-file]");
357 
358 	if (geteuid() == 0) {
359 		allowed = getallowsoft();
360 		if (allowed == 0)
361 			setallowsoft(1);
362 	}
363 
364 	for (i = 1; i < argc; i++)
365 		fail += run_file(argv[1]);
366 
367 	if (geteuid() == 0 && allowed == 0)
368 		setallowsoft(0);
369 
370 	return fail > 0 ? 1 : 0;
371 }
372