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 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 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 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 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 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 * 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 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 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 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 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