commit 72ba1a8a75b1990156ec209538cc00498efcb43d
parent 9c6cdcc42dc9b0def23ed2839cb528f3867f9cfb
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(-)
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;
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;
}