diff --git a/Makefile b/Makefile index f0c39f8..e682b7e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ all: gcc -O0 -Wall -g tc-play.c crc32.c safe_mem.c io.c crypto.c hdr.c openssl/openssl/libcrypto.a -o tc-play -ldevmapper -lprop -lutil +experimental: + gcc -O0 -Wall -g tc-play.c crc32.c safe_mem.c io.c crypto-dev.c hdr.c openssl/openssl/libcrypto.a -o tc-play -ldevmapper -lprop -lutil clean: rm -f tc-play tc-play.core *.o ktrace.out diff --git a/crypto-dev.c b/crypto-dev.c new file mode 100644 index 0000000..7f16db3 --- /dev/null +++ b/crypto-dev.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2011 Alex Hornung . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "crc32.h" +#include "tc-play.h" + +static +int +getallowsoft(void) +{ + int old; + size_t olen; + + olen = sizeof(old); + + if (sysctlbyname("kern.cryptodevallowsoft", &old, &olen, NULL, 0) < 0) { + perror("accessing sysctl kern.cryptodevallowsoft failed"); + } + + return old; +} + +static +void +setallowsoft(int new) +{ + int old; + size_t olen, nlen; + + olen = nlen = sizeof(new); + + if (sysctlbyname("kern.cryptodevallowsoft", &old, &olen, &new, nlen) < 0) { + perror("accessing sysctl kern.cryptodevallowsoft failed"); + } +} + +static +int +syscrypt(int cipher, unsigned char *key, size_t klen, unsigned char *iv, + unsigned char *in, unsigned char *out, size_t len, int do_encrypt) +{ + struct session_op session; + struct crypt_op cryp; + int cryptodev_fd = -1, fd = -1; + + if ((cryptodev_fd = open("/dev/crypto", O_RDWR, 0)) < 0) { + perror("Could not open /dev/crypto"); + goto err; + } + if (ioctl(cryptodev_fd, CRIOGET, &fd) == -1) { + perror("CRIOGET failed"); + goto err; + } + memset(&session, 0, sizeof(session)); + session.cipher = cipher; + session.key = (caddr_t) key; + session.keylen = klen; + if (ioctl(fd, CIOCGSESSION, &session) == -1) { + perror("CIOCGSESSION failed"); + goto err; + } + memset(&cryp, 0, sizeof(cryp)); + cryp.ses = session.ses; + cryp.op = do_encrypt ? COP_ENCRYPT : COP_DECRYPT; + cryp.flags = 0; + cryp.len = len; + cryp.src = (caddr_t) in; + cryp.dst = (caddr_t) out; + cryp.iv = (caddr_t) iv; + cryp.mac = 0; + if (ioctl(fd, CIOCCRYPT, &cryp) == -1) { + perror("CIOCCRYPT failed"); + goto err; + } + if (ioctl(fd, CIOCFSESSION, &session.ses) == -1) { + perror("CIOCFSESSION failed"); + goto err; + } + close(fd); + close(cryptodev_fd); + return (0); + +err: + if (fd != -1) + close(fd); + if (cryptodev_fd != -1) + close(cryptodev_fd); + return (-1); +} + +static +int +get_cryptodev_cipher_id(struct tc_crypto_algo *cipher) +{ + if (strcmp(cipher->name, "AES-128-XTS") == 0) + return CRYPTO_AES_XTS; + else if (strcmp(cipher->name, "AES-256-XTS") == 0) + return CRYPTO_AES_XTS; + else + return -1; +} + +int +tc_crypto_init(void) +{ + int allowed; + + OpenSSL_add_all_algorithms(); + + allowed = getallowsoft(); + if (allowed == 0) + setallowsoft(1); + + return 0; +} + +int +tc_encrypt(struct tc_crypto_algo *cipher, unsigned char *key, + unsigned char *iv, + unsigned char *in, int in_len, unsigned char *out) +{ + int cipher_id; + + cipher_id = get_cryptodev_cipher_id(cipher); + if (cipher_id < 0) { + fprintf(stderr, "Cipher %s not found\n", cipher->name); + return ENOENT; + } + + return syscrypt(cipher_id, key, cipher->klen, iv, in, out, in_len, 1); +} + +int +tc_decrypt(struct tc_crypto_algo *cipher, unsigned char *key, + unsigned char *iv, + unsigned char *in, int in_len, unsigned char *out) +{ + int cipher_id; + + cipher_id = get_cryptodev_cipher_id(cipher); + if (cipher_id < 0) { + fprintf(stderr, "Cipher %s not found\n", cipher->name); + return ENOENT; + } + + return syscrypt(cipher_id, key, cipher->klen, iv, in, out, in_len, 0); +} + +int +pbkdf2(const char *pass, int passlen, const unsigned char *salt, int saltlen, + int iter, const char *hash_name, int keylen, unsigned char *out) +{ + const EVP_MD *md; + int r; + + md = EVP_get_digestbyname(hash_name); + if (md == NULL) { + printf("Hash %s not found\n", hash_name); + return ENOENT; + } + r = PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, md, + keylen, out); + + if (r == 0) { + printf("Error in PBKDF2\n"); + return EINVAL; + } + + return 0; +} + +int +apply_keyfiles(unsigned char *pass, size_t pass_memsz, const char *keyfiles[], + int nkeyfiles) +{ + int pl, k; + unsigned char *kpool; + unsigned char *kdata; + int kpool_idx; + size_t i, kdata_sz; + uint32_t crc; + + if (pass_memsz < MAX_PASSSZ) { + fprintf(stderr, "Not enough memory for password manipluation\n"); + return ENOMEM; + } + + pl = strlen(pass); + memset(pass+pl, 0, MAX_PASSSZ-pl); + + if ((kpool = alloc_safe_mem(KPOOL_SZ)) == NULL) { + fprintf(stderr, "Error allocating memory for keyfile pool\n"); + return ENOMEM; + } + + memset(kpool, 0, KPOOL_SZ); + + for (k = 0; k < nkeyfiles; k++) { +#ifdef DEBUG + printf("Loading keyfile %s into kpool\n", keyfiles[k]); +#endif + kpool_idx = 0; + crc = ~0U; + kdata_sz = MAX_KFILE_SZ; + + if ((kdata = read_to_safe_mem(keyfiles[k], 0, &kdata_sz)) == NULL) { + fprintf(stderr, "Error reading keyfile %s content\n", + keyfiles[k]); + free_safe_mem(kpool); + return EIO; + } + + for (i = 0; i < kdata_sz; i++) { + crc = crc32_intermediate(crc, kdata[i]); + + kpool[kpool_idx++] += (unsigned char)(crc >> 24); + kpool[kpool_idx++] += (unsigned char)(crc >> 16); + kpool[kpool_idx++] += (unsigned char)(crc >> 8); + kpool[kpool_idx++] += (unsigned char)(crc); + + /* Wrap around */ + if (kpool_idx == KPOOL_SZ) + kpool_idx = 0; + } + + free_safe_mem(kdata); + } + +#ifdef DEBUG + printf("Applying kpool to passphrase\n"); +#endif + /* Apply keyfile pool to passphrase */ + for (i = 0; i < KPOOL_SZ; i++) + pass[i] += kpool[i]; + + free_safe_mem(kpool); + + return 0; +} diff --git a/crypto.c b/crypto.c index c58540d..f3bf5cb 100644 --- a/crypto.c +++ b/crypto.c @@ -42,16 +42,17 @@ tc_crypto_init(void) } int -tc_encrypt(const char *cipher_name, unsigned char *key, unsigned char *iv, +tc_encrypt(struct tc_crypto_algo *cipher, unsigned char *key, + unsigned char *iv, unsigned char *in, int in_len, unsigned char *out) { const EVP_CIPHER *evp; EVP_CIPHER_CTX ctx; int outl, tmplen; - evp = EVP_get_cipherbyname(cipher_name); + evp = EVP_get_cipherbyname(cipher->name); if (evp == NULL) { - printf("Cipher %s not found\n", cipher_name); + fprintf(stderr, "Cipher %s not found\n", cipher->name); return ENOENT; } @@ -64,16 +65,17 @@ tc_encrypt(const char *cipher_name, unsigned char *key, unsigned char *iv, } int -tc_decrypt(const char *cipher_name, unsigned char *key, unsigned char *iv, +tc_decrypt(struct tc_crypto_algo *cipher, unsigned char *key, + unsigned char *iv, unsigned char *in, int in_len, unsigned char *out) { const EVP_CIPHER *evp; EVP_CIPHER_CTX ctx; int outl, tmplen; - evp = EVP_get_cipherbyname(cipher_name); + evp = EVP_get_cipherbyname(cipher->name); if (evp == NULL) { - printf("Cipher %s not found\n", cipher_name); + fprintf(stderr, "Cipher %s not found\n", cipher->name); return ENOENT; } @@ -94,7 +96,7 @@ pbkdf2(const char *pass, int passlen, const unsigned char *salt, int saltlen, md = EVP_get_digestbyname(hash_name); if (md == NULL) { - printf("Hash %s not found\n", hash_name); + fprintf(stderr, "Hash %s not found\n", hash_name); return ENOENT; } r = PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, md, diff --git a/hdr.c b/hdr.c index 4873a90..baa8cd0 100644 --- a/hdr.c +++ b/hdr.c @@ -43,7 +43,8 @@ #define HOST_TO_LE(n, v) v = htole ## n (v) struct tchdr_dec * -decrypt_hdr(struct tchdr_enc *ehdr, char *algo, unsigned char *key) +decrypt_hdr(struct tchdr_enc *ehdr, struct tc_crypto_algo *cipher, + unsigned char *key) { struct tchdr_dec *dhdr; unsigned char iv[128]; @@ -56,8 +57,8 @@ decrypt_hdr(struct tchdr_enc *ehdr, char *algo, unsigned char *key) memset(iv, 0, sizeof(iv)); - error = tc_decrypt(algo, key, iv, ehdr->enc, sizeof(struct tchdr_dec), - (unsigned char *)dhdr); + error = tc_decrypt(cipher, key, iv, ehdr->enc, + sizeof(struct tchdr_dec), (unsigned char *)dhdr); if (error) { fprintf(stderr, "Header decryption failed\n"); free_safe_mem(dhdr); @@ -189,7 +190,7 @@ create_hdr(unsigned char *pass, int passlen, struct pbkdf_prf_algo *prf_algo, HOST_TO_BE(32, dhdr->crc_dhdr); memset(iv, 0, sizeof(iv)); - error = tc_encrypt(cipher->name, key, iv, (unsigned char *)dhdr, + error = tc_encrypt(cipher, key, iv, (unsigned char *)dhdr, sizeof(struct tchdr_dec), ehdr->enc); if (error) { fprintf(stderr, "Header encryption failed\n"); diff --git a/tc-play.c b/tc-play.c index a752287..22e74bc 100644 --- a/tc-play.c +++ b/tc-play.c @@ -193,7 +193,7 @@ process_hdr(const char *dev, unsigned char *pass, int passlen, printf("\nTrying cipher %s\n", tc_crypto_algos[j].name); #endif - dhdr = decrypt_hdr(ehdr, tc_crypto_algos[j].name, key); + dhdr = decrypt_hdr(ehdr, &tc_crypto_algos[j], key); if (dhdr == NULL) { continue; } @@ -410,7 +410,6 @@ dm_setup(const char *mapname, struct tcplay_info *info) if ((params = alloc_safe_mem(512)) == NULL) { fprintf(stderr, "could not allocate safe parameters memory"); return ENOMEM; - } /* aes-cbc-essiv:sha256 7997f8af... 0 /dev/ad0s0a 8 */ diff --git a/tc-play.h b/tc-play.h index 5749062..c74b2a2 100644 --- a/tc-play.h +++ b/tc-play.h @@ -110,9 +110,11 @@ int write_mem(const char *dev, off_t offset, size_t blksz, void *mem, size_t byt int read_passphrase(char *prompt, char *pass, size_t passlen); int tc_crypto_init(void); -int tc_encrypt(const char *cipher_name, unsigned char *key, unsigned char *iv, +int tc_encrypt(struct tc_crypto_algo *cipher, unsigned char *key, + unsigned char *iv, unsigned char *in, int in_len, unsigned char *out); -int tc_decrypt(const char *cipher_name, unsigned char *key, unsigned char *iv, +int tc_decrypt(struct tc_crypto_algo *cipher, unsigned char *key, + unsigned char *iv, unsigned char *in, int in_len, unsigned char *out); int pbkdf2(const char *pass, int passlen, const unsigned char *salt, int saltlen, int iter, const char *hash_name, int keylen, unsigned char *out); @@ -123,7 +125,7 @@ struct tchdr_enc *create_hdr(unsigned char *pass, int passlen, struct pbkdf_prf_algo *prf_algo, struct tc_crypto_algo *cipher, size_t sec_sz, size_t total_blocks, off_t offset, size_t blocks, int hidden); -struct tchdr_dec *decrypt_hdr(struct tchdr_enc *ehdr, char *algo, +struct tchdr_dec *decrypt_hdr(struct tchdr_enc *ehdr, struct tc_crypto_algo *cipher, unsigned char *key); int verify_hdr(struct tchdr_dec *hdr);