diff options
Diffstat (limited to 'src/file2key.c')
-rw-r--r-- | src/file2key.c | 324 |
1 files changed, 210 insertions, 114 deletions
diff --git a/src/file2key.c b/src/file2key.c index 99347d7..4288ffc 100644 --- a/src/file2key.c +++ b/src/file2key.c @@ -16,171 +16,267 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "keccak.h" - #include <stdio.h> -#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> -#include <stddef.h> #include <string.h> #include <stdlib.h> +#include <fcntl.h> #include <passphrase.h> +#include <libkeccak.h> + /** * Random string created with `dd if=/dev/random bs=1024 count=1 | tr -d -c a-zA-Z0-9` - * - * DO NOT EDIT! */ #define STATIC_SALT "5EbppWrYxMuBKQmbDz8rOCVCONsSLas74qrjMLTiJqsYWcTePNeshVXcmAWGkh88VeFh" +/** + * Prompt string that tells you to enter your passphrase + */ +#ifndef PASSPHRASE_PROMPT_STRING +# define PASSPHRASE_PROMPT_STRING "[file2key] Enter passphrase: " +# warning: you should personalise PASSPHRASE_PROMPT_STRING. +#endif + +/** + * The rate parameter for the Keccak sponge when hashing master passphrase + */ +#ifndef PASSPHRASE_KECCAK_RATE +# define PASSPHRASE_KECCAK_RATE 576 +#endif + +/** + * The capacity parameter for the Keccak sponge when hashing master passphrase + */ +#ifndef PASSPHRASE_KECCAK_CAPACITY +# define PASSPHRASE_KECCAK_CAPACITY 1024 +#endif + +/** + * The output parameter for the Keccak sponge when hashing master passphrase + */ +#ifndef PASSPHRASE_KECCAK_OUTPUT +# define PASSPHRASE_KECCAK_OUTPUT 32 +#endif + +/** + * The number of times to squeeze the master passphrase + */ +#ifndef PASSPHRASE_KECCAK_SQUEEZES +# define PASSPHRASE_KECCAK_SQUEEZES 10000 +#endif + + +/** + * Map from hexadecimal to colour-coded hexadecimal + */ const char* const COLOUR_HEX[] = { - "\033[31m0", - "\033[31m1", - "\033[31m2", - "\033[31m3", - "\033[31m4", - "\033[32m5", - "\033[32m6", - "\033[32m7", - "\033[32m8", - "\033[32m9", - "\033[34ma", - "\033[34mb", - "\033[34mc", - "\033[34md", - "\033[34me", - "\033[34mf", + ['0'] = "\033[31m0", + ['1'] = "\033[31m1", + ['2'] = "\033[31m2", + ['3'] = "\033[31m3", + ['4'] = "\033[31m4", + ['5'] = "\033[32m5", + ['6'] = "\033[32m6", + ['7'] = "\033[32m7", + ['8'] = "\033[32m8", + ['9'] = "\033[32m9", + ['a'] = "\033[34ma", + ['b'] = "\033[34mb", + ['c'] = "\033[34mc", + ['d'] = "\033[34md", + ['e'] = "\033[34me", + ['f'] = "\033[34mf", }; -int main(int argc, char** argv) +#define USER_ERROR(string) \ + (fprintf(stderr, "%s: %s.\n", execname, string), 1) + +#define tt(expr) if ((r = (expr))) goto fail + +#define t(expr) if (expr) goto pfail + + + +/** + * `argv[0]` from `main` + */ +static char* execname; + + + +/** + * Ask the user for the passphrase + * + * @param passphrase Output parameter for the passphrase + * @return Zero on success, an appropriate exit value on error + */ +static int get_passphrase(char** passphrase) { - int is_echo_disabled = 0; - int is_hash_initialised = 0; + passphrase_disable_echo(); + fprintf(stderr, "%s", PASSPHRASE_PROMPT_STRING); + fflush(stderr); + *passphrase = passphrase_read(); + if (*passphrase == NULL) + perror(execname); + passphrase_reenable_echo(); + return *passphrase ? 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) +{ +#define SQUEEZES PASSPHRASE_KECCAK_SQUEEZES + libkeccak_spec_t spec; + libkeccak_state_t 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) || (SQUEEZES <= 0)) + return USER_ERROR("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"); + + if (libkeccak_state_initialise(&state, &spec)) + return perror(execname), 2; + + if (libkeccak_digest(&state, passphrase, strlen(passphrase), 0, NULL, SQUEEZES == 1 ? hashsum : NULL)) + return perror(execname), libkeccak_state_destroy(&state), 2; + if (SQUEEZES > 2) libkeccak_fast_squeeze(&state, SQUEEZES - 2); + if (SQUEEZES > 1) libkeccak_squeeze(&state, hashsum); + + libkeccak_state_destroy(&state); - FILE* file = NULL; + libkeccak_behex_lower(hexsum, hashsum, sizeof(hashsum) / sizeof(char)); + fprintf(stderr, "%s: passphrase hash: ", execname); + 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; +#undef SQUEEZES +} + + +/** + * Here we go! + * + * @param argc The number of command line argumnets + * @param argv Command line argumnets + * @return Zero on success, 1 on user error, 2 on system error + */ +int main(int argc, char** argv) +{ + libkeccak_generalised_spec_t gspec; + libkeccak_spec_t spec; + libkeccak_state_t state; char* passphrase = NULL; - size_t passphrase_n; - char* hash; + char* hash = NULL; + size_t hash_size = 0; + size_t hash_ptr = 0; char* data = NULL; - size_t ptr; - size_t blksize; - size_t got; + size_t data_size = 0; + size_t data_ptr = 0; + size_t blksize = 4096; + int r, fd = -1; struct stat attr; - if ((argc != 2) || (argv[1][0] == '-')) - { - printf("USAGE: %s FILE\n", *argv); - return 0; - } + execname = *argv; - if (stat(argv[1], &attr) < 0) - goto pfail; - if (S_ISREG(attr.st_mode) == 0) + if ((argc != 2) || (argv[1][0] == '-')) { - fprintf(stderr, "%s: input file is not a regular file\n", *argv); - goto fail; + fprintf(stderr, "USAGE: %s FILE\n", execname); + return !!strcmp(argv[1], "--help"); } - if (attr.st_size < 100 << 10) - fprintf(stderr, "%s: warning: input file is small (less than 100 KB)\n", *argv); - file = fopen(argv[1], "r"); - if (file == NULL) - goto pfail; - data = malloc((size_t)(attr.st_size) * sizeof(char)); - if (data == NULL) - goto pfail; + libkeccak_generalised_spec_initialise(&gspec); + libkeccak_degeneralise_spec(&gspec, &spec); + t (libkeccak_state_initialise(&state, &spec)); - blksize = attr.st_blksize ? (size_t)(attr.st_blksize) : (size_t)(8 << 10); + tt (get_passphrase(&passphrase)); + tt (hash_passphrase(passphrase)); - for (ptr = 0; ptr < (size_t)(attr.st_size); ptr += got) - { - got = fread(data + ptr, 1, blksize, file); - if (got < blksize) - { - if (ferror(file)) - goto pfail; - break; - } - } + t (libkeccak_update(&state, passphrase, strlen(passphrase))); - fclose(file), file = NULL; + passphrase_wipe(passphrase, strlen(passphrase)); + free(passphrase), passphrase = NULL; - passphrase_disable_echo(), is_echo_disabled = 1; - fprintf(stderr, "[%s] Enter passphrase: ", *argv); - fflush(stderr); - passphrase = passphrase_read(); - if (passphrase == NULL) - goto pfail; - passphrase_n = strlen(passphrase); - passphrase_reenable_echo(), is_echo_disabled = 0; + t ((fd = open(argv[1], O_RDONLY), fd < 0)); + if (fstat(fd, &attr) == 0) + if (attr.st_blksize > 0) + blksize = (size_t)(attr.st_blksize); - initialise(), is_hash_initialised = 1; + hash_size = (size_t)(spec.output / 8); + t ((hash = malloc(hash_size), hash == NULL)); + t ((data = malloc(blksize), data == NULL)); - update(passphrase, passphrase_n); - passphrase_wipe(passphrase, passphrase_n); - free(passphrase), passphrase = NULL; + t (libkeccak_digest(&state, STATIC_SALT, strlen(STATIC_SALT), 0, NULL, hash)); - for (ptr = 0; ptr < (size_t)(attr.st_size); ptr += 72) + for (;;) { - size_t i, n = (size_t)(attr.st_size) - ptr; - if (n > 72) - n = 72; + size_t start; + ssize_t n; - if (ptr == 0) + if (hash_ptr == hash_size) + libkeccak_squeeze(&state, hash), hash_ptr = 0; + + if (data_ptr == data_size) { - char* phash = malloc((20 * 6) + 1 * sizeof(char)); - if (phash == NULL) - goto pfail; - hash = digest(STATIC_SALT, strlen(STATIC_SALT)); - - for (i = 0; i < 10; i++) - { - memcpy(phash + (i * 2 + 0) * 6, COLOUR_HEX[((unsigned char)(hash[i]) >> 4) & 15], 6 * sizeof(char)); - memcpy(phash + (i * 2 + 1) * 6, COLOUR_HEX[((unsigned char)(hash[i]) >> 0) & 15], 6 * sizeof(char)); - } - phash[20 * 6] = '\0'; - - fprintf(stderr, "%s: passphrase hash: %s\033[00m\n", *argv, phash); - free(phash); + n = read(fd, data, blksize); + t (n < 0); + if (n == 0) + break; + data_size = (size_t)n; } - else - hash = squeeze(); - for (i = 0; i < n; i++) - *(hash + i) ^= *(data + ptr + i); + start = data_ptr; + while ((hash_ptr < hash_size) && (data_ptr < data_size)) + data[data_ptr++] ^= hash[hash_ptr++]; - if (fwrite(hash, 1, n, stdout) < n) - goto pfail; + while (start < data_ptr) + { + n = write(STDOUT_FILENO, data + start, data_ptr - start); + t (n <= 0); + start += (size_t)n; + } } - free(data), data = NULL; - dispose(), is_hash_initialised = 0; - return 0; + r = 0; + goto done; pfail: + r = 2; perror(*argv); fail: - if (file) - fclose(file); - if (data) - free(data); - if (is_echo_disabled) - passphrase_reenable_echo(); if (passphrase) - { - passphrase_wipe(passphrase, passphrase_n); - free(passphrase); - } - if (is_hash_initialised) - dispose(); - return 1; + passphrase_wipe(passphrase, strlen(passphrase)); + free(passphrase); + done: + libkeccak_state_destroy(&state); + free(data); + free(hash); + if (fd >= 0) + close(fd); + return r; } |