1279377Simp/* OpenBSD S/Key (skeysubr.c)
2279377Simp *
3279377Simp * Authors:
4279377Simp *          Neil M. Haller <nmh@thumper.bellcore.com>
5279377Simp *          Philip R. Karn <karn@chicago.qualcomm.com>
6279377Simp *          John S. Walden <jsw@thumper.bellcore.com>
7279377Simp *          Scott Chasin <chasin@crimelab.com>
8279377Simp *          Todd C. Miller <millert@openbsd.org>
9279377Simp *
10279377Simp * S/Key misc routines.
11279377Simp *
12279377Simp * $OpenBSD: skeysubr.c,v 1.36 2023/03/08 04:43:05 guenther Exp $
13279377Simp */
14279377Simp
15279377Simp#include <stdio.h>
16279377Simp#include <stdlib.h>
17279377Simp#include <string.h>
18279377Simp#include <ctype.h>
19279377Simp#include <signal.h>
20279377Simp#include <termios.h>
21279377Simp#include <unistd.h>
22279377Simp#include <md5.h>
23279377Simp#include <sha1.h>
24279377Simp#include <rmd160.h>
25279377Simp
26279377Simp#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
33static void keycrunch_md5(char *, char *, size_t);
34static void keycrunch_sha1(char *, char *, size_t);
35static void keycrunch_rmd160(char *, char *, size_t);
36static void skey_echo(int);
37static void trapped(int);
38
39/* Current hash type (index into skey_algorithm_table array) */
40static int skey_hash_type = SKEY_HASH_DEFAULT;
41
42/*
43 * Hash types we support.
44 * Each has an associated keycrunch() and f() function.
45 */
46struct skey_algorithm_table {
47	const char *name;
48	void (*keycrunch)(char *, char *, size_t);
49};
50static 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 */
65int
66keycrunch(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
88static void
89keycrunch_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
106static void
107keycrunch_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
135static void
136keycrunch_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 */
158void
159f(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 */
165void
166rip(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) */
175char *
176readpass(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) */
204char *
205readskey(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 */
217static void
218trapped(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 */
232int
233atob8(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 */
258int
259btoa8(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 */
272int
273htoi(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 */
285char *
286skipspace(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 */
298void
299backspace(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 */
323void
324sevenbit(char *s)
325{
326	while (*s)
327		*s++ &= 0x7f;
328}
329
330/* Set hash algorithm type */
331char *
332skey_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 */
347const char *
348skey_get_algorithm(void)
349{
350	return(skey_algorithm_table[skey_hash_type].name);
351}
352
353/* Turn echo on/off */
354static void
355skey_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