aestest.c revision 1.1
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
46static int
47syscrypt(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
99err:
100	if (fd != -1)
101		close(fd);
102	if (cryptodev_fd != -1)
103		close(cryptodev_fd);
104	return (-1);
105}
106
107static int
108getallowsoft(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
123static void
124setallowsoft(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
138static int
139match(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 */
164static const char *
165startswith(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 */
178static void
179parsehex(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
218static int
219do_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
246static int
247run_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
350int
351main(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