diff options
Diffstat (limited to 'encpipe.c')
| -rw-r--r-- | encpipe.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/encpipe.c b/encpipe.c new file mode 100644 index 0000000..79500c6 --- /dev/null +++ b/encpipe.c @@ -0,0 +1,234 @@ +#include "encpipe_p.h" + +static struct option getopt_long_options[] = { + { "help", 0, NULL, 'h' }, { "decrypt", 0, NULL, 'd' }, + { "encrypt", 0, NULL, 'e' }, { "in", 1, NULL, 'i' }, + { "out", 1, NULL, 'o' }, { "p", 1, NULL, 'p' }, + { NULL, 0, NULL, 0 } +}; +static const char *getopt_options = "hdei:o:p:"; + +static void +usage(void) +{ + puts( + "Usage:\n\n" + "Encrypt: encpipe -e -p <password> [-i <inputfile>] [-o <outputfile>]\n" + "Decrypt: encpipe -d -p <password> [-i <inputfile>] [-o <outputfile>]"); + exit(0); +} + +static void +options_parse(Context *ctx, int argc, char *argv[]) +{ + int opt_flag; + int option_index = 0; + + ctx->encrypt = -1; + ctx->in = NULL; + ctx->out = NULL; + ctx->password = NULL; + optind = 0; +#ifdef _OPTRESET + optreset = 1; +#endif + while ((opt_flag = getopt_long(argc, argv, getopt_options, + getopt_long_options, &option_index)) != -1) { + switch (opt_flag) { + case 'd': + ctx->encrypt = 0; + break; + case 'e': + ctx->encrypt = 1; + break; + case 'i': + ctx->in = optarg; + break; + case 'o': + ctx->out = optarg; + break; + case 'p': + ctx->password = optarg; + break; + default: + usage(); + } + } + if (ctx->password == NULL || ctx->encrypt == -1) { + usage(); + } +} + +static int +file_open(const char *file, int create) +{ + int fd; + + if (file == NULL || (file[0] == '-' && file[1] == 0)) { + return create ? STDOUT_FILENO : STDIN_FILENO; + } + if (create) { + fd = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0644); + } else { + fd = open(file, O_RDONLY); + } + if (fd == -1) { + fprintf(stderr, "Unable to access [%s]: [%s]\n", file, strerror(errno)); + exit(1); + } + return fd; +} + +static void +derive_key(Context *ctx) +{ + static uint8_t master_key[hydro_pwhash_MASTERKEYBYTES] = { 0 }; + size_t password_len = strlen(ctx->password); + + if (hydro_pwhash_deterministic(ctx->key, sizeof ctx->key, ctx->password, + password_len, HYDRO_CONTEXT, master_key, + PWHASH_OPSLIMIT, PWHASH_MEMLIMIT, + PWHASH_THREADS) != 0) { + fprintf(stderr, "Password hashing failed\n"); + exit(1); + } + hydro_memzero(ctx->password, password_len); +} + +static int +stream_encrypt(Context *ctx) +{ + unsigned char *const chunk_size_p = ctx->buf; + unsigned char *const chunk = chunk_size_p + 4; + uint64_t chunk_id; + ssize_t max_chunk_size; + ssize_t chunk_size; + + assert(ctx->sizeof_buf > 4 + hydro_secretbox_HEADERBYTES); + max_chunk_size = ctx->sizeof_buf - 4 - hydro_secretbox_HEADERBYTES; + assert(max_chunk_size < 0x7fffffff); + chunk_id = 0; + while ((chunk_size = + safe_read_partial(ctx->fd_in, chunk, max_chunk_size)) >= 0) { + STORE32_LE(chunk_size_p, (uint32_t) chunk_size); + if (hydro_secretbox_encrypt(chunk, chunk, chunk_size, chunk_id, + HYDRO_CONTEXT, ctx->key) != 0) { + fprintf(stderr, "Encryption error\n"); + exit(1); + } + if (safe_write(ctx->fd_out, chunk_size_p, + 4 + hydro_secretbox_HEADERBYTES + (size_t) chunk_size, + -1) < 0) { + perror("write()"); + exit(1); + } + if (chunk_size == 0) { + break; + } + chunk_id++; + } + if (chunk_size < 0) { + perror("read()"); + exit(1); + } + return 0; +} + +static int +stream_decrypt(Context *ctx) +{ + unsigned char *const chunk_size_p = ctx->buf; + unsigned char *const chunk = chunk_size_p + 4; + uint64_t chunk_id; + ssize_t readnb; + ssize_t max_chunk_size; + ssize_t chunk_size; + + assert(ctx->sizeof_buf > 4 + hydro_secretbox_HEADERBYTES); + max_chunk_size = ctx->sizeof_buf - 4 - hydro_secretbox_HEADERBYTES; + assert(max_chunk_size < 0x7fffffff); + chunk_id = 0; + while ((readnb = safe_read(ctx->fd_in, chunk_size_p, 4)) == 4) { + chunk_size = LOAD32_LE(chunk_size_p); + if (chunk_size > max_chunk_size) { + fprintf(stderr, "Chunk size too large ([%zd] > [%zd])\n", + chunk_size, max_chunk_size); + exit(1); + } + if (safe_read(ctx->fd_in, chunk, + (size_t) chunk_size + hydro_secretbox_HEADERBYTES) != + chunk_size + hydro_secretbox_HEADERBYTES) { + fprintf(stderr, "Chunk too short ([%zd] bytes expected)\n", + chunk_size); + exit(1); + } + if (hydro_secretbox_decrypt(chunk, chunk, + chunk_size + hydro_secretbox_HEADERBYTES, + chunk_id, HYDRO_CONTEXT, ctx->key) != 0) { + fprintf(stderr, "Unable to decrypt chunk #%" PRIu64 " - ", + chunk_id); + if (chunk_id == 0) { + fprintf(stderr, "Wrong password or key?\n"); + } else { + fprintf(stderr, "Corrupted or incomplete file?\n"); + } + exit(1); + } + if (chunk_size == 0) { + break; + } + if (safe_write(ctx->fd_out, chunk, chunk_size, -1) < 0) { + perror("write()"); + exit(1); + } + chunk_id++; + } + if (readnb < 0) { + perror("read()"); + exit(1); + } + if (chunk_size != 0) { + fprintf(stderr, "Premature end of file\n"); + exit(1); + } + return 0; +} + +int +main(int argc, char *argv[]) +{ + Context ctx; + + if (hydro_init() < 0) { + fprintf(stderr, "Unable to initialize the crypto library"); + exit(1); + } + memset(&ctx, 0, sizeof ctx); + options_parse(&ctx, argc, argv); + derive_key(&ctx); + ctx.sizeof_buf = DEFAULT_BUFFER_SIZE; + if (ctx.sizeof_buf < MIN_BUFFER_SIZE) { + ctx.sizeof_buf = MIN_BUFFER_SIZE; + } else if (ctx.sizeof_buf > MAX_BUFFER_SIZE) { + ctx.sizeof_buf = MAX_BUFFER_SIZE; + } + if ((ctx.buf = malloc(ctx.sizeof_buf)) == NULL) { + perror("malloc()"); + exit(1); + } + assert(sizeof HYDRO_CONTEXT == hydro_secretbox_CONTEXTBYTES); + + ctx.fd_in = file_open(ctx.in, 0); + ctx.fd_out = file_open(ctx.out, 1); + if (ctx.encrypt) { + stream_encrypt(&ctx); + } else { + stream_decrypt(&ctx); + } + free(ctx.buf); + close(ctx.fd_out); + close(ctx.fd_in); + hydro_memzero(&ctx, sizeof ctx); + + return 0; +} |