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