xref: /dragonfly/test/testcases/crypto/twofish/twofish_test.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1 /*      $OpenBSD: djm $  */
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) Twofish with test vectors provided by Dr Brian Gladman
30  */
31 
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/ioctl.h>
35 #include <sys/sysctl.h>
36 #include <crypto/cryptodev.h>
37 #include <err.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <ctype.h>
44 
45 static int
syscrypt(const unsigned char * key,size_t klen,const unsigned char * in,unsigned char * out,size_t len,int do_encrypt)46 syscrypt(const unsigned char *key, size_t klen, const unsigned char *in,
47     unsigned char *out, size_t len, int do_encrypt)
48 {
49           struct session_op session;
50           struct crypt_op cryp;
51           int cryptodev_fd = -1, fd = -1;
52           u_char iv[32];
53 
54           /*
55            * Kludge; the kernel doesn't support ECB encryption so we
56            * use a all-zero IV and encrypt a single block only, so the
57            * result should be the same.
58            */
59           bzero(iv, sizeof(iv));
60 
61           if ((cryptodev_fd = open("/dev/crypto", O_RDWR, 0)) < 0) {
62                     warn("/dev/crypto");
63                     goto err;
64           }
65           if (ioctl(cryptodev_fd, CRIOGET, &fd) == -1) {
66                     warn("CRIOGET failed");
67                     goto err;
68           }
69           memset(&session, 0, sizeof(session));
70           session.cipher = CRYPTO_TWOFISH_CBC;
71           session.key = (caddr_t) key;
72           session.keylen = klen;
73           if (ioctl(fd, CIOCGSESSION, &session) == -1) {
74                     warn("CIOCGSESSION");
75                     goto err;
76           }
77           memset(&cryp, 0, sizeof(cryp));
78           cryp.ses = session.ses;
79           cryp.op = do_encrypt ? COP_ENCRYPT : COP_DECRYPT;
80           cryp.flags = 0;
81           cryp.len = len;
82           cryp.src = (caddr_t) in;
83           cryp.dst = (caddr_t) out;
84           cryp.iv = (caddr_t) iv;
85           cryp.mac = 0;
86           if (ioctl(fd, CIOCCRYPT, &cryp) == -1) {
87                     warn("CIOCCRYPT");
88                     goto err;
89           }
90           if (ioctl(fd, CIOCFSESSION, &session.ses) == -1) {
91                     warn("CIOCFSESSION");
92                     goto err;
93           }
94           close(fd);
95           close(cryptodev_fd);
96           return (0);
97 
98 err:
99           if (fd != -1)
100                     close(fd);
101           if (cryptodev_fd != -1)
102                     close(cryptodev_fd);
103           return (-1);
104 }
105 
106 static int
getallowsoft(void)107 getallowsoft(void)
108 {
109           int mib[2], old;
110           size_t olen;
111 
112           olen = sizeof(old);
113 
114           if (sysctlbyname("kern.cryptodevallowsoft", &old, &olen, NULL, 0) < 0)
115                     err(1, "sysctl failed");
116 
117           return old;
118 }
119 
120 static void
setallowsoft(int new)121 setallowsoft(int new)
122 {
123           int mib[2], old;
124           size_t olen, nlen;
125 
126           olen = nlen = sizeof(new);
127 
128           if (sysctlbyname("kern.cryptodevallowsoft", &old, &olen, &new, nlen) < 0)
129                     err(1, "sysctl failed");
130 }
131 
132 static int
match(unsigned char * a,unsigned char * b,size_t len)133 match(unsigned char *a, unsigned char *b, size_t len)
134 {
135           size_t i;
136 
137           if (memcmp(a, b, len) == 0)
138                     return (1);
139 
140           fprintf(stderr, "decrypt/plaintext mismatch\n");
141 
142           for (i = 0; i < len; i++)
143                     fprintf(stderr, "%2.2x", a[i]);
144           fprintf(stderr, "\n");
145           for (i = 0; i < len; i++)
146                     fprintf(stderr, "%2.2x", b[i]);
147           fprintf(stderr, "\n");
148 
149           return (0);
150 }
151 
152 static void
print_hex(unsigned char * a,size_t len)153 print_hex(unsigned char *a, size_t len)
154 {
155           while (len-- > 0)
156                     fprintf(stderr, "%02x,", *a++);
157           fprintf(stderr, "\n");
158 }
159 
160 /*
161  * Match expected substring at start of line. If sequence is match, return
162  * a pointer to the first character in the string past the sequence and and
163  * following whitespace.
164  * returns NULL is the start of the line does not match.
165  */
166 static const char *
startswith(const char * line,const char * startswith)167 startswith(const char *line, const char *startswith)
168 {
169           size_t len = strlen(startswith);
170 
171           if (strncmp(line, startswith, len) != 0)
172                     return NULL;
173           line = line + len;
174           while (isspace(*line))
175                     line++;
176           return line;
177 }
178 
179 /* Read a hex string and convert to bytes */
180 static void
parsehex(const char * hex,u_char ** s,u_int * lenp)181 parsehex(const char *hex, u_char **s, u_int *lenp)
182 {
183           u_char *ret, v;
184           u_int i, len;
185           char c;
186 
187           len = i = 0;
188           ret = NULL;
189           v = 0;
190           while ((c = *(hex++)) != '\0') {
191                     if (strchr(" \t\r\n", c) != NULL)
192                               continue;
193                     if (c >= '0' && c <= '9')
194                               v |= c - '0';
195                     else if (c >= 'a' && c <= 'f')
196                               v |= 10 + (c - 'a');
197                     else if (c >= 'A' && c <= 'F')
198                               v |= 10 + c - 'A';
199                     else
200                               errx(1, "%s: invalid character \"%c\" in hex string",
201                                   __func__, c);
202                     switch (++i) {
203                     case 1:
204                               v <<= 4;
205                               break;
206                     case 2:
207                               if ((ret = realloc(ret, ++len)) == NULL)
208                                         errx(1, "realloc(%u)", len);
209                               ret[len - 1] = v;
210                               v = 0;
211                               i = 0;
212                     }
213           }
214           if (i != 0)
215                     errx(1, "%s: odd number of characters in hex string", __func__);
216           *lenp = len;
217           *s = ret;
218 }
219 
220 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)221 do_tests(const char *filename, int test_num, u_char *key, u_int keylen,
222     u_char *plaintext, u_char *ciphertext, u_int textlen)
223 {
224           char result[32];
225           int fail = 0;
226 
227 #if 0
228           fprintf(stderr, "Encrypting: \n");
229           print_hex(key, keylen);
230           print_hex(plaintext, textlen);
231           print_hex(ciphertext, textlen);
232 #endif
233           /* Encrypt test */
234           if (syscrypt(key, keylen, plaintext, result, textlen, 1) < 0) {
235                     warnx("encrypt with /dev/crypto failed");
236                     fail++;
237           } else if (!match(result, ciphertext, textlen)) {
238                     fprintf(stderr, "on encrypt (result, ciphertext)\n");
239                     fail++;
240           } else
241                     printf("OK encrypt test vector %s %u\n", filename, test_num);
242 
243           /* Decrypt test */
244           if (syscrypt(key, keylen, ciphertext, result, textlen, 0) < 0) {
245                     warnx("decrypt with /dev/crypto failed");
246                     fail++;
247           } else if (!match(result, plaintext, textlen)) {
248                     fprintf(stderr, "on decrypt (result, plaintext)\n");
249                     fail++;
250           } else
251                     printf("OK decrypt test vector %s %u\n", filename, test_num);
252 
253           return fail;
254 }
255 
256 static int
run_file(const char * filename)257 run_file(const char *filename)
258 {
259           FILE *tv;
260           char buf[1024], *eol;
261           const char *cp, *errstr;
262           int lnum = 0, fail = 0;
263           u_char *key, *plaintext, *ciphertext;
264           u_int keylen, textlen, tmp;
265           int blocksize, keysize, test;
266 
267           if ((tv = fopen(filename, "r")) == NULL)
268                     err(1, "fopen(\"%s\")", filename);
269 
270           keylen = textlen = tmp = 0;
271           key = ciphertext = plaintext = NULL;
272           keysize = test = -1;
273           blocksize = 128;
274           while ((fgets(buf, sizeof(buf), tv)) != NULL) {
275                     lnum++;
276                     eol = buf + strlen(buf) - 1;
277                     if (*eol != '\n')
278                               errx(1, "line %d: too long", lnum);
279                     if (eol > buf && *(eol - 1) == '\r')
280                               eol--;
281                     *eol = '\0';
282                     if ((cp = startswith(buf, "BLOCKSIZE=")) != NULL) {
283                               if (blocksize != -1)
284                                         errx(1, "line %d: blocksize already set", lnum);
285                               blocksize = (int)strtonum(cp, 128, 128, &errstr);
286                               if (errstr)
287                                         errx(1, "line %d: blocksize is %s: \"%s\"",
288                                             lnum, errstr, cp);
289                     } else if ((cp = startswith(buf, "KEYSIZE=")) != NULL) {
290                               /*
291                                * On a keysize change, run scheduled test before
292                                * doing anything else; at least if there is a test
293                                * to perform.
294                                */
295                               if (plaintext != NULL && ciphertext != NULL &&
296                                   key != NULL && blocksize > 0 && keysize > 0) {
297                                         fail += do_tests(filename, test, key, keylen,
298                                             plaintext, ciphertext, textlen);
299 
300                                         /* And reset the test number */
301                                         test = -1;
302                               }
303                               keysize = (int)strtonum(cp, 128, 256, &errstr);
304                               if (errstr)
305                                         errx(1, "line %d: keysize is %s: \"%s\"",
306                                             lnum, errstr, cp);
307                               if (keysize != 128 && keysize != 192 && keysize != 256)
308                                         errx(1, "line %d: XXX only 128,192 or 256 "
309                                             "bit keys (keysize = %d)",
310                                             lnum, keysize);
311                     } else if ((cp = startswith(buf, "PT=")) != NULL) {
312                               if (plaintext != NULL)
313                                         free(plaintext);
314                               parsehex(cp, &plaintext, &tmp);
315                               if (tmp * 8 != (u_int)blocksize)
316                                         errx(1, "line %d: plaintext len %u != "
317                                             "blocklen %d", lnum, tmp, blocksize);
318                               if (textlen != 0) {
319                                         if (textlen != tmp)
320                                                   errx(1, "line %d: plaintext len %u != "
321                                                       "ciphertext len %d", lnum, tmp,
322                                                       textlen);
323                               } else
324                                         textlen = tmp;
325                     } else if ((cp = startswith(buf, "CT=")) != NULL) {
326                               if (ciphertext != NULL)
327                                         free(ciphertext);
328                               parsehex(cp, &ciphertext, &tmp);
329                               if (tmp * 8 != (u_int)blocksize)
330                                         errx(1, "line %d: ciphertext len %u != "
331                                             "blocklen %d", lnum, tmp, blocksize);
332                               if (textlen != 0) {
333                                         if (textlen != tmp)
334                                                   errx(1, "line %d: ciphertext len %u != "
335                                                       "plaintext len %d", lnum, tmp,
336                                                       textlen);
337                               } else
338                                         textlen = tmp;
339                     } else if ((cp = startswith(buf, "KEY=")) != NULL) {
340                               if (key != NULL)
341                                         free(key);
342                               parsehex(cp, &key, &keylen);
343                               if (keylen * 8 != (u_int)keysize)
344                                         errx(1, "line %d: ciphertext len %u != "
345                                             "blocklen %d", lnum, tmp, textlen);
346                     } else if ((cp = startswith(buf, "I=")) != NULL) {
347                               if (test == -1)
348                                         goto parsetest;
349 
350                               if (plaintext == NULL || ciphertext == NULL ||
351                                   key == NULL || blocksize == -1 || keysize == -1) {
352                                         errx(1, "line %d: new test before "
353                                             "parameters", lnum);
354                               }
355                               /* do the tests */
356                               fail += do_tests(filename, test, key, keylen,
357                                   plaintext, ciphertext, textlen);
358 parsetest:
359                               test = (int)strtonum(cp, 0, 65536, &errstr);
360                               if (errstr)
361                                         errx(1, "line %d: test is %s: \"%s\"",
362                                             lnum, errstr, cp);
363                     } else {
364                               /* don't care */
365                               continue;
366                     }
367           }
368           fclose(tv);
369 
370           return fail;
371 }
372 
373 int
main(int argc,char ** argv)374 main(int argc, char **argv)
375 {
376           int allowed = 0, fail = 0, i;
377 
378           if (argc < 2)
379                     errx(1, "usage: twofish_test [test-vector-file]");
380 
381           if (geteuid() == 0) {
382                     allowed = getallowsoft();
383                     if (allowed == 0)
384                               setallowsoft(1);
385           }
386 
387           for (i = 1; i < argc; i++)
388                     fail += run_file(argv[1]);
389 
390           if (geteuid() == 0 && allowed == 0)
391                     setallowsoft(0);
392 
393           return fail > 0 ? 1 : 0;
394 }
395