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.35 2019/01/25 00:19:26 millert 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 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 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 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 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 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 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 * 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 * 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 /*ARGSUSED*/ 218 static void 219 trapped(int sig) 220 { 221 write(STDERR_FILENO, "^C\n", 3); 222 223 /* Turn on echo if necessary */ 224 skey_echo(1); 225 226 _exit(1); 227 } 228 229 /* 230 * Convert 16-byte hex-ascii string to 8-byte binary array 231 * Returns 0 on success, -1 on error 232 */ 233 int 234 atob8(char *out, char *in) 235 { 236 int i; 237 int val; 238 239 if (in == NULL || out == NULL) 240 return(-1); 241 242 for (i=0; i < 8; i++) { 243 if ((in = skipspace(in)) == NULL) 244 return(-1); 245 if ((val = htoi(*in++)) == -1) 246 return(-1); 247 *out = val << 4; 248 249 if ((in = skipspace(in)) == NULL) 250 return(-1); 251 if ((val = htoi(*in++)) == -1) 252 return(-1); 253 *out++ |= val; 254 } 255 return(0); 256 } 257 258 /* Convert 8-byte binary array to 16-byte hex-ascii string */ 259 int 260 btoa8(char *out, char *in) 261 { 262 if (in == NULL || out == NULL) 263 return(-1); 264 265 (void)snprintf(out, 17, "%02x%02x%02x%02x%02x%02x%02x%02x", 266 in[0] & 0xff, in[1] & 0xff, in[2] & 0xff, in[3] & 0xff, 267 in[4] & 0xff, in[5] & 0xff, in[6] & 0xff, in[7] & 0xff); 268 269 return(0); 270 } 271 272 /* Convert hex digit to binary integer */ 273 int 274 htoi(int c) 275 { 276 if ('0' <= c && c <= '9') 277 return(c - '0'); 278 if ('a' <= c && c <= 'f') 279 return(10 + c - 'a'); 280 if ('A' <= c && c <= 'F') 281 return(10 + c - 'A'); 282 return(-1); 283 } 284 285 /* Skip leading spaces from the string */ 286 char * 287 skipspace(char *cp) 288 { 289 while (*cp == ' ' || *cp == '\t') 290 cp++; 291 292 if (*cp == '\0') 293 return(NULL); 294 else 295 return(cp); 296 } 297 298 /* Remove backspaced over characters from the string */ 299 void 300 backspace(char *buf) 301 { 302 char bs = 0x8; 303 char *cp = buf; 304 char *out = buf; 305 306 while (*cp) { 307 if (*cp == bs) { 308 if (out == buf) { 309 cp++; 310 continue; 311 } else { 312 cp++; 313 out--; 314 } 315 } else { 316 *out++ = *cp++; 317 } 318 319 } 320 *out = '\0'; 321 } 322 323 /* Make sure line is all seven bits */ 324 void 325 sevenbit(char *s) 326 { 327 while (*s) 328 *s++ &= 0x7f; 329 } 330 331 /* Set hash algorithm type */ 332 char * 333 skey_set_algorithm(char *new) 334 { 335 int i; 336 337 for (i = 0; skey_algorithm_table[i].name; i++) { 338 if (strcmp(new, skey_algorithm_table[i].name) == 0) { 339 skey_hash_type = i; 340 return(new); 341 } 342 } 343 344 return(NULL); 345 } 346 347 /* Get current hash type */ 348 const char * 349 skey_get_algorithm(void) 350 { 351 return(skey_algorithm_table[skey_hash_type].name); 352 } 353 354 /* Turn echo on/off */ 355 static void 356 skey_echo(int action) 357 { 358 static struct termios term; 359 static int echo = 0; 360 361 if (action == 0) { 362 /* Turn echo off */ 363 (void) tcgetattr(fileno(stdin), &term); 364 if ((echo = (term.c_lflag & ECHO))) { 365 term.c_lflag &= ~ECHO; 366 (void) tcsetattr(fileno(stdin), TCSAFLUSH|TCSASOFT, &term); 367 } 368 } else if (action && echo) { 369 /* Turn echo on */ 370 term.c_lflag |= ECHO; 371 (void) tcsetattr(fileno(stdin), TCSAFLUSH|TCSASOFT, &term); 372 echo = 0; 373 } 374 } 375