├── hkdf_test.h ├── Makefile.am ├── .gitignore ├── main.h ├── ui.h ├── shares.h ├── hkdf.h ├── Makefile ├── COPYING ├── util.h ├── crypt.h ├── file.h ├── common.h ├── ui.c ├── hkdf.c ├── README ├── file.c ├── crypt.c ├── shares.c ├── util.c ├── hkdf_test.c └── main.c /hkdf_test.h: -------------------------------------------------------------------------------- 1 | void print_hex(const unsigned char *, size_t, size_t); 2 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS=threshcrypt 2 | threshcrypt_SOURCES=crypt.c file.c main.c shares.c ui.c util.c 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.o 4 | *.bin 5 | *.dat 6 | *.tcy 7 | tags 8 | threshcrypt 9 | threshcrypt_* 10 | hkdf_test 11 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | /* threshcrypt main.h 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #ifndef THRESHCRYPT_MAIN_H_ 8 | #define THRESHCRYPT_MAIN_H_ 9 | 10 | void usage(FILE*); 11 | int main(int, char **); 12 | 13 | /* vim: set ts=2 sw=2 et ai si: */ 14 | #endif /* THRESHCRYPT_MAIN_H_ */ 15 | -------------------------------------------------------------------------------- /ui.h: -------------------------------------------------------------------------------- 1 | /* threshcrypt ui.h 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #ifndef THRESHCRYPT_UI_H_ 8 | #define THRESHCRYPT_UI_H_ 9 | 10 | int load_term(struct termios *); 11 | int save_term(struct termios *); 12 | 13 | int get_pass(char *, uint8_t, const char *, const char *, const char *, int); 14 | 15 | /* vim: set ts=2 sw=2 et ai si: */ 16 | #endif /* THRESHCRYPT_UI_H_ */ 17 | -------------------------------------------------------------------------------- /shares.h: -------------------------------------------------------------------------------- 1 | /* threshcrypt shares.h 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #ifndef THRESHCRYPT_SHARES_H_ 8 | #define THRESHCRYPT_SHARES_H_ 9 | 10 | void gen_sharenrs(unsigned char *, unsigned char); 11 | 12 | int tc_gfsplit(header_data_t *); 13 | int tc_gfcombine(header_data_t *); 14 | int unlock_shares(const unsigned char *, size_t, header_data_t *); 15 | 16 | /* vim: set ts=2 sw=2 et ai si: */ 17 | #endif /* THRESHCRYPT_SHARES_H_ */ 18 | -------------------------------------------------------------------------------- /hkdf.h: -------------------------------------------------------------------------------- 1 | #ifndef _HKDF_H_ 2 | #define _HKDF_H_ 3 | 4 | int hkdf_extract(int, const unsigned char *, unsigned long, 5 | const unsigned char *, unsigned long, 6 | unsigned char *, unsigned long *); 7 | 8 | int hkdf_expand(int, const unsigned char *, unsigned long, 9 | const unsigned char *, unsigned long, 10 | unsigned char *, unsigned long); 11 | 12 | int hkdf(int, const unsigned char *, unsigned long, 13 | const unsigned char *, unsigned long, 14 | const unsigned char *, unsigned long, 15 | unsigned char *, unsigned long); 16 | /* vim: set ts=2 sw=2 et ai si: */ 17 | #endif /* _HKDF_H_ */ 18 | 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | HEADERS = main.h ui.h shares.h cyrpt.h util.h file.h common.h 2 | OBJECTS = main.o ui.o shares.o crypt.o util.o file.o 3 | LIBS = -lgfshare -ltomcrypt 4 | COMPILE = gcc $(CFLAGS) -g -pedantic -std=gnu99 -Wall -Wextra -funsigned-char -Wno-pointer-sign 5 | 6 | .c.o: 7 | $(COMPILE) -c $< -o $@ 8 | 9 | threshcrypt: $(OBJECTS) 10 | $(COMPILE) $(OBJECTS) $(LIBS) -o threshcrypt 11 | 12 | threshcrypt_static: $(OBJECTS) 13 | $(COMPILE) -static $(OBJECTS) $(LIBS) -o threshcrypt_static 14 | strip threshcrypt_static 15 | @which upx && upx --best threshcrypt_static || true 16 | 17 | threshcrypt_embed: $(OBJECTS) 18 | $(COMPILE) $(OBJECTS) -Wl,-Bstatic $(LIBS) -Wl,-Bdynamic -o threshcrypt_embed 19 | strip threshcrypt_embed 20 | 21 | hkdf_test: hkdf.o hkdf_test.o 22 | $(COMPILE) hkdf.o hkdf_test.o -ltomcrypt -o hkdf_test 23 | 24 | static: threshcrypt_static 25 | 26 | embed: threshcrypt_embed 27 | 28 | extra: threshcrypt threshcrypt_embed threshcrypt_static 29 | 30 | all: threshcrypt 31 | 32 | clean: 33 | rm -f threshcrypt threshcrypt_* hkdf_test *.o 34 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2012 Ryan Castellucci, All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS 18 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 19 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24 | THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* threshcrypt util.h 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #ifndef THRESCRYPT_UTIL_H_ 8 | #define THRESCRYPT_UTIL_H_ 9 | 10 | #define KEYMEM_SIZE 49152 /* 48kb */ 11 | 12 | void * safe_malloc(size_t); 13 | 14 | #ifndef HAS_MEMSET_S 15 | int memset_s(void *, size_t, int, size_t); 16 | #endif 17 | 18 | #define safe_free(ptr) _safe_free((void **) &ptr, __FILE__, __LINE__) 19 | void _safe_free(void **, const char *, int); 20 | 21 | #define wipe_free(ptr, size) _wipe_free((void **) &ptr, size, __FILE__, __LINE__) 22 | void _wipe_free(void **, size_t, const char *, int); 23 | 24 | /* security critical memory wipe */ 25 | #define MEMWIPE(p, s) memset_s(p, s, 0x33, s) 26 | #define MEMWIPE_V(p, s, v) memset_s(p, s, v, s) 27 | 28 | /* non-security memory zero */ 29 | #define MEMZERO(p, s) memset(p, 0, s) 30 | 31 | void memxor(unsigned char *, const unsigned char *, size_t); 32 | 33 | void fill_rand(unsigned char *, unsigned int); 34 | void fill_prng(unsigned char *, unsigned int); 35 | 36 | void free_header(header_data_t *); 37 | void wipe_shares(header_data_t *); 38 | 39 | void keymem_init(keymem_t *); 40 | void keymem_wipe(keymem_t *); 41 | void keymem_destroy(keymem_t *); 42 | void * keymem_alloc(keymem_t *, size_t); 43 | 44 | void * sec_malloc(size_t); 45 | #define sec_free(ptr) _sec_free((void **) &ptr) 46 | void _sec_free(void **); 47 | 48 | /* vim: set ts=2 sw=2 et ai si: */ 49 | #endif /* THRESCRYPT_UTIL_H_ */ 50 | -------------------------------------------------------------------------------- /crypt.h: -------------------------------------------------------------------------------- 1 | /* threshcrypt crypt.h 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #ifndef THRESHCRYPT_CRYPT_H_ 8 | #define THRESHCRYPT_CRYPT_H_ 9 | 10 | #define encrypt_data(idat, odat, sz, mk, ks, nh, hs) \ 11 | crypt_data(idat, odat, sz, mk, ks, nh, NULL, hs, NULL, MODE_ENCRYPT) 12 | #define decrypt_data(idat, odat, sz, mk, ks, ch, hs) \ 13 | crypt_data(idat, odat, sz, mk, ks, NULL, ch, hs, NULL, MODE_DECRYPT) 14 | #define encrypt_block(idat, odat, sz, mk, ks, nh, hs, iv) \ 15 | crypt_data(idat, odat, sz, mk, ks, nh, NULL, hs, &iv, MODE_ENCRYPT) 16 | #define decrypt_block(idat, odat, sz, mk, ks, ch, hs, iv) \ 17 | crypt_data(idat, odat, sz, mk, ks, NULL, ch, hs, &iv, MODE_DECRYPT) 18 | int crypt_data(const unsigned char *, unsigned char *, size_t, 19 | const unsigned char *, size_t, unsigned char *, 20 | const unsigned char *, size_t, unsigned char **, int); 21 | 22 | int hmac_vrfymem(int, const unsigned char *, unsigned long, 23 | const unsigned char *, unsigned long, 24 | const unsigned char *, unsigned long *); 25 | 26 | #define pbkdf2_vrfy(a, b, c, d, e, f, g, h) \ 27 | _pbkdf2_vrfy(a, b, c, d, e, f, g, (unsigned long *)h) 28 | int _pbkdf2_vrfy(const unsigned char *, unsigned long, 29 | const unsigned char *, unsigned long, 30 | int, int, 31 | const unsigned char *, unsigned long *); 32 | 33 | /* vim: set ts=2 sw=2 et ai si: */ 34 | #endif /* THRESHCRYPT_CRYPT_H_ */ 35 | -------------------------------------------------------------------------------- /file.h: -------------------------------------------------------------------------------- 1 | /* threshcrypt file.h 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #ifndef THRESCRYPT_FILE_H_ 8 | #define THRESCRYPT_FILE_H_ 9 | 10 | int parse_header(unsigned char *, header_data_t *); 11 | int write_header(header_data_t *, int); 12 | 13 | /* File format specification 14 | ; global stuff 15 | 0x0000: magic[8] 16 | 0x0008: version[4] 17 | 18 | 0x000c: cipher[1] 19 | 0x000d: hash[1] 20 | 0x000e: nshares[1] 21 | 0x000f: thresh[1] // if zero, then figure it out 22 | 23 | 0x0010: key_size[1] 24 | 0x0011: hmac_size[1] 25 | 0x0012: share_size[1] 26 | 27 | ; master key stuff 28 | 0x0040: iter[4] 29 | 0x0044: salt[12] 30 | 0x0050: master_hmac[hmac_size] 31 | 32 | share 1..N { 33 | 0x(sharen * 0x0080): 34 | + 0x00: iter[4]; 35 | + 0x0a: salt[12] 36 | + 0x0e: share_ctxt[share_size] 37 | + 0x60: share_hmac[hmac_size]; 38 | } 39 | */ 40 | 41 | #define THRCR_MAGIC {'T','h','r','C','r','\r','\n','\0'} 42 | #define THRCR_MAGIC_LEN 8 43 | 44 | #define THRCR_VERSION {'\0','\0','\1','\x00'} 45 | #define THRCR_VERSION_LEN 4 46 | 47 | /* file format offset macros */ 48 | /* The nested additions should get merged by the compiler */ 49 | #define HDR_MAGIC(x) (x) 50 | #define HDR_VERSION(x) HDR_MAGIC(x + THRCR_MAGIC_LEN) 51 | #define HDR_CIPHER(x) HDR_VERSION(x + THRCR_VERSION_LEN) 52 | #define HDR_HASH(x) HDR_CIPHER(x + 1) 53 | #define HDR_KDF(x) HDR_HASH(x + 1) 54 | #define HDR_NSHARES(x) HDR_KDF(x + 1) 55 | #define HDR_THRESH(x) HDR_NSHARES(x + 1) 56 | #define HDR_KEY_SIZE(x) HDR_THRESH(x + 1) 57 | #define HDR_HMAC_SIZE(x) HDR_KEY_SIZE(x + 1) 58 | #define HDR_SHARE_SIZE(x) HDR_HMAC_SIZE(x + 1) 59 | /* byte 20 */ 60 | 61 | /* master key info starts at byte 64 */ 62 | #define HDR_MASTER_ITER(x) (x + 64) 63 | #define HDR_MASTER_SALT(x) HDR_MASTER_ITER(x + 4) 64 | #define HDR_MASTER_HMAC(x) HDR_MASTER_SALT(x + SALT_SIZE) 65 | 66 | /* share start offsets are (n + 1) * 128 bytes */ 67 | #define HDR_SHR_ITER(x, n) (x + ((n + 1) << 7)) 68 | #define HDR_SHR_SALT(x, n) HDR_SHR_ITER(x + 4, n) 69 | #define HDR_SHR_CTXT(x, n) HDR_SHR_SALT(x + SALT_SIZE, n) 70 | #define HDR_SHR_HMAC(x, n) HDR_SHR_ITER(x + 96, n) 71 | /* end of file format offset macros */ 72 | 73 | /* vim: set ts=2 sw=2 et ai si: */ 74 | #endif /* THRESCRYPT_FILE_H_ */ 75 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* threshcrypt common.h 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #ifndef THRESHCRYPT_COMMON_H_ 8 | #define THRESHCRYPT_COMMON_H_ 9 | 10 | #define THRCR_VERSION_STR "0.0.1.0" 11 | 12 | #define MODE_UNKNOWN 0 13 | #define MODE_ENCRYPT 1 14 | #define MODE_DECRYPT 2 15 | 16 | #define DEFAULT_ITERATIONS 31337 17 | #define DEFAULT_SHARECOUNT 3 18 | #define DEFAULT_THRESHOLD 2 19 | #define DEFAULT_KEY_BITS 256 20 | 21 | #define MAX_ITER_MS 60000 22 | #define MAX_KEY_SIZE 64 23 | #define MAX_HMAC_SIZE 32 24 | #define MAX_SHARE_SIZE 72 25 | 26 | #define SUBKEY_ITER 16 27 | 28 | #define SALT_SIZE 12 29 | #define HMAC_SIZE 16 30 | 31 | #define HEADER_SIZE 32768 32 | #define BUFFER_SIZE 65536 33 | 34 | /* return codes */ 35 | #define THRCR_OK 0 36 | #define THRCR_ERROR 1 37 | #define THRCR_NOMAGIC 2 38 | #define THRCR_BADMODE 3 39 | #define THRCR_BADDATA 4 40 | #define THRCR_BADMAC 5 41 | #define THRCR_ENCERR 6 42 | #define THRCR_DECERR 7 43 | #define THRCR_IOERR 8 44 | #define THRCR_READERR 9 45 | #define THRCR_WRITEERR 10 46 | 47 | #define THRCR_NOOP 64 48 | 49 | /* macro functions */ 50 | #define pbkdf2(p, pl, s, ss, i, h, k, ks) \ 51 | pkcs_5_alg2(p, pl, s, ss, i, h, k, (unsigned long *)ks) 52 | 53 | #ifndef MIN 54 | #define MIN(a,b) ((a)<(b))?(a):(b) 55 | #endif 56 | 57 | #ifndef MAX 58 | #define MAX(a,b) ((a)>(b))?(a):(b) 59 | #endif 60 | 61 | typedef struct { 62 | unsigned char *ptr; 63 | uint32_t off; 64 | uint32_t pos; 65 | uint32_t lck; 66 | uint32_t len; 67 | } keymem_t; 68 | 69 | typedef struct { 70 | unsigned char *key; /* SENSITIVE */ 71 | unsigned char *ptxt; /* SENSITIVE */ 72 | int32_t iter; 73 | unsigned char salt[SALT_SIZE]; 74 | unsigned char *ctxt; 75 | unsigned char *hmac; 76 | } share_data_t; 77 | 78 | typedef struct { 79 | unsigned char *master_key; /* SENSITIVE */ 80 | unsigned char magic[8]; 81 | unsigned char version[4]; 82 | uint8_t cipher; 83 | uint8_t hash; 84 | uint8_t kdf; 85 | uint8_t nshares; 86 | uint8_t thresh; 87 | /* all sizes in bytes */ 88 | uint8_t key_size; 89 | uint8_t hmac_size; 90 | uint8_t share_size; 91 | int32_t master_iter; 92 | unsigned char master_salt[SALT_SIZE]; 93 | unsigned char *master_hmac; 94 | share_data_t *shares; 95 | keymem_t *keymem; 96 | unsigned char *tmp_share_key; 97 | unsigned char *tmp_share_ptxt; 98 | } header_data_t; 99 | 100 | /* vim: set ts=2 sw=2 et ai si: */ 101 | #endif /* THRESHCRYPT_COMMON_H_ */ 102 | -------------------------------------------------------------------------------- /ui.c: -------------------------------------------------------------------------------- 1 | /* threshcrypt ui.c 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* for mlock */ 16 | #include 17 | 18 | #include "common.h" 19 | #include "util.h" 20 | #include "ui.h" 21 | 22 | int load_term(struct termios *termios_p) { 23 | if (tcsetattr(fileno(stdin), TCSANOW, termios_p) != 0) { 24 | fprintf(stderr, "Failed to load terminal settings"); 25 | return -1; 26 | } 27 | return 0; 28 | } 29 | 30 | int save_term(struct termios *termios_p) { 31 | if (tcgetattr(fileno(stdin), termios_p) != 0) { 32 | fprintf(stderr, "Failed to save terminal settings"); 33 | return -1; 34 | } 35 | return 0; 36 | } 37 | 38 | #define get_pass_return(ret) i = ret; goto get_pass_return; 39 | 40 | int get_pass(char *pass, uint8_t pass_size, const char *prompt, 41 | const char *vprompt, const char *rprompt, int verify) { 42 | struct termios old_term, new_term; 43 | uint8_t i, j; 44 | int chr; 45 | char *vpass = sec_malloc(pass_size + sizeof(char)); 46 | 47 | assert(pass_size > 1); 48 | do { 49 | if (save_term(&old_term) != 0) { 50 | get_pass_return(-1); 51 | } 52 | new_term = old_term; 53 | fprintf(stderr, "%s", prompt); 54 | /* Turn off echo */ 55 | new_term.c_lflag &= ~ECHO; 56 | if (load_term(&new_term) != 0) { 57 | get_pass_return(-1); 58 | } 59 | 60 | i = 0; 61 | while (i < pass_size - 1) { 62 | chr = getchar(); 63 | if (chr >= 32 && chr <= 126) { 64 | pass[i] = chr; 65 | i++; 66 | } else if (chr == '\b' && i > 0) { /* backspace */ 67 | pass[i] = '\0'; 68 | i--; 69 | } else if (chr == '\n') { 70 | pass[i] = '\0'; 71 | break; 72 | } 73 | } 74 | /* restore echo */ 75 | if (load_term(&old_term) != 0) { 76 | get_pass_return(-1); 77 | } 78 | if (vprompt != NULL) { 79 | fprintf(stderr, "\033[0G\033[2K"); 80 | j = get_pass(vpass, pass_size, vprompt, NULL, NULL, 0); 81 | if (j != i || memcmp(pass, vpass, i) != 0) { 82 | MEMWIPE(vpass, pass_size); 83 | MEMWIPE(pass, pass_size); 84 | if (verify > 1) { 85 | fprintf(stderr, "%s\n", rprompt); 86 | verify--; 87 | } else { 88 | get_pass_return(-1); 89 | } 90 | } else { 91 | MEMWIPE(vpass, pass_size); 92 | assert(i == j); 93 | assert(i == strlen(pass)); 94 | get_pass_return(i); 95 | } 96 | } else { 97 | break; 98 | } 99 | } while (verify > 0); 100 | fprintf(stderr, "\n"); 101 | 102 | get_pass_return: 103 | sec_free(vpass); 104 | return i; 105 | } 106 | /* vim: set ts=2 sw=2 et ai si: */ 107 | -------------------------------------------------------------------------------- /hkdf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "hkdf.h" 8 | 9 | #ifndef MIN 10 | #define MIN(a,b) ((a)<(b))?(a):(b) 11 | #endif 12 | 13 | /* This is mostly just a wrapper around hmac_memory */ 14 | int hkdf_extract(int hash_idx, const unsigned char *salt, unsigned long saltlen, 15 | const unsigned char *in, unsigned long inlen, 16 | unsigned char *out, unsigned long *outlen) { 17 | /* libtomcrypt chokes on a zero length HMAC key, so we need to check for 18 | that. HMAC specifies that keys shorter than the hash's blocksize are 19 | 0 padded to the block size. HKDF specifies that a NULL salt is to be 20 | substituted with a salt comprised of hashLen 0 bytes. HMAC's padding 21 | means that in either case the HMAC is actually using a blocksize long 22 | zero filled key. Unless blocksize < hashLen (which wouldn't make any 23 | sense), we can use a single 0 byte as the HMAC key and still generate 24 | valid results for HKDF. */ 25 | /* if (((long)salt & saltlen) == 0) { <- would save a few instructions */ 26 | if (salt == NULL || saltlen == 0) { 27 | return hmac_memory(hash_idx, "", 1, in, inlen, out, outlen); 28 | } else { 29 | return hmac_memory(hash_idx, salt, saltlen, in, inlen, out, outlen); 30 | } 31 | } 32 | 33 | int hkdf_expand(int hash_idx, const unsigned char *in, unsigned long inlen, 34 | const unsigned char *info, unsigned long infolen, 35 | unsigned char *out, unsigned long outlen) { 36 | const unsigned long hashsize = hash_descriptor[hash_idx].hashsize; 37 | int err; 38 | unsigned char N; 39 | unsigned long Noutlen, outoff; 40 | 41 | unsigned char *T, *dat; 42 | unsigned long Tlen, datlen; 43 | 44 | /* RFC5869 parameter restrictions */ 45 | if (inlen < hashsize || outlen > hashsize * 255) 46 | return CRYPT_INVALID_ARG; 47 | if (info == NULL && infolen != 0) 48 | return CRYPT_INVALID_ARG; 49 | assert(out != NULL); 50 | 51 | Tlen = hashsize + infolen + 1; 52 | T = XMALLOC(Tlen); /* Replace with static buffer? */ 53 | if (T == NULL) { 54 | return CRYPT_MEM; 55 | } 56 | XMEMCPY(T + hashsize, info, infolen); 57 | 58 | /* HMAC data T(1) doesn't include a previous hash value */ 59 | dat = T + hashsize; 60 | datlen = Tlen - hashsize; 61 | 62 | N = 0; 63 | outoff = 0; /* offset in out to write to */ 64 | while (1) { /* an exit condition breaks mid-loop */ 65 | Noutlen = MIN(hashsize, outlen - outoff); 66 | T[Tlen - 1] = ++N; 67 | if ((err = hmac_memory(hash_idx, in, inlen, dat, datlen, 68 | out + outoff, &Noutlen)) != CRYPT_OK) { 69 | XMEMSET(T, 0, Tlen); /* wipe */ 70 | XFREE(T); 71 | return err; 72 | } 73 | outoff += Noutlen; 74 | 75 | if (outoff >= outlen) /* loop exit condition */ 76 | break; 77 | 78 | /* All subsequent HMAC data T(N) DOES include the previous hash value */ 79 | XMEMCPY(T, out + hashsize * (N-1), hashsize); 80 | if (N == 1) { 81 | dat = T; 82 | datlen = Tlen; 83 | } 84 | } 85 | XMEMSET(T, 0, Tlen); /* wipe */ 86 | XFREE(T); 87 | return CRYPT_OK; 88 | } 89 | 90 | /* all in one step */ 91 | int hkdf(int hash_idx, const unsigned char *salt, unsigned long saltlen, 92 | const unsigned char *in, unsigned long inlen, 93 | const unsigned char *info, unsigned long infolen, 94 | unsigned char *out, unsigned long outlen) { 95 | unsigned long hashsize = hash_descriptor[hash_idx].hashsize; 96 | int err; 97 | unsigned char *extracted = XMALLOC(hashsize); /* replace with static buffer? */ 98 | if (extracted == NULL) { 99 | return CRYPT_MEM; 100 | } 101 | if ((err = hkdf_extract(hash_idx, salt, saltlen, in, inlen, extracted, &hashsize)) != 0) { 102 | XMEMSET(extracted, 0, hashsize); /* wipe */ 103 | XFREE(extracted); 104 | return err; 105 | } 106 | err = hkdf_expand(hash_idx, extracted, hashsize, info, infolen, out, outlen); 107 | XMEMSET(extracted, 0, hashsize); /* wipe */ 108 | XFREE(extracted); 109 | return err; 110 | } 111 | 112 | 113 | /* vim: set ts=2 sw=2 et ai si: */ 114 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | DESCRIPTION 2 | 3 | threshcrypt - A password-based implementation of threshold encryption 4 | 5 | LICENSE 6 | 7 | Simplified BSD License. See the included 'COPYING' file. 8 | 9 | INSTALL 10 | 11 | Just run 'make' and copy the binary somewhere. You can also run 'make static' 12 | to build a staticly linked version, which will be compressed with UPX if it is 13 | installed in your $PATH. Tested on x86_64 & i386 Linux with gcc and glibc. t 14 | 15 | USAGE 16 | 17 | Encrypt: 18 | threshcrypt -t threshold -n shares input_file [output_file] 19 | 20 | Decrypt: 21 | threshcrypt input_file [output_file] 22 | 23 | See main.c for other options until I update this file. 24 | 25 | NOTES 26 | 27 | * This is *ALPHA* software. No attempt will be made to maintain file format 28 | backwards compatibility until a beta relase. 29 | 30 | * Currently, no automatic adjustment is done to the work factor of the KDF 31 | to compensate for the number of encrypted shares it needs to be run against. 32 | With a large number of shares the default parameters will be rather slow to 33 | verify a share password. 34 | 35 | CRYPTO DESIGN 36 | 37 | Operation overview: 38 | 39 | When encrypting, first the user is prompted to enter passwords for each 40 | share. As each password is confirmed, it is fed to PBKDF2 along with a 41 | random 12 byte salt in order to generate a 'share key'. As soon as the 42 | share key has been generated, the password is zeroed in memory. 43 | 44 | Once all the share keys have been generated, a random master key is 45 | generated, using the share keys as an additional entropy source (this 46 | helps if your RNG/PRNG is poor quality). The master key is then 47 | MAC'd and split into a series of shares using Shamir's secret sharing. 48 | Each share is encrypted with the corresponding share key and then zeroed 49 | in memory. 50 | 51 | Finally, the plaintext is read in chunks which are then encrypted and MAC'd 52 | to prevent the data from being read/tampered with. 53 | 54 | 55 | For decryption, the user is prompted to enter 'any password' with an 56 | indication of how many shares have been unlocked and how many are needed 57 | to recover the master key. For each password, *all* shares are checked in 58 | turn and a running total how how many more shares have been been unlocked 59 | is kept (multiple shares can have the same password). Once enough shares 60 | have been decrypted, the master key is recovered. The master key is then 61 | tested against the checksum. If the master key looks good, then the 62 | ciphertext chunks will be verified and decrypted. 63 | 64 | 65 | 66 | Other notes: 67 | 68 | The chunked file data is stored using CTR+HMAC encrypt-then-mac. 69 | I may later add options to GCM / EAX / other authenticated encryption 70 | modes, but it's not worth the trouble right now. Simmilarly, currently the 71 | only supported ciphers and hashes are AES and SHA-256 respectively. 72 | 73 | I (ab)use PBKDF2 a bit as a key expansion function. This is because if you 74 | specify a larger than hashlen for PBKDF2's output size and use that key 75 | material for anything other than a single large key it can be attacked 76 | in parallel. I'd rather use the HKDF expansion function to generate the 77 | subkeys, but I didn't find a library and didn't want to screw it up^W^W^W 78 | implement it myself. 79 | 80 | I *ALSO* (ab)use PBKDF2 with a small number of iterations as a replacement 81 | for HMAC to verify the master key. Plain HMAC would probably be fine for 82 | that, but I'm paranoid and this is how LUKS does master key verification. 83 | This scheme will hopefully add some additional protection in case the hash 84 | algorithm used is broken. 85 | 86 | TODO 87 | 88 | * add support for other KDFs besides pbkdf2, starting with scrypt 89 | * implement locked_malloc/unlock_free and use it for libgfshare and libtomcrypt 90 | structs 91 | - should page align and round up size to whole pages to ensure freeing has no 92 | side effects 93 | * copy code from crypt_data to ctr_hmac_block which will take a ctr sturct as 94 | an argument. 95 | - wrap with ctr_hmac_dec_block and ctr_hmac_enc_block macros 96 | - convert crypt_data to a wrapper for the above 97 | * implement ctr_hmac_fd wrapped by ctr_hmac_dec_fd and ctr_hmac_enc_fd 98 | * mlock sensitive memory regions in libgfshare and libtomcrypt before using them 99 | * do user interaction on /dev/tty so that data can be accepted on stdin 100 | -------------------------------------------------------------------------------- /file.c: -------------------------------------------------------------------------------- 1 | /* threshcrypt file.c 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "common.h" 18 | #include "util.h" 19 | #include "file.h" 20 | 21 | /* buf should be a 32768+ byte buffer containing header data from a file */ 22 | int parse_header(unsigned char *buf, header_data_t *header) { 23 | unsigned char magic[THRCR_MAGIC_LEN] = THRCR_MAGIC; 24 | /*unsigned char version[THRCR_VERSION_LEN] = THRCR_VERSION;*/ 25 | 26 | share_data_t *share; 27 | 28 | /* Check if the data starts with our magic before attempting to parse */ 29 | if (memcmp(HDR_MAGIC(buf), magic, THRCR_MAGIC_LEN)) 30 | return THRCR_NOMAGIC; 31 | 32 | /* Identification data */ 33 | memcpy(header->magic, HDR_MAGIC(buf), THRCR_MAGIC_LEN); 34 | memcpy(header->version, HDR_VERSION(buf), THRCR_VERSION_LEN); 35 | 36 | /* Parameter data */ 37 | memcpy(&(header->cipher), HDR_CIPHER(buf), 1); 38 | memcpy(&(header->hash), HDR_HASH(buf), 1); 39 | memcpy(&(header->nshares), HDR_NSHARES(buf), 1); 40 | memcpy(&(header->thresh), HDR_THRESH(buf), 1); 41 | memcpy(&(header->key_size), HDR_KEY_SIZE(buf), 1); 42 | memcpy(&(header->hmac_size), HDR_HMAC_SIZE(buf), 1); 43 | memcpy(&(header->share_size), HDR_SHARE_SIZE(buf), 1); 44 | 45 | /* Sanity check some critical values */ 46 | if (header->key_size > MAX_KEY_SIZE || header->hmac_size > MAX_HMAC_SIZE || 47 | header->share_size > MAX_SHARE_SIZE || header->nshares < 1 || 48 | header->thresh < 1 || header->thresh > header->nshares) 49 | return THRCR_BADDATA; 50 | 51 | header->master_hmac = safe_malloc(header->hmac_size); 52 | header->shares = safe_malloc(header->nshares * sizeof(share_data_t)); 53 | 54 | /* Master key data */ 55 | LOAD32H(header->master_iter, HDR_MASTER_ITER(buf)); 56 | memcpy(header->master_salt, HDR_MASTER_SALT(buf), SALT_SIZE); 57 | memcpy(header->master_hmac, HDR_MASTER_HMAC(buf), header->hmac_size); 58 | 59 | uint8_t i; 60 | /* Share data */ 61 | for (i = 0; i < header->nshares; i++) { 62 | share = &(header->shares[i]); 63 | 64 | share->ctxt = safe_malloc(header->share_size); 65 | share->hmac = safe_malloc(header->hmac_size); 66 | 67 | LOAD32H(share->iter, HDR_SHR_ITER(buf, i)); 68 | memcpy(share->salt, HDR_SHR_SALT(buf, i), SALT_SIZE); 69 | memcpy(share->ctxt, HDR_SHR_CTXT(buf, i), header->share_size); 70 | memcpy(share->hmac, HDR_SHR_HMAC(buf, i), header->hmac_size); 71 | } 72 | 73 | return THRCR_OK; 74 | } 75 | 76 | int write_header(header_data_t *header, int fd) { 77 | unsigned char buf[HEADER_SIZE]; 78 | 79 | /* zero it - don't want to leak uninitialized memory to disk */ 80 | MEMZERO(buf, HEADER_SIZE); 81 | 82 | /* Identification data */ 83 | memcpy(HDR_MAGIC(buf), header->magic, 8); 84 | memcpy(HDR_VERSION(buf), header->version, 4); 85 | 86 | /* Parameter data */ 87 | memcpy(HDR_CIPHER(buf), &(header->cipher), 1); 88 | memcpy(HDR_HASH(buf), &(header->hash), 1); 89 | memcpy(HDR_NSHARES(buf), &(header->nshares), 1); 90 | memcpy(HDR_THRESH(buf), &(header->thresh), 1); 91 | memcpy(HDR_KEY_SIZE(buf), &(header->key_size), 1); 92 | memcpy(HDR_HMAC_SIZE(buf), &(header->hmac_size), 1); 93 | memcpy(HDR_SHARE_SIZE(buf), &(header->share_size), 1); 94 | 95 | /* Master key data */ 96 | STORE32H(header->master_iter, HDR_MASTER_ITER(buf)); 97 | memcpy(HDR_MASTER_SALT(buf), header->master_salt, SALT_SIZE); 98 | memcpy(HDR_MASTER_HMAC(buf), header->master_hmac, header->hmac_size); 99 | 100 | uint8_t i; 101 | /* Share data */ 102 | for (i = 0; i < header->nshares; i++) { 103 | share_data_t *share = &(header->shares[i]); 104 | STORE32H(share->iter, HDR_SHR_ITER(buf, i)); 105 | memcpy(HDR_SHR_SALT(buf, i), share->salt, SALT_SIZE); 106 | memcpy(HDR_SHR_CTXT(buf, i), share->ctxt, header->share_size); 107 | memcpy(HDR_SHR_HMAC(buf, i), share->hmac, header->hmac_size); 108 | } 109 | 110 | if (write(fd, buf, HEADER_SIZE) < HEADER_SIZE) { 111 | fprintf(stderr, "Short write on header\n"); 112 | return THRCR_WRITEERR; 113 | } 114 | 115 | return THRCR_OK; 116 | } 117 | 118 | /* vim: set ts=2 sw=2 et ai si: */ 119 | -------------------------------------------------------------------------------- /crypt.c: -------------------------------------------------------------------------------- 1 | /* threshcrypt crypt.c 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /* for mlock */ 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | #include 20 | 21 | #include "common.h" 22 | #include "util.h" 23 | #include "crypt.h" 24 | 25 | #define crypt_data_return(r) ret = r; goto crypt_data_return; 26 | 27 | /* XXX */ 28 | /* IF YOU CALL THIS MULTIPLE TIMES WITH THE SAME KEY YOU MUST PROVIDE AN IV POINTER! */ 29 | int crypt_data(const unsigned char *data_in, 30 | unsigned char *data_out, size_t data_size, 31 | const unsigned char *data_mkey, size_t data_mkey_size, 32 | unsigned char *data_new_hmac, 33 | const unsigned char *data_chk_hmac, 34 | size_t data_hmac_size, 35 | unsigned char **IV_start, 36 | int mode) { 37 | if (mode != MODE_ENCRYPT && mode != MODE_DECRYPT) { 38 | fprintf(stderr, "crypt_data called with invalid mode %d\n", mode); 39 | return -1; 40 | } 41 | 42 | symmetric_CTR ctr; 43 | #ifdef _POSIX_MEMLOCK_RANGE 44 | if (mlock(&ctr, sizeof(ctr)) != 0) { 45 | fprintf(stderr, "WARNING: mlock failed at %s:%d - ", __FILE__, __LINE__); 46 | perror(""); 47 | } 48 | #endif 49 | int err; 50 | int ret = 0; /* return code */ 51 | unsigned char *IV; 52 | unsigned long IV_size = 16; 53 | int hash_idx = find_hash("sha256"); 54 | size_t data_ckey_size, data_hkey_size; 55 | data_ckey_size = data_hkey_size = data_mkey_size; 56 | unsigned char *subkeys = safe_malloc(data_ckey_size + data_hkey_size); 57 | #ifdef _POSIX_MEMLOCK_RANGE 58 | if (mlock(subkeys, data_ckey_size + data_hkey_size) != 0) { 59 | fprintf(stderr, "WARNING: mlock failed at %s:%d - ", __FILE__, __LINE__); 60 | perror(""); 61 | } 62 | #endif 63 | unsigned char *data_ckey = subkeys + 0; 64 | unsigned char *data_hkey = subkeys + data_ckey_size; 65 | 66 | pbkdf2(data_mkey, data_mkey_size, "H", 1, SUBKEY_ITER, hash_idx, data_hkey, &data_hkey_size); 67 | pbkdf2(data_mkey, data_mkey_size, "C", 1, SUBKEY_ITER, hash_idx, data_ckey, &data_ckey_size); 68 | if (IV_start == NULL || *IV_start == NULL) { 69 | IV = safe_malloc(IV_size); 70 | /* fprintf(stderr, "Initializing key-based IV\n"); */ 71 | /* This is at least as secure as starting with a zeroed IV */ 72 | pbkdf2(data_mkey, data_mkey_size, "I", 1, SUBKEY_ITER, hash_idx, IV, &IV_size); 73 | } 74 | if (IV_start != NULL) { 75 | if (*IV_start != NULL) { 76 | /* fprintf(stderr, "IV = *IV_start\n"); */ 77 | IV = *IV_start; 78 | } else { 79 | /* fprintf(stderr, "*IV_start = IV\n"); */ 80 | *IV_start = IV; 81 | } 82 | } 83 | 84 | if (mode == MODE_DECRYPT && data_chk_hmac != NULL) { 85 | if ((err = hmac_vrfymem(hash_idx, 86 | data_hkey, data_hkey_size, 87 | data_in, data_size, data_chk_hmac, 88 | (long unsigned int *)&data_hmac_size)) != CRYPT_OK) { 89 | crypt_data_return(THRCR_BADMAC); 90 | } 91 | } 92 | 93 | /* LTC_CTR_RFC3686 is needed to avoid reusing a counter value. */ 94 | if ((err = ctr_start(find_cipher("aes"), IV, data_ckey, data_ckey_size, 0, 95 | CTR_COUNTER_BIG_ENDIAN | LTC_CTR_RFC3686, &ctr)) != CRYPT_OK) { 96 | fprintf(stderr, "Error initializing cipher: %d\n", err); 97 | crypt_data_return(-1); 98 | } 99 | 100 | /* ctr_encrypt is used for both encryption and decryption */ 101 | if ((err = ctr_encrypt(data_in, data_out, data_size, &ctr)) != CRYPT_OK) { 102 | fprintf(stderr, "ctr_encrypt error: %s\n", error_to_string(err)); 103 | ctr_done(&ctr); /* done with cipher, clean up keys */ 104 | crypt_data_return(-1); 105 | } 106 | ctr_done(&ctr); /* done with cipher, clean up keys */ 107 | 108 | if (mode == MODE_ENCRYPT && data_new_hmac != NULL) { 109 | if ((err = hmac_memory(hash_idx, 110 | data_hkey, data_hkey_size, 111 | data_out, data_size, data_new_hmac, 112 | (long unsigned int *)&data_hmac_size)) != CRYPT_OK) { 113 | fprintf(stderr, "hmac error: %s\n", error_to_string(err)); 114 | crypt_data_return(-1); 115 | } 116 | } 117 | 118 | crypt_data_return: 119 | /* before actually returning, make sure key material isn't in memory */ 120 | MEMWIPE(&ctr, sizeof(ctr)); 121 | MEMWIPE(subkeys, data_ckey_size + data_hkey_size); 122 | #ifdef _POSIX_MEMLOCK_RANGE 123 | munlock(subkeys, data_ckey_size + data_hkey_size); 124 | #endif 125 | safe_free(subkeys); 126 | /* save the IV */ 127 | if (IV_start != NULL && *IV_start != NULL) { 128 | /* fprintf(stderr, "*IV_start = ctr.ctr\n"); */ 129 | ctr_getiv(*IV_start, &IV_size, &ctr); 130 | } else { 131 | safe_free(IV); 132 | } 133 | return ret; 134 | } 135 | 136 | /* Like hmac_memory, but verifies */ 137 | int hmac_vrfymem(int hash, 138 | const unsigned char *key, unsigned long keylen, 139 | const unsigned char *in, unsigned long inlen, 140 | const unsigned char *vrfy, unsigned long *outlen) { 141 | unsigned char *out = safe_malloc(*outlen); 142 | int err; 143 | if ((err = hmac_memory(hash, key, keylen, in, inlen, out, outlen)) != CRYPT_OK) { 144 | safe_free(out); 145 | fprintf(stderr, "hmac_vrfymem: hmac_memory failed\n"); 146 | return err; 147 | } 148 | if (memcmp(vrfy, out, *outlen) != 0) { 149 | safe_free(out); 150 | return CRYPT_ERROR; 151 | } 152 | safe_free(out); 153 | return CRYPT_OK; 154 | } 155 | 156 | int _pbkdf2_vrfy(const unsigned char *pass, unsigned long pass_len, 157 | const unsigned char *salt, unsigned long salt_len, 158 | int iter, int hash_idx, 159 | const unsigned char *vrfy, unsigned long *vrfylen) { 160 | unsigned char *out = safe_malloc(*vrfylen); 161 | int err; 162 | if ((err = pbkdf2(pass, pass_len, salt, salt_len, 163 | iter, hash_idx, out, vrfylen)) != CRYPT_OK) { 164 | safe_free(out); 165 | return err; 166 | } 167 | if (memcmp(vrfy, out, *vrfylen) != 0) { 168 | safe_free(out); 169 | return CRYPT_ERROR; 170 | } 171 | safe_free(out); 172 | return CRYPT_OK; 173 | } 174 | 175 | /* vim: set ts=2 sw=2 et ai si: */ 176 | -------------------------------------------------------------------------------- /shares.c: -------------------------------------------------------------------------------- 1 | /* threshcrypt shares.c 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | * Portions of this file are derived from libgfshare examples which are 6 | * Copyright Daniel Silverstone 2006-2011 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include "common.h" 20 | #include "util.h" 21 | #include "shares.h" 22 | #include "crypt.h" 23 | 24 | /* put n random non-zero uchars into *sharenrs with no duplicates */ 25 | void gen_sharenrs(unsigned char *sharenrs, unsigned char n) { 26 | int i; 27 | unsigned int r; 28 | unsigned char t; 29 | unsigned char buf[255]; 30 | 31 | for (i = 0; i < 255; i++) buf[i] = i + 1; 32 | 33 | while(i > 0) { 34 | do { 35 | fill_prng((unsigned char *)&r, sizeof(r)); 36 | } while (r > (UINT_MAX - (UINT_MAX % i) - 1)); /* avoids bias */ 37 | r %= i--; 38 | 39 | t = buf[i]; 40 | buf[i] = buf[r]; 41 | buf[r] = t; 42 | } 43 | 44 | assert(i == 0); 45 | 46 | while (i < n) { 47 | sharenrs[i] = buf[i]; 48 | i++; 49 | } 50 | } 51 | 52 | int tc_gfsplit(header_data_t *header) { 53 | int err; 54 | unsigned int i; 55 | share_data_t *share; 56 | gfshare_ctx *G = NULL; 57 | 58 | assert(header->nshares > 0); 59 | assert(header->thresh > 0); 60 | assert(header->thresh <= header->nshares); 61 | 62 | unsigned char *sharenrs = safe_malloc(header->nshares); 63 | 64 | /* master key setup */ 65 | fill_rand(header->master_key, header->key_size); 66 | fill_prng(header->master_salt, SALT_SIZE); 67 | /* Add (hopefully) some extra protection against potentially weak RNG 68 | * TODO use a hash for this... */ 69 | for (i = 0; i < header->nshares; i++ ) { 70 | share = &(header->shares[i]); 71 | memxor(header->master_key, share->key, header->key_size); 72 | } 73 | 74 | size_t hmac_size = header->hmac_size; 75 | if ((err = pbkdf2(header->master_key, header->key_size, 76 | header->master_salt, SALT_SIZE, SUBKEY_ITER, 77 | find_hash("sha256"), header->master_hmac, &hmac_size)) != CRYPT_OK) { 78 | fprintf(stderr, "PBKDF2 failed: %d\n", err); 79 | } 80 | /* end master key setup */ 81 | 82 | #ifdef CRYPTODEBUG 83 | fprintf(stderr, "MasterSlt: "); 84 | for (i = 0;i < SALT_SIZE;i++) { 85 | fprintf(stderr, "%02x", header->master_salt[i]); 86 | } 87 | fprintf(stderr, "\n"); 88 | 89 | fprintf(stderr, "MasterKey: "); 90 | for (i = 0;i < header->key_size;i++) { 91 | fprintf(stderr, "%02x", header->master_key[i]); 92 | } 93 | fprintf(stderr, "\n"); 94 | 95 | fprintf(stderr, "MasterMAC: "); 96 | for (i = 0;i < header->hmac_size;i++) { 97 | fprintf(stderr, "%02x", header->master_hmac[i]); 98 | } 99 | fprintf(stderr, "\n\n"); 100 | #endif 101 | 102 | if (header->thresh > 1) { 103 | gen_sharenrs(sharenrs, header->nshares); 104 | /* TODO figure out how to mlock G's memory */ 105 | G = gfshare_ctx_init_enc(sharenrs, header->nshares, header->thresh, header->key_size); 106 | /* G->buffer could be free'd and reinitialized with size G->buffersize - wrapper? */ 107 | if (G == NULL) { 108 | perror("gfshare_ctx_init_enc"); 109 | return -1; 110 | } 111 | gfshare_ctx_enc_setsecret(G, header->master_key); 112 | } 113 | 114 | for (i = 0; i < header->nshares; i++ ) { 115 | share = &(header->shares[i]); 116 | if (share->ptxt == NULL) 117 | share->ptxt = keymem_alloc(header->keymem, header->share_size); 118 | share->ctxt = safe_malloc(header->share_size); 119 | share->hmac = safe_malloc(header->hmac_size); 120 | 121 | share->ptxt[0] = sharenrs[i]; 122 | if (header->thresh == 1) { 123 | memcpy(share->ptxt + 1, header->master_key, header->key_size); 124 | } else { 125 | gfshare_ctx_enc_getshare(G, i, share->ptxt + 1); 126 | } 127 | 128 | #ifdef CRYPTODEBUG 129 | fprintf(stderr, "Salt [%02x]: ", i); 130 | for (j = 0;j < SALT_SIZE;j++) { 131 | fprintf(stderr, "%02x", share->salt[j]); 132 | } 133 | fprintf(stderr, "\n"); 134 | 135 | fprintf(stderr, "Key [%02x]: ", i); 136 | for (j = 0;j < header->key_size;j++) { 137 | fprintf(stderr, "%02x", share->key[j]); 138 | } 139 | fprintf(stderr, "\n"); 140 | 141 | fprintf(stderr, "Share[%02x]: ", i); 142 | for (j = 0;j < header->share_size;j++) { 143 | fprintf(stderr, "%02x", share->ptxt[j]); 144 | } 145 | fprintf(stderr, "\n"); 146 | #endif /* CRYPTODEBUG */ 147 | 148 | if ((err = encrypt_data(share->ptxt, share->ctxt, header->share_size, 149 | share->key, header->key_size, 150 | share->hmac, header->hmac_size)) != 0) { 151 | fprintf(stderr, "Encrypt failed: %d\n", err); 152 | } 153 | 154 | #ifdef CRYPTODEBUG 155 | fprintf(stderr, "Crypt[%02x]: ", i); 156 | for (j = 0;j < header->share_size;j++) { 157 | 158 | fprintf(stderr, "%02x", share->ctxt[j]); 159 | } 160 | fprintf(stderr, "\n"); 161 | #endif /* CRYPTODEBUG */ 162 | 163 | #ifdef TESTDECRYPT 164 | if ((err = decrypt_data(share->ctxt, share->ptxt, header->share_size, 165 | share->key, header->key_size, 166 | share->hmac, header->hmac_size)) != 0) { 167 | fprintf(stderr, "Decrypt failed: %d\n", err); 168 | } 169 | #ifdef CRYPTODEBUG 170 | fprintf(stderr, "Plain[%02x]: ", i); 171 | for (j = 0;j < header->share_size;j++) { 172 | fprintf(stderr, "%02x", share->ptxt[j]); 173 | } 174 | fprintf(stderr, "\n"); 175 | #endif /* CRYPTODEBUG */ 176 | #endif /* TESTDECRYPT */ 177 | MEMWIPE(share->ptxt, header->share_size); 178 | MEMWIPE(share->key, header->key_size); 179 | #ifdef CRYPTODEBUG 180 | fprintf(stderr, "MAC [%02x]: ", i); 181 | for (j = 0;j < header->hmac_size;j++) { 182 | fprintf(stderr, "%02x", share->hmac[j]); 183 | } 184 | fprintf(stderr, "\n\n"); 185 | #endif /* CRYPTODEBUG */ 186 | } 187 | 188 | /* wipe sensitive data and free memory */ 189 | if (header->thresh > 1) { 190 | gfshare_ctx_free(G); 191 | } 192 | wipe_free(sharenrs, header->nshares); 193 | /* header->master_key stays so that it can be used for file encryption */ 194 | return 0; 195 | } 196 | 197 | int unlock_shares(const unsigned char *pass, size_t pass_len, header_data_t *header) { 198 | int i, err, ret; 199 | ret = 0; 200 | if (header->tmp_share_key == NULL) 201 | header->tmp_share_key = keymem_alloc(header->keymem, header->key_size); 202 | if (header->tmp_share_ptxt == NULL) 203 | header->tmp_share_ptxt = keymem_alloc(header->keymem, header->share_size); 204 | 205 | for (i = 0; i < header->nshares; i++) { 206 | share_data_t *share; 207 | share = &(header->shares[i]); 208 | 209 | assert(share->key == NULL); 210 | if (share->ptxt == NULL) { 211 | share->key = header->tmp_share_key; 212 | share->ptxt = header->tmp_share_ptxt; 213 | 214 | unsigned long key_size; 215 | key_size = header->key_size; 216 | 217 | fprintf(stderr, "\033[0G\033[2KChecking share %d", i); 218 | if ((err = pbkdf2(pass, pass_len, share->salt, SALT_SIZE, share->iter, 219 | find_hash("sha256"), share->key, &key_size)) != CRYPT_OK) { 220 | /* on an hmac failure (wrong password) */ 221 | MEMWIPE(share->key, header->key_size); 222 | continue; 223 | } 224 | if ((err = decrypt_data(share->ctxt, share->ptxt, header->share_size, 225 | share->key, header->key_size, 226 | share->hmac, header->hmac_size)) == 0) { 227 | fprintf(stderr, "\033[0G\033[2KUnlocked share %d\n", i); 228 | /* new ptxt region for the next share */ 229 | header->tmp_share_ptxt = keymem_alloc(header->keymem, header->share_size); 230 | ret++; 231 | } else { 232 | MEMWIPE(share->ptxt, header->share_size); 233 | share->ptxt = NULL; 234 | } 235 | MEMWIPE(share->key, header->key_size); 236 | share->key = NULL; 237 | } 238 | } 239 | fprintf(stderr, "\033[0G\033[2K"); 240 | return ret; 241 | } 242 | 243 | int tc_gfcombine(header_data_t *header) { 244 | int i, loaded, err, ret; 245 | err = ret = loaded = 0; 246 | 247 | assert(header->master_key == NULL); 248 | header->master_key = keymem_alloc(header->keymem, header->key_size); 249 | 250 | if (header->thresh == 1) { 251 | for (i = 0; i < header->nshares; i++) { 252 | share_data_t *share; 253 | share = &(header->shares[i]); 254 | if (share->ptxt != NULL) { 255 | memcpy(header->master_key, share->ptxt + 1, header->key_size); 256 | break; 257 | } 258 | } 259 | } else { 260 | gfshare_ctx *G; 261 | unsigned char sharenrs[256]; 262 | MEMZERO(sharenrs, sizeof(sharenrs)); 263 | 264 | /* Initialize a gfshare context for decoding */ 265 | G = gfshare_ctx_init_dec(sharenrs, header->thresh, header->key_size); 266 | 267 | for (i = 0; i < header->nshares; i++) { 268 | share_data_t *share; 269 | share = &(header->shares[i]); 270 | if (share->ptxt != NULL) { 271 | sharenrs[loaded] = share->ptxt[0]; 272 | gfshare_ctx_dec_newshares(G, sharenrs); 273 | gfshare_ctx_dec_giveshare(G, loaded, share->ptxt + 1); 274 | if (++loaded == header->thresh) 275 | break; 276 | } 277 | } 278 | gfshare_ctx_dec_extract(G, header->master_key); 279 | gfshare_ctx_free(G); 280 | } 281 | return ret; 282 | } 283 | 284 | /* vim: set ts=2 sw=2 et ai si: */ 285 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* threshcrypt util.c 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* for mlock */ 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include "common.h" 23 | #include "util.h" 24 | 25 | void * safe_malloc(size_t size) { 26 | void *ptr = malloc(size); 27 | if (ptr == NULL) { 28 | perror("malloc"); 29 | fprintf(stderr, "malloc(%ud) returned NULL\n", (unsigned int)size); 30 | exit(EXIT_FAILURE); 31 | } 32 | MEMZERO(ptr, size); 33 | return ptr; 34 | } 35 | 36 | /* from CMU CERT MSC06-C */ 37 | #ifndef HAS_MEMSET_S 38 | int memset_s(void *v, size_t smax, int c, size_t n) { 39 | if (v == NULL) return EINVAL; 40 | if (smax > SIZE_MAX) return EINVAL; 41 | if (n > smax) return EINVAL; 42 | 43 | volatile unsigned char *p = v; 44 | while (smax-- && n--) { 45 | *p++ = c; 46 | } 47 | 48 | return 0; 49 | } 50 | #endif 51 | 52 | /* util.h: #define safe_free(ptr) _safe_free((void **) &ptr, __FILE__, __LINE__) */ 53 | void _safe_free(void **ptr, const char *file, int line) { 54 | if (*ptr != NULL) { 55 | free(*ptr); 56 | *ptr = NULL; 57 | } else { 58 | fprintf(stderr, "Warning: [file %s, line %d]: called safe_free on a null pointer\n", file, line); 59 | } 60 | } 61 | 62 | /* util.h: #define wipe_free(ptr, size) _wipe_free((void **) &ptr, size, __FILE__, __LINE__) */ 63 | void _wipe_free(void **ptr, size_t size, const char *file, int line) { 64 | if (*ptr != NULL) { 65 | MEMWIPE(*ptr, size); 66 | free(*ptr); 67 | *ptr = NULL; 68 | } else { 69 | fprintf(stderr, "Warning: [file %s, line %d]: called wipe_free on a null pointer\n", file, line); 70 | } 71 | } 72 | 73 | void memxor(unsigned char *p1, const unsigned char *p2, size_t size) { 74 | size_t i = 0; 75 | for (i = 0;i < size;i++) { 76 | p1[i] ^= p2[i]; 77 | } 78 | } 79 | 80 | /* for non-key secret data - can be freed */ 81 | void * sec_malloc(size_t size) { 82 | assert(sizeof(long) >= sizeof(void *)); 83 | int pagesize = sysconf(_SC_PAGESIZE); 84 | int metadata_size = sizeof(void *) + sizeof(size_t); 85 | 86 | /* pad size up to an increment of pagesize if needed */ 87 | if (size % pagesize != 0) 88 | size = (size + pagesize) & ~(pagesize-1); 89 | 90 | void *main_ptr = safe_malloc(metadata_size + size + pagesize); 91 | void *data_ptr = (void *)((long)main_ptr + metadata_size); 92 | size_t *size_ptr; 93 | void **base_ptr; 94 | 95 | /* check if it's already aligned */ 96 | if ((long)(data_ptr) % pagesize != 0) 97 | /* move up data_ptr to the next page boundry */ 98 | data_ptr = (void *)((((long)(data_ptr) & ~(pagesize-1)) + pagesize)); 99 | 100 | /* prevent swapping of this memory if possible */ 101 | #ifdef _POSIX_MEMLOCK_RANGE 102 | if (mlock(data_ptr, size) != 0) { 103 | fprintf(stderr, "sec_malloc: could not lock %zu bytes\n", size); 104 | perror(""); 105 | } 106 | #endif 107 | 108 | /* save the locked memory size so we can wipe it later */ 109 | size_ptr = (void *)((long)data_ptr - sizeof(size_t)); 110 | *size_ptr = size; 111 | /* save the base pointer so we can free it later */ 112 | base_ptr = (void *)((long)data_ptr - metadata_size); 113 | *base_ptr = main_ptr; 114 | 115 | /*fprintf(stderr, " main_ptr: %p\n", main_ptr); 116 | fprintf(stderr, " data_ptr: %p\n", data_ptr); 117 | fprintf(stderr, " size_ptr: %p\n", (void *)size_ptr); 118 | fprintf(stderr, " base_ptr: %p\n", (void *)base_ptr); 119 | fprintf(stderr, "*base_ptr: %p\n", *base_ptr); 120 | fprintf(stderr, "*size_ptr: %zu\n", *size_ptr); 121 | fprintf(stderr, " size: %zu\n", size);*/ 122 | return data_ptr; 123 | } 124 | 125 | /* util.h: #define sec_free(ptr) _sec_free((void **) &ptr) */ 126 | void _sec_free(void ** data_ptr) { 127 | assert(sizeof(long) >= sizeof(void *)); 128 | assert(data_ptr != NULL); 129 | if (*data_ptr == NULL) { 130 | fprintf(stderr, "Warning: attempted double _sec_free\n"); 131 | return; 132 | } 133 | 134 | int metadata_size = sizeof(void *) + sizeof(size_t); 135 | 136 | void **base_ptr; 137 | size_t *size_ptr; 138 | 139 | /* load the locked allocation size */ 140 | size_ptr = (void *)((long)*data_ptr - sizeof(size_t)); 141 | /* load the base pointer */ 142 | base_ptr = (void *)((long)*data_ptr - metadata_size); 143 | 144 | /*fprintf(stderr, " data_ptr: %p\n", *data_ptr); 145 | fprintf(stderr, " size_ptr: %p\n", (void *)size_ptr); 146 | fprintf(stderr, " base_ptr: %p\n", (void *)base_ptr); 147 | fprintf(stderr, "*base_ptr: %p\n", *base_ptr); 148 | fprintf(stderr, "*size_ptr: %zu\n", *size_ptr);*/ 149 | 150 | MEMWIPE(*data_ptr, *size_ptr); 151 | #ifdef _POSIX_MEMLOCK_RANGE 152 | if (munlock(*data_ptr, *size_ptr) != 0) { 153 | fprintf(stderr, "_sec_free: could not unlock %zu bytes\n", *size_ptr); 154 | perror(""); 155 | } 156 | #endif 157 | *data_ptr = NULL; 158 | 159 | if (*base_ptr != NULL) { 160 | void *tmp_ptr = *base_ptr; 161 | /* We could clear *base_ptr after the free to save a few instructions, but 162 | doing that makes memory checkers angry */ 163 | *base_ptr = NULL; 164 | free(tmp_ptr); 165 | } else { 166 | fprintf(stderr, "Fatal: attempted partial double _sec_free\n"); 167 | exit(EXIT_FAILURE); 168 | } 169 | } 170 | 171 | void keymem_init(keymem_t *keymem) { 172 | assert(keymem != NULL); 173 | assert(sizeof(long) == sizeof(void *)); 174 | int pagesize = sysconf(_SC_PAGESIZE); 175 | if (keymem->ptr != NULL) { 176 | fprintf(stderr, "Warning: Tried to re-initialize keymem\n"); 177 | return; 178 | } 179 | /* Set up page aligned memory */ 180 | keymem->ptr = safe_malloc(KEYMEM_SIZE + pagesize); /* we rely on this being zero filled */ 181 | if ((long)(keymem->ptr) % pagesize == 0) { 182 | keymem->off = 0; 183 | } else { 184 | keymem->off = ((((long)(keymem->ptr) & ~(pagesize-1)) + pagesize) - (long)(keymem->ptr)); 185 | } 186 | keymem->pos = 0; 187 | keymem->lck = 0; 188 | keymem->len = KEYMEM_SIZE; 189 | /* fprintf(stderr, "keymem_init: %p + %d\n", keymem->ptr, keymem->off); */ 190 | } 191 | 192 | /* There are no corrosponding 'free' or 'realloc' functions. */ 193 | void * keymem_alloc(keymem_t *keymem, size_t size) { 194 | assert(keymem != NULL); 195 | int pagesize = sysconf(_SC_PAGESIZE); 196 | if (keymem->ptr == NULL) { 197 | keymem_init(keymem); 198 | } 199 | void *ptr = keymem->ptr + keymem->off + keymem->pos; 200 | /* verify we have enough remaining space for the requested allocation */ 201 | if ((keymem->pos += size) > keymem->len) { 202 | fprintf(stderr, "keymem_alloc: could not allocate %d bytes\n", (unsigned int)size); 203 | exit(EXIT_FAILURE); 204 | } 205 | #ifdef _POSIX_MEMLOCK_RANGE 206 | while (keymem->pos + size > keymem->lck) { 207 | /* fprintf(stderr, "keymem_alloc: locking %d bytes @ %p+0x%04x\n", pagesize, keymem->ptr + keymem->off, keymem->lck); DEBUG */ 208 | if (mlock(keymem->ptr + keymem->off + keymem->lck, pagesize) != 0) { 209 | fprintf(stderr, "keymem_alloc: could not lock %d bytes (%d already locked)\n", pagesize, keymem->lck); 210 | perror(""); 211 | } else { 212 | keymem->lck += pagesize; 213 | } 214 | } 215 | #endif 216 | MEMZERO(ptr, size); 217 | /* fprintf(stderr, "keymem_alloc: %p-%p\n", ptr, (char *)ptr + size - 1); */ 218 | return ptr; 219 | } 220 | 221 | void keymem_wipe(keymem_t *keymem) { 222 | assert(keymem != NULL); 223 | if (keymem->ptr != NULL) { 224 | MEMWIPE(keymem->ptr + keymem->off, keymem->len); 225 | #ifdef _POSIX_MEMLOCK_RANGE 226 | if (keymem->lck > 0) { 227 | munlock(keymem->ptr + keymem->off, keymem->lck); 228 | } 229 | #endif 230 | } 231 | /* reset position markers */ 232 | keymem->pos = 0; 233 | keymem->lck = 0; 234 | } 235 | 236 | void keymem_destroy(keymem_t *keymem) { 237 | keymem_wipe(keymem); 238 | if (keymem->ptr != NULL) { 239 | free(keymem->ptr); 240 | } 241 | /* clear everything else */ 242 | keymem->ptr = NULL; 243 | keymem->off = 0; 244 | keymem->len = 0; 245 | } 246 | 247 | void fill_rand(unsigned char *buffer, 248 | unsigned int count) { 249 | size_t n; 250 | 251 | #if defined(LTC_DEVRANDOM) && defined(TRY_URANDOM_FIRST) 252 | /* Override libtomcrypt's use of /dev/urandom */ 253 | FILE *devrandom; 254 | devrandom = fopen("/dev/random", "rb"); 255 | if (!devrandom) { 256 | fprintf(stderr, "WARNING: Unable to access /dev/random\n"); 257 | #endif 258 | if ((n = rng_get_bytes(buffer, count, NULL)) < count) { 259 | fprintf(stderr, "Short read from rng; requested %d bytes, got %zd bytes\n", count, n); 260 | exit(EXIT_FAILURE); 261 | } 262 | #if defined(LTC_DEVRANDOM) && defined(TRY_URANDOM_FIRST) 263 | } else { 264 | n = fread(buffer, 1, count, devrandom); 265 | if (n < count) { 266 | perror("Short read from /dev/random"); 267 | fclose(devrandom); 268 | exit(EXIT_FAILURE); 269 | } 270 | fclose(devrandom); 271 | } 272 | #endif 273 | } 274 | 275 | void fill_prng(unsigned char *buffer, 276 | unsigned int count) { 277 | extern prng_state prng; 278 | size_t n; 279 | 280 | if ((n = fortuna_read(buffer, count, &prng)) < count) { 281 | fprintf(stderr, "Short read from prng; requested %d bytes, got %zd bytes\n", count, n); 282 | exit(EXIT_FAILURE); 283 | } 284 | } 285 | 286 | /* Free header memory */ 287 | void free_header(header_data_t *header) { 288 | if (header != NULL) { 289 | if (header->keymem != NULL) { 290 | keymem_destroy(header->keymem); 291 | safe_free(header->keymem); 292 | } 293 | if (header->shares != NULL) { 294 | int i; 295 | for (i = 0; i < header->nshares;i++) { 296 | share_data_t *share; 297 | share = &(header->shares[i]); 298 | if (share->ctxt != NULL) 299 | safe_free(share->ctxt); 300 | if (share->hmac != NULL) 301 | safe_free(share->hmac); 302 | } 303 | safe_free(header->shares); 304 | } 305 | if (header->master_hmac != NULL) 306 | safe_free(header->master_hmac); 307 | } 308 | safe_free(header); 309 | } 310 | 311 | void wipe_shares(header_data_t *header) { 312 | int i; 313 | 314 | if (header != NULL && header->shares != NULL) { 315 | /* wipe/free pointers within each share */ 316 | for (i = 0; i < header->nshares;i++) { 317 | share_data_t *share; 318 | share = &(header->shares[i]); 319 | if (share->key != NULL) 320 | MEMWIPE(share->key, header->key_size); 321 | if (share->ptxt != NULL) 322 | MEMWIPE(share->ptxt, header->share_size); 323 | } 324 | } 325 | } 326 | 327 | /* vim: set ts=2 sw=2 et ai si: */ 328 | -------------------------------------------------------------------------------- /hkdf_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "hkdf.h" 16 | #include "hkdf_test.h" 17 | 18 | void print_hex(const unsigned char * ptr, size_t len, size_t width) { 19 | size_t i; 20 | if (len > 0) 21 | printf(" 0x"); 22 | for (i = 0; i < len; i++) { 23 | if (i > 0 && i % width == 0) { 24 | printf(" \n "); 25 | } 26 | printf("%02x", ptr[i]); 27 | } 28 | printf(" (%d octets)\n", (int)len); 29 | } 30 | 31 | /* This can leak memory */ 32 | unsigned char * hexstrstr(const char * hexstr) { 33 | int len = strlen(hexstr); 34 | assert(len % 2 == 0); 35 | assert(len > 1); 36 | 37 | len >>= 1; /* bit shift to divide by two */ 38 | 39 | unsigned char *out = XMALLOC(len); 40 | if (out == NULL) 41 | abort(); 42 | 43 | while (len-- > 0) 44 | sscanf(hexstr + len * 2, "%02hhx", out + len); 45 | 46 | return out; 47 | } 48 | 49 | int test_hkdf(const char * hash, 50 | const unsigned char * IKM, size_t IKM_len, 51 | const unsigned char * salt, size_t salt_len, 52 | const unsigned char * info, size_t info_len, 53 | const unsigned char * PRK, size_t PRK_len, 54 | const unsigned char * OMK, size_t OMK_len) { 55 | int hash_idx = find_hash(hash); 56 | unsigned long hashsize = hash_descriptor[hash_idx].hashsize; 57 | 58 | assert(PRK_len == hashsize); 59 | 60 | int ret = 0; 61 | 62 | unsigned char *test_PRK = XMALLOC(PRK_len); 63 | if (test_PRK == NULL) 64 | abort(); 65 | unsigned char *test_OMK = XMALLOC(OMK_len); 66 | if (test_OMK == NULL) 67 | abort(); 68 | 69 | XMEMSET(test_PRK, 0x55, PRK_len); 70 | XMEMSET(test_OMK, 0x55, OMK_len); 71 | 72 | printf("Hash = %s\n", hash); 73 | printf("IKM ="); 74 | print_hex(IKM, IKM_len, IKM_len >= 32 ? 16 : 32); 75 | printf("salt ="); 76 | if (salt != NULL) { 77 | print_hex(salt, salt_len, salt_len >= 32 ? 16 : 32); 78 | } else { 79 | printf(" not provided (defaults to HashLen zero octets)\n"); 80 | } 81 | printf("info ="); 82 | print_hex(info, info_len, info_len >= 32 ? 16 : 32); 83 | 84 | printf("L = %d\n\n", (int)OMK_len); 85 | 86 | hkdf_extract(hash_idx, salt, salt_len, IKM, IKM_len, test_PRK, &hashsize); 87 | printf("PRK ="); 88 | print_hex(test_PRK, hashsize, hashsize >= 32 ? 16 : 32); 89 | if (XMEMCMP(PRK, test_PRK, PRK_len) != 0) { 90 | ret = -1; 91 | printf("PRK FAILED\n"); 92 | goto test_abort; 93 | } 94 | 95 | hkdf_expand(hash_idx, test_PRK, hashsize, info, info_len, test_OMK, OMK_len); 96 | printf("OMK ="); 97 | print_hex(test_OMK, OMK_len, OMK_len >= 32 ? 16 : 32); 98 | if (XMEMCMP(OMK, test_OMK, OMK_len) != 0) { 99 | ret = -1; 100 | printf("OMK FAILED\n"); 101 | goto test_abort; 102 | } 103 | 104 | test_abort: 105 | XFREE(test_PRK); 106 | XFREE(test_OMK); 107 | return ret; 108 | } 109 | int main() { 110 | 111 | /* register tomcrypt hash algorithms */ 112 | if (register_hash(&sha1_desc) == -1) { 113 | fprintf(stderr, "Failed to register SHA-1\n"); 114 | exit(EXIT_FAILURE); 115 | } 116 | 117 | if (register_hash(&sha256_desc) == -1) { 118 | fprintf(stderr, "Failed to register SHA-256\n"); 119 | exit(EXIT_FAILURE); 120 | } 121 | 122 | if (register_hash(&sha384_desc) == -1) { 123 | fprintf(stderr, "Failed to register SHA-384\n"); 124 | exit(EXIT_FAILURE); 125 | } 126 | 127 | if (register_hash(&sha512_desc) == -1) { 128 | fprintf(stderr, "Failed to register SHA-512\n"); 129 | exit(EXIT_FAILURE); 130 | } 131 | 132 | if (register_hash(&whirlpool_desc) == -1) { 133 | fprintf(stderr, "Failed to register WHIRLPOOL\n"); 134 | exit(EXIT_FAILURE); 135 | } 136 | /* end tomcrypt algorithm registration */ 137 | 138 | int ret = 0; 139 | 140 | printf("Test Case 1\n"); 141 | if (test_hkdf("sha256", 142 | hexstrstr("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" 143 | "0b0b0b0b0b0b"), 22, 144 | hexstrstr("000102030405060708090a0b0c"), 13, 145 | hexstrstr("f0f1f2f3f4f5f6f7f8f9"), 10, 146 | hexstrstr("077709362c2e32df0ddc3f0dc47bba63" 147 | "90b6c73bb50f9c3122ec844ad7c2b3e5"), 32, 148 | hexstrstr("3cb25f25faacd57a90434f64d0362f2a" 149 | "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" 150 | "34007208d5b887185865"), 42) == 0) { 151 | printf("Test Case 1: OKAY\n"); 152 | } else { 153 | printf("Test Case 1: FAIL\n"); 154 | ret = 1; 155 | } 156 | 157 | printf("\n"); 158 | 159 | printf("Test Case 2\n"); 160 | if (test_hkdf("sha256", 161 | hexstrstr("000102030405060708090a0b0c0d0e0f" 162 | "101112131415161718191a1b1c1d1e1f" 163 | "202122232425262728292a2b2c2d2e2f" 164 | "303132333435363738393a3b3c3d3e3f" 165 | "404142434445464748494a4b4c4d4e4f"), 80, 166 | hexstrstr("606162636465666768696a6b6c6d6e6f" 167 | "707172737475767778797a7b7c7d7e7f" 168 | "808182838485868788898a8b8c8d8e8f" 169 | "909192939495969798999a9b9c9d9e9f" 170 | "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"), 80, 171 | hexstrstr("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" 172 | "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" 173 | "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" 174 | "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" 175 | "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"), 80, 176 | hexstrstr("06a6b88c5853361a06104c9ceb35b45c" 177 | "ef760014904671014a193f40c15fc244"), 32, 178 | hexstrstr("b11e398dc80327a1c8e7f78c596a4934" 179 | "4f012eda2d4efad8a050cc4c19afa97c" 180 | "59045a99cac7827271cb41c65e590e09" 181 | "da3275600c2f09b8367793a9aca3db71" 182 | "cc30c58179ec3e87c14c01d5c1f3434f" 183 | "1d87"), 82) == 0) { 184 | printf("Test Case 2: OKAY\n"); 185 | } else { 186 | printf("Test Case 2: FAIL\n"); 187 | ret = 1; 188 | } 189 | 190 | printf("\n"); 191 | 192 | printf("Test Case 3\n"); 193 | if (test_hkdf("sha256", 194 | hexstrstr("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" 195 | "0b0b0b0b0b0b"), 22, 196 | "", 0, 197 | "", 0, 198 | hexstrstr("19ef24a32c717b167f33a91d6f648bdf" 199 | "96596776afdb6377ac434c1c293ccb04"), 32, 200 | hexstrstr("8da4e775a563c18f715f802a063c5a31" 201 | "b8a11f5c5ee1879ec3454e5f3c738d2d" 202 | "9d201395faa4b61a96c8"), 42) == 0) { 203 | printf("Test Case 3: OKAY\n"); 204 | } else { 205 | printf("Test Case 3: FAIL\n"); 206 | ret = 1; 207 | } 208 | 209 | printf("\n"); 210 | 211 | printf("Test Case 4\n"); 212 | if (test_hkdf("sha1", 213 | hexstrstr("0b0b0b0b0b0b0b0b0b0b0b"), 11, 214 | hexstrstr("000102030405060708090a0b0c"), 13, 215 | hexstrstr("f0f1f2f3f4f5f6f7f8f9"), 10, 216 | hexstrstr("9b6c18c432a7bf8f0e71c8eb88f4b30b" 217 | "aa2ba243"), 20, 218 | hexstrstr("085a01ea1b10f36933068b56efa5ad81" 219 | "a4f14b822f5b091568a9cdd4f155fda2" 220 | "c22e422478d305f3f896"), 42) == 0) { 221 | printf("Test Case 4: OKAY\n"); 222 | } else { 223 | printf("Test Case 4: FAIL\n"); 224 | ret = 1; 225 | } 226 | 227 | printf("\n"); 228 | 229 | printf("Test Case 5\n"); 230 | if (test_hkdf("sha1", 231 | hexstrstr("000102030405060708090a0b0c0d0e0f" 232 | "101112131415161718191a1b1c1d1e1f" 233 | "202122232425262728292a2b2c2d2e2f" 234 | "303132333435363738393a3b3c3d3e3f" 235 | "404142434445464748494a4b4c4d4e4f"), 80, 236 | hexstrstr("606162636465666768696a6b6c6d6e6f" 237 | "707172737475767778797a7b7c7d7e7f" 238 | "808182838485868788898a8b8c8d8e8f" 239 | "909192939495969798999a9b9c9d9e9f" 240 | "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"), 80, 241 | hexstrstr("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" 242 | "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" 243 | "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" 244 | "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" 245 | "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"), 80, 246 | hexstrstr("8adae09a2a307059478d309b26c4115a" 247 | "224cfaf6"), 20, 248 | hexstrstr("0bd770a74d1160f7c9f12cd5912a06eb" 249 | "ff6adcae899d92191fe4305673ba2ffe" 250 | "8fa3f1a4e5ad79f3f334b3b202b2173c" 251 | "486ea37ce3d397ed034c7f9dfeb15c5e" 252 | "927336d0441f4c4300e2cff0d0900b52" 253 | "d3b4"), 82) == 0) { 254 | printf("Test Case 5: OKAY\n"); 255 | } else { 256 | printf("Test Case 5: FAIL\n"); 257 | ret = 1; 258 | } 259 | 260 | printf("\n"); 261 | 262 | printf("Test Case 6\n"); 263 | if (test_hkdf("sha1", 264 | hexstrstr("0b0b0b0b0b0b0b0b0b0b0b0b0b" 265 | "0b0b0b0b0b0b0b0b0b"), 22, 266 | "", 0, 267 | "", 0, 268 | hexstrstr("da8c8a73c7fa77288ec6f5e7c297786a" 269 | "a0d32d01"), 20, 270 | hexstrstr("0ac1af7002b3d761d1e55298da9d0506" 271 | "b9ae52057220a306e07b6b87e8df21d0" 272 | "ea00033de03984d34918"), 42) == 0) { 273 | printf("Test Case 6: OKAY\n"); 274 | } else { 275 | printf("Test Case 6: FAIL\n"); 276 | ret = 1; 277 | } 278 | 279 | printf("\n"); 280 | 281 | printf("Test Case 7\n"); 282 | if (test_hkdf("sha1", 283 | hexstrstr("0c0c0c0c0c0c0c0c0c0c0c0c0c" 284 | "0c0c0c0c0c0c0c0c0c"), 22, 285 | NULL, 0, 286 | "", 0, 287 | hexstrstr("2adccada18779e7c2077ad2eb19d3f3e" 288 | "731385dd"), 20, 289 | hexstrstr("2c91117204d745f3500d636a62f64f0a" 290 | "b3bae548aa53d423b0d1f27ebba6f5e5" 291 | "673a081d70cce7acfc48"), 42) == 0) { 292 | printf("Test Case 7: OKAY\n"); 293 | } else { 294 | printf("Test Case 7: FAIL\n"); 295 | ret = 1; 296 | } 297 | 298 | printf("Test Case 8\n"); 299 | if (test_hkdf("sha1", 300 | hexstrstr("0c0c0c0c0c0c0c0c0c0c0c0c0c" 301 | "0c0c0c0c0c0c0c0c0c"), 22, 302 | NULL, 0, 303 | "", 0, 304 | hexstrstr("2adccada18779e7c2077ad2eb19d3f3e" 305 | "731385dd"), 20, 306 | hexstrstr("2c91117204d745f3500d636a62f64f0a" 307 | "b3bae548aa53d423b0d1f27ebba6f5e5" 308 | "673a081d70cce7acfc48"), 1) == 0) { 309 | printf("Test Case 8: OKAY\n"); 310 | } else { 311 | printf("Test Case 8: FAIL\n"); 312 | ret = 1; 313 | } 314 | 315 | return ret; 316 | } 317 | 318 | /* vim: set ts=2 sw=2 et ai si: */ 319 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* threshcrypt main.c 2 | * Copyright 2012 Ryan Castellucci 3 | * This software is published under the terms of the Simplified BSD License. 4 | * Please see the 'COPYING' file for details. 5 | * Portions of this file are derived from libgfshare examples which are 6 | * Copyright Daniel Silverstone 2006-2011 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #include "common.h" 26 | #include "ui.h" 27 | #include "file.h" 28 | #include "util.h" 29 | #include "crypt.h" 30 | #include "shares.h" 31 | #include "main.h" 32 | 33 | struct termios orig_term_set; 34 | 35 | static char* progname; 36 | 37 | prng_state prng; 38 | 39 | /* globals so that signal/atexit handlers can get at them */ 40 | static header_data_t *header; 41 | static keymem_t *keymem; 42 | 43 | static void cleanup(void) { 44 | /* fprintf(stderr, "Freeing header\n"); */ 45 | free_header(header); 46 | } 47 | 48 | static void sig_handle(int sig) { 49 | if (sig == SIGINT) { load_term(&orig_term_set); fprintf(stderr, "\n"); } 50 | fprintf(stderr, "%s caught, exiting\n", strsignal(sig)); 51 | exit(EXIT_FAILURE); 52 | } 53 | 54 | /* Calculate the number of iterations needed to take a given amount of time */ 55 | static int pbkdf2_itertime(int hash_idx, size_t size, int msec) { 56 | struct timeval time1; 57 | struct timeval time2; 58 | 59 | /* salt and pass values don't matter */ 60 | unsigned char *salt = "Your mother was a hamster..."; 61 | unsigned char *pass = "...and your father smelt of elderberries!"; 62 | unsigned char *buf = safe_malloc(size); 63 | 64 | int iter = 1617; /* this number is of no significance */ 65 | float duration = 0; /* seconds */ 66 | float spi = 0; /* seconds per iter */ 67 | 68 | /* loop until we find a value for iter that takes at least 0.10 seconds */ 69 | while (duration < 0.10) { /* minimum time */ 70 | gettimeofday(&time1, NULL); 71 | pbkdf2(pass, strlen(pass), salt, strlen(salt), iter, hash_idx, buf, &size); 72 | gettimeofday(&time2, NULL); 73 | duration = (time2.tv_sec - time1.tv_sec) + (float)(time2.tv_usec - time1.tv_usec) / 1000000; 74 | spi = 1000 * duration / iter; /* calculate seconds per iter */ 75 | /*fprintf(stderr, "PBKDF2: %6.3fs,%7diter,%10.3e\n", duration, iter, spi);*/ 76 | /* set iter to a value expected to take around 0.12 seconds*/ 77 | iter = 0.12 * 1000 / spi; 78 | } 79 | safe_free(buf); 80 | if (msec > INT_MAX * spi) { /* return INT_MAX instead of undefined behaviour */ 81 | fprintf(stderr, "PBKDF2: ??.???s,%7diter\n", INT_MAX); 82 | return INT_MAX; 83 | } 84 | fprintf(stderr, "PBKDF2: %6.3fs,%7diter\n", (float) msec / 1000, (int)(msec / spi)); 85 | return (int)(msec / spi); 86 | } 87 | 88 | void usage(FILE* stream) { 89 | fprintf(stream, "\ 90 | Usage: threshcrypt [options] infile [outfile]\n\ 91 | \n\ 92 | Where options are:\n\ 93 | \n\ 94 | -h show this screen (all other options ignored)\n\ 95 | -V print version infomation (all other options ignored)\n\ 96 | \n\ 97 | -d decrypt (default if no options specified)\n\ 98 | -e encrypt (default if any other options are set)\n\ 99 | \n\ 100 | -n total number of passwords to enter\n\ 101 | -t minimum number of passwords that will be required to decrypt\n\ 102 | \n\ 103 | -m time in milliseconds to iterate for pbkdf2\n\ 104 | -i number of iterations to use for pbkdf2\n\ 105 | \n\ 106 | The shares option defaults to %d.\n\ 107 | The threshold option defaults to %d.\n\ 108 | ", DEFAULT_SHARECOUNT, DEFAULT_THRESHOLD ); 109 | } 110 | 111 | #define OPTSTRING "b:i:t:n:m:hVed" 112 | int main(int argc, char **argv) { 113 | unsigned int sharecount = DEFAULT_SHARECOUNT; 114 | unsigned int threshold = DEFAULT_THRESHOLD; 115 | unsigned int key_bits = DEFAULT_KEY_BITS; 116 | int mode = MODE_UNKNOWN; 117 | int iter = DEFAULT_ITERATIONS; 118 | int iter_ms = 0; 119 | 120 | unsigned char buf[BUFFER_SIZE]; 121 | 122 | char *in_file, *out_file; 123 | int in_fd, out_fd; 124 | 125 | char *endptr; 126 | int optnr; 127 | 128 | unsigned int i; 129 | int ret, err; 130 | ret = err = 0; 131 | 132 | /* sleep(1); 133 | unsigned char *sec_test = sec_malloc(4097); 134 | fprintf(stderr, "\n"); 135 | sleep(1); 136 | sec_free(sec_test); 137 | sleep(1); */ 138 | /* malloc the header and keymem structs */ 139 | header = safe_malloc(sizeof(header_data_t)); 140 | keymem = safe_malloc(sizeof(keymem_t)); 141 | 142 | /* make sure key material is wiped on exit */ 143 | atexit(cleanup); 144 | 145 | /* set up signal handlers */ 146 | save_term(&orig_term_set); 147 | /* catch some signals we can */ 148 | signal(SIGINT, sig_handle); 149 | signal(SIGHUP, sig_handle); 150 | signal(SIGUSR1, sig_handle); 151 | signal(SIGUSR2, sig_handle); 152 | signal(SIGTERM, sig_handle); 153 | signal(SIGPIPE, sig_handle); 154 | signal(SIGABRT, sig_handle); 155 | 156 | progname = argv[0]; 157 | /* Seed the PRNG */ 158 | srandom( time(NULL) ^ (getpid() << (sizeof(int) * 4)) ); 159 | 160 | /* Setup fortuna PRNG */ 161 | if (register_prng(&fortuna_desc) == -1) { 162 | fprintf(stderr, "Error registering Fortuna\n"); 163 | exit(EXIT_FAILURE); 164 | } 165 | 166 | if ((err = rng_make_prng(128, find_prng("fortuna"), &prng, NULL)) != CRYPT_OK) { 167 | fprintf(stderr, "Error starting Fortuna: %s\n", error_to_string(err)); 168 | exit(EXIT_FAILURE); 169 | } 170 | 171 | if ((err = fortuna_ready(&prng)) != CRYPT_OK) { 172 | fprintf(stderr, "Fortuna not ready: %s\n", error_to_string(err)); 173 | exit(EXIT_FAILURE); 174 | } 175 | /* end fortuna setup */ 176 | 177 | /* register other tomcrypt algorithms */ 178 | if (register_cipher(&aes_desc) == -1) { 179 | fprintf(stderr, "Failed to register AES\n"); 180 | exit(EXIT_FAILURE); 181 | } 182 | 183 | if (register_hash(&sha1_desc) == -1) { 184 | fprintf(stderr, "Failed to register SHA-1\n"); 185 | exit(EXIT_FAILURE); 186 | } 187 | 188 | if (register_hash(&sha256_desc) == -1) { 189 | fprintf(stderr, "Failed to register SHA-256\n"); 190 | exit(EXIT_FAILURE); 191 | } 192 | /* end tomcrypt algorithm registration */ 193 | 194 | #ifndef _POSIX_MEMLOCK_RANGE 195 | fprintf(stderr, "\ 196 | Warning: Unable to lock virtual memory on this system, sensitive key data may\n\ 197 | be paged out to disk.\n"); 198 | #endif 199 | 200 | /* Set the prng for gfshare */ 201 | gfshare_fill_rand = fill_prng; 202 | 203 | /* parse command line arguments */ 204 | while( (optnr = getopt(argc, argv, OPTSTRING)) != -1 ) { 205 | switch( optnr ) { 206 | case 'V': 207 | fprintf(stdout, "\ 208 | threshcrypt %s, Copyright 2012 Ryan Castellucci \n\ 209 | This is free software; see the source for copying conditions. There is NO\n\ 210 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ 211 | ", THRCR_VERSION_STR); 212 | return 0; 213 | break; 214 | case 'h': 215 | fprintf(stdout, "threshcrypt\n"); 216 | usage(stdout); 217 | return 0; 218 | break; 219 | case 'e': 220 | if (mode == MODE_DECRYPT) { 221 | fprintf(stderr, "%s: Conflicting mode option -e, mode set to decrypt by previous option\n", progname); 222 | return 1; 223 | } 224 | mode = MODE_ENCRYPT; 225 | break; 226 | case 'd': 227 | if (mode == MODE_ENCRYPT) { 228 | fprintf(stderr, "%s: Conflicting mode option -d, mode set to decrypt by previous option\n", progname); 229 | return 1; 230 | } 231 | mode = MODE_DECRYPT; 232 | break; 233 | case 'i': 234 | if (mode == MODE_DECRYPT) { 235 | fprintf(stderr, "%s: Conflicting option -i, mode set to decrypt by previous option\n", progname); 236 | return 1; 237 | } 238 | mode = MODE_ENCRYPT; 239 | iter = strtoul( optarg, &endptr, 10 ); 240 | if (*endptr != 0 || *optarg == 0 || 241 | iter < 1024 || iter > INT_MAX ) { 242 | fprintf(stderr, "%s: Invalid argument to option -i (%d)\n", progname, iter); 243 | usage(stderr); 244 | return 1; 245 | } 246 | break; 247 | case 'm': 248 | if (mode == MODE_DECRYPT) { 249 | fprintf(stderr, "%s: Conflicting option -m, mode set to decrypt by previous option\n", progname); 250 | return 1; 251 | } 252 | iter_ms = strtoul(optarg, &endptr, 10); 253 | mode = MODE_ENCRYPT; 254 | if (*endptr != 0 || *optarg == 0 || 255 | iter_ms < 1 || iter_ms > MAX_ITER_MS ) { 256 | fprintf(stderr, "%s: Invalid argument to option -m (%d)\n", progname, iter_ms); 257 | usage(stderr); 258 | return 1; 259 | } 260 | break; 261 | case 'b': 262 | key_bits = strtoul( optarg, &endptr, 10 ); 263 | if (mode == MODE_DECRYPT) { 264 | fprintf(stderr, "%s: Conflicting option -b, mode set to decrypt by previous option\n", progname); 265 | return 1; 266 | } 267 | mode = MODE_ENCRYPT; 268 | if (*endptr != 0 || *optarg == 0 || 269 | (key_bits != 128 && key_bits != 192 && key_bits != 256) ) { 270 | fprintf(stderr, "%s: Invalid argument to option -b (%d)\n", progname, key_bits); 271 | usage(stderr); 272 | return 1; 273 | } 274 | break; 275 | case 'n': 276 | sharecount = strtoul( optarg, &endptr, 10 ); 277 | if (mode == MODE_DECRYPT) { 278 | fprintf(stderr, "%s: Conflicting option -n, mode set to decrypt by previous option\n", progname); 279 | return 1; 280 | } 281 | mode = MODE_ENCRYPT; 282 | if( *endptr != 0 || *optarg == 0 || 283 | sharecount < 1 || sharecount > 255 ) { 284 | fprintf(stderr, "%s: Invalid argument to option -n (%d)\n", progname, sharecount ); 285 | usage(stderr); 286 | return 1; 287 | } 288 | break; 289 | case 't': 290 | threshold = strtoul( optarg, &endptr, 10 ); 291 | if (mode == MODE_DECRYPT) { 292 | fprintf(stderr, "%s: Conflicting option -t, mode set to decrypt by previous option\n", progname); 293 | return 1; 294 | } 295 | mode = MODE_ENCRYPT; 296 | if( *endptr != 0 || *optarg == 0 || threshold < 1) { 297 | fprintf(stderr, "%s: Invalid argument to option -t (%d)\n", progname, threshold ); 298 | usage(stderr); 299 | return 1; 300 | } 301 | break; 302 | } 303 | } 304 | 305 | if (threshold > sharecount) { 306 | fprintf(stderr, "%s: argument to -n must not be smaller than argument to -t\n", progname); 307 | usage(stderr); 308 | return 1; 309 | } 310 | 311 | /*fprintf(stderr, "%d, %d\n", optind, argc);*/ 312 | if (optind == (argc - 2)) { 313 | in_file = argv[optind++]; 314 | out_file = argv[optind++]; 315 | } else if (optind == (argc - 1)) { 316 | in_file = argv[optind++]; 317 | out_file = NULL; 318 | } else { 319 | fprintf(stderr, "%s: Bad argument count\n", progname); 320 | usage(stderr); 321 | return 1; 322 | } 323 | /* end command line argument parsing */ 324 | 325 | /* some initialization */ 326 | if ((in_fd = open(in_file, O_RDONLY)) < 0) { 327 | fprintf(stderr, "%s: Failed to open '%s' for reading: ", progname, in_file); 328 | perror(""); 329 | exit(EXIT_FAILURE); 330 | } 331 | 332 | if (mode == MODE_UNKNOWN) 333 | mode = MODE_DECRYPT; 334 | 335 | size_t key_size = key_bits / 8; 336 | size_t salt_size = SALT_SIZE; 337 | size_t hmac_size = HMAC_SIZE; 338 | size_t share_size = key_size + 1; 339 | 340 | /* keymem_init(keymem); */ 341 | header->keymem = keymem; 342 | 343 | unsigned char pass[256]; 344 | unsigned char prompt[64]; 345 | unsigned char vprompt[64]; 346 | int pass_ret, hash_idx; 347 | 348 | hash_idx = find_hash("sha256"); 349 | if (mode == MODE_DECRYPT) { 350 | /* Read in what we hope is a threshcrypt header */ 351 | if (read(in_fd, buf, HEADER_SIZE) < HEADER_SIZE) { 352 | /* Not a threshcrypt file - too small */ 353 | fprintf(stderr, "%s: Error reading header of '%s': too small\n", progname, in_file); 354 | return THRCR_ERROR; 355 | } 356 | if ((err = parse_header(buf, header)) != THRCR_OK) { 357 | switch(err) { 358 | case THRCR_NOMAGIC: 359 | fprintf(stderr, "%s: Error reading header of '%s': no magic\n", progname, in_file); 360 | return THRCR_NOMAGIC; 361 | break; 362 | case THRCR_BADDATA: 363 | fprintf(stderr, "%s: Error reading header of '%s': bad data\n", progname, in_file); 364 | return THRCR_BADDATA; 365 | break; 366 | } 367 | /* shouldn't be reached */ 368 | fprintf(stderr, "%s: Unexpected return for parse_header: %d\n", progname, err); 369 | return THRCR_ERROR; 370 | } 371 | 372 | /* unlock shares */ 373 | int unlocked = 0; 374 | while (unlocked < header->thresh) { 375 | fprintf(stderr, "More passwords are required to meet decryption threshold.\n"); 376 | snprintf(prompt, 64, "Enter any remaining share password [%d/%d]: ", unlocked, header->thresh); 377 | pass_ret = get_pass(pass, 128, prompt, NULL, NULL, 0); 378 | if (pass_ret < 0) { 379 | fprintf(stderr, "Password entry failed.\n"); 380 | return THRCR_ERROR; 381 | } else if (pass_ret < 1) { 382 | fprintf(stderr, "Password must be at least one character(s)\n"); 383 | } else { 384 | assert(pass_ret == (int)strlen(pass)); 385 | 386 | int unlock_ret; 387 | if ((unlock_ret = unlock_shares(pass, pass_ret, header))){ 388 | unlocked += unlock_ret; 389 | } 390 | MEMWIPE(pass, sizeof(pass)); 391 | } 392 | } 393 | fprintf(stderr, "Decrypting data...\n"); 394 | /* Recover master key */ 395 | tc_gfcombine(header); 396 | 397 | assert(header->master_key != NULL); 398 | 399 | /* verify master key */ 400 | size_t hmac_size = header->hmac_size; 401 | if ((err = pbkdf2_vrfy(header->master_key, header->key_size, header->master_salt, SALT_SIZE, 402 | SUBKEY_ITER, hash_idx, header->master_hmac, &hmac_size)) != CRYPT_OK) { 403 | fprintf(stderr, "Master key verification failed!\n"); 404 | exit(EXIT_FAILURE); 405 | } 406 | /* share ptxt/keys no longer needed */ 407 | wipe_shares(header); 408 | 409 | /* open output file */ 410 | if (out_file != NULL) { 411 | if ((out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { 412 | fprintf(stderr, "%s: Failed to open '%s' for writing: ", progname, out_file); 413 | perror(""); 414 | exit(EXIT_FAILURE); 415 | } 416 | } else { 417 | out_fd = fileno(stdout); 418 | } 419 | 420 | /* decrypt data */ 421 | ssize_t len; 422 | uint32_t dlen; 423 | unsigned char *IV = NULL; 424 | do { 425 | unsigned char blkmac[32]; 426 | 427 | len = read(in_fd, buf, 4); 428 | if (len < 4) { 429 | fprintf(stderr, "%s: Error: short read of blocklen in '%s'\n", progname, in_file); 430 | ret = THRCR_READERR; 431 | break; 432 | } 433 | LOAD32H(dlen, buf); 434 | if (dlen > sizeof(buf)) { 435 | fprintf(stderr, "%s: Error: blocklen larger than BUFFER_SIZE: '%s'\n", progname, in_file); 436 | ret = THRCR_BADDATA; 437 | break; 438 | } 439 | if ((len = read(in_fd, buf, dlen)) < (ssize_t)dlen) { 440 | fprintf(stderr, "%s: Error: short read of blockdat in '%s'\n", progname, in_file); 441 | ret = THRCR_READERR; 442 | break; 443 | } 444 | if ((len = read(in_fd, blkmac, header->hmac_size)) < header->hmac_size) { 445 | fprintf(stderr, "%s: Error: short read of blockmac in '%s'\n", progname, in_file); 446 | ret = THRCR_READERR; 447 | break; 448 | } 449 | if ((err = decrypt_block(buf, buf, dlen, 450 | header->master_key, header->key_size, 451 | blkmac, header->hmac_size, IV)) != THRCR_OK) { 452 | fprintf(stderr, "Error: Failed to decrypt block\n"); 453 | ret = THRCR_DECERR; 454 | break; 455 | } 456 | if ((err = write(out_fd, buf, dlen) < (ssize_t)dlen)) { 457 | if (err == -1) { 458 | perror("Error writing output"); 459 | } else { 460 | fprintf(stderr, "Error: Short write to output\n"); 461 | } 462 | ret = THRCR_WRITEERR; 463 | break; 464 | } 465 | } while (dlen > 0); 466 | close(in_fd); 467 | close(out_fd); 468 | safe_free(IV); 469 | MEMWIPE(buf, BUFFER_SIZE); 470 | return ret; 471 | } /* end MODE_DECRYPT */ 472 | 473 | if (mode == MODE_ENCRYPT) { 474 | unsigned char magic[THRCR_MAGIC_LEN] = THRCR_MAGIC; 475 | unsigned char version[THRCR_VERSION_LEN] = THRCR_VERSION; 476 | 477 | memcpy(header->magic, magic, THRCR_MAGIC_LEN); 478 | memcpy(header->version, version, THRCR_VERSION_LEN); 479 | header->cipher = 1; /* Hardcoded for now */ 480 | header->hash = 1; /* Hardcoded for now */ 481 | header->kdf = 1; /* Hardcoded for now */ 482 | header->nshares = sharecount; 483 | header->thresh = threshold; 484 | header->key_size = key_size; 485 | header->hmac_size = hmac_size; 486 | header->share_size = share_size; 487 | header->master_iter = SUBKEY_ITER; 488 | header->master_hmac = safe_malloc(hmac_size); 489 | header->master_key = keymem_alloc(header->keymem, key_size); 490 | header->shares = safe_malloc(sharecount * sizeof(share_data_t)); 491 | 492 | if (iter_ms) { 493 | iter = pbkdf2_itertime(hash_idx, key_size, iter_ms); 494 | } 495 | for (i = 0;i < sharecount; i++) { 496 | snprintf( prompt, 64, "Enter Password [%d/%d]: ", i + 1, sharecount); 497 | snprintf(vprompt, 64, "Verify Password [%d/%d]: ", i + 1, sharecount); 498 | pass_ret = get_pass(pass, 128, prompt, vprompt, "Passwords did not match, please try again.", 10); 499 | if (pass_ret < 0) { 500 | fprintf(stderr, "Password entry failed.\n"); 501 | exit(EXIT_FAILURE); 502 | } else if (pass_ret < 1) { 503 | fprintf(stderr, "Password must be at least one character(s)\n"); 504 | i--; /* Retry this keyslot */ 505 | } else { 506 | assert(pass_ret == (int)strlen(pass)); 507 | share_data_t *share = &(header->shares[i]); 508 | share->iter = MAX(1024, iter ^ (random() & 0x01ff)); 509 | share->key = keymem_alloc(header->keymem, key_size); 510 | fill_prng(share->salt, salt_size); 511 | pbkdf2(pass, pass_ret, share->salt, salt_size, share->iter, hash_idx, share->key, &key_size); 512 | MEMWIPE(pass, sizeof(pass)); 513 | } 514 | } 515 | ret = tc_gfsplit(header); 516 | /* master_key generated and set, shares should be clean */ 517 | 518 | /* open output file */ 519 | if (out_file != NULL) { 520 | if ((out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { 521 | fprintf(stderr, "%s: Failed to open '%s' for writing: ", progname, out_file); 522 | perror(""); 523 | exit(EXIT_FAILURE); 524 | } 525 | } else { 526 | out_fd = fileno(stdout); 527 | } 528 | write_header(header, out_fd); 529 | /* encrypt data */ 530 | ssize_t len; 531 | unsigned char *IV = NULL; 532 | assert(header->master_key != NULL); 533 | do { 534 | if ((len = read(in_fd, buf, BUFFER_SIZE)) < 0) { 535 | perror("Input file read error"); 536 | exit(EXIT_FAILURE); 537 | } 538 | unsigned char blklen[4]; 539 | unsigned char blkmac[32]; 540 | 541 | if ((err = encrypt_block(buf, buf, len, 542 | header->master_key, header->key_size, 543 | blkmac, header->hmac_size, IV)) != THRCR_OK) { 544 | fprintf(stderr, "Error: Failed to encrypt block\n"); 545 | MEMWIPE(buf, BUFFER_SIZE); 546 | exit(EXIT_FAILURE); 547 | } 548 | uint32_t ulen = len; 549 | STORE32H(ulen, blklen); 550 | if ((err = write(out_fd, blklen, 4) < 4) || 551 | (err = write(out_fd, buf, len) < (ssize_t)len) || 552 | (err = write(out_fd, blkmac, header->hmac_size) < (ssize_t)(header->hmac_size))) { 553 | if (err == -1) { 554 | perror("Error writing output"); 555 | } else { 556 | fprintf(stderr, "Error: Short write to output\n"); 557 | } 558 | ret = THRCR_WRITEERR; 559 | break; 560 | } 561 | /* The final block is zero len and acts as an authenticate EoF marker */ 562 | } while (len > 0); 563 | close(in_fd); 564 | close(out_fd); 565 | safe_free(IV); 566 | MEMWIPE(buf, BUFFER_SIZE); 567 | return ret; 568 | } 569 | 570 | close(in_fd); 571 | return THRCR_ERROR; 572 | } 573 | 574 | /* vim: set ts=2 sw=2 et ai si: */ 575 | --------------------------------------------------------------------------------