1 /* OpenBSD S/Key (skeysubr.c) 2 * 3 * Authors: 4 * Neil M. Haller <nmh@thumper.bellcore.com> 5 * Philip R. Karn <karn@chicago.qualcomm.com> 6 * John S. Walden <jsw@thumper.bellcore.com> 7 * Scott Chasin <chasin@crimelab.com> 8 * Todd C. Miller <Todd.Miller@courtesan.com> 9 * 10 * S/Key misc routines. 11 * 12 * $OpenBSD: skeysubr.c,v 1.20 2001/06/23 21:03:47 millert Exp $ 13 */ 14 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <ctype.h> 19 #include <signal.h> 20 #include <termios.h> 21 #include <md4.h> 22 #include <md5.h> 23 #include <sha1.h> 24 #include <rmd160.h> 25 26 #include "skey.h" 27 28 /* Default hash function to use (index into skey_hash_types array) */ 29 #ifndef SKEY_HASH_DEFAULT 30 #define SKEY_HASH_DEFAULT 1 31 #endif 32 33 static int keycrunch_md4 __P((char *, char *, char *)); 34 static int keycrunch_md5 __P((char *, char *, char *)); 35 static int keycrunch_sha1 __P((char *, char *, char *)); 36 static int keycrunch_rmd160 __P((char *, char *, char *)); 37 static void lowcase __P((char *)); 38 static void skey_echo __P((int)); 39 static void trapped __P((int)); 40 41 /* Current hash type (index into skey_hash_types array) */ 42 static int skey_hash_type = SKEY_HASH_DEFAULT; 43 44 /* 45 * Hash types we support. 46 * Each has an associated keycrunch() and f() function. 47 */ 48 #define SKEY_ALGORITH_LAST 4 49 struct skey_algorithm_table { 50 const char *name; 51 int (*keycrunch) __P((char *, char *, char *)); 52 }; 53 static struct skey_algorithm_table skey_algorithm_table[] = { 54 { "md4", keycrunch_md4 }, 55 { "md5", keycrunch_md5 }, 56 { "sha1", keycrunch_sha1 }, 57 { "rmd160", keycrunch_rmd160 } 58 }; 59 60 61 /* 62 * Crunch a key: 63 * concatenate the seed and the password, run through hash function and 64 * collapse to 64 bits. This is defined as the user's starting key. 65 */ 66 int 67 keycrunch(result, seed, passwd) 68 char *result; /* SKEY_BINKEY_SIZE result */ 69 char *seed; /* Seed, any length */ 70 char *passwd; /* Password, any length */ 71 { 72 return(skey_algorithm_table[skey_hash_type].keycrunch(result, seed, passwd)); 73 } 74 75 static int 76 keycrunch_md4(result, seed, passwd) 77 char *result; /* SKEY_BINKEY_SIZE result */ 78 char *seed; /* Seed, any length */ 79 char *passwd; /* Password, any length */ 80 { 81 char *buf = NULL; 82 MD4_CTX md; 83 u_int32_t results[4]; 84 unsigned int buflen; 85 86 /* 87 * If seed and passwd are defined we are in keycrunch() mode, 88 * else we are in f() mode. 89 */ 90 if (seed && passwd) { 91 buflen = strlen(seed) + strlen(passwd); 92 if ((buf = (char *)malloc(buflen + 1)) == NULL) 93 return(-1); 94 (void)strcpy(buf, seed); 95 lowcase(buf); 96 (void)strcat(buf, passwd); 97 sevenbit(buf); 98 } else { 99 buf = result; 100 buflen = SKEY_BINKEY_SIZE; 101 } 102 103 /* Crunch the key through MD4 */ 104 MD4Init(&md); 105 MD4Update(&md, (unsigned char *)buf, buflen); 106 MD4Final((unsigned char *)results, &md); 107 108 /* Fold result from 128 to 64 bits */ 109 results[0] ^= results[2]; 110 results[1] ^= results[3]; 111 112 (void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); 113 114 if (buf != result) 115 (void)free(buf); 116 117 return(0); 118 } 119 120 static int 121 keycrunch_md5(result, seed, passwd) 122 char *result; /* SKEY_BINKEY_SIZE result */ 123 char *seed; /* Seed, any length */ 124 char *passwd; /* Password, any length */ 125 { 126 char *buf; 127 MD5_CTX md; 128 u_int32_t results[4]; 129 unsigned int buflen; 130 131 /* 132 * If seed and passwd are defined we are in keycrunch() mode, 133 * else we are in f() mode. 134 */ 135 if (seed && passwd) { 136 buflen = strlen(seed) + strlen(passwd); 137 if ((buf = (char *)malloc(buflen + 1)) == NULL) 138 return(-1); 139 (void)strcpy(buf, seed); 140 lowcase(buf); 141 (void)strcat(buf, passwd); 142 sevenbit(buf); 143 } else { 144 buf = result; 145 buflen = SKEY_BINKEY_SIZE; 146 } 147 148 /* Crunch the key through MD5 */ 149 MD5Init(&md); 150 MD5Update(&md, (unsigned char *)buf, buflen); 151 MD5Final((unsigned char *)results, &md); 152 153 /* Fold result from 128 to 64 bits */ 154 results[0] ^= results[2]; 155 results[1] ^= results[3]; 156 157 (void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); 158 159 if (buf != result) 160 (void)free(buf); 161 162 return(0); 163 } 164 165 static int 166 keycrunch_sha1(result, seed, passwd) 167 char *result; /* SKEY_BINKEY_SIZE result */ 168 char *seed; /* Seed, any length */ 169 char *passwd; /* Password, any length */ 170 { 171 char *buf; 172 SHA1_CTX sha; 173 unsigned int buflen; 174 int i, j; 175 176 /* 177 * If seed and passwd are defined we are in keycrunch() mode, 178 * else we are in f() mode. 179 */ 180 if (seed && passwd) { 181 buflen = strlen(seed) + strlen(passwd); 182 if ((buf = (char *)malloc(buflen + 1)) == NULL) 183 return(-1); 184 (void)strcpy(buf, seed); 185 lowcase(buf); 186 (void)strcat(buf, passwd); 187 sevenbit(buf); 188 } else { 189 buf = result; 190 buflen = SKEY_BINKEY_SIZE; 191 } 192 193 /* Crunch the key through SHA1 */ 194 SHA1Init(&sha); 195 SHA1Update(&sha, (unsigned char *)buf, buflen); 196 SHA1Final(NULL, &sha); 197 198 /* Fold 160 to 64 bits */ 199 sha.state[0] ^= sha.state[2]; 200 sha.state[1] ^= sha.state[3]; 201 sha.state[0] ^= sha.state[4]; 202 203 /* 204 * SHA1 is a big endian algorithm but RFC2289 mandates that 205 * the result be in little endian form, so we copy to the 206 * result buffer manually. 207 */ 208 for (i = 0, j = 0; j < 8; i++, j += 4) { 209 result[j] = (u_char)(sha.state[i] & 0xff); 210 result[j+1] = (u_char)((sha.state[i] >> 8) & 0xff); 211 result[j+2] = (u_char)((sha.state[i] >> 16) & 0xff); 212 result[j+3] = (u_char)((sha.state[i] >> 24) & 0xff); 213 } 214 215 if (buf != result) 216 (void)free(buf); 217 218 return(0); 219 } 220 221 static int 222 keycrunch_rmd160(result, seed, passwd) 223 char *result; /* SKEY_BINKEY_SIZE result */ 224 char *seed; /* Seed, any length */ 225 char *passwd; /* Password, any length */ 226 { 227 char *buf; 228 RMD160_CTX rmd; 229 u_int32_t results[5]; 230 unsigned int buflen; 231 232 /* 233 * If seed and passwd are defined we are in keycrunch() mode, 234 * else we are in f() mode. 235 */ 236 if (seed && passwd) { 237 buflen = strlen(seed) + strlen(passwd); 238 if ((buf = (char *)malloc(buflen + 1)) == NULL) 239 return(-1); 240 (void)strcpy(buf, seed); 241 lowcase(buf); 242 (void)strcat(buf, passwd); 243 sevenbit(buf); 244 } else { 245 buf = result; 246 buflen = SKEY_BINKEY_SIZE; 247 } 248 249 /* Crunch the key through RMD-160 */ 250 RMD160Init(&rmd); 251 RMD160Update(&rmd, (unsigned char *)buf, buflen); 252 RMD160Final((unsigned char *)results, &rmd); 253 254 /* Fold 160 to 64 bits */ 255 results[0] ^= results[2]; 256 results[1] ^= results[3]; 257 results[0] ^= results[4]; 258 259 (void)memcpy((void *)result, (void *)results, SKEY_BINKEY_SIZE); 260 261 if (buf != result) 262 (void)free(buf); 263 264 return(0); 265 } 266 267 /* 268 * The one-way hash function f(). 269 * Takes SKEY_BINKEY_SIZE bytes and returns SKEY_BINKEY_SIZE bytes in place. 270 */ 271 void 272 f(x) 273 char *x; 274 { 275 (void)skey_algorithm_table[skey_hash_type].keycrunch(x, NULL, NULL); 276 } 277 278 /* Strip trailing cr/lf from a line of text */ 279 void 280 rip(buf) 281 char *buf; 282 { 283 buf += strcspn(buf, "\r\n"); 284 285 if (*buf) 286 *buf = '\0'; 287 } 288 289 /* Read in secret password (turns off echo) */ 290 char * 291 readpass(buf, n) 292 char *buf; 293 int n; 294 { 295 void (*old_handler) __P(()); 296 297 /* Turn off echoing */ 298 skey_echo(0); 299 300 /* Catch SIGINT and save old signal handler */ 301 old_handler = signal(SIGINT, trapped); 302 303 (void)fgets(buf, n, stdin); 304 rip(buf); 305 306 (void)putc('\n', stderr); 307 (void)fflush(stderr); 308 309 /* Restore signal handler and turn echo back on */ 310 if (old_handler != SIG_ERR) 311 (void)signal(SIGINT, old_handler); 312 skey_echo(1); 313 314 sevenbit(buf); 315 316 return(buf); 317 } 318 319 /* Read in an s/key OTP (does not turn off echo) */ 320 char * 321 readskey(buf, n) 322 char *buf; 323 int n; 324 { 325 (void)fgets(buf, n, stdin); 326 rip(buf); 327 328 sevenbit(buf); 329 330 return(buf); 331 } 332 333 /* Signal handler for trapping ^C */ 334 static void 335 trapped(sig) 336 int sig; 337 { 338 (void)fputs("^C\n", stderr); 339 (void)fflush(stderr); 340 341 /* Turn on echo if necesary */ 342 skey_echo(1); 343 344 exit(-1); 345 } 346 347 /* 348 * Convert 8-byte hex-ascii string to binary array 349 * Returns 0 on success, -1 on error 350 */ 351 int 352 atob8(out, in) 353 char *out; 354 char *in; 355 { 356 int i; 357 int val; 358 359 if (in == NULL || out == NULL) 360 return(-1); 361 362 for (i=0; i < 8; i++) { 363 if ((in = skipspace(in)) == NULL) 364 return(-1); 365 if ((val = htoi(*in++)) == -1) 366 return(-1); 367 *out = val << 4; 368 369 if ((in = skipspace(in)) == NULL) 370 return(-1); 371 if ((val = htoi(*in++)) == -1) 372 return(-1); 373 *out++ |= val; 374 } 375 return(0); 376 } 377 378 /* Convert 8-byte binary array to hex-ascii string */ 379 int 380 btoa8(out, in) 381 char *out; 382 char *in; 383 { 384 int i; 385 386 if (in == NULL || out == NULL) 387 return(-1); 388 389 for (i=0; i < 8; i++) { 390 (void)sprintf(out, "%02x", *in++ & 0xff); 391 out += 2; 392 } 393 return(0); 394 } 395 396 /* Convert hex digit to binary integer */ 397 int 398 htoi(c) 399 int c; 400 { 401 if ('0' <= c && c <= '9') 402 return(c - '0'); 403 if ('a' <= c && c <= 'f') 404 return(10 + c - 'a'); 405 if ('A' <= c && c <= 'F') 406 return(10 + c - 'A'); 407 return(-1); 408 } 409 410 /* Skip leading spaces from the string */ 411 char * 412 skipspace(cp) 413 char *cp; 414 { 415 while (*cp == ' ' || *cp == '\t') 416 cp++; 417 418 if (*cp == '\0') 419 return(NULL); 420 else 421 return(cp); 422 } 423 424 /* Remove backspaced over characters from the string */ 425 void 426 backspace(buf) 427 char *buf; 428 { 429 char bs = 0x8; 430 char *cp = buf; 431 char *out = buf; 432 433 while (*cp) { 434 if (*cp == bs) { 435 if (out == buf) { 436 cp++; 437 continue; 438 } else { 439 cp++; 440 out--; 441 } 442 } else { 443 *out++ = *cp++; 444 } 445 446 } 447 *out = '\0'; 448 } 449 450 /* Make sure line is all seven bits */ 451 void 452 sevenbit(s) 453 char *s; 454 { 455 while (*s) 456 *s++ &= 0x7f; 457 } 458 459 /* Set hash algorithm type */ 460 char * 461 skey_set_algorithm(new) 462 char *new; 463 { 464 int i; 465 466 for (i = 0; i < SKEY_ALGORITH_LAST; i++) { 467 if (strcmp(new, skey_algorithm_table[i].name) == 0) { 468 skey_hash_type = i; 469 return(new); 470 } 471 } 472 473 return(NULL); 474 } 475 476 /* Get current hash type */ 477 const char * 478 skey_get_algorithm() 479 { 480 return(skey_algorithm_table[skey_hash_type].name); 481 } 482 483 /* Turn echo on/off */ 484 static void 485 skey_echo(action) 486 int action; 487 { 488 static struct termios term; 489 static int echo = 0; 490 491 if (action == 0) { 492 /* Turn echo off */ 493 (void) tcgetattr(fileno(stdin), &term); 494 if ((echo = (term.c_lflag & ECHO))) { 495 term.c_lflag &= ~ECHO; 496 (void) tcsetattr(fileno(stdin), TCSAFLUSH|TCSASOFT, &term); 497 } 498 } else if (action && echo) { 499 /* Turn echo on */ 500 term.c_lflag |= ECHO; 501 (void) tcsetattr(fileno(stdin), TCSAFLUSH|TCSASOFT, &term); 502 echo = 0; 503 } 504 } 505 506 /* Convert string to lower case */ 507 static void 508 lowcase(s) 509 char *s; 510 { 511 char *p; 512 513 for (p = s; *p; p++) 514 if (isupper(*p)) 515 *p = tolower(*p); 516 } 517