state.c - 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
---
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 }