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