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