diff options
Diffstat (limited to 'file2key.c')
-rw-r--r-- | file2key.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/file2key.c b/file2key.c new file mode 100644 index 0000000..77b9fc3 --- /dev/null +++ b/file2key.c @@ -0,0 +1,251 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <passphrase.h> +#include <libkeccak.h> + +#include "config.h" +#include "settings.h" + + +/** + * Map from hexadecimal to colour-coded hexadecimal + */ +const char *const COLOUR_HEX[] = { + ['0'] = "\033[31m0", + ['1'] = "\033[31m1", + ['2'] = "\033[32m2", + ['3'] = "\033[33m3", + ['4'] = "\033[32m4", + ['5'] = "\033[33m5", + ['6'] = "\033[31m6", + ['7'] = "\033[34m7", + ['8'] = "\033[34m8", + ['9'] = "\033[34m9", + ['a'] = "\033[32ma", + ['b'] = "\033[31mb", + ['c'] = "\033[33mc", + ['d'] = "\033[34md", + ['e'] = "\033[33me", + ['f'] = "\033[32mf", +}; + + + +/** + * `argv[0]` from `main` + */ +static char *argv0; + + + +/** + * Ask the user for the passphrase + * + * @param passphrasep Output parameter for the passphrase + * @return Zero on success, an appropriate exit value on error + */ +static int +get_passphrase(char **passphrasep) +{ + int ttyfd; + ttyfd = open("/dev/tty", O_RDONLY); + if (ttyfd < 0) { + perror(argv0); + return 2; + } + passphrase_disable_echo1(ttyfd); + fprintf(stderr, "%s", PASSPHRASE_PROMPT_STRING); + fflush(stderr); + *passphrasep = passphrase_read2(ttyfd, PASSPHRASE_READ_EXISTING); + if (!*passphrasep) + perror(argv0); + passphrase_reenable_echo1(ttyfd); + close(ttyfd); + return *passphrasep ? 0 : 2; +} + + +/** + * Hash, and display, passphrase so to hint the + * user whether it as typed correctly or not + * + * @param passphrase The passphrase + * @return Zero on success, an appropriate exit value on error + */ +static int +hash_passphrase(const char *passphrase) +{ + struct libkeccak_spec spec; + struct libkeccak_state state; + char hashsum[PASSPHRASE_KECCAK_OUTPUT / 8]; + char hexsum[PASSPHRASE_KECCAK_OUTPUT / 4 + 1]; + size_t i, n; + + spec.bitrate = PASSPHRASE_KECCAK_RATE; + spec.capacity = PASSPHRASE_KECCAK_CAPACITY; + spec.output = PASSPHRASE_KECCAK_OUTPUT; + + if (libkeccak_spec_check(&spec) || PASSPHRASE_KECCAK_SQUEEZES <= 0) { + fprintf(stderr, "%s: bad passhprase hashing parameters, please recompile file2key with with " + "proper values on PASSPHRASE_KECCAK_RATE, PASSPHRASE_KECCAK_CAPACITY, " + "PASSPHRASE_KECCAK_OUTPUT and PASSPHRASE_KECCAK_SQUEEZES", argv0); + return 1; + } + + if (libkeccak_state_initialise(&state, &spec)) { + perror(argv0); + return 2; + } + + if (libkeccak_digest(&state, passphrase, strlen(passphrase), 0, NULL, PASSPHRASE_KECCAK_SQUEEZES == 1 ? hashsum : NULL)) { + perror(argv0); + libkeccak_state_destroy(&state); + return 2; + } + if (PASSPHRASE_KECCAK_SQUEEZES > 2) + libkeccak_fast_squeeze(&state, PASSPHRASE_KECCAK_SQUEEZES - 2); + if (PASSPHRASE_KECCAK_SQUEEZES > 1) + libkeccak_squeeze(&state, hashsum); + + libkeccak_state_destroy(&state); + + libkeccak_behex_lower(hexsum, hashsum, sizeof(hashsum) / sizeof(char)); + fprintf(stderr, "%s: passphrase hash: ", argv0); + for (i = 0, n = strlen(hexsum); i < n; i++) + fprintf(stderr, "%s", COLOUR_HEX[(unsigned char)hexsum[i]]); + fprintf(stderr, "\033[00m\n"); + + return 0; +} + + +/** + * @return 0: success + * 1: user error + * 2: on system error + */ +int +main(int argc, char *argv[]) +{ + struct libkeccak_generalised_spec gspec; + struct libkeccak_spec spec; + struct libkeccak_state state; + char *passphrase = NULL; + char *hash = NULL; + size_t hash_size = 0; + size_t hash_ptr = 0; + char *data = NULL; + size_t data_size = 0; + size_t data_ptr = 0; + size_t blksize = 4096; + int r, fd = -1; + struct stat attr; + size_t start; + ssize_t n; + + argv0 = *argv++, argc--; + if (argc > 0 && argv[0][0] == '-') { + if (argv[0][1] == '-' && !argv[0][2]) + argv++, argc--; + else if (argv[0][1]) + goto usage; + } + if (argc > 1) { + usage: + fprintf(stderr, "usage: %s [file]\n", argv0); + return 1; + } + + if (argc > 0 && strcmp(argv[0], "-")) { + fd = open(argv[0], O_RDONLY); + if (fd < 0) + goto pfail; + } else { + fd = STDIN_FILENO; + } + if (isatty(fd)) { + fprintf(stderr, "%s: input file must not be a terminal\n", argv0); + close(fd); + return 1; + } + if (fstat(fd, &attr) == 0) + if (attr.st_blksize > 0) + blksize = (size_t)attr.st_blksize; + + libkeccak_generalised_spec_initialise(&gspec); + libkeccak_degeneralise_spec(&gspec, &spec); + if (libkeccak_state_initialise(&state, &spec)) + goto pfail; + if ((r = get_passphrase(&passphrase))) + goto fail; + if ((r = hash_passphrase(passphrase))) + goto fail; + if (libkeccak_update(&state, passphrase, strlen(passphrase))) + goto pfail; + passphrase_wipe(passphrase, strlen(passphrase)); + free(passphrase); + passphrase = NULL; + + hash_size = (size_t)(spec.output / 8); + if (!(hash = malloc(hash_size))) + goto pfail; + if (!(data = malloc(blksize))) + goto pfail; + + if (libkeccak_digest(&state, KEY_PEPPER, strlen(KEY_PEPPER), 0, NULL, hash)) + goto pfail; + + for (;;) { + if (hash_ptr == hash_size) { + libkeccak_squeeze(&state, hash); + hash_ptr = 0; + } + + if (data_ptr == data_size) { + n = read(fd, data, blksize); + if (n <= 0) { + if (!n) + break; + goto pfail; + } + data_size = (size_t)n; + data_ptr = 0; + } + + start = data_ptr; + while (hash_ptr < hash_size && data_ptr < data_size) + data[data_ptr++] ^= hash[hash_ptr++]; + + while (start < data_ptr) { + n = write(STDOUT_FILENO, &data[start], data_ptr - start); + if (n <= 0) + goto pfail; + start += (size_t)n; + } + } + + r = 0; + goto done; + +pfail: + r = 2; + perror(*argv); +fail: + if (passphrase) { + passphrase_wipe(passphrase, strlen(passphrase)); + free(passphrase); + } +done: + libkeccak_state_destroy(&state); + free(data); + free(hash); + if (argc > 0 && fd >= 0) + close(fd); + return r; +} |