projects/libpws

view pws-file.c @ 13:2bb1bbac1d0a

Added tag version-1.0.0 for changeset 1926dfc9feb0
author Guido Berhoerster <guido+libpws@berhoerster.name>
date Sun Aug 04 21:37:56 2019 +0200 (2019-08-04)
parents d541e748cfd8
children
line source
1 /*
2 * Copyright (C) 2015 Guido Berhoerster <guido+libpws@berhoerster.name>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
24 #include "compat.h"
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <errno.h>
31 #ifdef HAVE_ENDIAN_H
32 #include <endian.h>
33 #endif /* HAVE_ENDIAN_H */
34 #ifdef HAVE_SYS_ENDIAN_H
35 #include <sys/endian.h>
36 #endif /* HAVE_ENDIAN_H */
37 #include <nettle/twofish.h>
38 #include <nettle/cbc.h>
39 #include <nettle/hmac.h>
40 #include <nettle/sha.h>
42 #include "pws-internal.h"
44 #define MAX_ITER (1 << 22)
45 #define DEFAULT_ITER 10000
46 #define KEY_SIZE 32UL
47 #define SALT_SIZE 32UL
48 #define METADATA_SIZE (sizeof (psafe3_tag) + SALT_SIZE + 4 +\
49 SHA256_DIGEST_SIZE + KEY_SIZE + KEY_SIZE + TWOFISH_BLOCK_SIZE)
51 static const unsigned char psafe3_tag[] = { 'P', 'W', 'S', '3' };
52 static const unsigned char eof_marker[] = { 'P', 'W', 'S', '3', '-', 'E', 'O',
53 'F', 'P', 'W', 'S', '3', '-', 'E', 'O', 'F' };
55 RB_HEAD(empty_groups_tree, pws3_field);
57 RB_HEAD(records_tree, pws3_record);
59 struct pws3_file {
60 struct pws3_field *fields[256];
61 struct empty_groups_tree *empty_groups_tree;
62 struct records_tree *records_tree;
63 struct pws_file_error {
64 enum pws_error_code code;
65 int errnum;
66 char *msg;
67 } error;
68 };
70 struct twofish_cbc_ctx CBC_CTX(struct twofish_ctx, TWOFISH_BLOCK_SIZE);
72 struct pws_file_ctx {
73 FILE *fp;
74 unsigned char *mem;
75 size_t mem_size;
76 size_t mem_pos;
77 struct twofish_cbc_ctx cipher_ctx;
78 struct hmac_sha256_ctx hmac_ctx;
79 struct pws3_file *pws_file;
80 uint32_t n_iter;
81 };
83 static int empty_groups_cmp(struct pws3_field *, struct pws3_field *);
84 static int record_cmp(struct pws3_record *, struct pws3_record *);
85 RB_PROTOTYPE_STATIC(empty_groups_tree, pws3_field, tree_entry, empty_groups_cmp)
86 RB_PROTOTYPE_STATIC(records_tree, pws3_record, tree_entry, record_cmp)
88 RB_GENERATE_STATIC(empty_groups_tree, pws3_field, tree_entry, empty_groups_cmp)
90 RB_GENERATE_STATIC(records_tree, pws3_record, tree_entry, record_cmp)
92 static int
93 empty_groups_cmp(struct pws3_field *field1, struct pws3_field *field2)
94 {
95 PWS_ASSERT(pws3_field_is_header(field1) &&
96 pws3_field_is_header(field2));
97 PWS_ASSERT((pws3_field_get_type(field1) ==
98 PWS3_HEADER_FIELD_EMPTY_GROUPS) &&
99 (pws3_field_get_type(field2) == PWS3_HEADER_FIELD_EMPTY_GROUPS));
101 return (strcmp(pws3_field_get_text(field1),
102 pws3_field_get_text(field2)));
103 }
105 static int
106 record_cmp(struct pws3_record *record1, struct pws3_record *record2)
107 {
108 struct pws3_field *uuid_field1;
109 struct pws3_field *uuid_field2;
111 uuid_field1 = pws3_record_get_field(record1, PWS3_RECORD_FIELD_UUID);
112 uuid_field2 = pws3_record_get_field(record2, PWS3_RECORD_FIELD_UUID);
113 PWS_ASSERT((uuid_field1 != NULL) && (uuid_field2 != NULL));
115 return (memcmp(pws3_field_get_uuid(uuid_field1),
116 pws3_field_get_uuid(uuid_field2), PWS3_UUID_SIZE));
117 }
119 static void
120 pws_set_system_error(struct pws3_file *pws_file, enum pws_error_code code,
121 int errnum, const char *fmt, ...)
122 {
123 char system_error_buf[4096] = "";
124 size_t system_error_len;
125 int error_len;
126 va_list args;
127 va_list args2;
129 pws_file->error.code = code;
130 pws_file->error.errnum = errnum;
132 strerror_r(errnum, system_error_buf, sizeof (system_error_buf) - 1);
133 system_error_len = strlen(system_error_buf);
135 pws_free(pws_file->error.msg, (pws_file->error.msg != NULL) ?
136 strlen(pws_file->error.msg) + 1 : 0);
137 if (fmt != NULL) {
138 va_start(args, fmt);
139 va_copy(args2, args);
140 error_len = vsnprintf(NULL, 0, fmt, args);
141 pws_file->error.msg = pws_alloc(error_len + 2 +
142 system_error_len + 1);
143 if (pws_file->error.msg == NULL) {
144 va_end(args2);
145 va_end(args);
146 return;
147 }
148 vsnprintf(pws_file->error.msg, error_len + 1, fmt, args2);
149 va_end(args2);
150 va_end(args);
151 strcpy(pws_file->error.msg + error_len, ": ");
152 strcpy(pws_file->error.msg + error_len + 2, system_error_buf);
153 } else {
154 pws_file->error.msg = pws_alloc(system_error_len + 1);
155 snprintf(pws_file->error.msg, system_error_len + 1, "%s",
156 system_error_buf);
157 }
158 }
160 static void
161 pws_set_error(struct pws3_file *pws_file, enum pws_error_code code,
162 const char *fmt, ...)
163 {
164 va_list args;
165 va_list args2;
166 int error_len;
168 pws_file->error.code = code;
169 pws_file->error.errnum = 0;
171 pws_free(pws_file->error.msg, (pws_file->error.msg != NULL) ?
172 strlen(pws_file->error.msg) + 1 : 0);
173 va_start(args, fmt);
174 va_copy(args2, args);
175 error_len = vsnprintf(NULL, 0, fmt, args);
176 pws_file->error.msg = pws_alloc(error_len + 1);
177 if (pws_file->error.msg == NULL) {
178 va_end(args2);
179 va_end(args);
180 return;
181 }
182 vsnprintf(pws_file->error.msg, error_len + 1, fmt, args2);
183 va_end(args2);
184 va_end(args);
185 }
187 static int
188 read_buf(struct pws_file_ctx *ctx, unsigned char *buf, size_t buf_size)
189 {
190 if (ctx->fp != NULL) {
191 if (fread(buf, 1, buf_size, ctx->fp) != buf_size) {
192 if (ferror(ctx->fp) != 0) {
193 return (-1);
194 } else if (feof(ctx->fp) != 0) {
195 return (1);
196 }
197 }
198 } else {
199 PWS_ASSERT(ctx->mem != NULL);
200 if (ctx->mem_size - ctx->mem_pos < buf_size) {
201 return (1);
202 }
203 memcpy(buf, &ctx->mem[ctx->mem_pos], buf_size);
204 ctx->mem_pos += buf_size;
205 }
207 return (0);
208 }
210 static int
211 write_buf(struct pws_file_ctx *ctx, const unsigned char *buf, size_t buf_size)
212 {
213 size_t remaining;
214 unsigned char *tmp;
216 if (ctx->fp != NULL) {
217 if (fwrite(buf, 1, buf_size, ctx->fp) != buf_size) {
218 return (-1);
219 }
220 if (fflush(ctx->fp) != 0) {
221 return (-1);
222 }
223 } else {
224 remaining = ctx->mem_size - ctx->mem_pos;
225 if (remaining < buf_size) {
226 tmp = pws_realloc(ctx->mem, ctx->mem_size +
227 (buf_size - remaining));
228 if (tmp == NULL) {
229 return (-1);
230 }
231 ctx->mem = tmp;
232 ctx->mem_size += (buf_size - remaining);
233 }
234 memcpy(&ctx->mem[ctx->mem_pos], buf, buf_size);
235 ctx->mem_pos += buf_size;
236 }
238 return (0);
239 }
241 static void
242 pws_file_clear(struct pws3_file *pws_file)
243 {
244 size_t i;
245 struct pws3_field *empty_group_field;
246 struct pws3_field *empty_group_field_tmp;
247 struct pws3_record *record;
248 struct pws3_record *record_tmp;
250 for (i = 0x00; i <= 0xff; i++) {
251 pws3_field_destroy(pws_file->fields[i]);
252 pws_file->fields[i] = NULL;
253 }
255 RB_FOREACH_SAFE(empty_group_field, empty_groups_tree,
256 pws_file->empty_groups_tree, empty_group_field_tmp) {
257 pws3_field_destroy(RB_REMOVE(empty_groups_tree,
258 pws_file->empty_groups_tree, empty_group_field));
259 }
261 RB_FOREACH_SAFE(record, records_tree, pws_file->records_tree,
262 record_tmp) {
263 pws3_record_destroy(RB_REMOVE(records_tree,
264 pws_file->records_tree, record));
265 }
266 }
268 void
269 pws3_file_destroy(struct pws3_file *pws_file)
270 {
271 if (pws_file == NULL) {
272 return;
273 }
275 pws_free(pws_file->error.msg, (pws_file->error.msg != NULL) ?
276 strlen(pws_file->error.msg) + 1 : 0);
277 pws_file_clear(pws_file);
278 pws_free(pws_file->empty_groups_tree,
279 sizeof (struct empty_groups_tree));
280 pws_free(pws_file->records_tree, sizeof (struct records_tree));
281 pws_free(pws_file, sizeof (struct pws3_file));
282 }
284 struct pws3_file *
285 pws3_file_create(void)
286 {
287 struct pws3_field *version_field = NULL;
288 struct pws3_file *pws_file = NULL;
289 size_t i;
291 /* version field is mandatory */
292 version_field = pws3_field_create(1, PWS3_HEADER_FIELD_VERSION);
293 if (version_field == NULL) {
294 goto err;
295 }
296 pws3_field_set_uint16(version_field, PWS3_VERSION);
298 pws_file = pws_alloc(sizeof (struct pws3_file));
299 if (pws_file == NULL) {
300 goto err;
301 }
302 for (i = 0x00; i <= 0xff; i++) {
303 pws_file->fields[i] = NULL;
304 }
305 pws_file->empty_groups_tree = NULL;
306 pws_file->records_tree = NULL;
307 pws_file->error.errnum = 0;
308 pws_file->error.code = 0;
309 pws_file->error.msg = NULL;
311 pws_file->empty_groups_tree =
312 pws_alloc(sizeof (struct empty_groups_tree));
313 if (pws_file->empty_groups_tree == NULL) {
314 goto err;
315 }
316 RB_INIT(pws_file->empty_groups_tree);
318 pws_file->records_tree = pws_alloc(sizeof (struct records_tree));
319 if (pws_file->records_tree == NULL) {
320 goto err;
321 }
322 RB_INIT(pws_file->records_tree);
324 pws3_file_set_header_field(pws_file, version_field);
326 return (pws_file);
327 err:
328 pws3_field_destroy(version_field);
329 if (pws_file != NULL) {
330 pws_free(pws_file->records_tree, sizeof (struct records_tree));
331 pws_free(pws_file->empty_groups_tree,
332 sizeof (struct empty_groups_tree));
333 }
334 pws_free(pws_file, sizeof (struct pws3_file));
336 return (NULL);
337 }
339 enum pws_error_code
340 pws3_file_get_error_code(struct pws3_file *pws_file)
341 {
342 return (pws_file->error.code);
343 }
345 const char *
346 pws3_file_get_error_message(struct pws3_file *pws_file)
347 {
348 return ((pws_file->error.msg != NULL) ? pws_file->error.msg : "");
349 }
351 static void
352 stretch_key(unsigned char *stretched_key, uint32_t n_iter, const char *key,
353 size_t key_size, const unsigned char *salt, size_t salt_size)
354 {
355 uint32_t i;
356 struct sha256_ctx md_ctx;
358 sha256_init(&md_ctx);
359 sha256_update(&md_ctx, key_size, (uint8_t *)key);
360 sha256_update(&md_ctx, salt_size, salt);
361 sha256_digest(&md_ctx, SHA256_DIGEST_SIZE, stretched_key);
363 for (i = 0; i < n_iter; i++) {
364 sha256_update(&md_ctx, SHA256_DIGEST_SIZE, stretched_key);
365 sha256_digest(&md_ctx, SHA256_DIGEST_SIZE, stretched_key);
366 }
367 }
369 static int
370 read_metadata(struct pws_file_ctx *ctx, const char *password)
371 {
372 int retval = -1;
373 unsigned char buf[METADATA_SIZE];
374 unsigned char *p = buf;
375 unsigned char *stretched_key = NULL;
376 unsigned char *key_k = NULL;
377 unsigned char *key_l = NULL;
378 int read_retval;
379 unsigned char salt[SALT_SIZE];
380 unsigned char key_digest[SHA256_DIGEST_SIZE];
381 struct sha256_ctx md_ctx;
382 struct twofish_ctx cipher_ctx;
384 stretched_key = pws_secure_alloc(SHA256_DIGEST_SIZE);
385 if (stretched_key == NULL) {
386 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
387 "out of memory");
388 goto out;
389 }
391 key_k = pws_secure_alloc(KEY_SIZE);
392 if (key_k == NULL) {
393 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
394 "out of memory");
395 goto out;
396 }
398 key_l = pws_secure_alloc(KEY_SIZE);
399 if (key_l == NULL) {
400 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
401 "out of memory");
402 goto out;
403 }
405 read_retval = read_buf(ctx, buf, sizeof (buf));
406 if (read_retval == 1) {
407 pws_set_error(ctx->pws_file, PWS_ERR_TRUNCATED_FILE,
408 "unexpected end of file");
409 goto out;
410 } else if (read_retval != 0) {
411 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
412 NULL);
413 goto out;
414 }
416 /* check tag */
417 if (memcmp(p, psafe3_tag, sizeof (psafe3_tag)) != 0) {
418 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
419 "unknown filetype");
420 goto out;
421 }
422 p += sizeof (psafe3_tag);
424 /* salt */
425 memcpy(salt, p, SALT_SIZE);
426 p += SALT_SIZE;
428 /* iterations */
429 memcpy(&ctx->n_iter, p, 4);
430 ctx->n_iter = le32toh(ctx->n_iter);
431 if ((ctx->n_iter < 1) || (ctx->n_iter > MAX_ITER)) {
432 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
433 "invalid number of iterations: %d", ctx->n_iter);
434 goto out;
435 }
436 p += 4;
438 /* verify password */
439 stretch_key(stretched_key, ctx->n_iter, password, strlen(password),
440 salt, SALT_SIZE);
441 sha256_init(&md_ctx);
442 sha256_update(&md_ctx, SHA256_DIGEST_SIZE, stretched_key);
443 sha256_digest(&md_ctx, SHA256_DIGEST_SIZE, key_digest);
444 if (memcmp(key_digest, p, SHA256_DIGEST_SIZE) != 0) {
445 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
446 "wrong password");
447 goto out;
448 }
449 p += SHA256_DIGEST_SIZE;
451 /* decrypt keys */
452 twofish_set_key(&cipher_ctx, KEY_SIZE, stretched_key);
453 twofish_decrypt(&cipher_ctx, KEY_SIZE, key_k, p);
454 p += KEY_SIZE;
455 twofish_decrypt(&cipher_ctx, KEY_SIZE, key_l, p);
456 p += KEY_SIZE;
458 /* set key for decryption */
459 twofish_set_key(&ctx->cipher_ctx.ctx, KEY_SIZE, key_k);
461 /* set IV */
462 CBC_SET_IV(&ctx->cipher_ctx, p);
464 /* set key for HMAC */
465 HMAC_SET_KEY(&ctx->hmac_ctx, &nettle_sha256, KEY_SIZE, key_l);
467 retval = 0;
469 out:
470 pws_secure_free(stretched_key, (stretched_key != NULL) ?
471 SHA256_DIGEST_SIZE : 0);
472 pws_secure_free(key_l, (key_l != NULL) ? KEY_SIZE : 0);
473 pws_secure_free(key_k, (key_k != NULL) ? KEY_SIZE : 0);
475 return (retval);
476 }
478 static int
479 read_block(struct pws_file_ctx *ctx, unsigned char *block)
480 {
481 unsigned char buf[TWOFISH_BLOCK_SIZE];
482 int read_retval;
484 read_retval = read_buf(ctx, buf, sizeof (buf));
485 if (read_retval == 1) {
486 pws_set_error(ctx->pws_file, PWS_ERR_TRUNCATED_FILE,
487 "unexpected end of file");
488 return (-1);
489 } else if (read_retval != 0) {
490 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
491 NULL);
492 return (-1);
493 }
495 /* reached the EOF block marking the end of encrypted records */
496 if (memcmp(buf, eof_marker, TWOFISH_BLOCK_SIZE) == 0) {
497 return (1);
498 }
500 CBC_DECRYPT(&ctx->cipher_ctx, twofish_decrypt, TWOFISH_BLOCK_SIZE,
501 block, buf);
503 return (0);
504 }
506 static int
507 read_field(struct pws_file_ctx *ctx, struct pws3_field **fieldp, int is_header)
508 {
509 int retval = -1;
510 enum pws_data_type data_type;
511 struct pws3_field *field = NULL;
512 unsigned char *block_buf = NULL;
513 unsigned char *p;
514 int read_retval;
515 unsigned char *field_buf = NULL;
516 uint32_t field_size;
517 size_t remaining;
518 size_t field_buf_size = 0;
519 uint8_t field_type = 0xff;
520 time_t data_time;
521 uint32_t data_uint32;
522 uint16_t data_uint16;
523 uint8_t data_uint8;
525 block_buf = pws_secure_alloc(TWOFISH_BLOCK_SIZE);
526 if (block_buf == NULL) {
527 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
528 "out of memory");
529 goto out;
530 }
532 next_field:
533 p = block_buf;
535 /* read first block */
536 read_retval = read_block(ctx, block_buf);
537 if (read_retval != 0) {
538 retval = read_retval;
539 goto out;
540 }
542 /* determine field length */
543 memcpy(&field_size, p, 4);
544 remaining = field_buf_size = field_size = le32toh(field_size);
545 p += 4;
546 /* determine field type */
547 memcpy(&field_type, p, 1);
548 p++;
550 /* check for end of header fields or end of record */
551 if ((is_header && (field_type == PWS3_HEADER_FIELD_END)) ||
552 (!is_header && (field_type == PWS3_RECORD_FIELD_END))) {
553 retval = 1;
554 goto out;
555 }
557 /* skip empty fields */
558 if (field_size == 0) {
559 goto next_field;
560 }
562 /* determine data type */
563 data_type = pws3_field_get_data_type(&(struct pws3_field){ .is_header =
564 is_header, .field_type = field_type });
566 /* make room for a terminating \0 in text fields */
567 if (data_type == PWS_DATA_TYPE_TEXT) {
568 field_buf_size++;
569 }
571 /* validate field length */
572 switch (data_type) {
573 case PWS_DATA_TYPE_UUID:
574 if ((field_size != 0) && (field_size != PWS3_UUID_SIZE)) {
575 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
576 "invalid field length");
577 goto out;
578 }
579 break;
580 case PWS_DATA_TYPE_TIME: /* FALLTHROUGH */
581 case PWS_DATA_TYPE_UINT32:
582 if ((field_size != 0) && (field_size != 4)) {
583 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
584 "invalid field length");
585 goto out;
586 }
587 break;
588 case PWS_DATA_TYPE_UINT8:
589 if ((field_size != 0) && (field_size != 1)) {
590 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
591 "invalid field length");
592 goto out;
593 }
594 break;
595 case PWS_DATA_TYPE_UINT16:
596 if ((field_size != 0) && (field_size != 2)) {
597 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
598 "invalid field length");
599 goto out;
600 }
601 break;
602 default:
603 /* text or bytes */
604 if ((!is_header &&
605 (field_type == PWS3_RECORD_FIELD_PASSWORD) &&
606 (field_buf_size > PWS3_MAX_PASSWORD_LEN)) ||
607 (field_buf_size > PWS3_MAX_FIELD_SIZE)) {
608 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
609 "invalid field length");
610 goto out;
611 }
612 }
614 field = pws3_field_create(is_header, field_type);
615 if (field == NULL) {
616 pws_set_system_error(ctx->pws_file, PWS_ERR_NO_MEMORY, errno,
617 NULL);
618 goto out;
619 }
621 /* create field */
622 if (field_buf_size > 0) {
623 if (!is_header && (field_type == PWS3_RECORD_FIELD_PASSWORD)) {
624 field_buf = pws_secure_alloc(field_buf_size);
625 } else {
626 field_buf = pws_alloc(field_buf_size);
627 }
628 if (field_buf == NULL) {
629 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
630 "out of memory");
631 goto out;
632 }
633 memset(field_buf, 0, field_buf_size);
635 memcpy(field_buf, p, MIN(remaining,
636 (size_t)TWOFISH_BLOCK_SIZE - (p - block_buf)));
637 remaining -= MIN(remaining,
638 (size_t)TWOFISH_BLOCK_SIZE - (p - block_buf));
640 while (remaining > 0) {
641 read_retval = read_block(ctx, block_buf);
642 if (read_retval != 0) {
643 goto out;
644 }
645 memcpy(field_buf + (field_size - remaining),
646 block_buf, MIN(remaining, TWOFISH_BLOCK_SIZE));
647 remaining -= MIN(remaining, TWOFISH_BLOCK_SIZE);
648 }
650 hmac_sha256_update(&ctx->hmac_ctx, field_size, field_buf);
652 switch (data_type) {
653 case PWS_DATA_TYPE_UUID:
654 retval = pws3_field_set_uuid(field, field_buf);
655 break;
656 case PWS_DATA_TYPE_TEXT:
657 retval = pws3_field_set_text(field, (char *)field_buf);
658 break;
659 case PWS_DATA_TYPE_TIME:
660 memcpy(&data_uint32, field_buf, 4);
661 data_time = le32toh(data_uint32);
662 retval = pws3_field_set_time(field, data_time);
663 break;
664 case PWS_DATA_TYPE_UINT8:
665 memcpy(&data_uint8, field_buf, 1);
666 retval = pws3_field_set_uint8(field, data_uint8);
667 break;
668 case PWS_DATA_TYPE_UINT16:
669 memcpy(&data_uint16, field_buf, 2);
670 data_uint16 = le16toh(data_uint16);
671 retval = pws3_field_set_uint16(field, data_uint16);
672 break;
673 case PWS_DATA_TYPE_UINT32:
674 memcpy(&data_uint32, field_buf, 4);
675 data_uint32 = le32toh(data_uint32);
676 retval = pws3_field_set_uint32(field, data_uint32);
677 break;
678 case PWS_DATA_TYPE_BYTES:
679 retval = pws3_field_set_bytes(field, field_buf,
680 field_buf_size);
681 }
682 if (retval != 0) {
683 goto out;
684 }
685 }
687 retval = 0;
689 out:
690 if (!is_header && (field_type == PWS3_RECORD_FIELD_PASSWORD)) {
691 pws_secure_free(field_buf, field_buf_size);
692 } else {
693 pws_free(field_buf, field_buf_size);
694 }
695 pws_secure_free(block_buf, (block_buf != NULL) ?
696 (size_t)TWOFISH_BLOCK_SIZE : 0);
698 if (retval == 0) {
699 *fieldp = field;
700 } else {
701 pws3_field_destroy(field);
702 }
704 return (retval);
705 }
707 static int
708 read_header(struct pws_file_ctx *ctx)
709 {
710 int retval;
711 struct pws3_field *field = NULL;
713 /* the header must start with a version field */
714 retval = read_field(ctx, &field, 1);
715 if (retval != 0) {
716 /* error or end of headers */
717 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
718 "header does not start with a version field");
719 return (-1);
720 } else if (field->field_type != PWS3_HEADER_FIELD_VERSION) {
721 /* header does not start with a version field */
722 pws3_field_destroy(field);
723 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
724 "header does not start with a version field");
725 return (-1);
726 } else if (field->value.uint16 > PWS3_VERSION) {
727 /* unsupported database version */
728 pws3_field_destroy(field);
729 pws_set_error(ctx->pws_file, PWS_ERR_UNSUPPORTED_VERSION,
730 "unsupported database version");
731 return (-1);
732 }
733 pws3_file_set_header_field(ctx->pws_file, field);
735 for (;;) {
736 retval = read_field(ctx, &field, 1);
737 if (retval == 1) {
738 /* end of headers */
739 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
740 "unexpected end of headers");
741 break;
742 } else if (retval != 0) {
743 return (-1);
744 }
745 pws3_file_set_header_field(ctx->pws_file, field);
746 }
748 return (0);
749 }
751 static int
752 read_records(struct pws_file_ctx *ctx)
753 {
754 int retval;
755 struct pws3_record *record = NULL;
756 struct pws3_field *field = NULL;
758 for (;;) {
759 /*
760 * a record must consist of at least three fields, instead of
761 * the first field there could also be an EOF marker
762 */
763 retval = read_field(ctx, &field, 0);
764 if (retval == 1) {
765 /* EOF marker */
766 retval = 0;
767 goto out;
768 } else if (retval != 0) {
769 /* read error */
770 goto out;
771 } else if (field->field_type == PWS3_RECORD_FIELD_END) {
772 /* empty record */
773 retval = -1;
774 goto out;
775 }
777 record = pws3_record_create();
778 if (record == NULL) {
779 pws_set_system_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
780 errno, NULL);
781 goto out;
782 }
784 pws3_record_set_field(record, field);
785 field = NULL;
787 /* read the remaining fileds */
788 for (;;) {
789 retval = read_field(ctx, &field, 0);
790 if (retval == 1) {
791 /* end of record */
792 break;
793 } else if (retval != 0) {
794 /* read error */
795 retval = -1;
796 goto out;
797 }
798 pws3_record_set_field(record, field);
799 field = NULL;
800 }
802 /* check whether UUID is not empty */
803 if (pws3_record_get_field(record, PWS3_RECORD_FIELD_UUID) ==
804 NULL) {
805 /* record is missing mandatory fields */
806 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_RECORD,
807 "record is missing mandatory fields");
808 pws3_record_destroy(record);
809 retval = -1;
810 goto out;
811 }
813 pws3_file_insert_record(ctx->pws_file, record);
814 record = NULL;
815 }
817 out:
818 if (retval != 0) {
819 pws3_field_destroy(field);
820 pws3_record_destroy(record);
821 }
823 return (retval);
824 }
826 static int
827 verify_checksum(struct pws_file_ctx *ctx)
828 {
829 int retval;
830 unsigned char hmac_file[SHA256_DIGEST_SIZE];
831 unsigned char hmac[SHA256_DIGEST_SIZE];
833 retval = read_buf(ctx, hmac_file, sizeof (hmac_file));
834 if (retval == 1) {
835 pws_set_error(ctx->pws_file, PWS_ERR_TRUNCATED_FILE,
836 "unexpected end of file");
837 return (-1);
838 } else if (retval != 0) {
839 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
840 NULL);
841 return (-1);
842 }
844 hmac_sha256_digest(&ctx->hmac_ctx, sizeof (hmac), hmac);
845 if (memcmp(hmac_file, hmac, sizeof (hmac_file)) != 0) {
846 /* inconsistent database */
847 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_CHECKSUM,
848 "checksum failed");
849 return (-1);
850 }
852 return (0);
853 }
855 static int
856 pws3_file_read(struct pws3_file *pws_file, const char *password,
857 unsigned char *s, size_t n, FILE *fp)
858 {
859 int retval = -1;
860 struct pws_file_ctx ctx = {
861 .fp = fp,
862 .mem = s,
863 .mem_size = n,
864 .mem_pos = 0,
865 .pws_file = pws_file,
866 };
868 pws_file_clear(pws_file);
870 retval = read_metadata(&ctx, password);
871 if (retval != 0) {
872 goto out;
873 }
875 retval = read_header(&ctx);
876 if (retval != 0) {
877 goto out;
878 }
880 retval = read_records(&ctx);
881 if (retval != 0) {
882 goto out;
883 }
885 retval = verify_checksum(&ctx);
886 if (retval != 0) {
887 goto out;
888 }
890 out:
891 if (retval != 0) {
892 pws_file_clear(ctx.pws_file);
893 }
895 return (retval);
896 }
898 int
899 pws3_file_read_mem(struct pws3_file *pws_file, const char *password,
900 unsigned char *s, size_t n)
901 {
902 return (pws3_file_read(pws_file, password, s, n, NULL));
903 }
905 int
906 pws3_file_read_stream(struct pws3_file *pws_file, const char *password,
907 FILE *fp)
908 {
909 return (pws3_file_read(pws_file, password, NULL, 0, fp));
910 }
912 static int
913 write_metadata(struct pws_file_ctx *ctx, const char *password)
914 {
915 int retval = -1;
916 unsigned char *stretched_key = NULL;
917 unsigned char *key_k = NULL;
918 unsigned char *key_l = NULL;
919 unsigned char metadata[METADATA_SIZE];
920 unsigned char *p = metadata;
921 unsigned char *salt;
922 uint32_t n_iter_le;
923 struct sha256_ctx md_ctx;
924 unsigned char *b1;
925 unsigned char *b3;
926 unsigned char *iv;
927 struct twofish_ctx cipher_ctx;
929 stretched_key = pws_secure_alloc(SHA256_DIGEST_SIZE);
930 if (stretched_key == NULL) {
931 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
932 "out of memory");
933 goto out;
934 }
936 key_k = pws_secure_alloc(KEY_SIZE);
937 if (key_k == NULL) {
938 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
939 "out of memory");
940 goto out;
941 }
943 key_l = pws_secure_alloc(KEY_SIZE);
944 if (key_l == NULL) {
945 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
946 "out of memory");
947 goto out;
948 }
950 /* generate new keys */
951 if (pws_random_bytes(key_k, KEY_SIZE) != 0) {
952 pws_set_error(ctx->pws_file, PWS_ERR_GENERIC_ERROR,
953 "failed to generate key");
954 goto out;
955 }
957 if (pws_random_bytes(key_l, KEY_SIZE) != 0) {
958 pws_set_error(ctx->pws_file, PWS_ERR_GENERIC_ERROR,
959 "failed to generate key");
960 goto out;
961 }
963 /* tag */
964 memcpy(p, psafe3_tag, sizeof (psafe3_tag));
965 p += sizeof (psafe3_tag);
967 /* generate new salt */
968 salt = p;
969 if (pws_random_bytes(salt, SALT_SIZE) != 0) {
970 pws_set_error(ctx->pws_file, PWS_ERR_GENERIC_ERROR,
971 "failed to generate salt");
972 goto out;
973 }
974 p += SALT_SIZE;
976 /* number of iterations */
977 n_iter_le = htole32(ctx->n_iter);
978 memcpy(p, &n_iter_le, 4);
979 p += 4;
981 /* stretch, hash password */
982 stretch_key(stretched_key, ctx->n_iter, password, strlen(password),
983 salt, SALT_SIZE);
984 sha256_init(&md_ctx);
985 sha256_update(&md_ctx, SHA256_DIGEST_SIZE, stretched_key);
986 sha256_digest(&md_ctx, SHA256_DIGEST_SIZE, p);
987 p += SHA256_DIGEST_SIZE;
989 b1 = p;
990 p += KEY_SIZE;
992 b3 = p;
993 p += KEY_SIZE;
995 /* generate IV */
996 iv = p;
997 if (pws_random_bytes(iv, TWOFISH_BLOCK_SIZE) != 0) {
998 pws_set_error(ctx->pws_file, PWS_ERR_GENERIC_ERROR,
999 "failed to generate IV");
1000 goto out;
1003 /* encrypt keys */
1004 twofish_set_key(&cipher_ctx, KEY_SIZE, stretched_key);
1005 twofish_encrypt(&cipher_ctx, KEY_SIZE, b1, key_k);
1006 twofish_encrypt(&cipher_ctx, KEY_SIZE, b3, key_l);
1008 /* set key for decryption */
1009 twofish_set_key(&ctx->cipher_ctx.ctx, KEY_SIZE, key_k);
1011 /* set IV */
1012 CBC_SET_IV(&ctx->cipher_ctx, p);
1014 /* set key for HMAC */
1015 hmac_sha256_set_key(&ctx->hmac_ctx, KEY_SIZE, key_l);
1017 /* write metadata */
1018 if (write_buf(ctx, metadata, sizeof (metadata)) != 0) {
1019 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
1020 NULL);
1021 retval = -1;
1022 goto out;
1025 retval = 0;
1027 out:
1028 pws_secure_free(key_k, (key_k != NULL) ? KEY_SIZE : 0);
1029 pws_secure_free(key_l, (key_l != NULL) ? KEY_SIZE : 0);
1030 pws_secure_free(stretched_key, (stretched_key != NULL) ?
1031 SHA256_DIGEST_SIZE : 0);
1033 return (retval);
1036 static int
1037 write_block(struct pws_file_ctx *ctx, unsigned char *block)
1039 unsigned char buf[TWOFISH_BLOCK_SIZE];
1041 CBC_ENCRYPT(&ctx->cipher_ctx, twofish_encrypt, sizeof (buf), buf,
1042 block);
1044 if (write_buf(ctx, buf, sizeof (buf)) != 0) {
1045 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
1046 NULL);
1047 return (-1);
1050 return (0);
1053 static int
1054 write_field(struct pws_file_ctx *ctx, struct pws3_field *field)
1056 int retval = -1;
1057 unsigned char *buf = NULL;
1058 unsigned char *p;
1059 unsigned char *field_data;
1060 enum pws_data_type data_type;
1061 size_t blocks = (field->size + 4 + 1) / TWOFISH_BLOCK_SIZE +
1062 ((field->size + 4 + 1) % TWOFISH_BLOCK_SIZE != 0);
1063 size_t i;
1064 size_t j;
1065 uint32_t len_le;
1067 buf = pws_secure_alloc(TWOFISH_BLOCK_SIZE);
1068 if (buf == NULL) {
1069 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
1070 "out of memory");
1071 goto out;
1074 data_type = pws3_field_get_data_type(field);
1076 for (i = 0, j = 0; i < blocks; i++) {
1077 p = field_data = buf;
1078 if (pws_random_bytes(buf, TWOFISH_BLOCK_SIZE) != 0) {
1079 pws_set_error(ctx->pws_file, PWS_ERR_GENERIC_ERROR,
1080 "could not get random numbers");
1081 goto out;
1084 /* the first block of the field contains the length and type */
1085 if (i == 0) {
1086 len_le = htole32(field->size);
1087 memcpy(p, &len_le, 4);
1088 p += 4;
1090 *p = field->field_type;
1091 p++;
1092 field_data = p;
1095 while ((j < field->size) &&
1096 (p - buf < (ptrdiff_t)TWOFISH_BLOCK_SIZE)) {
1097 switch (data_type) {
1098 case PWS_DATA_TYPE_UINT8:
1099 *p = field->value.uint8;
1100 break;
1101 case PWS_DATA_TYPE_UINT16:
1102 /* little endian */
1103 *p = (field->value.uint16 >> (8 * j)) & 0xff;
1104 break;
1105 case PWS_DATA_TYPE_TIME: /* FALLTHROUGH */
1106 case PWS_DATA_TYPE_UINT32:
1107 /* little endian */
1108 *p = (field->value.uint32 >> (8 * j)) & 0xff;
1109 break;
1110 case PWS_DATA_TYPE_TEXT:
1111 *p = field->value.text[j];
1112 break;
1113 case PWS_DATA_TYPE_UUID:
1114 *p = field->value.uuid[j];
1115 break;
1116 default:
1117 *p = field->value.bytes[j];
1120 p++;
1121 j++;
1124 hmac_sha256_update(&ctx->hmac_ctx, p - field_data, field_data);
1126 retval = write_block(ctx, buf);
1127 if (retval != 0) {
1128 goto out;
1132 retval = 0;
1134 out:
1135 pws_secure_free(buf, (buf != NULL) ? TWOFISH_BLOCK_SIZE : 0);
1137 return (retval);
1140 static int
1141 write_header(struct pws_file_ctx *ctx)
1143 int retval = -1;
1144 size_t i;
1145 struct pws3_field *version_field;
1146 struct pws3_field *field = NULL;
1147 struct pws3_field *end_field = NULL;
1149 end_field = pws3_field_create(1, PWS3_HEADER_FIELD_END);
1150 if (end_field == NULL) {
1151 pws_set_system_error(ctx->pws_file, PWS_ERR_NO_MEMORY, errno,
1152 NULL);
1153 goto out;
1156 version_field = pws3_file_get_header_field(ctx->pws_file,
1157 PWS3_HEADER_FIELD_VERSION);
1158 if (version_field == NULL) {
1159 /* add mandatory version header version_field if necessary */
1160 version_field = pws3_field_create(1, PWS3_HEADER_FIELD_VERSION);
1161 if (version_field == NULL) {
1162 pws_set_system_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
1163 errno, NULL);
1164 goto out;
1166 pws3_field_set_uint16(version_field, PWS3_VERSION);
1167 pws3_file_set_header_field(ctx->pws_file, version_field);
1169 retval = write_field(ctx, version_field);
1170 if (retval != 0) {
1171 goto out;
1174 for (i = 0x01; i < 0xff; i++) {
1175 if (ctx->pws_file->fields[i] != NULL) {
1176 retval = write_field(ctx, ctx->pws_file->fields[i]);
1177 if (retval != 0) {
1178 goto out;
1183 RB_FOREACH(field, empty_groups_tree, ctx->pws_file->empty_groups_tree) {
1184 retval = write_field(ctx, field);
1185 if (retval != 0) {
1186 goto out;
1190 retval = write_field(ctx, end_field);
1191 if (retval != 0) {
1192 goto out;
1195 out:
1196 pws3_field_destroy(end_field);
1198 return (retval);
1201 static int
1202 write_records(struct pws_file_ctx *ctx)
1204 int retval = -1;
1205 struct pws3_field *end_field = NULL;
1206 size_t i;
1207 struct pws3_record *record;
1209 end_field = pws3_field_create(0, PWS3_RECORD_FIELD_END);
1210 if (end_field == NULL) {
1211 pws_set_system_error(ctx->pws_file, PWS_ERR_NO_MEMORY, errno,
1212 NULL);
1213 goto out;
1216 RB_FOREACH(record, records_tree, ctx->pws_file->records_tree) {
1217 /* record fields */
1218 for (i = 0x01; i < 0xff; i++) {
1219 if (record->fields[i] != NULL) {
1220 retval = write_field(ctx, record->fields[i]);
1221 if (retval != 0) {
1222 goto out;
1227 /* end of entry marker */
1228 retval = write_field(ctx, end_field);
1229 if (retval != 0) {
1230 goto out;
1234 /* end of file marker */
1235 if (write_buf(ctx, eof_marker, sizeof (eof_marker)) != 0) {
1236 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
1237 NULL);
1238 retval = -1;
1239 goto out;
1242 retval = 0;
1244 out:
1245 pws3_field_destroy(end_field);
1247 return (retval);
1250 static int
1251 write_checksum(struct pws_file_ctx *ctx)
1253 unsigned char hmac[SHA256_DIGEST_SIZE];
1255 hmac_sha256_digest(&ctx->hmac_ctx, sizeof (hmac), hmac);
1257 if (write_buf(ctx, hmac, sizeof (hmac)) != 0) {
1258 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
1259 NULL);
1260 return (-1);
1263 return (0);
1266 static int
1267 pws3_file_write(struct pws3_file *pws_file, const char *password,
1268 uint32_t n_iter, unsigned char **memp, size_t *mem_sizep, FILE *fp)
1270 int retval = -1;
1271 struct pws_file_ctx ctx = {
1272 .fp = fp,
1273 .pws_file = pws_file,
1274 .n_iter = n_iter
1275 };
1277 retval = write_metadata(&ctx, password);
1278 if (retval != 0) {
1279 goto out;
1282 retval = write_header(&ctx);
1283 if (retval != 0) {
1284 goto out;
1287 retval = write_records(&ctx);
1288 if (retval != 0) {
1289 goto out;
1292 retval = write_checksum(&ctx);
1293 if (retval != 0) {
1294 goto out;
1297 if (memp != NULL) {
1298 *memp = ctx.mem;
1299 *mem_sizep = ctx.mem_size;
1302 out:
1303 if (retval != 0) {
1304 pws_free(ctx.mem, ctx.mem_size);
1307 return (retval);
1310 int
1311 pws3_file_write_mem(struct pws3_file *pws_file, const char *password,
1312 uint32_t n_iter, unsigned char **memp, size_t *mem_sizep)
1314 PWS_ASSERT(memp != NULL);
1315 PWS_ASSERT(mem_sizep != NULL);
1317 return (pws3_file_write(pws_file, password, n_iter, memp, mem_sizep,
1318 NULL));
1321 int
1322 pws3_file_write_stream(struct pws3_file *pws_file, const char *password,
1323 uint32_t n_iter, FILE *fp)
1325 PWS_ASSERT(fp != NULL);
1327 return (pws3_file_write(pws_file, password, n_iter, NULL, NULL, fp));
1330 void
1331 pws3_file_set_header_field(struct pws3_file *pws_file, struct pws3_field *field)
1333 PWS_ASSERT(pws3_field_is_header(field));
1334 PWS_ASSERT((pws3_field_get_data_type(field) != PWS_DATA_TYPE_TEXT) ||
1335 (field->value.text != NULL));
1336 PWS_ASSERT((pws3_field_get_data_type(field) != PWS_DATA_TYPE_BYTES) ||
1337 (field->value.bytes != NULL));
1339 if (field->field_type == PWS3_HEADER_FIELD_EMPTY_GROUPS) {
1340 pws3_file_insert_empty_group(pws_file, field);
1341 return;
1344 pws3_field_destroy(pws3_file_remove_header_field(pws_file,
1345 field->field_type));
1346 pws_file->fields[field->field_type] = field;
1349 struct pws3_field *
1350 pws3_file_get_header_field(struct pws3_file *pws_file, uint8_t field_type)
1352 if (field_type == PWS3_HEADER_FIELD_EMPTY_GROUPS) {
1353 return (pws3_file_first_empty_group(pws_file));
1356 return (pws_file->fields[field_type]);
1359 struct pws3_field *
1360 pws3_file_remove_header_field(struct pws3_file *pws_file, uint8_t field_type)
1362 struct pws3_field *field;
1364 if (field_type == PWS3_HEADER_FIELD_EMPTY_GROUPS) {
1365 return (NULL);
1368 field = pws3_file_get_header_field(pws_file, field_type);
1369 pws_file->fields[field_type] = NULL;
1371 return (field);
1374 void
1375 pws3_file_insert_empty_group(struct pws3_file *pws_file,
1376 struct pws3_field *field)
1378 const char *group_name;
1380 PWS_ASSERT(pws3_field_is_header(field));
1381 PWS_ASSERT(pws3_field_get_type(field) ==
1382 PWS3_HEADER_FIELD_EMPTY_GROUPS);
1384 group_name = pws3_field_get_text(field);
1385 pws3_field_destroy(pws3_file_remove_empty_group(pws_file, group_name));
1386 RB_INSERT(empty_groups_tree, pws_file->empty_groups_tree, field);
1389 struct pws3_field *
1390 pws3_file_get_empty_group(struct pws3_file *pws_file, const char *group_name)
1392 return (RB_FIND(empty_groups_tree, pws_file->empty_groups_tree,
1393 (&(struct pws3_field){ .is_header = 1,
1394 .field_type = PWS3_HEADER_FIELD_EMPTY_GROUPS,
1395 .value.text = (char *)group_name })));
1398 struct pws3_field *
1399 pws3_file_remove_empty_group(struct pws3_file *pws_file, const char *group_name)
1401 struct pws3_field *field;
1403 field = RB_FIND(empty_groups_tree, pws_file->empty_groups_tree,
1404 (&(struct pws3_field){ .is_header = 1,
1405 .field_type = PWS3_HEADER_FIELD_EMPTY_GROUPS,
1406 .value.text = (char *)group_name }));
1407 if (field != NULL) {
1408 RB_REMOVE(empty_groups_tree, pws_file->empty_groups_tree,
1409 field);
1412 return (field);
1415 struct pws3_field *
1416 pws3_file_first_empty_group(struct pws3_file *pws_file)
1418 return (RB_MIN(empty_groups_tree, pws_file->empty_groups_tree));
1421 struct pws3_field *
1422 pws3_file_last_empty_group(struct pws3_file *pws_file)
1424 return (RB_MAX(empty_groups_tree, pws_file->empty_groups_tree));
1427 struct pws3_field *
1428 pws3_file_next_empty_group(struct pws3_file *pws_file, struct pws3_field *field)
1430 return (RB_NEXT(empty_groups_tree, pws_file->empty_groups_tree, field));
1433 struct pws3_field *
1434 pws3_file_prev_empty_group(struct pws3_file *pws_file, struct pws3_field *field)
1436 return (RB_PREV(empty_groups_tree, pws_file->empty_groups_tree, field));
1439 void
1440 pws3_file_insert_record(struct pws3_file *pws_file, struct pws3_record *record)
1442 struct pws3_field *uuid_field;
1443 const unsigned char *uuid;
1445 uuid_field = pws3_record_get_field(record, PWS3_RECORD_FIELD_UUID);
1446 PWS_ASSERT(uuid_field != NULL);
1447 uuid = pws3_field_get_uuid(uuid_field);
1449 /* replace existing record */
1450 pws3_record_destroy(pws3_file_remove_record(pws_file, uuid));
1452 RB_INSERT(records_tree, pws_file->records_tree, record);
1455 struct pws3_record *
1456 pws3_file_get_record(struct pws3_file *pws_file,
1457 const unsigned char uuid[static PWS3_UUID_SIZE])
1459 struct pws3_field uuid_field = {
1460 .is_header = 0,
1461 .field_type = PWS3_RECORD_FIELD_UUID
1462 };
1463 struct pws3_record search_record = {
1464 .fields[PWS3_RECORD_FIELD_UUID] = &uuid_field
1465 };
1467 memcpy(uuid_field.value.uuid, uuid, PWS3_UUID_SIZE);
1469 return (RB_FIND(records_tree, pws_file->records_tree, &search_record));
1472 struct pws3_record *
1473 pws3_file_remove_record(struct pws3_file *pws_file,
1474 const unsigned char uuid[static PWS3_UUID_SIZE])
1476 struct pws3_record *record;
1478 record = pws3_file_get_record(pws_file, uuid);
1479 if (record != NULL) {
1480 RB_REMOVE(records_tree, pws_file->records_tree, record);
1483 return (record);
1486 struct pws3_record *
1487 pws3_file_first_record(struct pws3_file *pws_file)
1489 return (RB_MIN(records_tree, pws_file->records_tree));
1492 struct pws3_record *
1493 pws3_file_last_record(struct pws3_file *pws_file)
1495 return (RB_MAX(records_tree, pws_file->records_tree));
1498 struct pws3_record *
1499 pws3_file_next_record(struct pws3_file *pws_file, struct pws3_record *record)
1501 return (RB_NEXT(records_tree, pws_file->records_tree, record));
1504 struct pws3_record *
1505 pws3_file_prev_record(struct pws3_file *pws_file, struct pws3_record *record)
1507 return (RB_PREV(records_tree, pws_file->records_tree, record));