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