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