/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #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; }