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 }