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