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
39 static void f_md4(char *);
40 static void f_md5(char *);
41 static void f_sha1(char *);
42 /* static void f_rmd160(char *x); */
43 static int keycrunch_md4(char *, const char *, const char *);
44 static int keycrunch_md5(char *, const char *, const char *);
45 static int keycrunch_sha1(char *, const char *, const char *);
46 /* static int keycrunch_rmd160(char *, const char *, const char *); */
47 static void lowcase(char *);
48 static void skey_echo(int);
49 static void trapped(int) __dead;
50 static char *mkSeedPassword(const char *, const char *, size_t *);
51
52 /* Current hash type (index into skey_hash_types array) */
53 static 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
60 struct skey_algorithm_table {
61 const char *name;
62 int (*keycrunch)(char *, const char *, const char *);
63 void (*f)(char *);
64 };
65 static 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 */
keycrunch(char * result,const char * seed,const char * passwd)81 int 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
mkSeedPassword(const char * seed,const char * passwd,size_t * buflen)88 static 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
keycrunch_md4(char * result,const char * seed,const char * passwd)104 static 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
keycrunch_md5(char * result,const char * seed,const char * passwd)131 static 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
keycrunch_sha1(char * result,const char * seed,const char * passwd)158 static 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
198 static 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 */
f(char * x)228 void f(char *x)
229 {
230 skey_algorithm_table[skey_hash_type].f(x);
231 }
232
f_md4(char * x)233 static 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
f_md5(char * x)249 static 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
f_sha1(char * x)265 static 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
288 static 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 */
rip(char * buf)307 void 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) */
readpass(char * buf,int n)316 char *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) */
readskey(char * buf,int n)343 char *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*/
trapped(int sig)356 static 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 */
atob8(char * out,const char * in)371 int 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 */
btoa8(char * out,const char * in)396 int 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 */
htoi(int c)412 int 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 */
skipspace(const char * cp)424 const 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 */
backspace(char * buf)436 void 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 */
sevenbit(char * s)460 void sevenbit(char *s)
461 {
462 while (*s)
463 *s++ &= 0x7f;
464 }
465
466 /* Set hash algorithm type */
skey_set_algorithm(const char * new)467 const 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 */
skey_get_algorithm(void)482 const char *skey_get_algorithm(void)
483 {
484 return(skey_algorithm_table[skey_hash_type].name);
485 }
486
487 /* Turn echo on/off */
skey_echo(int action)488 static 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 */
lowcase(char * s)509 static 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