xref: /netbsd/lib/libskey/skeysubr.c (revision 6550d01e)
1 /*	$NetBSD: skeysubr.c,v 1.26 2006/10/28 15:35:28 agc Exp $	*/
2 
3 /* S/KEY v1.1b (skeysubr.c)
4  *
5  * Authors:
6  *          Neil M. Haller <nmh@thumper.bellcore.com>
7  *          Philip R. Karn <karn@chicago.qualcomm.com>
8  *          John S. Walden <jsw@thumper.bellcore.com>
9  *
10  * Modifications:
11  *          Scott Chasin <chasin@crimelab.com>
12  *          Todd C. Miller <Todd.Miller@courtesan.com>
13  *
14  * S/KEY misc routines.
15  */
16 
17 #include <sys/cdefs.h>
18 __RCSID("$NetBSD: skeysubr.c,v 1.26 2006/10/28 15:35:28 agc 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);
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 }
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 charaters 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()
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