/**
* file2key – A simple command that generates a key from a file and a passphrase
*
* Copyright © 2014 Mattias Andrée (maandree@member.fsf.org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
/**
* Random string created with `dd if=/dev/random bs=1024 count=1 | tr -d -c a-zA-Z0-9`
*/
#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[] =
{
['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",
};
#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)
{
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);
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;
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;
execname = *argv;
if ((argc != 2) || (argv[1][0] == '-'))
{
fprintf(stderr, "USAGE: %s FILE\n", execname);
return !!strcmp(argv[1], "--help");
}
libkeccak_generalised_spec_initialise(&gspec);
libkeccak_degeneralise_spec(&gspec, &spec);
t (libkeccak_state_initialise(&state, &spec));
tt (get_passphrase(&passphrase));
tt (hash_passphrase(passphrase));
t (libkeccak_update(&state, passphrase, strlen(passphrase)));
passphrase_wipe(passphrase, strlen(passphrase));
free(passphrase), passphrase = NULL;
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);
hash_size = (size_t)(spec.output / 8);
t ((hash = malloc(hash_size), hash == NULL));
t ((data = malloc(blksize), data == NULL));
t (libkeccak_digest(&state, STATIC_SALT, strlen(STATIC_SALT), 0, NULL, hash));
for (;;)
{
size_t start;
ssize_t n;
if (hash_ptr == hash_size)
libkeccak_squeeze(&state, hash), hash_ptr = 0;
if (data_ptr == data_size)
{
n = read(fd, data, blksize);
t (n < 0);
if (n == 0)
break;
data_size = (size_t)n;
}
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);
t (n <= 0);
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 (fd >= 0)
close(fd);
return r;
}