diff options
| author | 2017-12-30 03:29:35 +0100 | |
|---|---|---|
| committer | 2017-12-30 03:29:35 +0100 | |
| commit | ccb5ff9bb3a75d6ceac19e193eac447f7e84de79 (patch) | |
| tree | 6ca666ad8a1916dfcf0a016ec06ff43fb3f5c42a /src | |
| parent | f1e3970b52886c2e784d9d6a247788924eec6941 (diff) | |
| download | tweetpipe-ccb5ff9bb3a75d6ceac19e193eac447f7e84de79.tar.gz | |
Move things to src/
Diffstat (limited to 'src')
| -rw-r--r-- | src/common.h | 28 | ||||
| -rw-r--r-- | src/encpipe.c | 234 | ||||
| -rw-r--r-- | src/encpipe_p.h | 43 | ||||
| -rw-r--r-- | src/safe_rw.c | 81 | ||||
| -rw-r--r-- | src/safe_rw.h | 15 |
5 files changed, 401 insertions, 0 deletions
diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..8181c42 --- /dev/null +++ b/src/common.h @@ -0,0 +1,28 @@ +#ifndef common_H +#define common_H 1 + +#define LOAD32_LE(SRC) load32_le(SRC) +static inline uint32_t +load32_le(const uint8_t src[4]) +{ + uint32_t w = (uint32_t) src[0]; + w |= (uint32_t) src[1] << 8; + w |= (uint32_t) src[2] << 16; + w |= (uint32_t) src[3] << 24; + return w; +} + +#define STORE32_LE(DST, W) store32_le((DST), (W)) +static inline void +store32_le(uint8_t dst[4], uint32_t w) +{ + dst[0] = (uint8_t) w; + w >>= 8; + dst[1] = (uint8_t) w; + w >>= 8; + dst[2] = (uint8_t) w; + w >>= 8; + dst[3] = (uint8_t) w; +} + +#endif diff --git a/src/encpipe.c b/src/encpipe.c new file mode 100644 index 0000000..79500c6 --- /dev/null +++ b/src/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; +} diff --git a/src/encpipe_p.h b/src/encpipe_p.h new file mode 100644 index 0000000..3e8e474 --- /dev/null +++ b/src/encpipe_p.h @@ -0,0 +1,43 @@ +#ifndef encpipe_p_H +#define encpipe_p_H 1 + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/uio.h> + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <hydrogen.h> + +#include "common.h" +#include "safe_rw.h" + +#define MIN_BUFFER_SIZE 512 +#define MAX_BUFFER_SIZE 0x7fffffff +#define DEFAULT_BUFFER_SIZE (1 * 1024 * 1024) +#define HYDRO_CONTEXT "EncPipe" +#define PWHASH_OPSLIMIT 1000000 +#define PWHASH_MEMLIMIT 0 +#define PWHASH_THREADS 1 + +typedef struct Context_ { + char * in; + char * out; + char * password; + unsigned char key[hydro_secretbox_KEYBYTES]; + unsigned char *buf; + size_t sizeof_buf; + int fd_in; + int fd_out; + int encrypt; +} Context; + +#endif diff --git a/src/safe_rw.c b/src/safe_rw.c new file mode 100644 index 0000000..683d8b5 --- /dev/null +++ b/src/safe_rw.c @@ -0,0 +1,81 @@ + +#include <stdlib.h> +#include <sys/types.h> + +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <poll.h> +#include <unistd.h> + +#ifndef SSIZE_MAX +#define SSIZE_MAX (SIZE_MAX / 2 - 1) +#endif + +#include "safe_rw.h" + +ssize_t +safe_write(const int fd, const void *const buf_, size_t count, + const int timeout) +{ + struct pollfd pfd; + const char * buf = (const char *) buf_; + ssize_t written; + + pfd.fd = fd; + pfd.events = POLLOUT; + + assert(count <= SSIZE_MAX); + while (count > (size_t) 0) { + while ((written = write(fd, buf, count)) <= (ssize_t) 0) { + if (errno == EAGAIN) { + if (poll(&pfd, (nfds_t) 1, timeout) == 0) { + errno = ETIMEDOUT; + goto ret; + } + } else if (errno != EINTR) { + goto ret; + } + } + buf += written; + count -= (size_t) written; + } +ret: + return (ssize_t)(buf - (const char *) buf_); +} + +ssize_t +safe_read(const int fd, void *const buf_, size_t count) +{ + unsigned char *buf = (unsigned char *) buf_; + ssize_t readnb; + + assert(count <= SSIZE_MAX); + do { + while ((readnb = read(fd, buf, count)) < (ssize_t) 0 && errno == EINTR) + ; + if (readnb < (ssize_t) 0) { + return readnb; + } + if (readnb == (ssize_t) 0) { + break; + } + count -= (size_t) readnb; + buf += readnb; + } while (count > (ssize_t) 0); + + return (ssize_t)(buf - (unsigned char *) buf_); +} + +ssize_t +safe_read_partial(const int fd, void *const buf_, const size_t max_count) +{ + unsigned char *const buf = (unsigned char *) buf_; + ssize_t readnb; + + assert(max_count <= SSIZE_MAX); + while ((readnb = read(fd, buf, max_count)) < (ssize_t) 0 && errno == EINTR) + ; + + return readnb; +} diff --git a/src/safe_rw.h b/src/safe_rw.h new file mode 100644 index 0000000..31f4aa6 --- /dev/null +++ b/src/safe_rw.h @@ -0,0 +1,15 @@ +#ifndef safe_rw_H +#define safe_rw_H + +#include <stdlib.h> +#include <sys/types.h> + +ssize_t safe_write(const int fd, const void* const buf_, size_t count, + const int timeout); + +ssize_t safe_read(const int fd, void* const buf_, size_t count); + +ssize_t safe_read_partial(const int fd, void* const buf_, + const size_t max_count); + +#endif |