Encrypt/authenticate snapshot metadata - dedup - deduplicating backup program HTML git clone git://bitreich.org/dedup/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/dedup/ DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- DIR commit 72ba1a8a75b1990156ec209538cc00498efcb43d DIR parent 9c6cdcc42dc9b0def23ed2839cb528f3867f9cfb HTML Author: sin <sin@2f30.org> Date: Sun, 12 May 2019 15:24:55 +0100 Encrypt/authenticate snapshot metadata Diffstat: M bencrypt.c | 2 +- M snap.c | 197 +++++++++++++++++++++++++------ 2 files changed, 160 insertions(+), 39 deletions(-) --- DIR diff --git a/bencrypt.c b/bencrypt.c @@ -112,7 +112,7 @@ becreat(struct bctx *bctx, char *path, int mode) return -1; } - /* Ensure that if caller requested encryption, a key was provided */ + /* Ensure a key has been provided if caller requested encryption */ if (type != EDNONETYPE && !param.keyloaded) { seterr("expected encryption key"); return -1; DIR diff --git a/snap.c b/snap.c @@ -10,6 +10,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <strings.h> #include <unistd.h> #include <sodium.h> @@ -18,6 +19,7 @@ #include "misc.h" #include "queue.h" #include "snap.h" +#include "state.h" /* snapshot header constants */ #define SHDRMAGIC "SNAPSNAPPYSNOOP" @@ -29,7 +31,9 @@ #define VMAJSHIFT 8 #define VMAJMASK 0xff -#define SHDRSIZE (NSHDRMAGIC + 24 + 24 + 8 + 8) +#define SHDRSIZE (NSHDRMAGIC + 24 + 8 + 8) + +extern struct param param; /* misc helpers */ extern int pack(unsigned char *, char *, ...); @@ -38,7 +42,6 @@ extern int unpack(unsigned char *, char *, ...); /* Snapshot header structure */ struct shdr { char magic[NSHDRMAGIC]; /* magic number for file(1) */ - unsigned char nonce[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES]; unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; uint64_t flags; /* version number */ uint64_t nbd; /* number of block hashes */ @@ -52,6 +55,7 @@ struct mdnode { struct sctx { TAILQ_HEAD(mdhead, mdnode) mdhead; /* list of hashes contained in snapshot */ struct mdnode *mdnext; /* next hash to be returned via sget() */ + int crypto; /* when set, snapshots are encrypted */ int fd; /* underlying snapshot file descriptor */ int rdonly; /* when set, ssync() is a no-op */ struct shdr shdr; /* snapshot header */ @@ -69,9 +73,8 @@ unpackshdr(int fd, struct shdr *shdr) return -1; } - n = unpack(buf, "'16'24'24qq", + n = unpack(buf, "'16'24qq", shdr->magic, - shdr->nonce, shdr->header, &shdr->flags, &shdr->nbd); @@ -87,9 +90,8 @@ packshdr(int fd, struct shdr *shdr) unsigned char buf[SHDRSIZE]; int n; - n = pack(buf, "'16'24'24qq", + n = pack(buf, "'16'24qq", shdr->magic, - shdr->nonce, shdr->header, shdr->flags, shdr->nbd); @@ -103,52 +105,99 @@ packshdr(int fd, struct shdr *shdr) } static int -loadmd(struct sctx *sctx) +initmdhead(struct sctx *sctx) { - struct mdnode *mdnode; + unsigned char ad[SHDRSIZE]; + struct shdr *shdr; + uint64_t i; - mdnode = calloc(1, sizeof(*mdnode)); - if (mdnode == NULL) { - seterr("calloc: %s", strerror(errno)); + if (lseek(sctx->fd, 0, SEEK_SET) < 0) { + seterr("lseek: %s", strerror(errno)); return -1; } - if (xread(sctx->fd, mdnode->md, MDSIZE) != MDSIZE) { - free(mdnode); - seterr("failed to read block hash: %s", strerror(errno)); + + if (xread(sctx->fd, ad, sizeof(ad)) != sizeof(ad)) { + seterr("failed to read snapshot header: %s\n", strerror(errno)); return -1; } - TAILQ_INSERT_TAIL(&sctx->mdhead, mdnode, e); - return 0; -} - -static int -initmdhead(struct sctx *sctx) -{ - struct shdr *shdr; - uint64_t i; shdr = &sctx->shdr; - for (i = 0; i < shdr->nbd; i++) { - if (loadmd(sctx) == 0) - continue; + if (sctx->crypto) { + crypto_secretstream_xchacha20poly1305_state state; + struct shdr *shdr; + + shdr = &sctx->shdr; + if (crypto_secretstream_xchacha20poly1305_init_pull(&state, + shdr->header, + param.key) != 0) { + seterr("invalid crypto header"); + return -1; + } - /* Cleanup */ - while (!TAILQ_EMPTY(&sctx->mdhead)) { + for (i = 0; i < shdr->nbd; i++) { + unsigned char buf[MDSIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; + unsigned char md[MDSIZE]; + struct mdnode *mdnode; + unsigned char tag; + + if (xread(sctx->fd, buf, sizeof(buf)) != sizeof(buf)) { + seterr("failed to read block hash: %s", strerror(errno)); + goto err0; + } + + if (crypto_secretstream_xchacha20poly1305_pull(&state, md, NULL, &tag, + buf, sizeof(buf), + ad, sizeof(ad)) != 0) { + seterr("authentication failed"); + goto err0; + } + + mdnode = calloc(1, sizeof(*mdnode)); + if (mdnode == NULL) { + seterr("calloc: %s", strerror(errno)); + goto err0; + } + memcpy(mdnode->md, md, MDSIZE); + TAILQ_INSERT_TAIL(&sctx->mdhead, mdnode, e); + } + } else { + for (i = 0; i < shdr->nbd; i++) { + unsigned char md[MDSIZE]; struct mdnode *mdnode; - mdnode = TAILQ_FIRST(&sctx->mdhead); - TAILQ_REMOVE(&sctx->mdhead, mdnode, e); - free(mdnode); + if (xread(sctx->fd, md, MDSIZE) != MDSIZE) { + seterr("failed to read block hash: %s", strerror(errno)); + goto err0; + } + + mdnode = calloc(1, sizeof(*mdnode)); + if (mdnode == NULL) { + seterr("calloc: %s", strerror(errno)); + goto err0; + } + memcpy(mdnode->md, md, MDSIZE); + TAILQ_INSERT_TAIL(&sctx->mdhead, mdnode, e); } - return -1; } return 0; + +err0: + /* Cleanup */ + while (!TAILQ_EMPTY(&sctx->mdhead)) { + struct mdnode *mdnode; + + mdnode = TAILQ_FIRST(&sctx->mdhead); + TAILQ_REMOVE(&sctx->mdhead, mdnode, e); + free(mdnode); + } + return -1; } int screat(char *path, int mode, struct sctx **sctx) { struct shdr *shdr; + int crypto; int fd; if (path == NULL || sctx == NULL) { @@ -156,6 +205,22 @@ screat(char *path, int mode, struct sctx **sctx) return -1; } + /* Determine algorithm type */ + if (strcasecmp(param.ealgo, "none") == 0) { + crypto = 0; + } else if (strcasecmp(param.ealgo, "XChaCha20-Poly1305") == 0) { + crypto = 1; + } else { + seterr("invalid encryption type: %s", param.ealgo); + return -1; + } + + /* Ensure a key has been provided if caller requested encryption */ + if (crypto && !param.keyloaded) { + seterr("expected encryption key"); + return -1; + } + if (sodium_init() < 0) { seterr("sodium_init: failed"); return -1; @@ -176,6 +241,7 @@ screat(char *path, int mode, struct sctx **sctx) TAILQ_INIT(&(*sctx)->mdhead); (*sctx)->mdnext = NULL; + (*sctx)->crypto = crypto; (*sctx)->fd = fd; shdr = &(*sctx)->shdr; @@ -196,6 +262,7 @@ int sopen(char *path, int flags, int mode, struct sctx **sctx) { struct shdr *shdr; + int crypto; int fd; if (path == NULL || sctx == NULL) { @@ -209,6 +276,16 @@ sopen(char *path, int flags, int mode, struct sctx **sctx) return -1; } + /* Determine algorithm type */ + if (strcasecmp(param.ealgo, "none") == 0) { + crypto = 0; + } else if (strcasecmp(param.ealgo, "XChaCha20-Poly1305") == 0) { + crypto = 1; + } else { + seterr("invalid encryption type: %s", param.ealgo); + return -1; + } + if (sodium_init() < 0) { seterr("sodium_init: failed"); return -1; @@ -229,6 +306,7 @@ sopen(char *path, int flags, int mode, struct sctx **sctx) TAILQ_INIT(&(*sctx)->mdhead); (*sctx)->mdnext = NULL; + (*sctx)->crypto = crypto; (*sctx)->fd = fd; (*sctx)->rdonly = 1; @@ -340,16 +418,59 @@ ssync(struct sctx *sctx) } shdr = &sctx->shdr; - if (packshdr(sctx->fd, shdr) < 0) - return -1; - TAILQ_FOREACH(mdnode, &sctx->mdhead, e) { - if (xwrite(sctx->fd, mdnode->md, MDSIZE) != MDSIZE) { - seterr("failed to write block hash: %s", - strerror(errno)); + if (sctx->crypto) { + unsigned char ad[SHDRSIZE]; + crypto_secretstream_xchacha20poly1305_state state; + + crypto_secretstream_xchacha20poly1305_init_push(&state, + shdr->header, + param.key); + + if (packshdr(sctx->fd, shdr) < 0) + return -1; + + if (lseek(sctx->fd, 0, SEEK_SET) < 0) { + seterr("lseek: %s", strerror(errno)); + return -1; + } + + if (xread(sctx->fd, ad, sizeof(ad)) != sizeof(ad)) { + seterr("failed to read snapshot header: %s\n", strerror(errno)); return -1; } + + TAILQ_FOREACH(mdnode, &sctx->mdhead, e) { + unsigned char buf[MDSIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; + unsigned char tag; + + if (TAILQ_LAST(&sctx->mdhead, mdhead) == mdnode) + tag = crypto_secretstream_xchacha20poly1305_TAG_FINAL; + else + tag = 0; + + crypto_secretstream_xchacha20poly1305_push(&state, + buf, NULL, + mdnode->md, MDSIZE, + ad, sizeof(ad), tag); + if (xwrite(sctx->fd, buf, sizeof(buf)) != sizeof(buf)) { + seterr("failed to write block hash: %s", + strerror(errno)); + return -1; + } + } + } else { + if (packshdr(sctx->fd, shdr) < 0) + return -1; + TAILQ_FOREACH(mdnode, &sctx->mdhead, e) { + if (xwrite(sctx->fd, mdnode->md, MDSIZE) != MDSIZE) { + seterr("failed to write block hash: %s", + strerror(errno)); + return -1; + } + } } fsync(sctx->fd); + return 0; }