/* * TEA Total Copyright (c) Alex Holden 2000, 2001. * * This code is public domain; you can do whatever you want with it, though I * would prefer you to contribute any bug fixes back to me if possible. * This software comes with NO WARRANTY WHATSOEVER, not even the implied * warranties of merchantability and fitness for a particular purpose. * * readkey.c: Handles the loading of a key file. */ #include #include #include #include "teatotal.h" #include "readkey.h" #ifdef BUILD_PPKEYS #include "md5.h" #include "btea.h" /* * Generates a key from a password. */ void read_pass(teastate *state, u32 *key, int in, int confirm) { struct MD5Context md5state; char pwbuf1[64], pwbuf2[64], *pass = NULL; struct termios ti; int i, n, nn, passlen = 0; /* Check if the password was specified on the command line */ if(in) { if(state->inkeypass) pass = state->inkeypass; } else { if(state->outkeypass) pass = state->outkeypass; } /* If not, read it in from the console instead */ if(!pass) { /* Write a message requesting the user to enter the password */ safe_write(STDERR_FILENO, READKEY_REQ_MSG, (sizeof(READKEY_REQ_MSG) - 1)); /* Check that this is being run from a terminal */ if(isatty(STDIN_FILENO)) { /* Save the current terminal attributes */ tcgetattr(STDIN_FILENO, &origtermattr); /* Register a cleanup function to restore the * attributes on exit */ atexit(restore_termattr); /* Clear ECHO on STDIN so that the password isn't * echoed to the console, and flush the input buffer */ tcgetattr(STDIN_FILENO, &ti); ti.c_lflag &= ~(ECHO); tcsetattr(STDIN_FILENO, TCSAFLUSH, &ti); } /* Read the password */ n = read(STDIN_FILENO, pwbuf1, 64); /* Write a message requesting user to confirm the password */ if(confirm) { /* Print newline */ #ifdef DOS_LINE_ENDINGS safe_write(STDERR_FILENO, "\r\n", 2); #else safe_write(STDERR_FILENO, "\n", 1); #endif safe_write(STDERR_FILENO, READKEY_REQ_MSG2, (sizeof(READKEY_REQ_MSG2) - 1)); /* Read the password */ nn = read(STDIN_FILENO, pwbuf2, 64); if((n != nn) || memcmp(pwbuf1, pwbuf2, n)) die("Passwords don't match"); } /* Turn echo back on */ restore_termattr(); /* Print newline */ #ifdef DOS_LINE_ENDINGS safe_write(STDERR_FILENO, "\r\n", 2); #else safe_write(STDERR_FILENO, "\n", 1); #endif /* Count the password length up to the \n or \r */ pass = pwbuf1; for(i = 0; i < n; i++) { if(pass[i] && (pass[i] != '\n') && (pass[i] != '\r')) passlen++; else break; } } else passlen = strlen(pass); /* Initialise the MD5 state structure */ MD5Init(&md5state); /* This probably wouldn't be a great way to do this if people could * read the result (as in a crypt() implementation), but we're really * just using the MD5 digest as a way to turn an arbitrary length * string into a fixed length 128 bit key. I'm hoping the multiple * iterations will reduce the likelyhood of producing a weak key due to * the hash not having enough data to work with. */ for(i = KEY_MD5_ITERATIONS; i; i--) MD5Update(&md5state, (md5byte *)pass, passlen); /* Finalise the MD5 engine and retrieve the key */ MD5Final((md5byte *)key, &md5state); #if BYTE_ORDER == LITTLE_ENDIAN for(i = 0; i < 4; i++) key[i] = swapu32(key[i]); #endif } /* * Asks for the key password and uses the hash of it to decrypt the key. */ static void decrypt_key(teastate *state, u8 *b) { u32 key[4], *l = (u32 *) b; /* Get the key from the password */ read_pass(state, key, 1, 0); /* Decrypt the key */ #if BYTE_ORDER == LITTLE_ENDIAN btea(l, key, (20 / 4), 1, 1); #else btea(l, key, (20 / 4), 1, 0); #endif /* Check that the decryption succeeded */ if(*l != KEY_MAGIC) die("Incorrect password"); } #endif /* Reads the key file state->keyfile into state->k */ void read_key(teastate *state) { int fd; u8 binkey[20]; u32 *l; int a, i; int protected = 0; char *buf, *p; /* Allocate the key input buffer */ buf = safe_malloc(KEY_LENGTH); /* Check that a key file was specified */ if(!state->keyfile) die("No key file specified"); /* Open the key file */ if(!(fd = open(state->keyfile, O_RDONLY))) die("Key file open failed"); /* Read the key */ if(safe_read(fd, buf, KEY_LENGTH) < KEY_LENGTH) die("Error reading key file"); /* Close the key file */ close(fd); /* Check the magic number is valid */ if(memcmp(buf, KEY_HEADER, (sizeof(KEY_HEADER) - 1))) die("Not a key file"); /* Check that it has a known version number */ if(buf[sizeof(KEY_HEADER) - 1] == KEY_NOENC) protected = 0; else if(buf[sizeof(KEY_HEADER) - 1] == KEY_TEAENC) protected = 1; else die("Unknown key type"); /* Go through the buffer converting it to binary */ p = buf + sizeof(KEY_HEADER); for(i = 0; i < 20; i++) { if((a = hextoint(p)) == -1) die("Corrupt key"); binkey[i] = a; p += 2; } /* If the key was protected and we have support for decrypting it, * do so. If not, complain and exit. */ if(protected) { #ifdef BUILD_PPKEYS decrypt_key(state, binkey); #else die("Password protected key support is not available"); #endif } else { /* Check the magic number */ l = (u32 *)binkey; if(*l != KEY_MAGIC) die("Corrupt Key"); } /* Copy the decoded key, converting to local byte order */ l = (u32 *)&binkey[4]; for(i = 0; i < 4; i++) #if BYTE_ORDER == LITTLE_ENDIAN state->k[i] = swapu32(l[i]); #else state->k[i] = l[i]; #endif /* Free the key input buffer */ free(buf); }