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