xref: /openbsd-src/lib/libskey/skeysubr.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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