aboutsummaryrefslogblamecommitdiffstats
path: root/file2key.c
blob: 77b9fc3dba4665420c60b1c336c2cb35b879bd7a (plain) (tree)


























































































































































































































































                                                                                                                                  
/* 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;
}