diff --git a/puer.c b/puer.c index 3f9880a..ba3afca 100644 --- a/puer.c +++ b/puer.c @@ -543,20 +543,57 @@ ssize_t passphrase_prompt(unsigned char *passphrase, size_t size, const char *pr return index - 1; } +ssize_t passphrase_file(char *passfile, unsigned char passphrase[], size_t size) { + int file = open(passfile, O_RDONLY); + + // Read until newline + size_t index = 0; + for (;;) { + if (index >= size) { + fprintf(stderr, "Passphrase too long, maximum size is %zu bytes\n", size - 1); + close(file); + return -1; + } + + ssize_t bytes_read = read(file, &passphrase[index], size - index); + if (bytes_read == -1) { + perror("Failed to read passphrase"); + close(file); + return -1; + } else if (bytes_read == 0) { + fprintf(stderr, "Unexpected EOF\n"); + close(file); + return -1; + } + + index += bytes_read; + if (passphrase[index-1] == '\n') { + // Got end of line + break; + } + } + + close(file); + + return index - 1; +} + void usage(char *name) { - fprintf(stderr, "Usage: %s -d | -e [-f]\n\n", name); + fprintf(stderr, "Usage: %s -d | -e [-f] [-p passfile]\n\n", name); fprintf(stderr, "-d Decrypt\n"); fprintf(stderr, "-e Encrypt\n"); fprintf(stderr, "-f Force output to terminal\n"); + fprintf(stderr, "-p Read passphrase from a file instead of the terminal.\n"); } int main(int argc, char *argv[]) { bool encrypting = false; bool decrypting = false; bool force = false; + char *passfile = NULL; int opt; - while ((opt = getopt(argc, argv, "def")) != -1) { + while ((opt = getopt(argc, argv, "defp:")) != -1) { switch (opt) { case 'd': decrypting = true; @@ -567,12 +604,20 @@ int main(int argc, char *argv[]) { case 'f': force = true; break; + case 'p': + passfile = optarg; + break; default: usage(argv[0]); exit(1); } } + if (optind != argc) { + usage(argv[0]); + exit(1); + } + if ((!encrypting && !decrypting) || (encrypting && decrypting)) { usage(argv[0]); exit(1); @@ -609,30 +654,40 @@ int main(int argc, char *argv[]) { // Read passphrase unsigned char passphrase[128]; - ssize_t passphrase_len = passphrase_prompt(passphrase, sizeof(passphrase), "passphrase: "); - if (passphrase_len == -1) { - explicit_bzero(passphrase, sizeof(passphrase)); - exit(1); - } - - if (encrypting) { - // Have the user confirm the passphrase if encrypting, to avoid losing data - unsigned char confirm[128]; - ssize_t confirm_len = passphrase_prompt(confirm, sizeof(confirm), "confirm passphrase: "); - if (confirm_len == -1) { + ssize_t passphrase_len; + if (passfile == NULL) { + // Read from terminal if no passfile specified + passphrase_len = passphrase_prompt(passphrase, sizeof(passphrase), "passphrase: "); + if (passphrase_len == -1) { explicit_bzero(passphrase, sizeof(passphrase)); - explicit_bzero(confirm, sizeof(confirm)); exit(1); } - if (confirm_len != passphrase_len || memcmp(passphrase, confirm, passphrase_len) != 0) { - fprintf(stderr, "Passphrases do not match\n"); - explicit_bzero(passphrase, sizeof(passphrase)); + if (encrypting) { + // Have the user confirm the passphrase if encrypting, to avoid losing data + unsigned char confirm[sizeof(passphrase)]; + ssize_t confirm_len = passphrase_prompt(confirm, sizeof(confirm), "confirm passphrase: "); + if (confirm_len == -1) { + explicit_bzero(passphrase, sizeof(passphrase)); + explicit_bzero(confirm, sizeof(confirm)); + exit(1); + } + + if (confirm_len != passphrase_len || memcmp(passphrase, confirm, passphrase_len) != 0) { + fprintf(stderr, "Passphrases do not match\n"); + explicit_bzero(passphrase, sizeof(passphrase)); + explicit_bzero(confirm, sizeof(confirm)); + exit(1); + } + explicit_bzero(confirm, sizeof(confirm)); + } + } else { + passphrase_len = passphrase_file(passfile, passphrase, sizeof(passphrase)); + if (passphrase_len == -1) { + explicit_bzero(passphrase, sizeof(passphrase)); exit(1); } - - explicit_bzero(confirm, sizeof(confirm)); } // Derive key