commit eba08d1ff6618f1b3277dea6990a0afde7a0bc11
parent 042e687f46e7c334def143a387c332e6884cd078
Author: sin <sin@2f30.org>
Date: Sun, 19 May 2019 19:26:12 +0300
Add support for storing/retrieving an encrypted seed
The seed is XOR-ed with the initial state of the chunker to mitigate
against fingerprinting attacks.
Diffstat:
8 files changed, 93 insertions(+), 20 deletions(-)
diff --git a/dup-gc.c b/dup-gc.c
@@ -4,6 +4,7 @@
#include <err.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
diff --git a/dup-init.c b/dup-init.c
@@ -4,11 +4,14 @@
#include <err.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
+#include <sodium.h>
+
#include "arg.h"
#include "block.h"
#include "config.h"
@@ -40,21 +43,21 @@ savestate(char *repo)
}
static void
-loadkey(char *keyfile)
+loadkey(char *keyf)
{
int fd;
- if (keyfile == NULL)
+ if (keyf == NULL)
return;
- fd = open(keyfile, O_RDONLY);
+ fd = open(keyf, O_RDONLY);
if (fd < 0)
- err(1, "open: %s", keyfile);
+ err(1, "open: %s", keyf);
if (readkey(fd, param.key, sizeof(param.key)) < 0)
- printerr("readkey: %s", keyfile);
+ printerr("readkey: %s", keyf);
param.keyloaded = 1;
if (close(fd) < 0)
- err(1, "close: %s", keyfile);
+ err(1, "close: %s", keyf);
}
static void
@@ -70,7 +73,7 @@ main(int argc, char *argv[])
char spath[PATH_MAX];
char bpath[PATH_MAX];
struct bctx *bctx;
- char *keyfile = NULL;
+ char *keyf = NULL;
char *repo;
int lfd;
@@ -79,7 +82,7 @@ main(int argc, char *argv[])
ARGBEGIN {
case 'k':
- keyfile = EARGF(usage());
+ keyf = EARGF(usage());
break;
case 'E':
param.ealgo = EARGF(usage());
@@ -105,9 +108,16 @@ main(int argc, char *argv[])
usage();
};
- if (strcasecmp(param.ealgo, "XChaCha20-Poly1305") == 0 &&
- keyfile == NULL)
- errx(1, "expected encryption key");
+ if (strcasecmp(param.ealgo, "none") == 0) {
+ param.seed = 0;
+ } else if (strcasecmp(param.ealgo, "XChaCha20-Poly1305") == 0) {
+ if (keyf == NULL)
+ errx(1, "expected encryption key");
+ param.seed = randombytes_uniform(0xffffffff);
+ }
+
+ if (sodium_init() < 0)
+ errx(1, "sodium_init: failed");
if (snprintf(spath, sizeof(spath), "%s/%s",
repo, ARCHIVEPATH) >= sizeof(spath))
@@ -125,7 +135,7 @@ main(int argc, char *argv[])
if (mkdir(spath, 0700) < 0)
err(1, "mkdir: %s", spath);
- loadkey(keyfile);
+ loadkey(keyf);
savestate(repo);
if (bcreat(bpath, 0600, &bctx) < 0)
diff --git a/dup-keygen.c b/dup-keygen.c
@@ -3,6 +3,7 @@
#include <err.h>
#include <fcntl.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
diff --git a/dup-pack.c b/dup-pack.c
@@ -63,7 +63,8 @@ pack(struct sctx *sctx, struct bctx *bctx)
{
struct chunker *c;
- if ((c = copen(0, BSIZEMIN, BSIZEMAX, HMASKBITS, WINSIZE, 0)) == NULL)
+ if ((c = copen(0, BSIZEMIN, BSIZEMAX, HMASKBITS, WINSIZE,
+ param.seed)) == NULL)
printerr("copen");
while (cfill(c) > 0) {
diff --git a/dup-rm.c b/dup-rm.c
@@ -4,6 +4,7 @@
#include <err.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
diff --git a/dup-unpack.c b/dup-unpack.c
@@ -4,6 +4,7 @@
#include <err.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
diff --git a/state.c b/state.c
@@ -6,6 +6,8 @@
#include <string.h>
#include <strings.h>
+#include <sodium.h>
+
#include "config.h"
#include "misc.h"
#include "state.h"
@@ -27,7 +29,9 @@
#define ENONETYPE 0
#define ECHACHATYPE 1
-#define SHDRSIZE 8
+#define MSEEDSIZE 4
+#define CSEEDSIZE (MSEEDSIZE + 16)
+#define SHDRSIZE (8 + 24 + CSEEDSIZE)
/* misc helpers */
extern int pack(unsigned char *, char *, ...);
@@ -35,6 +39,8 @@ extern int unpack(unsigned char *, char *, ...);
struct shdr {
uint64_t flags;
+ unsigned char nonce[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES];
+ unsigned char seed[CSEEDSIZE];
};
/* Unpack state header */
@@ -43,7 +49,10 @@ unpackshdr(unsigned char *buf, struct shdr *shdr)
{
int n;
- n = unpack(buf, "q", &shdr->flags);
+ n = unpack(buf, "q'24'20",
+ &shdr->flags,
+ shdr->nonce,
+ shdr->seed);
assert(n == SHDRSIZE);
return n;
}
@@ -54,7 +63,10 @@ packshdr(unsigned char *buf, struct shdr *shdr)
{
int n;
- n = pack(buf, "q", shdr->flags);
+ n = pack(buf, "q'24'20",
+ shdr->flags,
+ shdr->nonce,
+ shdr->seed);
assert(n == SHDRSIZE);
return n;
}
@@ -65,6 +77,11 @@ writestate(int fd, struct param *par)
unsigned char buf[SHDRSIZE];
struct shdr shdr;
+ if (sodium_init() < 0) {
+ seterr("sodium_init: failed");
+ return -1;
+ }
+
/* Set version */
shdr.flags = (VMAJ << VMAJSHIFT) | VMIN;
@@ -80,11 +97,29 @@ writestate(int fd, struct param *par)
return -1;
}
+ /* Clear seed + authentication tag */
+ memset(shdr.seed, 0, sizeof(shdr.seed));
+
+ /* Pack seed */
+ shdr.seed[0] = par->seed;
+ shdr.seed[1] = par->seed >> 8;
+ shdr.seed[2] = par->seed >> 16;
+ shdr.seed[3] = par->seed >> 24;
+
/* Set encryption type */
if (strcasecmp(par->ealgo, "none") == 0) {
shdr.flags |= ENONETYPE << EALGOSHIFT;
+ memset(shdr.nonce, 0, sizeof(shdr.nonce));
} else if (strcasecmp(par->ealgo, "XChaCha20-Poly1305") == 0) {
+ unsigned long long elen;
+
shdr.flags |= ECHACHATYPE << EALGOSHIFT;
+ randombytes_buf(shdr.nonce, sizeof(shdr.nonce));
+ crypto_aead_xchacha20poly1305_ietf_encrypt(shdr.seed, &elen,
+ shdr.seed, MSEEDSIZE,
+ NULL, 0, NULL,
+ shdr.nonce, par->key);
+ assert(elen == CSEEDSIZE);
} else {
seterr("invalid encryption type: %s", par->ealgo);
return -1;
@@ -103,8 +138,14 @@ readstate(int fd, struct param *par)
{
unsigned char buf[SHDRSIZE];
struct shdr shdr;
+ unsigned long long dlen;
int algo;
+ if (sodium_init() < 0) {
+ seterr("sodium_init: failed");
+ return -1;
+ }
+
if (xread(fd, buf, SHDRSIZE) != SHDRSIZE) {
seterr("failed to read state header: %s", strerror(errno));
return -1;
@@ -142,10 +183,26 @@ readstate(int fd, struct param *par)
break;
case ECHACHATYPE:
par->ealgo = "XChaCha20-Poly1305";
+ if (crypto_aead_xchacha20poly1305_ietf_decrypt(shdr.seed, &dlen,
+ NULL,
+ shdr.seed, CSEEDSIZE,
+ NULL, 0,
+ shdr.nonce, par->key) < 0) {
+ seterr("authentication failed");
+ return -1;
+ }
+ assert(dlen == MSEEDSIZE);
break;
default:
seterr("invalid encryption type: %d", algo);
return -1;
}
+
+ /* Unpack seed */
+ par->seed = (uint32_t)shdr.seed[0];
+ par->seed |= (uint32_t)shdr.seed[1] << 8;
+ par->seed |= (uint32_t)shdr.seed[2] << 16;
+ par->seed |= (uint32_t)shdr.seed[3] << 24;
+
return 0;
}
diff --git a/state.h b/state.h
@@ -1,8 +1,9 @@
struct param {
- char *calgo;
- char *ealgo;
- unsigned char key[KEYSIZE];
- int keyloaded;
+ char *calgo; /* compression algorithm */
+ char *ealgo; /* encryption algorithm */
+ unsigned char key[KEYSIZE]; /* secret key */
+ int keyloaded; /* 1 if key is loaded, 0 otherwise */
+ uint32_t seed; /* XOR-ed with initial state of chunker */
};
extern int writestate(int, struct param *);