dedup

deduplicating backup program
git clone git://git.2f30.org/dedup
Log | Files | Refs | README | LICENSE

state.c (5150B)


      1 /* Routines to read and write repository parameters */
      2 #include <assert.h>
      3 #include <errno.h>
      4 #include <stdint.h>
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <strings.h>
      8 
      9 #include <sodium.h>
     10 
     11 #include "config.h"
     12 #include "misc.h"
     13 #include "state.h"
     14 
     15 #define VMIN            0
     16 #define VMAJ            1
     17 #define VMINMASK        0xff
     18 #define VMAJSHIFT       8
     19 #define VMAJMASK        0xff
     20 
     21 #define CALGOSHIFT      16
     22 #define CALGOMASK       0x7
     23 #define CNONETYPE       0
     24 #define CSNAPPYTYPE     1
     25 #define CLZ4TYPE        2
     26 
     27 #define EALGOSHIFT      19
     28 #define EALGOMASK       0x7
     29 #define ENONETYPE       0
     30 #define ECHACHATYPE     1
     31 
     32 #define NONCESIZE	crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
     33 #define MSEEDSIZE	4
     34 #define CSEEDSIZE	(MSEEDSIZE + crypto_aead_xchacha20poly1305_ietf_ABYTES)
     35 #define SHDRSIZE	(8 + NONCESIZE + CSEEDSIZE)
     36 
     37 /* misc helpers */
     38 extern int pack(unsigned char *, char *, ...);
     39 extern int unpack(unsigned char *, char *, ...);
     40 
     41 struct shdr {
     42 	uint64_t flags;
     43 	unsigned char nonce[NONCESIZE];
     44 	unsigned char seed[CSEEDSIZE];
     45 };
     46 
     47 /* Unpack state header */
     48 static int
     49 unpackshdr(unsigned char *buf, struct shdr *shdr)
     50 {
     51 	char fmt[BUFSIZ];
     52 	int n;
     53 
     54 	snprintf(fmt, sizeof(fmt), "q'%d'%d", NONCESIZE, CSEEDSIZE);
     55 	n = unpack(buf, fmt,
     56 	           &shdr->flags,
     57 	           shdr->nonce,
     58 	           shdr->seed);
     59 	assert(n == SHDRSIZE);
     60 	return n;
     61 }
     62 
     63 /* Pack state header */
     64 static int
     65 packshdr(unsigned char *buf, struct shdr *shdr)
     66 {
     67 	char fmt[BUFSIZ];
     68 	int n;
     69 
     70 	snprintf(fmt, sizeof(fmt), "q'%d'%d", NONCESIZE, CSEEDSIZE);
     71 	n = pack(buf, fmt,
     72 	         shdr->flags,
     73 	         shdr->nonce,
     74 	         shdr->seed);
     75 	assert(n == SHDRSIZE);
     76 	return n;
     77 }
     78 
     79 int
     80 writestate(int fd, struct param *par)
     81 {
     82 	unsigned char buf[SHDRSIZE];
     83 	struct shdr shdr;
     84 
     85 	if (sodium_init() < 0) {
     86 		seterr("sodium_init: failed");
     87 		return -1;
     88 	}
     89 
     90 	/* Set version */
     91 	shdr.flags = (VMAJ << VMAJSHIFT) | VMIN;
     92 
     93 	/* Set compression type */
     94 	if (strcasecmp(par->calgo, "none") == 0) {
     95 		shdr.flags |= CNONETYPE << CALGOSHIFT;
     96 	} else if (strcasecmp(par->calgo, "snappy") == 0) {
     97 		shdr.flags |= CSNAPPYTYPE << CALGOSHIFT;
     98 	} else if (strcasecmp(par->calgo, "lz4") == 0) {
     99 		shdr.flags |= CLZ4TYPE << CALGOSHIFT;
    100 	} else {
    101 		seterr("invalid compression type: %s", par->calgo);
    102 		return -1;
    103 	}
    104 
    105 	/* Clear seed + authentication tag */
    106 	memset(shdr.seed, 0, sizeof(shdr.seed));
    107 
    108 	/* Pack seed */
    109 	shdr.seed[0] = par->seed;
    110 	shdr.seed[1] = par->seed >> 8;
    111 	shdr.seed[2] = par->seed >> 16;
    112 	shdr.seed[3] = par->seed >> 24;
    113 
    114 	/* Set encryption type */
    115 	if (strcasecmp(par->ealgo, "none") == 0) {
    116 		shdr.flags |= ENONETYPE << EALGOSHIFT;
    117 		memset(shdr.nonce, 0, sizeof(shdr.nonce));
    118 	} else if (strcasecmp(par->ealgo, "XChaCha20-Poly1305") == 0) {
    119 		unsigned long long elen;
    120 
    121 		shdr.flags |= ECHACHATYPE << EALGOSHIFT;
    122 		randombytes_buf(shdr.nonce, sizeof(shdr.nonce));
    123 		crypto_aead_xchacha20poly1305_ietf_encrypt(shdr.seed, &elen,
    124 		                                           shdr.seed, MSEEDSIZE,
    125 		                                           NULL, 0, NULL,
    126 		                                           shdr.nonce, par->key);
    127 		assert(elen == CSEEDSIZE);
    128 	} else {
    129 		seterr("invalid encryption type: %s", par->ealgo);
    130 		return -1;
    131 	}
    132 
    133 	packshdr(buf, &shdr);
    134 	if (xwrite(fd, buf, SHDRSIZE) != SHDRSIZE) {
    135 		seterr("failed to write state header: %s", strerror(errno));
    136 		return -1;
    137 	}
    138 	return 0;
    139 }
    140 
    141 int
    142 readstate(int fd, struct param *par)
    143 {
    144 	unsigned char buf[SHDRSIZE];
    145 	struct shdr shdr;
    146 	unsigned long long dlen;
    147 	int algo;
    148 
    149 	if (sodium_init() < 0) {
    150 		seterr("sodium_init: failed");
    151 		return -1;
    152 	}
    153 
    154 	if (xread(fd, buf, SHDRSIZE) != SHDRSIZE) {
    155 		seterr("failed to read state header: %s", strerror(errno));
    156 		return -1;
    157 	}
    158 	unpackshdr(buf, &shdr);
    159 
    160 	/* If the major version is different, the format is incompatible */
    161 	if (((shdr.flags >> VMAJSHIFT) & VMAJMASK) != VMAJ) {
    162 		seterr("state header version mismatch");
    163 		return -1;
    164 	}
    165 
    166 	/* Populate param compression algo */
    167 	algo = (shdr.flags >> CALGOSHIFT) & CALGOMASK;
    168 	switch (algo) {
    169 	case CNONETYPE:
    170 		par->calgo = "none";
    171 		break;
    172 	case CSNAPPYTYPE:
    173 		par->calgo = "snappy";
    174 		break;
    175 	case CLZ4TYPE:
    176 		par->calgo = "lz4";
    177 		break;
    178 	default:
    179 		seterr("invalid compression type: %d", algo);
    180 		return -1;
    181 	}
    182 
    183 	/* Populate param encryption algo */
    184 	algo = (shdr.flags >> EALGOSHIFT) & EALGOMASK;
    185 	switch (algo) {
    186 	case ENONETYPE:
    187 		par->ealgo = "none";
    188 		break;
    189 	case ECHACHATYPE:
    190 		par->ealgo = "XChaCha20-Poly1305";
    191 		if (crypto_aead_xchacha20poly1305_ietf_decrypt(shdr.seed, &dlen,
    192 		                                               NULL,
    193 		                                               shdr.seed, CSEEDSIZE,
    194 		                                               NULL, 0,
    195 		                                               shdr.nonce, par->key) < 0) {
    196 			seterr("authentication failed");
    197 			return -1;
    198 		}
    199 		assert(dlen == MSEEDSIZE);
    200 		break;
    201 	default:
    202 		seterr("invalid encryption type: %d", algo);
    203 		return -1;
    204 	}
    205 
    206 	/* Unpack seed */
    207 	par->seed = (uint32_t)shdr.seed[0];
    208 	par->seed |= (uint32_t)shdr.seed[1] << 8;
    209 	par->seed |= (uint32_t)shdr.seed[2] << 16;
    210 	par->seed |= (uint32_t)shdr.seed[3] << 24;
    211 
    212 	return 0;
    213 }