db.c (5510B)
1 #include <stdbool.h> 2 #include <stdint.h> 3 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <unistd.h> 7 8 #include "db.h" 9 #include "util.h" 10 #include "token.h" 11 #include "tiny-AES-c/aes.h" 12 13 struct header { 14 uint8_t magic[4]; 15 uint8_t version; 16 }; 17 18 static bool verify_db_legacy(int fd, struct AES_ctx *c, uint8_t *keybuf) 19 { 20 uint8_t rbuf[AES_BLOCKLEN]; 21 int r; 22 size_t rused = 0; 23 struct header *h; 24 25 while ((r = read(fd, rbuf + rused, sizeof(rbuf) - rused)) > 0) 26 rused += r; 27 28 if (rused < sizeof(rbuf)) { 29 errno = TOTP_EEOF; 30 return false; 31 } 32 33 AES_init_ctx_iv(c, keybuf, keybuf + AES_KEYLEN); 34 rused = 0; 35 36 AES_CBC_decrypt_buffer(c, rbuf, sizeof(rbuf)); 37 h = (struct header *)(rbuf + rbuf[0] % (sizeof(rbuf) - sizeof(*h) - 1) + 1); 38 39 if (h->magic[0] == 'T' && 40 h->magic[1] == 'O' && 41 h->magic[2] == 'T' && 42 h->magic[3] == 'P' && 43 h->version == 1) 44 return true; 45 46 errno = TOTP_EMALF; 47 return false; 48 } 49 50 static bool verify_db(int fd, struct AES_ctx *c, uint8_t *keybuf) 51 { 52 uint8_t rbuf[AES_BLOCKLEN]; 53 int r; 54 size_t rused = 0; 55 struct header *h; 56 57 while ((r = read(fd, rbuf + rused, sizeof(rbuf) - rused)) > 0) 58 rused += r; 59 60 if (rused < sizeof(rbuf)) { 61 errno = TOTP_EEOF; 62 return false; 63 } 64 65 AES_init_ctx_iv(c, keybuf, rbuf); 66 rused = 0; 67 68 while ((r = read(fd, rbuf + rused, sizeof(rbuf) - rused)) > 0) 69 rused += r; 70 71 if (rused < sizeof(rbuf)) { 72 errno = TOTP_EEOF; 73 return false; 74 } 75 76 AES_CBC_decrypt_buffer(c, rbuf, sizeof(rbuf)); 77 h = (struct header *)(rbuf + rbuf[0] % (sizeof(rbuf) - sizeof(*h) - 1) + 1); 78 79 if (h->magic[0] == 'T' && 80 h->magic[1] == 'O' && 81 h->magic[2] == 'T' && 82 h->magic[3] == 'P' && 83 h->version == 1) 84 return true; 85 86 errno = TOTP_EMALF; 87 return false; 88 } 89 90 int db_open_read(const char *filename, struct AES_ctx *c, uint8_t *keybuf) { 91 int fd = open(filename, O_RDONLY); 92 93 if (fd < 0) 94 return fd; 95 96 if (verify_db(fd, c, keybuf)) 97 return fd; 98 99 if (!lseek(fd, 0, SEEK_SET) && verify_db_legacy(fd, c, keybuf)) 100 return fd; 101 102 close(fd); 103 return -1; 104 } 105 106 static int write_header(int fd, struct AES_ctx *c) 107 { 108 uint8_t wbuf[AES_BLOCKLEN]; 109 int w; 110 uint8_t *wp = wbuf; 111 uint8_t * const wend = 1[&wbuf]; 112 struct header *h; 113 114 randmem(wbuf, sizeof(wbuf)); 115 116 h = (struct header *)(wbuf + wbuf[0] % (sizeof(wbuf) - sizeof(*h) - 1) + 1); 117 118 h->magic[0] = 'T'; 119 h->magic[1] = 'O'; 120 h->magic[2] = 'T'; 121 h->magic[3] = 'P'; 122 h->version = 1; 123 124 AES_CBC_encrypt_buffer(c, wbuf, sizeof(wbuf)); 125 126 while (wp < wend && (w = write(fd, wp, wend - wp)) > 0) 127 wp += w; 128 129 if (w < 0) 130 return -1; 131 132 if (wp < wend) { 133 errno = EIO; 134 return -1; 135 } 136 137 return 0; 138 139 } 140 141 int write_salt(int fd, uint8_t *saltbuf) 142 { 143 uint8_t *wp = saltbuf; 144 uint8_t * const wend = saltbuf + AES_BLOCKLEN; 145 ssize_t w; 146 147 while (wp < wend && (w = write(fd, wp, wend - wp)) > 0) 148 wp += w; 149 150 return wp < wend; 151 } 152 153 int db_open_write(const char *filename, struct AES_ctx *c, uint8_t *keybuf) 154 { 155 uint8_t saltbuf[AES_BLOCKLEN]; 156 int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0600); 157 158 if (fd < 0) 159 return fd; 160 161 randmem(saltbuf, sizeof(saltbuf)); 162 AES_init_ctx_iv(c, keybuf, saltbuf); 163 164 if (write_salt(fd, saltbuf) || 165 write_header(fd, c)) { 166 int tmp_errno = errno; 167 (void)unlink(filename); 168 (void)close(fd); 169 errno = tmp_errno; 170 171 return -1; 172 } 173 174 return fd; 175 } 176 177 void db_foreach(int fd, struct AES_ctx *c, 178 void (*key_cb)(struct token *token, 179 void *data), 180 void *cb_data) 181 { 182 uint8_t decbuf[512]; 183 uint8_t rbuf[AES_BLOCKLEN]; 184 uint8_t *dp = decbuf; 185 uint8_t * const dend = 1[&decbuf]; 186 uint8_t *rp = rbuf; 187 uint8_t * const rend = 1[&rbuf]; 188 int r; 189 190 while ((r = read(fd, rp, rend - rp)) > 0) { 191 struct totpkey *kh = (struct totpkey *)decbuf; 192 if ((rp += r) < rend) 193 continue; 194 AES_CBC_decrypt_buffer(c, rbuf, sizeof(rbuf)); 195 if (dp + sizeof(rbuf) >= dend) 196 break; 197 dp = mempush(dp, rp = rbuf, sizeof(rbuf)); 198 199 if (decbuf + sizeof(*kh) > dp || 200 decbuf + sizeof(*kh) + kh->keylen + kh->desclen + kh->issuerlen > dp) 201 continue; 202 203 struct bytes key = bytesnc(&kh[1], kh->keylen); 204 struct bytes desc = bytesnc(key.end, kh->desclen); 205 struct bytes issuer = bytesnc(desc.end, kh->issuerlen); 206 key_cb(&(struct token){ 207 key, desc, issuer, 208 kh->digest, readbeu64(kh->t0), 209 kh->digits, kh->period, 210 true 211 }, cb_data); 212 213 dp = decbuf; 214 } 215 } 216 217 int db_add_key(int fd, struct AES_ctx *c, 218 struct token *token) 219 { 220 size_t ksz = sizeof(struct totpkey) + bytes_len(token->key) + bytes_len(token->desc) + bytes_len(token->issuer); 221 size_t i; 222 int w; 223 224 ksz = (ksz + AES_BLOCKLEN - 1) / AES_BLOCKLEN * AES_BLOCKLEN; 225 226 if (bytes_len(token->key) > UINT8_MAX || bytes_len(token->desc) > UINT8_MAX || bytes_len(token->issuer) > UINT8_MAX) { 227 errno = EMSGSIZE; 228 return -1; 229 } 230 231 uint8_t buffer[1024]; 232 uint8_t *wp = buffer; 233 uint64_t t0 = token->t0; 234 wp = mempush(wp, &(struct totpkey){ 235 .t0 = { t0 >> 56, t0 >> 48, t0 >> 40, t0 >> 32, t0 >> 24, t0 >> 16, t0 >> 8, t0 }, 236 .digest = token->digest, 237 .digits = token->digits, 238 .period = token->period, 239 .keylen = bytes_len(token->key), 240 .desclen = bytes_len(token->desc), 241 .issuerlen = bytes_len(token->issuer) }, sizeof(struct totpkey)); 242 wp = mempushb(wp, token->key); 243 wp = mempushb(wp, token->desc); 244 wp = mempushb(wp, token->issuer); 245 randmem(wp, buffer + ksz - wp); 246 247 for (i = 0; i < ksz; i += AES_BLOCKLEN) 248 AES_CBC_encrypt_buffer(c, buffer + i, AES_BLOCKLEN); 249 i = 0; 250 251 while ((w = write(fd, buffer + i, ksz - i)) > 0) 252 i += w; 253 254 if (w < 0) 255 return -errno; 256 return i != ksz; 257 } 258