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