Encrypt the private key.

pull/2/head
Christopher Wellons 2017-03-03 18:10:30 -05:00
parent 6c9cb4e14a
commit ebd34b593a
1 changed files with 186 additions and 24 deletions

210
enchive.c
View File

@ -3,6 +3,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#define OPTPARSE_IMPLEMENTATION
#include "docs.h"
@ -12,6 +13,11 @@
int curve25519_donna(u8 *p, const u8 *s, const u8 *b);
/* Global options. */
static char *global_random_device = "/dev/urandom";
static char *global_pubkey = 0;
static char *global_seckey = 0;
static struct {
char *name;
FILE *file;
@ -48,8 +54,24 @@ fatal(const char *fmt, ...)
exit(EXIT_FAILURE);
}
static void
get_passphrase_dumb(char *buf, size_t len, char *prompt)
{
size_t passlen;
fprintf(stderr, "warning: reading passphrase from stdin with echo\n");
fputs(prompt, stderr);
fflush(stderr);
if (!fgets(buf, len, stdin))
fatal("could not read passphrase");
passlen = strlen(buf);
if (buf[passlen - 1] < ' ')
buf[passlen - 1] = 0;
}
#if defined(__unix__) || defined(__APPLE__)
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
static FILE *
secure_creat(char *file)
@ -59,6 +81,36 @@ secure_creat(char *file)
return 0;
return fdopen(fd, "wb");
}
static void
get_passphrase(char *buf, size_t len, char *prompt)
{
int tty = open("/dev/tty", O_RDWR);
if (tty == -1) {
get_passphrase_dumb(buf, len, prompt);
} else {
char newline = '\n';
size_t i = 0;
struct termios old, new;
write(tty, prompt, strlen(prompt));
tcgetattr(tty, &old);
new = old;
new.c_lflag &= ~ECHO;
tcsetattr(tty, TCSANOW, &new);
errno = 0;
while (i < len - 1 && read(tty, buf + i, 1) == 1) {
if (buf[i] == '\n' || buf[i] == '\r')
break;
i++;
}
buf[i] = 0;
tcsetattr(tty, TCSANOW, &old);
write(tty, &newline, 1);
close(tty);
if (errno)
fatal("could not read passphrase from /dev/tty");
}
}
#else
/* fallback to standard open */
@ -67,12 +119,30 @@ secure_creat(char *file)
{
return fopen(file, "wb");
}
static void
get_passphrase(char *buf, size_t len, char *prompt)
{
get_passphrase_dumb(buf, len, prompt);
}
#endif
/* Global options. */
static char *global_random_device = "/dev/urandom";
static char *global_pubkey = 0;
static char *global_seckey = 0;
#define KEY_DERIVE_ITERATIONS 0x400000ul
static void
key_derive(char *key, u8 *buf)
{
unsigned long i;
SHA256_CTX ctx[1];
sha256_init(ctx);
sha256_final(ctx, buf);
for (i = 0; i < KEY_DERIVE_ITERATIONS; i++) {
sha256_init(ctx);
sha256_update(ctx, (void *)key, strlen(key));
sha256_update(ctx, buf, sizeof(buf));
sha256_final(ctx, buf);
}
}
static void
secure_entropy(void *buf, size_t len)
@ -156,7 +226,7 @@ symmetric_decrypt(FILE *in, FILE *out, u8 *key, u8 *iv)
chacha_ivsetup(ctx, iv);
sha256_init(hash);
/* Always keep SHA224_BLOCK_SIZE bytes in the buffer. */
/* Always keep SHA256_BLOCK_SIZE bytes in the buffer. */
if (!(fread(buffer[0], SHA256_BLOCK_SIZE, 1, in))) {
if (ferror(in))
fatal("cannot read ciphertext file");
@ -177,7 +247,7 @@ symmetric_decrypt(FILE *in, FILE *out, u8 *key, u8 *iv)
if (!fwrite(buffer[1], z, 1, out))
fatal("error writing plaintext file");
/* Move last SHA224_BLOCK_SIZE bytes to the front. */
/* Move last SHA256_BLOCK_SIZE bytes to the front. */
memmove(buffer[0], buffer[0] + z, SHA256_BLOCK_SIZE);
if (z < sizeof(buffer[0]) - SHA256_BLOCK_SIZE)
@ -232,7 +302,69 @@ default_secfile(void)
}
static void
load_key(char *file, u8 *key)
write_pubkey(char *file, u8 *key)
{
FILE *f = fopen(file, "wb");
if (!f)
fatal("failed to open key file for writing -- %s", file);
cleanup_register(f, file);
if (!fwrite(key, 32, 1, f))
fatal("failed to write key file -- %s", file);
if (fclose(f))
fatal("failed to flush key file -- %s", file);
}
static void
write_seckey(char *file, u8 *seckey, int encrypt)
{
FILE *secfile;
chacha_ctx cha[1];
SHA256_CTX sha[1];
u8 buf[8 + 24 + 32] = {0};
u8 key[32];
if (encrypt) {
char pass[2][256];
get_passphrase(pass[0], sizeof(pass[0]),
"passphrase (empty for none): ");
get_passphrase(pass[1], sizeof(pass[0]),
"passphrase (repeat): ");
if (strcmp(pass[0], pass[1]) != 0)
fatal("passphrase don't match");
if (!pass[0][0]) {
encrypt = 0;
} else {
key_derive(pass[0], key);
sha256_init(sha);
sha256_update(sha, key, 32);
sha256_final(sha, buf + 8);
secure_entropy(buf, 8);
}
}
if (encrypt) {
chacha_keysetup(cha, key, 256);
chacha_ivsetup(cha, buf);
chacha_encrypt_bytes(cha, seckey, buf + 32, 32);
} else {
memcpy(buf + 32, seckey, 32);
}
secfile = secure_creat(file);
if (!secfile)
fatal("failed to open key file for writing -- %s", file);
cleanup_register(secfile, file);
if (!fwrite(buf, sizeof(buf), 1, secfile))
fatal("failed to write key file -- %s", file);
if (fclose(secfile))
fatal("failed to flush key file -- %s", file);
}
static void
load_pubkey(char *file, u8 *key)
{
FILE *f = fopen(file, "rb");
if (!f)
@ -243,22 +375,43 @@ load_key(char *file, u8 *key)
}
static void
write_key(char *file, const u8 *key, int clobber)
load_seckey(char *file, u8 *seckey)
{
FILE *f;
FILE *secfile;
chacha_ctx cha[1];
SHA256_CTX sha[1];
u8 buf[8 + 24 + 32];
u8 empty[8] = {0};
u8 keyhash[SHA256_BLOCK_SIZE];
u8 key[32];
if (!clobber && fopen(file, "r"))
fatal("operation would clobber %s", file);
f = secure_creat(file);
if (!f)
fatal("failed to open key file for writing -- %s", file);
cleanup_register(f, file);
if (!fwrite(key, 32, 1, f))
fatal("failed to write key file -- %s", file);
if (fclose(f))
fatal("failed to flush key file -- %s", file);
secfile = fopen(file, "rb");
if (!secfile)
fatal("failed to open key file for reading -- %s", file);
if (!fread(buf, sizeof(buf), 1, secfile))
fatal("failed to read key file -- %s", file);
fclose(secfile);
if (memcmp(buf, empty, sizeof(empty)) != 0) {
char pass[256];
get_passphrase(pass, sizeof(pass), "passphrase: ");
key_derive(pass, key);
sha256_init(sha);
sha256_update(sha, key, 32);
sha256_final(sha, keyhash);
if (memcmp(keyhash, buf + 8, 24) != 0)
fatal("wrong passphrase");
chacha_keysetup(cha, key, 256);
chacha_ivsetup(cha, buf);
chacha_encrypt_bytes(cha, buf + 32, seckey, 32);
} else {
memcpy(seckey, buf + 32, 32);
}
}
enum command {
COMMAND_UNKNOWN = -2,
COMMAND_AMBIGUOUS = -1,
@ -293,6 +446,7 @@ command_keygen(struct optparse *options)
{
static const struct optparse_long keygen[] = {
{"force", 'f', OPTPARSE_NONE},
{"plain", 'u', OPTPARSE_NONE},
{0}
};
@ -301,6 +455,7 @@ command_keygen(struct optparse *options)
u8 public[32];
u8 secret[32];
int clobber = 0;
int encrypt = 1;
int option;
while ((option = optparse_long(options, keygen, 0)) != -1) {
@ -308,6 +463,9 @@ command_keygen(struct optparse *options)
case 'f':
clobber = 1;
break;
case 'u':
encrypt = 0;
break;
default:
fatal("%s", options->errmsg);
}
@ -315,13 +473,17 @@ command_keygen(struct optparse *options)
if (!pubfile)
pubfile = default_pubfile();
if (!clobber && fopen(pubfile, "r"))
fatal("operation would clobber %s", pubfile);
if (!secfile)
secfile = default_secfile();
if (!clobber && fopen(secfile, "r"))
fatal("operation would clobber %s", secfile);
generate_secret(secret);
compute_public(public, secret);
write_key(pubfile, public, clobber);
write_key(secfile, secret, clobber);
write_pubkey(pubfile, public);
write_seckey(secfile, secret, encrypt);
}
static void
@ -352,7 +514,7 @@ command_archive(struct optparse *options)
if (!pubfile)
pubfile = default_pubfile();
load_key(pubfile, public);
load_pubkey(pubfile, public);
infile = optparse_arg(options);
if (infile) {
@ -416,7 +578,7 @@ command_extract(struct optparse *options)
if (!secfile)
secfile = default_secfile();
load_key(secfile, secret);
load_seckey(secfile, secret);
infile = optparse_arg(options);
if (infile) {
@ -517,7 +679,7 @@ main(int argc, char **argv)
options->permute = 0;
(void)argc;
while ((option = optparse_long(options, global, 0)) != -1) {
while ((option = optparse_long(options, global, 0)) != -1) {
switch (option) {
case 'r':
global_random_device = options->optarg;