xref: /openbsd/regress/sys/crypto/aes/aestest.c (revision 49a6e16f)
1 /*      $OpenBSD: aestest.c,v 1.5 2021/12/13 16:56:49 deraadt 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 kernel AES implementation with test vectors provided by
30  * Dr Brian Gladman:  http://fp.gladman.plus.com/AES/
31  */
32 
33 #include <sys/types.h>
34 #include <crypto/aes.h>
35 #include <err.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <ctype.h>
41 
42 static int
docrypt(const unsigned char * key,size_t klen,const unsigned char * in,unsigned char * out,size_t len,int do_encrypt)43 docrypt(const unsigned char *key, size_t klen, const unsigned char *in,
44     unsigned char *out, size_t len, int do_encrypt)
45 {
46 	AES_CTX ctx;
47 	int error = 0;
48 
49 	memset(&ctx, 0, sizeof(ctx));
50 	error = AES_Setkey(&ctx, key, klen);
51 	if (error)
52 		return -1;
53 	if (do_encrypt)
54 		AES_Encrypt(&ctx, in, out);
55 	else
56 		AES_Decrypt(&ctx, in, out);
57 	return 0;
58 }
59 
60 static int
match(unsigned char * a,unsigned char * b,size_t len)61 match(unsigned char *a, unsigned char *b, size_t len)
62 {
63 	size_t i;
64 
65 	if (memcmp(a, b, len) == 0)
66 		return (1);
67 
68 	warnx("decrypt/plaintext mismatch");
69 
70 	for (i = 0; i < len; i++)
71 		printf("%2.2x", a[i]);
72 	printf("\n");
73 	for (i = 0; i < len; i++)
74 		printf("%2.2x", b[i]);
75 	printf("\n");
76 
77 	return (0);
78 }
79 
80 /*
81  * Match expected substring at start of line. If sequence is match, return
82  * a pointer to the first character in the string past the sequence and and
83  * following whitespace.
84  * returns NULL is the start of the line does not match.
85  */
86 static const char *
startswith(const char * line,const char * startswith)87 startswith(const char *line, const char *startswith)
88 {
89 	size_t len = strlen(startswith);
90 
91 	if (strncmp(line, startswith, len) != 0)
92 		return NULL;
93 	line = line + len;
94 	while (isspace((unsigned char)*line))
95 		line++;
96 	return line;
97 }
98 
99 /* Read a hex string and convert to bytes */
100 static void
parsehex(const char * hex,u_char ** s,u_int * lenp)101 parsehex(const char *hex, u_char **s, u_int *lenp)
102 {
103 	u_char *ret, v;
104 	u_int i, len;
105 	char c;
106 
107 	len = i = 0;
108 	ret = NULL;
109 	v = 0;
110 	while ((c = *(hex++)) != '\0') {
111 		if (strchr(" \t\r\n", c) != NULL)
112 			continue;
113 		if (c >= '0' && c <= '9')
114 			v |= c - '0';
115 		else if (c >= 'a' && c <= 'f')
116 			v |= 10 + (c - 'a');
117 		else if (c >= 'A' && c <= 'F')
118 			v |= 10 + c - 'A';
119 		else
120 			errx(1, "%s: invalid character \"%c\" in hex string",
121 			    __func__, c);
122 		switch (++i) {
123 		case 1:
124 			v <<= 4;
125 			break;
126 		case 2:
127 			if ((ret = realloc(ret, ++len)) == NULL)
128 				errx(1, "realloc(%u)", len);
129 			ret[len - 1] = v;
130 			v = 0;
131 			i = 0;
132 		}
133 	}
134 	if (i != 0)
135 		errx(1, "%s: odd number of characters in hex string", __func__);
136 	*lenp = len;
137 	*s = ret;
138 }
139 
140 static int
do_tests(const char * filename,int test_num,u_char * key,u_int keylen,u_char * plaintext,u_char * ciphertext,u_int textlen)141 do_tests(const char *filename, int test_num, u_char *key, u_int keylen,
142     u_char *plaintext, u_char *ciphertext, u_int textlen)
143 {
144 	char result[32];
145 	int fail = 0;
146 
147 	/* Encrypt test */
148 	if (docrypt(key, keylen, plaintext, result, textlen, 1) < 0) {
149 		warnx("encryption failed");
150 		fail++;
151 	} else if (!match(result, ciphertext, textlen)) {
152 		fail++;
153 	} else
154 		printf("OK encrypt test vector %s %u\n", filename, test_num);
155 
156 	/* Decrypt test */
157 	if (docrypt(key, keylen, ciphertext, result, textlen, 0) < 0) {
158 		warnx("decryption failed");
159 		fail++;
160 	} else if (!match(result, plaintext, textlen)) {
161 		fail++;
162 	} else
163 		printf("OK decrypt test vector %s %u\n", filename, test_num);
164 
165 	return fail;
166 }
167 
168 static int
run_file(const char * filename)169 run_file(const char *filename)
170 {
171 	FILE *tv;
172 	char buf[1024], *eol;
173 	const char *cp, *errstr;
174 	int lnum = 0, fail = 0;
175 	u_char *key, *plaintext, *ciphertext;
176 	u_int keylen, textlen, tmp;
177 	int blocksize, keysize, test;
178 
179 	if ((tv = fopen(filename, "r")) == NULL)
180 		err(1, "fopen(\"%s\")", filename);
181 
182 	keylen = textlen = tmp = 0;
183 	key = ciphertext = plaintext = NULL;
184 	blocksize = keysize = test = -1;
185 	while ((fgets(buf, sizeof(buf), tv)) != NULL) {
186 		lnum++;
187 		eol = buf + strlen(buf) - 1;
188 		if (*eol != '\n')
189 			errx(1, "line %d: too long", lnum);
190 		if (eol > buf && *(eol - 1) == '\r')
191 			eol--;
192 		*eol = '\0';
193 		if ((cp = startswith(buf, "BLOCKSIZE=")) != NULL) {
194 			if (blocksize != -1)
195 				errx(1, "line %d: blocksize already set", lnum);
196 			blocksize = (int)strtonum(cp, 128, 128, &errstr);
197 			if (errstr)
198 				errx(1, "line %d: blocksize is %s: \"%s\"",
199 				    lnum, errstr, cp);
200 		} else if ((cp = startswith(buf, "KEYSIZE=")) != NULL) {
201 			if (keysize != -1)
202 				errx(1, "line %d: keysize already set", lnum);
203 			keysize = (int)strtonum(cp, 128, 256, &errstr);
204 			if (errstr)
205 				errx(1, "line %d: keysize is %s: \"%s\"",
206 				    lnum, errstr, cp);
207 			if (keysize != 128 && keysize != 256)
208 				errx(1, "line %d: XXX only 128 or 256 "
209 				    "bit keys for now (keysize = %d)",
210 				    lnum, keysize);
211 		} else if ((cp = startswith(buf, "PT=")) != NULL) {
212 			if (plaintext != NULL)
213 				free(plaintext);
214 			parsehex(cp, &plaintext, &tmp);
215 			if (tmp * 8 != (u_int)blocksize)
216 				errx(1, "line %d: plaintext len %u != "
217 				    "blocklen %d", lnum, tmp, blocksize);
218 			if (textlen != 0) {
219 				if (textlen != tmp)
220 					errx(1, "line %d: plaintext len %u != "
221 					    "ciphertext len %d", lnum, tmp,
222 					    textlen);
223 			} else
224 				textlen = tmp;
225 		} else if ((cp = startswith(buf, "CT=")) != NULL) {
226 			if (ciphertext != NULL)
227 				free(ciphertext);
228 			parsehex(cp, &ciphertext, &tmp);
229 			if (tmp * 8 != (u_int)blocksize)
230 				errx(1, "line %d: ciphertext len %u != "
231 				    "blocklen %d", lnum, tmp, blocksize);
232 			if (textlen != 0) {
233 				if (textlen != tmp)
234 					errx(1, "line %d: ciphertext len %u != "
235 					    "plaintext len %d", lnum, tmp,
236 					    textlen);
237 			} else
238 				textlen = tmp;
239 		} else if ((cp = startswith(buf, "KEY=")) != NULL) {
240 			if (key != NULL)
241 				free(key);
242 			parsehex(cp, &key, &keylen);
243 			if (keylen * 8 != (u_int)keysize)
244 				errx(1, "line %d: ciphertext len %u != "
245 				    "blocklen %d", lnum, tmp, textlen);
246 		} else if ((cp = startswith(buf, "TEST=")) != NULL) {
247 			if (plaintext == NULL || ciphertext == NULL ||
248 			    key == NULL || blocksize == -1 || keysize == -1) {
249 				if (test != -1)
250 					errx(1, "line %d: new test before "
251 					    "parameters", lnum);
252 				goto parsetest;
253 			}
254 			/* do the tests */
255 			fail += do_tests(filename, test, key, keylen,
256 			    plaintext, ciphertext, textlen);
257  parsetest:
258 			test = (int)strtonum(cp, 0, 65536, &errstr);
259 			if (errstr)
260 				errx(1, "line %d: test is %s: \"%s\"",
261 				    lnum, errstr, cp);
262 		} else {
263 			/* don't care */
264 			continue;
265 		}
266 	}
267 	fclose(tv);
268 
269 	return fail;
270 }
271 
272 int
main(int argc,char ** argv)273 main(int argc, char **argv)
274 {
275 	int fail = 0, i;
276 
277 	if (argc < 2)
278 		errx(1, "usage: aestest [test-vector-file]");
279 
280 	for (i = 1; i < argc; i++)
281 		fail += run_file(argv[1]);
282 
283 	return fail > 0 ? 1 : 0;
284 }
285