${_libsmdb} \
${_libsmutil} \
libstand \
+ libtcplay \
libtelnet \
libusbhid \
libvgl \
--- /dev/null
+LIB= tcplay
+SHLIB_MAJOR?= 0
+WARNS?= 6
+MAN= tcplay.3
+
+MLINKS+= tcplay.3 tc_api_init.3
+MLINKS+= tcplay.3 tc_api_uninit.3
+MLINKS+= tcplay.3 tc_api_create_volume.3
+MLINKS+= tcplay.3 tc_api_map_volume.3
+MLINKS+= tcplay.3 tc_api_unmap_volume.3
+MLINKS+= tcplay.3 tc_api_check_cipher.3
+MLINKS+= tcplay.3 tc_api_check_prf_hash.3
+MLINKS+= tcplay.3 tc_api_get_error_msg.3
+MLINKS+= tcplay.3 tc_api_get_summary.3
+
+
+SRCS+= tcplay_api.c tcplay.c crc32.c safe_mem.c \
+ io.c crypto-dev.c hdr.c
+INCS+= tcplay_api.h
+
+LDFLAGS+= -Wl,-version-script=${.CURDIR}/tcplay.map
+
+.include <bsd.lib.mk>
--- /dev/null
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ */
+
+#include <stdlib.h>
+#include "crc32.h"
+
+uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+uint32_t
+crc32(const void *buf, size_t size)
+{
+ const uint8_t *p;
+ uint32_t crc;
+
+ p = buf;
+ crc = ~0U;
+
+ while (size--)
+ crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+
+ return crc ^ ~0U;
+}
+
+uint32_t
+crc32_intermediate(uint32_t crc, uint8_t d)
+{
+ return crc32_tab[(crc ^ d) & 0xFF] ^ (crc >> 8);
+}
--- /dev/null
+uint32_t crc32(const void *buf, size_t size);
+uint32_t crc32_intermediate(uint32_t crc, uint8_t d);
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
+ * 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 <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <crypto/cryptodev.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <openssl/evp.h>
+
+#include "crc32.h"
+#include "tcplay.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 if (strcmp(cipher->name, "TWOFISH-128-XTS") == 0)
+ return CRYPTO_TWOFISH_XTS;
+ else if (strcmp(cipher->name, "TWOFISH-256-XTS") == 0)
+ return CRYPTO_TWOFISH_XTS;
+ else if (strcmp(cipher->name, "SERPENT-128-XTS") == 0)
+ return CRYPTO_SERPENT_XTS;
+ else if (strcmp(cipher->name, "SERPENT-256-XTS") == 0)
+ return CRYPTO_SERPENT_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_cipher_chain_populate_keys(struct tc_cipher_chain *cipher_chain,
+ unsigned char *key)
+{
+ int total_key_bytes, used_key_bytes;
+ struct tc_cipher_chain *dummy_chain;
+
+ /*
+ * We need to determine the total key bytes as the key locations
+ * depend on it.
+ */
+ total_key_bytes = 0;
+ for (dummy_chain = cipher_chain;
+ dummy_chain != NULL;
+ dummy_chain = dummy_chain->next) {
+ total_key_bytes += dummy_chain->cipher->klen;
+ }
+
+ /*
+ * Now we need to get prepare the keys, as the keys are in
+ * forward order with respect to the cipher cascade, but
+ * the actual decryption is in reverse cipher cascade order.
+ */
+ used_key_bytes = 0;
+ for (dummy_chain = cipher_chain;
+ dummy_chain != NULL;
+ dummy_chain = dummy_chain->next) {
+ dummy_chain->key = alloc_safe_mem(dummy_chain->cipher->klen);
+ if (dummy_chain->key == NULL) {
+ tc_log(1, "tc_decrypt: Could not allocate key "
+ "memory\n");
+ return ENOMEM;
+ }
+
+ /* XXX: here we assume XTS operation! */
+ memcpy(dummy_chain->key,
+ key + used_key_bytes/2,
+ dummy_chain->cipher->klen/2);
+ memcpy(dummy_chain->key + dummy_chain->cipher->klen/2,
+ key + (total_key_bytes/2) + used_key_bytes/2,
+ dummy_chain->cipher->klen/2);
+
+ /* Remember how many key bytes we've seen */
+ used_key_bytes += dummy_chain->cipher->klen;
+ }
+
+ return 0;
+}
+
+int
+tc_encrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
+ unsigned char *iv,
+ unsigned char *in, int in_len, unsigned char *out)
+{
+ int cipher_id;
+ int err;
+
+ if ((err = tc_cipher_chain_populate_keys(cipher_chain, key)))
+ return err;
+
+#ifdef DEBUG
+ printf("tc_encrypt: starting chain\n");
+#endif
+
+ /*
+ * Now process the actual decryption, in forward cascade order.
+ */
+ for (;
+ cipher_chain != NULL;
+ cipher_chain = cipher_chain->next) {
+ cipher_id = get_cryptodev_cipher_id(cipher_chain->cipher);
+ if (cipher_id < 0) {
+ tc_log(1, "Cipher %s not found\n",
+ cipher_chain->cipher->name);
+ return ENOENT;
+ }
+
+#ifdef DEBUG
+ printf("tc_encrypt: Currently using cipher %s\n",
+ cipher_chain->cipher->name);
+#endif
+
+ err = syscrypt(cipher_id, cipher_chain->key,
+ cipher_chain->cipher->klen, iv, in, out, in_len, 1);
+
+ /* Deallocate this key, since we won't need it anymore */
+ free_safe_mem(cipher_chain->key);
+
+ if (err != 0)
+ return err;
+
+ /* Set next input buffer as current output buffer */
+ in = out;
+ }
+
+ return 0;
+}
+
+int
+tc_decrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
+ unsigned char *iv,
+ unsigned char *in, int in_len, unsigned char *out)
+{
+ int cipher_id;
+ int err;
+
+ if ((err = tc_cipher_chain_populate_keys(cipher_chain, key)))
+ return err;
+
+#ifdef DEBUG
+ printf("tc_decrypt: starting chain!\n");
+#endif
+
+ /*
+ * Now process the actual decryption, in reverse cascade order; so
+ * first find the last element in the chain.
+ */
+ for (; cipher_chain->next != NULL; cipher_chain = cipher_chain->next)
+ ;
+ for (;
+ cipher_chain != NULL;
+ cipher_chain = cipher_chain->prev) {
+ cipher_id = get_cryptodev_cipher_id(cipher_chain->cipher);
+ if (cipher_id < 0) {
+ tc_log(1, "Cipher %s not found\n",
+ cipher_chain->cipher->name);
+ return ENOENT;
+ }
+
+#ifdef DEBUG
+ printf("tc_decrypt: Currently using cipher %s\n",
+ cipher_chain->cipher->name);
+#endif
+
+ err = syscrypt(cipher_id, cipher_chain->key,
+ cipher_chain->cipher->klen, iv, in, out, in_len, 0);
+
+ /* Deallocate this key, since we won't need it anymore */
+ free_safe_mem(cipher_chain->key);
+
+ if (err != 0)
+ return err;
+
+ /* Set next input buffer as current output buffer */
+ in = out;
+ }
+
+ return 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) {
+ tc_log(1, "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) {
+ tc_log(1, "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) {
+ tc_log(1, "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;
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
+ * 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 <sys/types.h>
+#include <sys/endian.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "crc32.h"
+#include "tcplay.h"
+
+/* Endianess macros */
+#define BE_TO_HOST(n, v) v = be ## n ## toh(v)
+#define LE_TO_HOST(n, v) v = le ## n ## toh(v)
+#define HOST_TO_BE(n, v) v = htobe ## n (v)
+#define HOST_TO_LE(n, v) v = htole ## n (v)
+
+struct tchdr_dec *
+decrypt_hdr(struct tchdr_enc *ehdr, struct tc_cipher_chain *cipher_chain,
+ unsigned char *key)
+{
+ struct tchdr_dec *dhdr;
+ unsigned char iv[128];
+ int error;
+
+ if ((dhdr = alloc_safe_mem(sizeof(struct tchdr_dec))) == NULL) {
+ tc_log(1, "Error allocating safe tchdr_dec memory\n");
+ return NULL;
+ }
+
+ memset(iv, 0, sizeof(iv));
+
+ error = tc_decrypt(cipher_chain, key, iv, ehdr->enc,
+ sizeof(struct tchdr_dec), (unsigned char *)dhdr);
+ if (error) {
+ tc_log(1, "Header decryption failed\n");
+ free_safe_mem(dhdr);
+ return NULL;
+ }
+
+ BE_TO_HOST(16, dhdr->tc_ver);
+ LE_TO_HOST(16, dhdr->tc_min_ver);
+ BE_TO_HOST(32, dhdr->crc_keys);
+ BE_TO_HOST(64, dhdr->vol_ctime);
+ BE_TO_HOST(64, dhdr->hdr_ctime);
+ BE_TO_HOST(64, dhdr->sz_hidvol);
+ BE_TO_HOST(64, dhdr->sz_vol);
+ BE_TO_HOST(64, dhdr->off_mk_scope);
+ BE_TO_HOST(64, dhdr->sz_mk_scope);
+ BE_TO_HOST(32, dhdr->flags);
+ BE_TO_HOST(32, dhdr->sec_sz);
+ BE_TO_HOST(32, dhdr->crc_dhdr);
+
+ return dhdr;
+}
+
+int
+verify_hdr(struct tchdr_dec *hdr)
+{
+ uint32_t crc;
+
+ if (memcmp(hdr->tc_str, TC_SIG, sizeof(hdr->tc_str)) != 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Signature mismatch\n");
+#endif
+ return 0;
+ }
+
+ crc = crc32((void *)&hdr->keys, 256);
+ if (crc != hdr->crc_keys) {
+#ifdef DEBUG
+ fprintf(stderr, "CRC32 mismatch (crc_keys)\n");
+#endif
+ return 0;
+ }
+
+ switch(hdr->tc_ver) {
+ case 1:
+ case 2:
+ /* Unsupported header version */
+ tc_log(1, "Header version %d unsupported\n", hdr->tc_ver);
+ return 0;
+
+ case 3:
+ case 4:
+ hdr->sec_sz = 512;
+ break;
+ }
+
+ return 1;
+}
+
+struct tchdr_enc *
+create_hdr(unsigned char *pass, int passlen, struct pbkdf_prf_algo *prf_algo,
+ struct tc_cipher_chain *cipher_chain, size_t sec_sz,
+ size_t total_blocks __unused,
+ off_t offset, size_t blocks, int hidden)
+{
+ struct tchdr_enc *ehdr;
+ struct tchdr_dec *dhdr;
+ unsigned char *key;
+ unsigned char iv[128];
+ int error;
+
+ if ((dhdr = (struct tchdr_dec *)alloc_safe_mem(sizeof(*dhdr))) == NULL) {
+ tc_log(1, "could not allocate safe dhdr memory\n");
+ return NULL;
+ }
+
+ if ((ehdr = (struct tchdr_enc *)alloc_safe_mem(sizeof(*ehdr))) == NULL) {
+ tc_log(1, "could not allocate safe ehdr memory\n");
+ return NULL;
+ }
+
+ if ((key = alloc_safe_mem(MAX_KEYSZ)) == NULL) {
+ tc_log(1, "could not allocate safe key memory\n");
+ return NULL;
+ }
+
+ if ((error = get_random(ehdr->salt, sizeof(ehdr->salt))) != 0) {
+ tc_log(1, "could not get salt\n");
+ return NULL;
+ }
+
+ error = pbkdf2(pass, passlen,
+ ehdr->salt, sizeof(ehdr->salt),
+ prf_algo->iteration_count,
+ prf_algo->name, MAX_KEYSZ, key);
+ if (error) {
+ tc_log(1, "could not derive key\n");
+ return NULL;
+ }
+
+ memset(dhdr, 0, sizeof(*dhdr));
+
+ if ((error = get_random(dhdr->keys, sizeof(dhdr->keys))) != 0) {
+ tc_log(1, "could not get key random bits\n");
+ return NULL;
+ }
+
+ memcpy(dhdr->tc_str, "TRUE", 4);
+ dhdr->tc_ver = 5;
+ dhdr->tc_min_ver = 7;
+ dhdr->crc_keys = crc32((void *)&dhdr->keys, 256);
+ dhdr->sz_vol = blocks * sec_sz;
+ if (hidden)
+ dhdr->sz_hidvol = dhdr->sz_vol;
+ dhdr->off_mk_scope = offset * sec_sz;
+ dhdr->sz_mk_scope = blocks * sec_sz;
+ dhdr->sec_sz = sec_sz;
+ dhdr->flags = 0;
+
+ HOST_TO_BE(16, dhdr->tc_ver);
+ HOST_TO_LE(16, dhdr->tc_min_ver);
+ HOST_TO_BE(32, dhdr->crc_keys);
+ HOST_TO_BE(64, dhdr->sz_vol);
+ HOST_TO_BE(64, dhdr->sz_hidvol);
+ HOST_TO_BE(64, dhdr->off_mk_scope);
+ HOST_TO_BE(64, dhdr->sz_mk_scope);
+ HOST_TO_BE(32, dhdr->sec_sz);
+ HOST_TO_BE(32, dhdr->flags);
+
+ dhdr->crc_dhdr = crc32((void *)dhdr, 188);
+ HOST_TO_BE(32, dhdr->crc_dhdr);
+
+ memset(iv, 0, sizeof(iv));
+ error = tc_encrypt(cipher_chain, key, iv, (unsigned char *)dhdr,
+ sizeof(struct tchdr_dec), ehdr->enc);
+ if (error) {
+ tc_log(1, "Header encryption failed\n");
+ free_safe_mem(dhdr);
+ return NULL;
+ }
+
+ free_safe_mem(dhdr);
+ return ehdr;
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
+ * 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 <sys/types.h>
+#include <sys/diskslice.h>
+#include <sys/uio.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "tcplay.h"
+
+void *
+read_to_safe_mem(const char *file, off_t offset, size_t *sz)
+{
+ void *mem = NULL;
+ ssize_t r = 0;
+ int fd;
+
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ tc_log(1, "Error opening file %s\n", file);
+ return NULL;
+ }
+
+ if ((mem = alloc_safe_mem(*sz)) == NULL) {
+ tc_log(1, "Error allocating memory\n");
+ goto out;
+ }
+
+ if ((lseek(fd, offset, SEEK_SET) < 0)) {
+ tc_log(1, "Error seeking on file %s\n", file);
+ goto m_err;
+ }
+
+ if ((r = read(fd, mem, *sz)) <= 0) {
+ tc_log(1, "Error reading from file %s\n", file);
+ goto m_err;
+ }
+
+out:
+ *sz = r;
+ close(fd);
+ return mem;
+ /* NOT REACHED */
+
+m_err:
+ free_safe_mem(mem);
+ close(fd);
+ return NULL;
+}
+
+int
+get_random(unsigned char *buf, size_t len)
+{
+ int fd;
+ ssize_t r;
+ size_t rd = 0;
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; /* 10 ms */
+
+
+ if ((fd = open("/dev/random", O_RDONLY)) < 0) {
+ tc_log(1, "Error opening /dev/random\n");
+ return -1;
+ }
+
+ while (rd < len) {
+ if ((r = read(fd, buf+rd, len-rd)) < 0) {
+ tc_log(1, "Error reading from /dev/random\n");
+ close(fd);
+ return -1;
+ }
+ rd += r;
+ nanosleep(&ts, NULL);
+ }
+
+ close(fd);
+ return 0;
+}
+
+static size_t secure_erase_total_bytes = 0;
+static size_t secure_erase_erased_bytes = 0;
+
+static
+void
+secure_erase_summary(void)
+{
+ float pct_done;
+
+ pct_done = (1.0 * secure_erase_erased_bytes) /
+ (1.0 * secure_erase_total_bytes) * 100.0;
+ tc_log(0, "Securely erasing, %.0f%% done.\n", pct_done);
+}
+
+int
+secure_erase(const char *dev, size_t bytes, size_t blksz)
+{
+ size_t erased = 0;
+ int fd_rand, fd;
+ char buf[ERASE_BUFFER_SIZE];
+ ssize_t r, w;
+ size_t sz;
+
+ if (blksz > MAX_BLKSZ) {
+ tc_log(1, "blksz > MAX_BLKSZ\n");
+ return -1;
+ }
+
+ if ((fd_rand = open("/dev/urandom", O_RDONLY)) < 0) {
+ tc_log(1, "Error opening /dev/urandom\n");
+ return -1;
+ }
+
+ if ((fd = open(dev, O_WRONLY)) < 0) {
+ close(fd_rand);
+ tc_log(1, "Error opening %s\n", dev);
+ return -1;
+ }
+
+ summary_fn = secure_erase_summary;
+ secure_erase_total_bytes = bytes;
+
+ sz = ERASE_BUFFER_SIZE;
+ while (erased < bytes) {
+ secure_erase_erased_bytes = erased;
+ /* Switch to block size when not much is remaining */
+ if ((bytes - erased) <= ERASE_BUFFER_SIZE)
+ sz = blksz;
+
+ if ((r = read(fd_rand, buf, sz)) < 0) {
+ tc_log(1, "Error reading from /dev/urandom\n");
+ close(fd);
+ close(fd_rand);
+ summary_fn = NULL;
+ return -1;
+ }
+
+ if (r < (ssize_t)blksz)
+ continue;
+
+ if ((w = write(fd, buf, r)) < 0) {
+ tc_log(1, "Error writing to %s\n", dev);
+ close(fd);
+ close(fd_rand);
+ summary_fn = NULL;
+ return -1;
+ }
+
+ erased += (size_t)w;
+ }
+
+ close(fd);
+ close(fd_rand);
+
+ summary_fn = NULL;
+
+ return 0;
+}
+
+int
+get_disk_info(const char *dev, size_t *blocks, size_t *bsize)
+{
+ struct partinfo pinfo;
+ int fd;
+
+ if ((fd = open(dev, O_RDONLY)) < 0) {
+ tc_log(1, "Error opening %s\n", dev);
+ return -1;
+ }
+
+ memset(&pinfo, 0, sizeof(struct partinfo));
+
+ if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ *blocks = pinfo.media_blocks;
+ *bsize = pinfo.media_blksize;
+
+ close(fd);
+ return 0;
+}
+
+int
+write_mem(const char *dev, off_t offset, size_t blksz __unused, void *mem,
+ size_t bytes)
+{
+ ssize_t w;
+ int fd;
+
+ if ((fd = open(dev, O_WRONLY)) < 0) {
+ tc_log(1, "Error opening device %s\n", dev);
+ return -1;
+ }
+
+ if ((lseek(fd, offset, SEEK_SET) < 0)) {
+ tc_log(1, "Error seeking on device %s\n", dev);
+ close(fd);
+ return -1;
+ }
+
+ if ((w = write(fd, mem, bytes)) <= 0) {
+ tc_log(1, "Error writing to device %s\n", dev);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+int
+read_passphrase(const char *prompt, char *pass, size_t passlen, time_t timeout)
+{
+ struct termios termios_old, termios_new;
+ struct timeval to;
+ fd_set fds;
+ ssize_t n;
+ int fd, r = 0, cfd = 0, nready;
+
+ if ((fd = open("/dev/tty", O_RDONLY)) == -1) {
+ fd = STDIN_FILENO;
+ cfd = 1;
+ }
+
+ printf(prompt);
+ fflush(stdout);
+
+ memset(pass, 0, passlen);
+
+ tcgetattr(fd, &termios_old);
+ memcpy(&termios_new, &termios_old, sizeof(termios_new));
+ termios_new.c_lflag &= ~ECHO;
+ tcsetattr(fd, TCSAFLUSH, &termios_new);
+
+ if (timeout > 0) {
+ memset(&to, 0, sizeof(to));
+ to.tv_sec = timeout;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ nready = select(fd + 1, &fds, NULL, NULL, &to);
+ if (nready <= 0) {
+ r = EINTR;
+ goto out;
+ }
+ }
+
+ n = read(fd, pass, passlen-1);
+ if (n > 0) {
+ pass[n-1] = '\0'; /* Strip trailing \n */
+ } else {
+ r = EIO;
+ }
+
+out:
+ if (cfd)
+ close(fd);
+
+ tcsetattr(fd, TCSAFLUSH, &termios_old);
+ putchar('\n');
+
+ return r;
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
+ * 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 <sys/types.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "tcplay.h"
+
+struct safe_mem_hdr {
+ struct safe_mem_hdr *prev;
+ struct safe_mem_hdr *next;
+ struct safe_mem_tail *tail;
+ const char *file;
+ int line;
+ size_t alloc_sz;
+ char sig[8]; /* SAFEMEM */
+};
+
+struct safe_mem_tail {
+ char sig[8]; /* SAFEMEM */
+};
+
+static struct safe_mem_hdr *safe_mem_hdr_first = NULL;
+
+void *
+_alloc_safe_mem(size_t req_sz, const char *file, int line)
+{
+ struct safe_mem_hdr *hdr, *hdrp;
+ struct safe_mem_tail *tail;
+ size_t alloc_sz;
+ char *mem, *user_mem;
+
+ alloc_sz = req_sz + sizeof(*hdr) + sizeof(*tail);
+ if ((mem = malloc(alloc_sz)) == NULL)
+ return NULL;
+
+ if (mlock(mem, alloc_sz) < 0) {
+ free(mem);
+ return NULL;
+ }
+
+ memset(mem, 0, alloc_sz);
+
+ hdr = (struct safe_mem_hdr *)mem;
+ tail = (struct safe_mem_tail *)(mem + alloc_sz - sizeof(*tail));
+ user_mem = mem + sizeof(*hdr);
+
+ strcpy(hdr->sig, "SAFEMEM");
+ strcpy(tail->sig, "SAFEMEM");
+ hdr->tail = tail;
+ hdr->alloc_sz = alloc_sz;
+ hdr->file = file;
+ hdr->line = line;
+ hdr->next = NULL;
+
+ if (safe_mem_hdr_first == NULL) {
+ safe_mem_hdr_first = hdr;
+ } else {
+ hdrp = safe_mem_hdr_first;
+ while (hdrp->next != NULL)
+ hdrp = hdrp->next;
+ hdr->prev = hdrp;
+ hdrp->next = hdr;
+ }
+
+ return user_mem;
+}
+
+void
+_free_safe_mem(void *mem_ptr, const char *file, int line)
+{
+ struct safe_mem_hdr *hdr;
+ struct safe_mem_tail *tail;
+ size_t alloc_sz;
+ char *mem = mem_ptr;
+
+ mem -= sizeof(*hdr);
+ hdr = (struct safe_mem_hdr *)mem;
+ tail = (struct safe_mem_tail *)(mem + hdr->alloc_sz - sizeof(*tail));
+
+#ifdef DEBUG
+ fprintf(stderr, "freeing safe_mem (hdr): %#lx (%s:%d)\n",
+ (unsigned long)(void *)hdr, hdr->file, hdr->line);
+#endif
+
+ if (hdr->alloc_sz == 0) {
+ fprintf(stderr, "BUG: double-free at %s:%d !!!\n", file, line);
+ return;
+ }
+
+ /* Integrity checks */
+ if ((memcmp(hdr->sig, "SAFEMEM\0", 8) != 0) ||
+ (memcmp(tail->sig, "SAFEMEM\0", 8) != 0)) {
+ fprintf(stderr, "BUG: safe_mem buffer under- or overflow at "
+ "%s:%d !!!\n", file, line);
+ return;
+ }
+
+ if (safe_mem_hdr_first == NULL) {
+ fprintf(stderr, "BUG: safe_mem list should not be empty at "
+ "%s:%d !!!\n", file, line);
+ return;
+ }
+
+ if (hdr->prev != NULL)
+ hdr->prev->next = hdr->next;
+ if (hdr->next != NULL)
+ hdr->next->prev = hdr->prev;
+ if (safe_mem_hdr_first == hdr)
+ safe_mem_hdr_first = hdr->next;
+
+ alloc_sz = hdr->alloc_sz;
+ memset(mem, 0xFF, alloc_sz);
+ memset(mem, 0, alloc_sz);
+
+ free(mem);
+}
+
+void
+check_and_purge_safe_mem(void)
+{
+ struct safe_mem_hdr *hdr;
+ char *mem;
+ int ok;
+
+ if (safe_mem_hdr_first == NULL)
+ return;
+
+ hdr = safe_mem_hdr_first;
+ while ((hdr = safe_mem_hdr_first) != NULL) {
+ if ((hdr->alloc_sz > 0) &&
+ (memcmp(hdr->sig, "SAFEMEM\0", 8) == 0) &&
+ (memcmp(hdr->tail->sig, "SAFEMEM\0", 8) == 0))
+ ok = 1;
+ else
+ ok = 0;
+
+#ifdef DEBUG
+ fprintf(stderr, "un-freed safe_mem: %#lx (%s:%d) [integrity=%s]\n",
+ (unsigned long)(void *)hdr, hdr->file, hdr->line,
+ ok? "ok" : "failed");
+#endif
+ mem = (void *)hdr;
+ mem += sizeof(*hdr);
+ _free_safe_mem(mem, "check_and_purge_safe_mem", 0);
+ }
+}
--- /dev/null
+.\"
+.\" Copyright (c) 2011 The DragonFly Project. 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.
+.\" 3. Neither the name of The DragonFly Project nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific, prior written permission.
+.\"
+.\" 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.
+.\"
+.Dd July 5, 2011
+.Dt TCPLAY 3
+.Os
+.Sh NAME
+.Nm tc_api_init ,
+.Nm tc_api_uninit ,
+.Nm tc_api_create_volume ,
+.Nm tc_api_map_volume ,
+.Nm tc_api_unmap_volume ,
+.Nm tc_api_check_cipher ,
+.Nm tc_api_check_prf_hash ,
+.Nm tc_api_get_error_msg ,
+.Nm tc_api_get_summary
+.Nd TrueCrypt volume management
+.Sh LIBRARY
+.Lb libtcplay
+.Sh SYNOPSIS
+.In tcplay_api.h
+.Ft int
+.Fn tc_api_init "int verbose"
+.Ft int
+.Fn tc_api_uninit "void"
+.Ft int
+.Fn tc_api_create_volume "tc_api_opts *api_opts"
+.Ft int
+.Fn tc_api_map_volume "tc_api_opts *api_opts"
+.Ft int
+.Fn tc_api_unmap_volume "tc_api_opts *api_opts"
+.Ft int
+.Fn tc_api_check_cipher "tc_api_opts *api_opts"
+.Ft int
+.Fn tc_api_check_prf_hash "tc_api_opts *api_opts"
+.Ft const char *
+.Fn tc_api_get_error_msg "void"
+.Ft const char *
+.Fn tc_api_get_summary "void"
+.Sh DESCRIPTION
+The
+.Nm tcplay
+library provides an interface to create, query and map
+TrueCrypt-compatible
+volumes.
+.Pp
+The
+.Fn tc_api_create_volume ,
+.Fn tc_api_map_volume ,
+.Fn tc_api_unmap_volume ,
+.Fn tc_api_check_cipher
+and
+.Fn tc_api_check_prf_hash
+functions take a
+.Vt tc_api_opts
+data structure as only argument, which is defined as follows:
+.Bd -literal
+typedef struct tc_api_opts {
+ /* Common fields */
+ char *tc_device;
+ char *tc_passphrase;
+ const char **tc_keyfiles;
+
+ /* Fields for mapping */
+ char *tc_map_name;
+ int tc_password_retries;
+ int tc_interactive_prompt;
+ unsigned long tc_prompt_timeout;
+
+ /* Fields for creation */
+ char *tc_cipher;
+ char *tc_prf_hash;
+ char *tc_cipher_hidden;
+ char *tc_prf_hash_hidden;
+ size_t tc_size_hidden_in_blocks;
+ char *tc_passphrase_hidden;
+ const char **tc_keyfiles_hidden;
+} tc_api_opts;
+.Ed
+.Pp
+where the keyfile fields,
+.Fa tc_keyfiles
+and
+.Fa tc_keyfiles_hidden ,
+are
+.Dv NULL
+terminated arrays of key file strings.
+.Pp
+The
+.Fn tc_api_init
+function initializes the library internals and prepares it for use via
+the API.
+This function has to be called before using any other API function.
+If the
+.Fa verbose
+argument is non-zero, then the library will output information such as
+errors via stdout and stderr.
+.Pp
+The
+.Fn tc_api_uninit
+function clears up all internal secure memory, such as memory used for
+decrypted headers, passphrases, keyfiles, etc.
+.Pp
+The
+.Fn tc_api_create_volume
+function creates a new volume using the parameters specified in the
+.Fa api_opts
+argument.
+The new volume will be created on the device specified by
+.Fa tc_device
+using the cipher specified by
+.Fa tc_cipher
+and the pbkdf2 prf hash algorithm specified by
+.Fa tc_prf_hash
+and using the passphrase and keyfiles specified by
+.Fa tc_passphrase
+and
+.Fa tc_keyfiles
+respectively.
+If
+.Fa tc_size_hidden_in_blocks
+is not zero, a hidden volume of the given size will be created, using
+the cipher specified by
+.Fa tc_cipher_hidden
+and the pbkdf2 prf hash algorithm specified by
+.Fa tc_prf_hash_hidden .
+If
+.Fa tc_cipher_hidden
+or
+.Fa tc_prf_hash_hidden
+are
+.Dv NULL ,
+the same algorithm as for the outer volume will be used.
+The passphrase and keyfiles used are specified by
+.Fa tc_passphrase_hidden
+and
+.Fa tc_keyfiles_hidden
+respectively.
+.Pp
+The
+.Fn tc_api_map_volume
+function maps a volume using the parameters specified in the
+.Fa api_opts
+argument.
+The volume, which will be mapped as
+.Fa tc_map_name ,
+is specified in
+.Fa tc_device .
+The
+.Fa tc_interactive_prompt
+field determines whether the user will be prompted to enter a passphrase
+interactively or whether the passphrase in
+.Fa tc_passphrase
+will be used.
+If an interactive prompt is used, the prompt will time out after
+.Fa tc_prompt_timeout
+seconds.
+A value of 0 indicates that no timeout will occur.
+The number of passphrase entry retries is defined by
+.Fa tc_password_retries .
+Note that the
+.Fn tc_api_map_volume
+function does not support accessing an outer volume while
+protecting the hidden volume.
+Depending on the passphrase/keyfiles used
+either the outer or the hidden volume will be mapped.
+.Pp
+The
+.Fn tc_api_unmap_volume
+unmaps / closes the volume specified in
+.Fa tc_map_name .
+.Pp
+The
+.Fn tc_api_check_cipher
+function checks whether the cipher specified in the
+.Fa api_opts
+argument field
+.Fa tc_cipher
+is valid.
+.Pp
+The
+.Fn tc_api_check_prf_hash
+function checks whether the prf hash algorithm specified in the
+.Fa api_opts
+argument field
+.Fa tc_prf_hash
+is valid.
+.Pp
+The
+.Fn tc_api_get_error_msg
+function should be called whenver another API function returns
+.Dv TC_ERR .
+It returns a string containing a description of the error that
+occured.
+.Pp
+The
+.Fn tc_api_get_summary
+function returns a string containing a summary of the current
+progress of a certain operation.
+Currently only the volume erasing
+part of creating a new volume can provide a summary.
+When no summary is available, an empty string is returned.
+The output otherwise is equivalent to that of a
+.Dv SIGINFO
+signal when using
+.Xr tcplay 8 .
+.Sh RETURN VALUES
+All functions except
+.Fn tc_api_get_error_msg
+and
+.Fn tc_api_get_summary
+return either
+.Dv TC_OK
+to signal that the operation completed successfully, or
+.Dv TC_ERR
+to signal that an error occured.
+.Pp
+The
+.Fn tc_api_get_error_msg
+and
+.Fn tc_api_get_summary
+always return a valid, but possibly empty, string.
+.Sh COMPATIBILITY
+The
+.Nm tcplay
+library offers full compatibility with TrueCrypt volumes including
+hidden
+volumes, system encryption (map-only), keyfiles and cipher cascading.
+.Sh SEE ALSO
+.Xr tcplay 8
+.Sh HISTORY
+The
+.Nm tcplay
+library appeared in
+.Dx 2.11 .
+.Sh AUTHORS
+.An Alex Hornung
+
--- /dev/null
+/*
+ * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
+ * 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 <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <err.h>
+#include <time.h>
+#include <libdevmapper.h>
+#include <libutil.h>
+
+#include "crc32.h"
+#include "tcplay.h"
+
+
+/* XXX TODO:
+ * - LRW-benbi support? needs further work in dm-crypt and even opencrypto
+ * - secure buffer review (i.e: is everything that needs it using secure mem?)
+ * - mlockall? (at least MCL_FUTURE, which is the only one we support)
+ */
+
+summary_fn_t summary_fn = NULL;
+int tc_internal_verbose = 1;
+char tc_internal_log_buffer[LOG_BUFFER_SZ];
+
+void
+tc_log(int is_err, const char *fmt, ...)
+{
+ __va_list ap;
+ FILE *fp;
+
+ if (is_err)
+ fp = stderr;
+ else
+ fp = stdout;
+
+ __va_start(ap, fmt);
+
+ vsnprintf(tc_internal_log_buffer, LOG_BUFFER_SZ, fmt, ap);
+
+ if (tc_internal_verbose)
+ vfprintf(fp, fmt, ap);
+
+ __va_end(ap);
+}
+
+/* Supported algorithms */
+struct pbkdf_prf_algo pbkdf_prf_algos[] = {
+ { "RIPEMD160", 2000 }, /* needs to come before the other RIPEMD160 */
+ { "RIPEMD160", 1000 },
+ { "SHA512", 1000 },
+ { "whirlpool", 1000 },
+ { NULL, 0 }
+};
+
+struct tc_crypto_algo tc_crypto_algos[] = {
+#if 0
+ /* XXX: turns out TC doesn't support AES-128-XTS */
+ { "AES-128-XTS", "aes-xts-plain", 32, 8 },
+ { "TWOFISH-128-XTS", "twofish-xts-plain", 32, 8 },
+ { "SERPENT-128-XTS", "serpent-xts-plain", 32, 8 },
+#endif
+ { "AES-256-XTS", "aes-xts-plain", 64, 8 },
+ { "TWOFISH-256-XTS", "twofish-xts-plain", 64, 8 },
+ { "SERPENT-256-XTS", "serpent-xts-plain", 64, 8 },
+ { NULL, NULL, 0, 0 }
+};
+
+const char *valid_cipher_chains[][MAX_CIPHER_CHAINS] = {
+ { "AES-256-XTS", NULL },
+ { "TWOFISH-256-XTS", NULL },
+ { "SERPENT-256-XTS", NULL },
+ { "AES-256-XTS", "TWOFISH-256-XTS", "SERPENT-256-XTS", NULL },
+ { "SERPENT-256-XTS", "TWOFISH-256-XTS", "AES-256-XTS", NULL },
+#if 0
+ /* It seems that all the two-way cascades are the other way round... */
+ { "AES-256-XTS", "TWOFISH-256-XTS", NULL },
+ { "SERPENT-256-XTS", "AES-256-XTS", NULL },
+ { "TWOFISH-256-XTS", "SERPENT-256-XTS", NULL },
+
+#endif
+ { "TWOFISH-256-XTS", "AES-256-XTS", NULL },
+ { "AES-256-XTS", "SERPENT-256-XTS", NULL },
+ { "SERPENT-256-XTS", "TWOFISH-256-XTS", NULL },
+ { NULL }
+};
+
+struct tc_cipher_chain *tc_cipher_chains[MAX_CIPHER_CHAINS];
+
+static
+int
+tc_build_cipher_chains(void)
+{
+ struct tc_cipher_chain *chain, *elem, *prev;
+ int i = 0;
+ int k;
+
+ while (valid_cipher_chains[i][0] != NULL) {
+ chain = NULL;
+ prev = NULL;
+ k = 0;
+
+ while (valid_cipher_chains[i][k] != NULL) {
+ if ((elem = alloc_safe_mem(sizeof(*elem))) == NULL) {
+ tc_log(1, "Error allocating memory for "
+ "cipher chain\n");
+ return -1;
+ }
+
+ /* Initialize first element of chain */
+ if (chain == NULL) {
+ chain = elem;
+ elem->prev = NULL;
+ }
+
+ /* Populate previous element */
+ if (prev != NULL) {
+ prev->next = elem;
+ elem->prev = prev;
+ }
+
+ /* Assume we are the last element in the chain */
+ elem->next = NULL;
+
+ /* Initialize other fields */
+ elem->cipher = check_cipher(valid_cipher_chains[i][k], 0);
+ if (elem->cipher == NULL)
+ return -1;
+
+ elem->key = NULL;
+
+ prev = elem;
+ ++k;
+ }
+
+ /* Store cipher chain */
+ tc_cipher_chains[i++] = chain;
+
+ /* Integrity check */
+ if (i >= MAX_CIPHER_CHAINS) {
+ tc_log(1, "FATAL: tc_cipher_chains is full!!\n");
+ return -1;
+ }
+
+ /* Make sure array is NULL terminated */
+ tc_cipher_chains[i] = NULL;
+ }
+
+ return 0;
+}
+
+#ifdef DEBUG
+static void
+print_hex(unsigned char *buf, off_t start, size_t len)
+{
+ size_t i;
+
+ for (i = start; i < start+len; i++)
+ printf("%02x", buf[i]);
+
+ printf("\n");
+}
+#endif
+
+void
+print_info(struct tcplay_info *info)
+{
+ struct tc_cipher_chain *cipher_chain;
+ int klen = 0;
+
+ printf("PBKDF2 PRF:\t\t%s\n", info->pbkdf_prf->name);
+ printf("PBKDF2 iterations:\t%d\n", info->pbkdf_prf->iteration_count);
+
+ printf("Cipher:\t\t\t");
+ for (cipher_chain = info->cipher_chain;
+ cipher_chain != NULL;
+ cipher_chain = cipher_chain->next) {
+ printf("%s%c", cipher_chain->cipher->name,
+ (cipher_chain->next != NULL) ? ',' : '\n');
+ klen += cipher_chain->cipher->klen;
+ }
+
+ printf("Key Length:\t\t%d bits\n", klen*8);
+ printf("CRC Key Data:\t\t%#x\n", info->hdr->crc_keys);
+ printf("Sector size:\t\t%d\n", info->hdr->sec_sz);
+ printf("Volume size:\t\t%zu sectors\n", info->size);
+}
+
+static
+struct tcplay_info *
+new_info(const char *dev, struct tc_cipher_chain *cipher_chain,
+ struct pbkdf_prf_algo *prf, struct tchdr_dec *hdr, off_t start)
+{
+ struct tcplay_info *info;
+ int i;
+ int error;
+
+ if ((info = (struct tcplay_info *)alloc_safe_mem(sizeof(*info))) == NULL) {
+ tc_log(1, "could not allocate safe info memory\n");
+ return NULL;
+ }
+
+ info->dev = dev;
+ info->cipher_chain = cipher_chain;
+ info->pbkdf_prf = prf;
+ info->start = start;
+ info->hdr = hdr;
+ info->size = hdr->sz_mk_scope / hdr->sec_sz; /* volume size */
+ info->skip = hdr->off_mk_scope / hdr->sec_sz; /* iv skip */
+ info->offset = hdr->off_mk_scope / hdr->sec_sz; /* block offset */
+
+ /* Associate a key out of the key pool with each cipher in the chain */
+ error = tc_cipher_chain_populate_keys(cipher_chain, hdr->keys);
+ if (error) {
+ tc_log(1, "could not populate keys in cipher chain\n");
+ return NULL;
+ }
+
+ for (; cipher_chain != NULL; cipher_chain = cipher_chain->next) {
+ for (i = 0; i < cipher_chain->cipher->klen; i++)
+ sprintf(&cipher_chain->dm_key[i*2], "%02x",
+ cipher_chain->key[i]);
+ }
+
+ return info;
+}
+
+int
+adjust_info(struct tcplay_info *info, struct tcplay_info *hinfo)
+{
+ if (hinfo->hdr->sz_hidvol == 0)
+ return 1;
+
+ info->size -= hinfo->hdr->sz_hidvol / hinfo->hdr->sec_sz;
+ return 0;
+}
+
+int
+process_hdr(const char *dev, unsigned char *pass, int passlen,
+ struct tchdr_enc *ehdr, struct tcplay_info **pinfo)
+{
+ struct tchdr_dec *dhdr;
+ struct tcplay_info *info;
+ unsigned char *key;
+ int i, j, found, error;
+
+ *pinfo = NULL;
+
+ if ((key = alloc_safe_mem(MAX_KEYSZ)) == NULL) {
+ tc_log(1, "could not allocate safe key memory\n");
+ return ENOMEM;
+ }
+
+ /* Start search for correct algorithm combination */
+ found = 0;
+ for (i = 0; !found && pbkdf_prf_algos[i].name != NULL; i++) {
+#ifdef DEBUG
+ printf("\nTrying PRF algo %s (%d)\n", pbkdf_prf_algos[i].name,
+ pbkdf_prf_algos[i].iteration_count);
+ printf("Salt: ");
+ print_hex(ehdr->salt, 0, sizeof(ehdr->salt));
+#endif
+ error = pbkdf2(pass, passlen,
+ ehdr->salt, sizeof(ehdr->salt),
+ pbkdf_prf_algos[i].iteration_count,
+ pbkdf_prf_algos[i].name, MAX_KEYSZ, key);
+
+ if (error) {
+ tc_log(1, "pbkdf failed for algorithm %s\n",
+ pbkdf_prf_algos[i].name);
+ return EINVAL;
+ }
+
+#if 0
+ printf("Derived Key: ");
+ print_hex(key, 0, MAX_KEYSZ);
+#endif
+
+ for (j = 0; !found && tc_cipher_chains[j] != NULL; j++) {
+#ifdef DEBUG
+ printf("\nTrying cipher chain %d\n", j);
+#endif
+
+ dhdr = decrypt_hdr(ehdr, tc_cipher_chains[j], key);
+ if (dhdr == NULL) {
+ tc_log(1, "hdr decryption failed for cipher "
+ "chain %d\n", j);
+ return EINVAL;
+ }
+
+ if (verify_hdr(dhdr)) {
+#ifdef DEBUG
+ printf("tc_str: %.4s, tc_ver: %zd, tc_min_ver: %zd, "
+ "crc_keys: %d, sz_vol: %"PRIu64", "
+ "off_mk_scope: %"PRIu64", sz_mk_scope: %"PRIu64", "
+ "flags: %d, sec_sz: %d crc_dhdr: %d\n",
+ dhdr->tc_str, dhdr->tc_ver, dhdr->tc_min_ver,
+ dhdr->crc_keys, dhdr->sz_vol, dhdr->off_mk_scope,
+ dhdr->sz_mk_scope, dhdr->flags, dhdr->sec_sz,
+ dhdr->crc_dhdr);
+#endif
+ found = 1;
+ } else {
+ free_safe_mem(dhdr);
+ }
+ }
+ }
+
+ free_safe_mem(key);
+
+ if (!found)
+ return EINVAL;
+
+ if ((info = new_info(dev, tc_cipher_chains[j-1], &pbkdf_prf_algos[i-1],
+ dhdr, 0)) == NULL) {
+ return ENOMEM;
+ }
+
+ *pinfo = info;
+ return 0;
+}
+
+int
+create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles,
+ const char *h_keyfiles[], int n_hkeyfiles, struct pbkdf_prf_algo *prf_algo,
+ struct tc_cipher_chain *cipher_chain, struct pbkdf_prf_algo *h_prf_algo,
+ struct tc_cipher_chain *h_cipher_chain, char *passphrase,
+ char *h_passphrase, size_t hidden_blocks_in, int interactive)
+{
+ char *pass, *pass_again;
+ char *h_pass = NULL;
+ char buf[1024];
+ size_t blocks, blksz, hidden_blocks = 0;
+ struct tchdr_enc *ehdr, *hehdr = NULL;
+ int64_t tmp;
+ int error, r;
+
+ if (cipher_chain == NULL)
+ cipher_chain = tc_cipher_chains[0];
+ if (prf_algo == NULL)
+ prf_algo = &pbkdf_prf_algos[0];
+ if (h_cipher_chain == NULL)
+ h_cipher_chain = cipher_chain;
+ if (h_prf_algo == NULL)
+ h_prf_algo = prf_algo;
+
+ if ((error = get_disk_info(dev, &blocks, &blksz)) != 0) {
+ tc_log(1, "could not get disk info\n");
+ return -1;
+ }
+
+ if (blocks <= MIN_VOL_BLOCKS) {
+ tc_log(1, "Cannot create volumes on devices with less "
+ "than %d blocks/sectors\n", MIN_VOL_BLOCKS);
+ return -1;
+ }
+
+ if (interactive) {
+ if (((pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) ||
+ ((pass_again = alloc_safe_mem(MAX_PASSSZ)) == NULL)) {
+ tc_log(1, "could not allocate safe passphrase memory\n");
+ return -1;
+ }
+
+ if ((error = read_passphrase("Passphrase: ", pass, MAX_PASSSZ, 0) ||
+ (read_passphrase("Repeat passphrase: ", pass_again,
+ MAX_PASSSZ, 0)))) {
+ tc_log(1, "could not read passphrase\n");
+ return -1;
+ }
+
+ if (strcmp(pass, pass_again) != 0) {
+ tc_log(1, "Passphrases don't match\n");
+ return -1;
+ }
+
+ free_safe_mem(pass_again);
+ } else {
+ /* In batch mode, use provided passphrase */
+ if ((pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) {
+ tc_log(1, "could not allocate safe "
+ "passphrase memory");
+ return -1;
+ }
+
+ if (passphrase != NULL)
+ strcpy(pass, passphrase);
+ }
+
+ if (nkeyfiles > 0) {
+ /* Apply keyfiles to 'pass' */
+ if ((error = apply_keyfiles(pass, MAX_PASSSZ, keyfiles,
+ nkeyfiles))) {
+ tc_log(1, "could not apply keyfiles\n");
+ }
+ }
+
+ if (hidden) {
+ if (interactive) {
+ if (((h_pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) ||
+ ((pass_again = alloc_safe_mem(MAX_PASSSZ)) == NULL)) {
+ tc_log(1, "could not allocate safe "
+ "passphrase memory\n");
+ return -1;
+ }
+
+ if ((error = read_passphrase("Passphrase for hidden volume: ",
+ h_pass, MAX_PASSSZ, 0) ||
+ (read_passphrase("Repeat passphrase: ", pass_again,
+ MAX_PASSSZ, 0)))) {
+ tc_log(1, "could not read passphrase\n");
+ return -1;
+ }
+
+ if (strcmp(h_pass, pass_again) != 0) {
+ tc_log(1, "Passphrases for hidden volume don't "
+ "match\n");
+ return -1;
+ }
+
+ free_safe_mem(pass_again);
+ } else {
+ /* In batch mode, use provided passphrase */
+ if ((h_pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) {
+ tc_log(1, "could not allocate safe "
+ "passphrase memory");
+ return -1;
+ }
+
+ if (h_passphrase != NULL)
+ strcpy(h_pass, h_passphrase);
+ }
+
+ if (n_hkeyfiles > 0) {
+ /* Apply keyfiles to 'h_pass' */
+ if ((error = apply_keyfiles(h_pass, MAX_PASSSZ, h_keyfiles,
+ n_hkeyfiles))) {
+ tc_log(1, "could not apply keyfiles\n");
+ return -1;
+ }
+ }
+
+ if (interactive) {
+ hidden_blocks = 0;
+ } else {
+ hidden_blocks = hidden_blocks_in;
+ if (hidden_blocks == 0) {
+ tc_log(1, "hidden_blocks to create volume "
+ "cannot be zero!\n");
+ return -1;
+ }
+ }
+
+ /* This only happens in interactive mode */
+ while (hidden_blocks == 0) {
+ if ((r = humanize_number(buf, strlen("XXX MB"),
+ (int64_t)(blocks * blksz), "B", 0, 0)) < 0) {
+ sprintf(buf, "%zu bytes", (blocks * blksz));
+ }
+
+ printf("The total volume size of %s is %s (bytes)\n", dev, buf);
+ memset(buf, 0, sizeof(buf));
+ printf("Size of hidden volume (e.g. 127M): ");
+ fflush(stdout);
+
+ if ((fgets(buf, sizeof(buf), stdin)) == NULL) {
+ tc_log(1, "Could not read from stdin\n");
+ return -1;
+ }
+
+ /* get rid of trailing newline */
+ buf[strlen(buf)-1] = '\0';
+ if ((error = dehumanize_number(buf,
+ &tmp)) != 0) {
+ tc_log(1, "Could not interpret input: %s\n", buf);
+ return -1;
+ }
+
+ hidden_blocks = (size_t)tmp;
+ hidden_blocks /= blksz;
+ if (hidden_blocks >= blocks - MIN_VOL_BLOCKS) {
+ tc_log(1, "Hidden volume needs to be "
+ "smaller than the outer volume\n");
+ hidden_blocks = 0;
+ continue;
+ }
+ }
+ }
+
+ if (interactive) {
+ /* Show summary and ask for confirmation */
+ printf("Summary of actions:\n");
+ printf(" - Completely erase *EVERYTHING* on %s\n", dev);
+ printf(" - Create %svolume on %s\n", hidden?("outer "):"", dev);
+ if (hidden) {
+ printf(" - Create hidden volume of %zu bytes at end of "
+ "outer volume\n",
+ hidden_blocks * blksz);
+ }
+
+ printf("\n Are you sure you want to proceed? (y/n) ");
+ fflush(stdout);
+ if ((fgets(buf, sizeof(buf), stdin)) == NULL) {
+ tc_log(1, "Could not read from stdin\n");
+ return -1;
+ }
+
+ if ((buf[0] != 'y') && (buf[0] != 'Y')) {
+ tc_log(1, "User cancelled action(s)\n");
+ return -1;
+ }
+ }
+
+ /* erase volume */
+ if ((error = secure_erase(dev, blocks * blksz, blksz)) != 0) {
+ tc_log(1, "could not securely erase device %s\n", dev);
+ return -1;
+ }
+
+ /* create encrypted headers */
+ ehdr = create_hdr(pass, (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass),
+ prf_algo, cipher_chain, blksz, blocks, MIN_VOL_BLOCKS,
+ blocks-MIN_VOL_BLOCKS, 0);
+ if (ehdr == NULL) {
+ tc_log(1, "Could not create header\n");
+ return -1;
+ }
+
+ if (hidden) {
+ hehdr = create_hdr(h_pass,
+ (n_hkeyfiles > 0)?MAX_PASSSZ:strlen(h_pass), h_prf_algo,
+ h_cipher_chain,
+ blksz, blocks, blocks - hidden_blocks, hidden_blocks, 1);
+ if (hehdr == NULL) {
+ tc_log(1, "Could not create hidden volume header\n");
+ return -1;
+ }
+ }
+
+ if ((error = write_mem(dev, 0, blksz, ehdr, sizeof(*ehdr))) != 0) {
+ tc_log(1, "Could not write volume header to device\n");
+ return -1;
+ }
+
+ if (hidden) {
+ if ((error = write_mem(dev, HDR_OFFSET_HIDDEN, blksz, hehdr,
+ sizeof(*hehdr))) != 0) {
+ tc_log(1, "Could not write hidden volume header to "
+ "device\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static
+struct tcplay_info *
+info_map_common(const char *dev, int sflag, const char *sys_dev,
+ int protect_hidden, const char *keyfiles[], int nkeyfiles,
+ const char *h_keyfiles[], int n_hkeyfiles, char *passphrase,
+ char *passphrase_hidden, int interactive, int retries, time_t timeout)
+{
+ struct tchdr_enc *ehdr, *hehdr = NULL;
+ struct tcplay_info *info, *hinfo = NULL;
+ char *pass;
+ char *h_pass;
+ int error, error2 = 0;
+ size_t sz;
+
+ info = NULL;
+ if (retries < 1)
+ retries = 1;
+
+ while ((info == NULL) && retries-- > 0)
+ {
+ h_pass = NULL;
+ ehdr = NULL;
+ hehdr = NULL;
+
+ if ((pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) {
+ tc_log(1, "could not allocate safe passphrase memory\n");
+ return NULL;
+ }
+
+ if (interactive) {
+ if ((error = read_passphrase("Passphrase: ", pass,
+ MAX_PASSSZ, timeout))) {
+ tc_log(1, "could not read passphrase\n");
+ return NULL;
+ }
+ } else {
+ /* In batch mode, use provided passphrase */
+ if (passphrase != NULL)
+ strcpy(pass, passphrase);
+ }
+
+ if (nkeyfiles > 0) {
+ /* Apply keyfiles to 'pass' */
+ if ((error = apply_keyfiles(pass, MAX_PASSSZ, keyfiles,
+ nkeyfiles))) {
+ tc_log(1, "could not apply keyfiles");
+ return NULL;
+ }
+ }
+
+ if (protect_hidden) {
+ if ((h_pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) {
+ tc_log(1, "could not allocate safe passphrase memory\n");
+ return NULL;
+ }
+
+ if (interactive) {
+ if ((error = read_passphrase(
+ "Passphrase for hidden volume: ", h_pass,
+ MAX_PASSSZ, timeout))) {
+ tc_log(1, "could not read passphrase\n");
+ return NULL;
+ }
+ } else {
+ /* In batch mode, use provided passphrase */
+ if (passphrase_hidden != NULL)
+ strcpy(h_pass, passphrase_hidden);
+ }
+
+ if (n_hkeyfiles > 0) {
+ /* Apply keyfiles to 'pass' */
+ if ((error = apply_keyfiles(h_pass, MAX_PASSSZ, h_keyfiles,
+ n_hkeyfiles))) {
+ tc_log(1, "could not apply keyfiles");
+ return NULL;
+ }
+ }
+ }
+
+ sz = HDRSZ;
+ ehdr = (struct tchdr_enc *)read_to_safe_mem((sflag) ? sys_dev : dev,
+ (sflag) ? HDR_OFFSET_SYS : 0, &sz);
+ if (ehdr == NULL) {
+ tc_log(1, "error read hdr_enc: %s", dev);
+ return NULL;
+ }
+
+ if (!sflag) {
+ sz = HDRSZ;
+ hehdr = (struct tchdr_enc *)read_to_safe_mem(dev,
+ HDR_OFFSET_HIDDEN, &sz);
+ if (hehdr == NULL) {
+ tc_log(1, "error read hdr_enc: %s", dev);
+ return NULL;
+ }
+ } else {
+ hehdr = NULL;
+ }
+
+ error = process_hdr(dev, pass, (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass),
+ ehdr, &info);
+
+ /*
+ * Try to process hidden header if we have to protect the hidden
+ * volume, or the decryption/verification of the main header
+ * failed.
+ */
+ if (hehdr && (error || protect_hidden)) {
+ if (error) {
+ error2 = process_hdr(dev, pass,
+ (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass), hehdr,
+ &info);
+ } else if (protect_hidden) {
+ error2 = process_hdr(dev, h_pass,
+ (n_hkeyfiles > 0)?MAX_PASSSZ:strlen(h_pass), hehdr,
+ &hinfo);
+ }
+ }
+
+ /* We need both to protect a hidden volume */
+ if ((protect_hidden && (error || error2)) ||
+ (error && error2)) {
+ tc_log(1, "Incorrect password or not a TrueCrypt volume\n");
+ info = NULL;
+ hinfo = NULL;
+
+ /* Try again (or finish) */
+ free_safe_mem(pass);
+ if (h_pass)
+ free_safe_mem(h_pass);
+ if (ehdr)
+ free_safe_mem(ehdr);
+ if (hehdr)
+ free_safe_mem(hehdr);
+ continue;
+ }
+
+ if (protect_hidden) {
+ if (adjust_info(info, hinfo) != 0) {
+ tc_log(1, "Could not protect hidden volume\n");
+ return NULL;
+ }
+ }
+ }
+
+ return info;
+}
+
+int
+info_volume(const char *device, int sflag, const char *sys_dev,
+ int protect_hidden, const char *keyfiles[], int nkeyfiles,
+ const char *h_keyfiles[], int n_hkeyfiles,
+ char *passphrase, char *passphrase_hidden, int interactive, int retries,
+ time_t timeout)
+{
+ struct tcplay_info *info;
+
+ info = info_map_common(device, sflag, sys_dev, protect_hidden,
+ keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles,
+ passphrase, passphrase_hidden, interactive, retries, timeout);
+
+ if (info != NULL) {
+ if (interactive)
+ print_info(info);
+ }
+
+ return (info != NULL) ? 0 : -1;
+}
+
+int
+map_volume(const char *map_name, const char *device, int sflag,
+ const char *sys_dev, int protect_hidden, const char *keyfiles[],
+ int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles,
+ char *passphrase, char *passphrase_hidden, int interactive, int retries,
+ time_t timeout)
+
+{
+ struct tcplay_info *info;
+ int error;
+
+ info = info_map_common(device, sflag, sys_dev, protect_hidden,
+ keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles,
+ passphrase, passphrase_hidden, interactive, retries, timeout);
+
+ if (info == NULL)
+ return -1;
+
+ if ((error = dm_setup(map_name, info)) != 0) {
+ tc_log(1, "Could not set up mapping %s\n", map_name);
+ return -1;
+ }
+
+ if (interactive)
+ printf("All ok!");
+
+ return 0;
+}
+
+static
+int
+dm_remove_device(const char *name)
+{
+ struct dm_task *dmt = NULL;
+ int ret = EINVAL;
+
+ if ((dmt = dm_task_create(DM_DEVICE_REMOVE)) == NULL)
+ goto out;
+
+ if ((dm_task_set_name(dmt, name)) == 0)
+ goto out;
+
+ if ((dm_task_run(dmt)) == 0)
+ goto out;
+
+ ret = 0;
+out:
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ return ret;
+}
+
+int
+dm_setup(const char *mapname, struct tcplay_info *info)
+{
+ struct tc_cipher_chain *cipher_chain;
+ struct dm_task *dmt = NULL;
+ struct dm_info dmi;
+ char *params = NULL;
+ char *uu;
+ char *uu_stack[64];
+ int uu_stack_idx;
+ uint32_t status;
+ int ret = 0;
+ int j;
+ off_t start, offset;
+ char dev[PATH_MAX];
+ char map[PATH_MAX];
+
+ if ((params = alloc_safe_mem(512)) == NULL) {
+ tc_log(1, "could not allocate safe parameters memory");
+ return ENOMEM;
+ }
+
+ strcpy(dev, info->dev);
+ start = info->start;
+ offset = info->offset;
+ uu_stack_idx = 0;
+
+ /* Get to the end of the chain */
+ for (cipher_chain = info->cipher_chain; cipher_chain->next != NULL;
+ cipher_chain = cipher_chain->next)
+ ;
+
+ for (j= 0; cipher_chain != NULL;
+ cipher_chain = cipher_chain->prev, j++) {
+ /* aes-cbc-essiv:sha256 7997f8af... 0 /dev/ad0s0a 8 */
+ /* iv off---^ block off--^ */
+ snprintf(params, 512, "%s %s %"PRIu64 " %s %"PRIu64,
+ cipher_chain->cipher->dm_crypt_str, cipher_chain->dm_key,
+ info->skip, dev, offset);
+#ifdef DEBUG
+ printf("Params: %s\n", params);
+#endif
+
+ if ((dmt = dm_task_create(DM_DEVICE_CREATE)) == NULL) {
+ tc_log(1, "dm_task_create failed\n");
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * If this is the last element in the cipher chain, use the
+ * final map name. Otherwise pick a secondary name...
+ */
+ if (cipher_chain->prev == NULL)
+ strcpy(map, mapname);
+ else
+ sprintf(map, "%s.%d", mapname, j);
+
+ if ((dm_task_set_name(dmt, map)) == 0) {
+ tc_log(1, "dm_task_set_name failed\n");
+ ret = -1;
+ goto out;
+ }
+
+ uuid_create(&info->uuid, &status);
+ if (status != uuid_s_ok) {
+ tc_log(1, "uuid_create failed\n");
+ ret = -1;
+ goto out;
+ }
+
+ uuid_to_string(&info->uuid, &uu, &status);
+ if (uu == NULL) {
+ tc_log(1, "uuid_to_string failed\n");
+ ret = -1;
+ goto out;
+ }
+
+ if ((dm_task_set_uuid(dmt, uu)) == 0) {
+ free(uu);
+ tc_log(1, "dm_task_set_uuid failed\n");
+ ret = -1;
+ goto out;
+ }
+
+ free(uu);
+
+ if ((dm_task_add_target(dmt, start, info->size, "crypt", params)) == 0) {
+ tc_log(1, "dm_task_add_target failed\n");
+ ret = -1;
+ goto out;
+ }
+
+ if ((dm_task_run(dmt)) == 0) {
+ tc_log(1, "dm_task_task_run failed\n");
+ ret = -1;
+ goto out;
+ }
+
+ if ((dm_task_get_info(dmt, &dmi)) == 0) {
+ tc_log(1, "dm_task_get info failed\n");
+ /* XXX: probably do more than just erroring out... */
+ ret = -1;
+ goto out;
+ }
+
+ asprintf(&uu_stack[uu_stack_idx++], "%s", map);
+
+ offset = 0;
+ start = 0;
+ sprintf(dev, "/dev/mapper/%s.%d", mapname, j);
+
+ dm_task_destroy(dmt);
+ }
+
+out:
+ /*
+ * If an error occured, try to unroll changes made before it
+ * happened.
+ */
+ if (ret) {
+ j = uu_stack_idx;
+ while (j > 0) {
+#ifdef DEBUG
+ printf("Unrolling dm changes! j = %d (%s)\n", j-1,
+ uu_stack[j-1]);
+#endif
+ if ((ret = dm_remove_device(uu_stack[--j])) != 0) {
+ tc_log(1, "Tried to unroll dm changes, "
+ "giving up.\n");
+ break;
+ }
+ }
+ }
+
+ while (uu_stack_idx > 0)
+ free(uu_stack[--uu_stack_idx]);
+
+ free_safe_mem(params);
+
+ return ret;
+}
+
+int
+dm_teardown(const char *mapname, const char *device __unused)
+{
+#if 0
+ struct dm_task *dmt = NULL;
+ struct dm_info dmi;
+#endif
+ char map[PATH_MAX];
+ int i, error;
+
+ if ((error = dm_remove_device(mapname)) != 0) {
+ tc_log(1, "Could not remove mapping %s\n", mapname);
+ return error;
+ }
+
+ /* Try to remove other cascade devices */
+ for (i = 2; i >= 0; i--) {
+ sprintf(map, "%s.%d", mapname, i);
+ dm_remove_device(map);
+ }
+
+ return 0;
+}
+
+struct tc_crypto_algo *
+check_cipher(const char *cipher, int quiet)
+{
+ int i, found = 0;
+
+ for (i = 0; tc_crypto_algos[i].name != NULL; i++) {
+ if (strcmp(cipher, tc_crypto_algos[i].name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found && !quiet) {
+ fprintf(stderr, "Valid ciphers are: ");
+ for (i = 0; tc_crypto_algos[i].name != NULL; i++)
+ fprintf(stderr, "%s ", tc_crypto_algos[i].name);
+ fprintf(stderr, "\n");
+ return NULL;
+ }
+
+ return &tc_crypto_algos[i];
+}
+
+struct tc_cipher_chain *
+check_cipher_chain(char *cipher_chain, int quiet)
+{
+ struct tc_cipher_chain *cipher = NULL;
+ int i,k, nciphers = 0, mismatch = 0;
+ char *ciphers[8];
+ char *tmp_chain, *tmp_chain_free;
+ char *token;
+
+ if ((tmp_chain = strdup(cipher_chain)) == NULL) {
+ tc_log(1, "Could not allocate strdup memory\n");
+ return NULL;
+ }
+
+ tmp_chain_free = tmp_chain;
+
+ while ((token = strsep(&tmp_chain, ",")) != NULL)
+ ciphers[nciphers++] = token;
+
+ cipher = NULL;
+
+ for (i = 0; valid_cipher_chains[i][0] != NULL; i++) {
+ mismatch = 0;
+
+ for (k = 0; (valid_cipher_chains[i][k] != NULL); k++) {
+ /*
+ * If there are more ciphers in the chain than in the
+ * ciphers[] variable this is not the right chain.
+ */
+ if (k == nciphers) {
+ mismatch = 1;
+ break;
+ }
+
+ if (strcmp(ciphers[k], valid_cipher_chains[i][k]) != 0)
+ mismatch = 1;
+ }
+
+ /*
+ * If all ciphers matched and there are exactly nciphers,
+ * then we found the right cipher chain.
+ */
+ if ((k == nciphers) && !mismatch) {
+ cipher = tc_cipher_chains[i];
+ break;
+ }
+ }
+
+ if (cipher == NULL) {
+ tc_log(1, "Invalid cipher: %s\n", cipher_chain);
+ if (!quiet) {
+ fprintf(stderr, "Valid cipher chains are:\n");
+ for (i = 0; valid_cipher_chains[i][0] != NULL; i++) {
+ for (k = 0; valid_cipher_chains[i][k] != NULL;
+ k++) {
+ fprintf(stderr, "%s%c",
+ valid_cipher_chains[i][k],
+ (valid_cipher_chains[i][k+1] != NULL) ?
+ ',' : '\0');
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+ }
+
+ free(tmp_chain_free);
+ return cipher;
+}
+
+struct pbkdf_prf_algo *
+check_prf_algo(char *algo, int quiet)
+{
+ int i, found = 0;
+
+ for (i = 0; pbkdf_prf_algos[i].name != NULL; i++) {
+ if (strcmp(algo, pbkdf_prf_algos[i].name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found && !quiet) {
+ fprintf(stderr, "Valid PBKDF PRF algorithms are: ");
+ for (i = 0; pbkdf_prf_algos[i].name != NULL; i++)
+ fprintf(stderr, "%s ", pbkdf_prf_algos[i].name);
+ fprintf(stderr, "\n");
+ return NULL;
+ }
+
+ return &pbkdf_prf_algos[i];
+}
+
+int
+tc_play_init(void)
+{
+ int error;
+
+ if ((error = tc_build_cipher_chains()) != 0)
+ return error;
+
+ if ((error = tc_crypto_init()) != 0)
+ return error;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
+ * 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.
+ */
+
+/* Version of tcplay */
+#define MAJ_VER 0
+#define MIN_VER 8
+
+
+#define MAX_BLKSZ 4096
+#define MAX_KEYSZ 192
+#define HDRSZ 512
+#define HDR_OFFSET_SYS 31744 /* 512 * (63 -1) */
+#define TC_SIG "TRUE"
+#define MAX_PASSSZ 64
+#define KPOOL_SZ 64
+#define MAX_KFILE_SZ 1048576 /* 1 MB */
+#define MAX_KEYFILES 256
+#define HDR_OFFSET_HIDDEN 65536
+#define SALT_LEN 64
+#define MIN_VOL_BLOCKS 256
+#define MAX_CIPHER_CHAINS 64
+#define DEFAULT_RETRIES 3
+#define ERASE_BUFFER_SIZE 4*1024*1024 /* 4 MB */
+
+/* TrueCrypt Volume flags */
+#define TC_VOLFLAG_SYSTEM 0x01 /* system encryption */
+#define TC_VOLFLAG_INPLACE 0x02 /* non-system in-place-encrypted volume */
+
+#define LOG_BUFFER_SZ 1024
+#if 0
+#define DEBUG 1
+#endif
+
+#include <uuid.h>
+
+struct pbkdf_prf_algo {
+ const char *name;
+ int iteration_count;
+};
+
+struct tc_crypto_algo {
+ const char *name;
+ const char *dm_crypt_str;
+ int klen;
+ int ivlen;
+};
+
+struct tc_cipher_chain {
+ struct tc_crypto_algo *cipher;
+ unsigned char *key;
+ char dm_key[MAX_KEYSZ*2 + 1];
+
+ struct tc_cipher_chain *prev;
+ struct tc_cipher_chain *next;
+};
+
+struct tchdr_enc {
+ unsigned char salt[SALT_LEN]; /* Salt for PBKDF */
+ unsigned char enc[448]; /* Encrypted part of the header */
+} __attribute__((__packed__));
+
+struct tchdr_dec {
+ char tc_str[4]; /* ASCII string "TRUE" */
+ uint16_t tc_ver; /* Volume header format version */
+ uint16_t tc_min_ver;
+ uint32_t crc_keys; /* CRC32 of the key section */
+ uint64_t vol_ctime; /* Volume creation time */
+ uint64_t hdr_ctime; /* Header creation time */
+ uint64_t sz_hidvol; /* Size of hidden volume (set to zero
+ in non-hidden volumes) */
+ uint64_t sz_vol; /* Size of volume */
+ uint64_t off_mk_scope; /* Byte offset of the start of the
+ master key scope */
+ uint64_t sz_mk_scope; /* Size of the encrypted area within
+ the master key scope */
+ uint32_t flags; /* Flag bits
+ (bit 0: system encryption;
+ bit 1: non-system in-place-encrypted volume;
+ bits 2–31 are reserved) */
+ uint32_t sec_sz; /* Sector size (in bytes) */
+ unsigned char unused3[120];
+ uint32_t crc_dhdr; /* CRC32 of dec. header (except keys) */
+ unsigned char keys[256];
+} __attribute__((__packed__));
+
+struct tcplay_info {
+ const char *dev;
+ struct tchdr_dec *hdr;
+ struct tc_cipher_chain *cipher_chain;
+ struct pbkdf_prf_algo *pbkdf_prf;
+ char key[MAX_KEYSZ*2 + 1];
+ off_t start; /* Logical volume offset in table */
+ size_t size; /* Volume size */
+
+ off_t skip; /* IV offset */
+ off_t offset; /* Block offset */
+
+ /* Populated by dm_setup */
+ uuid_t uuid;
+};
+
+void *read_to_safe_mem(const char *file, off_t offset, size_t *sz);
+int get_random(unsigned char *buf, size_t len);
+int secure_erase(const char *dev, size_t bytes, size_t blksz);
+int get_disk_info(const char *dev, size_t *blocks, size_t *bsize);
+int write_mem(const char *dev, off_t offset, size_t blksz, void *mem, size_t bytes);
+int read_passphrase(const char *prompt, char *pass, size_t passlen,
+ time_t timeout);
+
+int tc_crypto_init(void);
+int tc_cipher_chain_populate_keys(struct tc_cipher_chain *cipher_chain,
+ unsigned char *key);
+int tc_encrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
+ unsigned char *iv,
+ unsigned char *in, int in_len, unsigned char *out);
+int tc_decrypt(struct tc_cipher_chain *cipher_chain, 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);
+int apply_keyfiles(unsigned char *pass, size_t pass_memsz, const char *keyfiles[],
+ int nkeyfiles);
+
+struct tchdr_enc *create_hdr(unsigned char *pass, int passlen,
+ struct pbkdf_prf_algo *prf_algo, struct tc_cipher_chain *cipher_chain,
+ 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,
+ struct tc_cipher_chain *cipher_chain, unsigned char *key);
+int verify_hdr(struct tchdr_dec *hdr);
+
+void *_alloc_safe_mem(size_t req_sz, const char *file, int line);
+void _free_safe_mem(void *mem, const char *file, int line);
+void check_and_purge_safe_mem(void);
+
+struct tc_crypto_algo *check_cipher(const char *cipher, int quiet);
+struct tc_cipher_chain *check_cipher_chain(char *cipher_chain, int quiet);
+struct pbkdf_prf_algo *check_prf_algo(char *algo, int quiet);
+
+int tc_play_init(void);
+void tc_log(int err, const char *fmt, ...);
+void print_info(struct tcplay_info *info);
+int adjust_info(struct tcplay_info *info, struct tcplay_info *hinfo);
+int process_hdr(const char *dev, unsigned char *pass, int passlen,
+ struct tchdr_enc *ehdr, struct tcplay_info **pinfo);
+int create_volume(const char *dev, int hidden, const char *keyfiles[],
+ int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles,
+ struct pbkdf_prf_algo *prf_algo, struct tc_cipher_chain *cipher_chain,
+ struct pbkdf_prf_algo *h_prf_algo, struct tc_cipher_chain *h_cipher_chain,
+ char *passphrase, char *h_passphrase, size_t hidden_blocks_in,
+ int interactive);
+int info_volume(const char *device, int sflag, const char *sys_dev,
+ int protect_hidden, const char *keyfiles[], int nkeyfiles,
+ const char *h_keyfiles[], int n_hkeyfiles,
+ char *passphrase, char *passphrase_hidden, int interactive, int retries,
+ time_t timeout);
+int map_volume(const char *map_name, const char *device, int sflag,
+ const char *sys_dev, int protect_hidden, const char *keyfiles[],
+ int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles,
+ char *passphrase, char *passphrase_hidden, int interactive, int retries,
+ time_t timeout);
+int dm_setup(const char *mapname, struct tcplay_info *info);
+int dm_teardown(const char *mapname, const char *device);
+
+typedef void(*summary_fn_t)(void);
+
+extern int tc_internal_verbose;
+extern char tc_internal_log_buffer[];
+extern summary_fn_t summary_fn;
+
+#define alloc_safe_mem(x) \
+ _alloc_safe_mem(x, __FILE__, __LINE__)
+
+#define free_safe_mem(x) \
+ _free_safe_mem(x, __FILE__, __LINE__)
--- /dev/null
+{
+global:
+ tc_api_init;
+ tc_api_uninit;
+ tc_api_create_volume;
+ tc_api_map_volume;
+ tc_api_unmap_volume;
+ tc_api_check_cipher;
+ tc_api_check_prf_hash;
+ tc_api_get_error_msg;
+ tc_api_get_summary;
+local:
+ *;
+};
--- /dev/null
+/*
+ * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "tcplay.h"
+#include "tcplay_api.h"
+
+int
+tc_api_init(int verbose)
+{
+ int error;
+
+ tc_internal_verbose = verbose;
+
+ if ((error = tc_play_init()) != 0)
+ return TC_ERR;
+ else
+ return TC_OK;
+}
+
+int
+tc_api_uninit(void)
+{
+ check_and_purge_safe_mem();
+ return TC_OK;
+}
+
+const char *
+tc_api_get_error_msg(void)
+{
+ return tc_internal_log_buffer;
+}
+
+const char *
+tc_api_get_summary(void)
+{
+ if (summary_fn != NULL) {
+ summary_fn();
+ return tc_internal_log_buffer;
+ }
+
+ return NULL;
+}
+
+int
+tc_api_create_volume(tc_api_opts *api_opts)
+{
+ int nkeyfiles, n_hkeyfiles = 0;
+ int create_hidden;
+ int err;
+
+ if ((api_opts == NULL) ||
+ (api_opts->tc_device == NULL)) {
+ errno = EFAULT;
+ return TC_ERR;
+ }
+
+ if ((err = tc_api_check_cipher(api_opts)) != TC_OK)
+ return TC_ERR;
+
+ if ((err = tc_api_check_prf_hash(api_opts)) != TC_OK)
+ return TC_ERR;
+
+ for (nkeyfiles = 0; (nkeyfiles < MAX_KEYFILES) &&
+ (api_opts->tc_keyfiles != NULL) &&
+ (api_opts->tc_keyfiles[nkeyfiles] != NULL); nkeyfiles++)
+ ;
+
+ create_hidden = 0;
+
+ if (api_opts->tc_size_hidden_in_blocks > 0) {
+ create_hidden = 1;
+ for (n_hkeyfiles = 0; (n_hkeyfiles < MAX_KEYFILES) &&
+ (api_opts->tc_keyfiles_hidden != NULL) &&
+ (api_opts->tc_keyfiles_hidden[n_hkeyfiles] != NULL);
+ n_hkeyfiles++)
+ ;
+ }
+
+ err = create_volume(api_opts->tc_device, create_hidden,
+ api_opts->tc_keyfiles, nkeyfiles,
+ api_opts->tc_keyfiles_hidden, n_hkeyfiles,
+ check_prf_algo(api_opts->tc_prf_hash, 1),
+ check_cipher_chain(api_opts->tc_cipher, 1),
+ check_prf_algo(api_opts->tc_prf_hash_hidden, 1),
+ check_cipher_chain(api_opts->tc_cipher_hidden, 1),
+ api_opts->tc_passphrase, api_opts->tc_passphrase_hidden,
+ api_opts->tc_size_hidden_in_blocks, 0 /* non-interactive */);
+
+ return (err) ? TC_ERR : TC_OK;
+}
+
+int
+tc_api_map_volume(tc_api_opts *api_opts)
+{
+ int nkeyfiles;
+ int err;
+
+ if ((api_opts == NULL) ||
+ (api_opts->tc_device == NULL)) {
+ errno = EFAULT;
+ return TC_ERR;
+ }
+
+ for (nkeyfiles = 0; (nkeyfiles < MAX_KEYFILES) &&
+ (api_opts->tc_keyfiles != NULL) &&
+ (api_opts->tc_keyfiles[nkeyfiles] != NULL); nkeyfiles++)
+ ;
+
+ err = map_volume(api_opts->tc_map_name, api_opts->tc_device,
+ /* sflag */ 0, /* sys_dev */ NULL,
+ /* protect_hidden */ 0, api_opts->tc_keyfiles, nkeyfiles,
+ /* h_keyfiles[] */ NULL, /* n_hkeyfiles */ 0,
+ api_opts->tc_passphrase, /* passphrase_hidden */ NULL,
+ api_opts->tc_interactive_prompt, api_opts->tc_password_retries,
+ (time_t)api_opts->tc_prompt_timeout);
+
+ return (err) ? TC_ERR : TC_OK;
+}
+
+int
+tc_api_unmap_volume(tc_api_opts *api_opts)
+{
+ int err;
+
+ if ((api_opts == NULL) ||
+ (api_opts->tc_map_name == NULL)) {
+ errno = EFAULT;
+ return TC_ERR;
+ }
+
+ err = dm_teardown(api_opts->tc_map_name, api_opts->tc_device);
+ return (err) ? TC_ERR : TC_OK;
+}
+
+int
+tc_api_check_cipher(tc_api_opts *api_opts)
+{
+ struct tc_cipher_chain *chain;
+
+ if (api_opts == NULL || api_opts->tc_cipher == NULL) {
+ errno = EFAULT;
+ return TC_ERR;
+ }
+
+ if ((chain = check_cipher_chain(api_opts->tc_cipher, 1)) != NULL)
+ return TC_OK;
+
+ errno = ENOENT;
+ return TC_ERR;
+}
+
+int
+tc_api_check_prf_hash(tc_api_opts *api_opts)
+{
+ struct pbkdf_prf_algo *prf_hash;
+
+ if (api_opts == NULL || api_opts->tc_prf_hash == NULL) {
+ errno = EFAULT;
+ return TC_ERR;
+ }
+
+ if ((prf_hash = check_prf_algo(api_opts->tc_prf_hash, 1)) != NULL)
+ return TC_OK;
+
+ errno = ENOENT;
+ return TC_ERR;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
+ * 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.
+ */
+
+#define TC_OK 0
+#define TC_ERR -1
+
+typedef struct tc_api_opts {
+ /* Common fields */
+ char *tc_device;
+ char *tc_passphrase;
+ const char **tc_keyfiles;
+
+ /* Fields for mapping */
+ char *tc_map_name;
+ int tc_password_retries;
+ int tc_interactive_prompt;
+ unsigned long tc_prompt_timeout;
+
+ /* Fields for creation */
+ char *tc_cipher;
+ char *tc_prf_hash;
+ char *tc_cipher_hidden;
+ char *tc_prf_hash_hidden;
+ size_t tc_size_hidden_in_blocks;
+ char *tc_passphrase_hidden;
+ const char **tc_keyfiles_hidden;
+} tc_api_opts;
+
+int tc_api_init(int verbose);
+int tc_api_uninit(void);
+int tc_api_create_volume(tc_api_opts *api_opts);
+int tc_api_map_volume(tc_api_opts *api_opts);
+int tc_api_unmap_volume(tc_api_opts *api_opts);
+int tc_api_check_cipher(tc_api_opts *api_opts);
+int tc_api_check_prf_hash(tc_api_opts *api_opts);
+const char *tc_api_get_error_msg(void);
+const char *tc_api_get_summary(void);
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "tcplay_api.h"
+
+int
+main(void)
+{
+ tc_api_opts api_opts;
+ int error;
+
+ error = tc_api_init(/* verbose */1);
+ assert(error == 0);
+
+ memset(&api_opts, 0, sizeof(api_opts));
+ api_opts.tc_device = "/dev/vn1s0";
+ api_opts.tc_passphrase = "apitest2";
+ api_opts.tc_keyfiles = NULL;
+ api_opts.tc_keyfiles_hidden = NULL;
+ api_opts.tc_size_hidden_in_blocks = 12000;
+ api_opts.tc_passphrase_hidden = "apihidden";
+ api_opts.tc_cipher = "AES-256-XTS,TWOFISH-256-XTS,SERPENT-256-XTS";
+ api_opts.tc_cipher_hidden = "SERPENT-256-XTS,TWOFISH-256-XTS";
+ api_opts.tc_prf_hash = "whirlpool";
+ api_opts.tc_prf_hash_hidden = "RIPEMD160";
+
+ error = tc_api_create_volume(&api_opts);
+ if (error)
+ printf("API ERROR: %s\n", tc_api_get_error_msg());
+ assert(error == 0);
+
+ error = tc_api_uninit();
+ assert(error == 0);
+
+ error = tc_api_init(/*verbose */ 1);
+ assert(error == 0);
+
+ api_opts.tc_passphrase = NULL;
+ api_opts.tc_map_name = "dragonfly-test";
+ api_opts.tc_password_retries = 5;
+ api_opts.tc_interactive_prompt = 1;
+ api_opts.tc_prompt_timeout = 5;
+ error = tc_api_map_volume(&api_opts);
+ if (error)
+ printf("API MAP ERROR: %s\n", tc_api_get_error_msg());
+ assert(error == 0);
+
+ system("dmsetup ls");
+ tc_api_unmap_volume(&api_opts);
+
+ error = tc_api_uninit();
+ assert(error == 0);
+
+ return 0;
+}
+
startslip \
swapon \
sysctl \
+ tcplay \
tunefs \
udevd \
umount \
--- /dev/null
+TCPLAY_DIR= ${.CURDIR}/../../lib/libtcplay
+
+PROG= tcplay
+MAN= tcplay.8
+WARNS?= 6
+
+SRCS= main.c
+SRCS+= tcplay.c crc32.c safe_mem.c io.c crypto-dev.c hdr.c
+
+LDADD= -lcrypto -ldevmapper -lprop -lutil
+
+CFLAGS+= -I${TCPLAY_DIR}
+
+.PATH: ${TCPLAY_DIR}
+
+.include <bsd.prog.mk>
--- /dev/null
+/*
+ * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
+ * 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 <sys/types.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <err.h>
+#include <time.h>
+#include <libutil.h>
+
+#include "tcplay.h"
+
+static
+void
+sig_handler(int sig)
+{
+ if ((sig == SIGUSR1 || sig == SIGINFO) && (summary_fn != NULL))
+ summary_fn();
+}
+
+static
+void
+usage(void)
+{
+ fprintf(stderr,
+ "Usage: tcplay <command> [options]\n"
+ "Valid commands and its arguments are:\n"
+ " -c, --create\n"
+ "\t Creates a new TC volume on the device specified by -d or --device\n"
+ " -i, --info\n"
+ "\t Gives information about the TC volume specified by -d or --device\n"
+ " -m <mapping name>, --map=<mapping name>\n"
+ "\t Creates a dm-crypt mapping with the given name for the device\n"
+ "\t specified by -d or --device\n"
+ "\n"
+ "Valid options and its arguments for 'create' are:\n"
+ " -a <pbkdf prf algorithm>, --pbkdf-prf=<pbkdf prf algorithm>\n"
+ "\t specifies which hashing function to use for the PBKDF password\n"
+ "\t derivation when creating a new volume\n"
+ "\t To see valid options, specify -a help\n"
+ " -b <cipher>, --cipher=<cipher>\n"
+ "\t specifies which cipher to use when creating a new TC volume\n"
+ "\t To see valid options, specify -b help\n"
+ " -x <pbkdf prf algorithm>, --pbkdf-prf=<pbkdf prf algorithm>\n"
+ "\t specifies which hashing function to use for the PBKDF password\n"
+ "\t derivation when creating a new hidden volume. By default, the\n"
+ "\t same as for the outer volume will be used\n"
+ "\t To see valid options, specify -a help\n"
+ " -y <cipher>, --cipher=<cipher>\n"
+ "\t specifies which cipher to use when creating a new hidden volume.\n"
+ "\t By default, the same as for the outer volume will be used\n"
+ "\t To see valid options, specify -b help\n"
+ " -g, --hidden\n"
+ "\t specifies that the newly created volume will contain a hidden volume\n"
+ "\n"
+ "Valid options and its arguments for 'info' and 'map' are:\n"
+ " -e, --protect-hidden\n"
+ "\t protect a hidden volume when mounting the outer volume\n"
+ " -s <disk path>, --system-encryption=<disk path>\n"
+ "\t specifies that the disk (e.g. /dev/da0) is using system encryption\n"
+ "\n"
+ "Valid options and its arguments common to all commands are:\n"
+ " -d <device path>, --device=<device path>\n"
+ "\t specifies the path to the volume to operate on (e.g. /dev/da0s1)\n"
+ " -k <key file>, --keyfile=<key file>\n"
+ "\t specifies a key file to use for the password derivation, can appear\n"
+ "\t multiple times\n"
+ " -f <key file>, --keyfile-hidden=<key file>\n"
+ "\t specifies a key file to use for the hidden volume password derivation\n"
+ "\t This option is only valid in combination with -e, --protect-hidden\n"
+ "\t or -g, --hidden\n"
+ );
+
+ exit(1);
+}
+
+static struct option longopts[] = {
+ { "create", no_argument, NULL, 'c' },
+ { "cipher", required_argument, NULL, 'b' },
+ { "cipher-hidden", required_argument, NULL, 'y' },
+ { "hidden", no_argument, NULL, 'g' },
+ { "pbkdf-prf", required_argument, NULL, 'a' },
+ { "pbkdf-prf-hidden", required_argument, NULL, 'x' },
+ { "info", no_argument, NULL, 'i' },
+ { "map", required_argument, NULL, 'm' },
+ { "keyfile", required_argument, NULL, 'k' },
+ { "keyfile-hidden", required_argument, NULL, 'f' },
+ { "protect-hidden", no_argument, NULL, 'e' },
+ { "device", required_argument, NULL, 'd' },
+ { "system-encryption", required_argument, NULL, 's' },
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+};
+
+int
+main(int argc, char *argv[])
+{
+ const char *dev = NULL, *sys_dev = NULL, *map_name = NULL;
+ const char *keyfiles[MAX_KEYFILES];
+ const char *h_keyfiles[MAX_KEYFILES];
+ int nkeyfiles;
+ int n_hkeyfiles;
+ int ch, error;
+ int sflag = 0, info_vol = 0, map_vol = 0, protect_hidden = 0,
+ create_vol = 0, contain_hidden = 0;
+ struct pbkdf_prf_algo *prf = NULL;
+ struct tc_cipher_chain *cipher_chain = NULL;
+ struct pbkdf_prf_algo *h_prf = NULL;
+ struct tc_cipher_chain *h_cipher_chain = NULL;
+
+ if ((error = tc_play_init()) != 0) {
+ fprintf(stderr, "Initialization failed, exiting.");
+ exit(1);
+ }
+
+ atexit(check_and_purge_safe_mem);
+ signal(SIGUSR1, sig_handler);
+ signal(SIGINFO, sig_handler);
+
+ nkeyfiles = 0;
+ n_hkeyfiles = 0;
+
+ while ((ch = getopt_long(argc, argv, "a:b:cd:efgh:ik:m:s:vx:y:",
+ longopts, NULL)) != -1) {
+ switch(ch) {
+ case 'a':
+ if (prf != NULL)
+ usage();
+ if ((prf = check_prf_algo(optarg, 0)) == NULL) {
+ if (strcmp(optarg, "help") == 0)
+ exit(0);
+ else
+ usage();
+ /* NOT REACHED */
+ }
+ break;
+ case 'b':
+ if (cipher_chain != NULL)
+ usage();
+ if ((cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
+ if (strcmp(optarg, "help") == 0)
+ exit(0);
+ else
+ usage();
+ /* NOT REACHED */
+ }
+ break;
+ case 'c':
+ create_vol = 1;
+ break;
+ case 'd':
+ dev = optarg;
+ break;
+ case 'e':
+ protect_hidden = 1;
+ break;
+ case 'f':
+ h_keyfiles[n_hkeyfiles++] = optarg;
+ break;
+ case 'g':
+ contain_hidden = 1;
+ break;
+ case 'i':
+ info_vol = 1;
+ break;
+ case 'k':
+ keyfiles[nkeyfiles++] = optarg;
+ break;
+ case 'm':
+ map_vol = 1;
+ map_name = optarg;
+ break;
+ case 's':
+ sflag = 1;
+ sys_dev = optarg;
+ break;
+ case 'v':
+ printf("tcplay v%d.%d\n", MAJ_VER, MIN_VER);
+ exit(0);
+ /* NOT REACHED */
+ case 'x':
+ if (h_prf != NULL)
+ usage();
+ if ((h_prf = check_prf_algo(optarg, 0)) == NULL) {
+ if (strcmp(optarg, "help") == 0)
+ exit(0);
+ else
+ usage();
+ /* NOT REACHED */
+ }
+ break;
+ case 'y':
+ if (h_cipher_chain != NULL)
+ usage();
+ if ((h_cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
+ if (strcmp(optarg, "help") == 0)
+ exit(0);
+ else
+ usage();
+ /* NOT REACHED */
+ }
+ break;
+ case 'h':
+ case '?':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Check arguments */
+ if (!((map_vol || info_vol || create_vol) && dev != NULL) ||
+ (map_vol && info_vol) ||
+ (map_vol && create_vol) ||
+ (create_vol && info_vol) ||
+ (contain_hidden && !create_vol) ||
+ (sflag && (sys_dev == NULL)) ||
+ (map_vol && (map_name == NULL)) ||
+ (!(protect_hidden || create_vol) && n_hkeyfiles > 0)) {
+ usage();
+ /* NOT REACHED */
+ }
+
+ /* Create a new volume */
+ if (create_vol) {
+ error = create_volume(dev, contain_hidden, keyfiles, nkeyfiles,
+ h_keyfiles, n_hkeyfiles, prf, cipher_chain, h_prf,
+ h_cipher_chain, NULL, NULL,
+ 0, 1 /* interactive */);
+ if (error) {
+ tc_log(1, "could not create new volume on %s\n", dev);
+ }
+ } else if (info_vol) {
+ error = info_volume(dev, sflag, sys_dev, protect_hidden,
+ keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, NULL, NULL,
+ 1 /* interactive */, DEFAULT_RETRIES, 0);
+ } else if (map_vol) {
+ error = map_volume(map_name,
+ dev, sflag, sys_dev, protect_hidden,
+ keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, NULL, NULL,
+ 1 /* interactive */, DEFAULT_RETRIES, 0);
+ }
+
+ return error;
+}
--- /dev/null
+.\"
+.\" Copyright (c) 2011
+.\" The DragonFly Project. 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.
+.\" 3. Neither the name of The DragonFly Project nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific, prior written permission.
+.\"
+.\" 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.
+.\"
+.Dd July 5, 2011
+.Dt TCPLAY 8
+.Os
+.Sh NAME
+.Nm tcplay
+.Nd tool to manage TrueCrypt volumes
+.Sh SYNOPSIS
+.Nm
+.Fl c
+.Fl d Ar device
+.Op Fl g
+.Op Fl a Ar pbkdf_hash
+.Op Fl b Ar cipher
+.Op Fl k Ar keyfile
+.Op Fl f Ar keyfile_hidden
+.Nm
+.Fl i
+.Fl d Ar device
+.Op Fl e
+.Op Fl s Ar system_device
+.Nm
+.Fl m Ar mapping
+.Fl d Ar device
+.Op Fl e
+.Op Fl s Ar system_device
+.Sh DESCRIPTION
+The
+.Nm
+utility provides full support for creating and opening/mapping
+TrueCrypt-compatible volumes.
+It supports the following commands, each with a set of options
+detailed further below:
+.Bl -tag -width indent
+.It Fl c , Fl -create
+Create a new encrypted TrueCrypt volume on the device
+specified by
+.Fl -device .
+.It Fl i , Fl -info
+Print out information about the encrypted device specified by
+.Fl -device .
+.It Fl m Ar mapping , Fl -map Ns = Ns Ar mapping
+Map the encrypted TrueCrypt volume on the device specified by
+.Fl -device
+as a
+.Xr dm 4
+mapping called
+.Ar mapping .
+.El
+.Pp
+Options common to all commands are:
+.Bl -tag -width indent
+.It Fl d Ar device , Fl -device Ns = Ns Ar device
+Specifies the disk
+.Ar device
+on which the TrueCrypt volume resides/will reside.
+This option is mandatory for all commands.
+.It Fl k Ar keyfile , Fl -keyfile Ns = Ns Ar keyfile
+Specifies a
+.Ar keyfile
+to use in addition to the passphrase.
+This option can appear multiple times; if so, multiple
+keyfiles will be used.
+.It Fl f Ar keyfile , Fl -keyfile-hidden Ns = Ns Ar keyfile
+Specifies a
+.Ar keyfile
+to use in addition to the passphrase when either creating a
+hidden volume or when protecting a hidden volume while mapping
+or querying the outer volume.
+If you only intend to map a hidden volume, the
+.Fl -keyfile
+option has to be used.
+This option can appear multiple times; if so, multiple
+keyfiles will be used.
+.El
+.Pp
+Additional options for the
+.Fl -create
+command are:
+.Bl -tag -width indent
+.It Fl a Ar pbkdf_hash , Fl -pbkdf-prf Ns = Ns Ar pbkdf_hash
+Specifies with hash algorithm to use for the PBKDF2 password
+derivation.
+To see which algorithms are supported, specify
+.Fl -pbkdf-prf Ns = Ns Ar help .
+.It Fl b Ar cipher , Fl -cipher Ns = Ns Ar cipher
+Specifies with cipher algorithm or cascade of ciphers to use
+to encrypt the new volume.
+To see which algorithms are supported, specify
+.Fl -cipher Ns = Ns Ar help .
+.It Fl x Ar pbkdf_hash , Fl -pbkdf-prf-hidden Ns = Ns Ar pbkdf_hash
+Specifies with hash algorithm to use for the PBKDF2 password
+derivation for the hidden volume.
+Only valid in conjunction with
+.Fl -hidden .
+If no algorithm is specified, the same as for the outer volume
+will be used.
+To see which algorithms are supported, specify
+.Fl -pbkdf-prf-hidden Ns = Ns Ar help .
+.It Fl y Ar cipher , Fl -cipher-hidden Ns = Ns Ar cipher
+Specifies with cipher algorithm or cascade of ciphers to use
+to encrypt the hidden volume on the new TrueCrypt volume.
+Only valid in conjunction with
+.Fl -hidden .
+If no cipher is specified, the same as for the outer volume
+will be used.
+To see which algorithms are supported, specify
+.Fl -cipher-hidden Ns = Ns Ar help .
+.It Fl g, Fl -hidden
+Specifies that the newly created volume will contain a hidden
+volume.
+The keyfiles applied to the passphrase for the hidden
+volume are those specified by
+.Fl -keyfile-hidden .
+The user will be prompted for the size of the hidden volume
+interactively.
+.El
+.Pp
+Additional options for the
+.Fl -info
+and
+.Fl -map
+commands are:
+.Bl -tag -width indent
+.It Fl e, Fl -protect-hidden
+Specifies that an outer volume will be queried or mapped, but
+its reported size will be adjusted accordingly to the size of
+the hidden volume contained in it.
+Both the hidden volume and outer volume passphrase and keyfiles
+will be required.
+.It Fl s Ar system_device , Fl -system-encryption Ns = Ns Ar
+system_device
+This option is required if you are attempting to access a device
+that uses system encryption, for example an encrypted
+.Tn Windows
+system partition.
+The
+.Fl -device
+option will point at the actual encrypted partition, while the
+.Ar system_device
+argument will point to the parent device (i.e.\& underlying physical disk)
+of the encrypted partition.
+.El
+.Sh EXAMPLES
+Create a new TrueCrypt volume on
+.Pa /dev/vn0
+using the cipher cascade
+of AES and Twofish and the Whirlpool hash algorithm for
+PBKDF2 password derivation and two keyfiles,
+.Pa one.key
+and
+.Pa two.key :
+.Bd -ragged -offset indent
+.Nm Fl -create
+.Fl -device Ns = Ns Ar /dev/vn0
+.Fl -cipher Ns = Ns Ar AES-256-XTS,TWOFISH-256-XTS
+.Fl -pbkdf-prf Ns = Ns Ar whirlpool
+.Fl -keyfile Ns = Ns Ar one.key
+.Fl -keyfile Ns = Ns Ar two.key
+.Ed
+.Pp
+Map the outer volume on the TrueCrypt volume on
+.Pa /dev/vn0
+as
+.Sy truecrypt1 ,
+but protect the hidden volume, using the keyfile
+.Pa hidden.key ,
+from being overwritten:
+.Bd -ragged -offset indent
+.Nm Fl -map Ns = Ns Ar truecrypt1
+.Fl -device Ns = Ns Ar /dev/vn0
+.Fl -protect-hidden
+.Fl -keyfile-hidden Ns = Ns Ar hidden.key
+.Ed
+.Pp
+Map the hidden volume on the TrueCrypt volume on
+.Pa /dev/vn0
+as
+.Sy truecrypt2 ,
+using the keyfile
+.Pa hidden.key :
+.Bd -ragged -offset indent
+.Nm Fl -map Ns = Ns Ar truecrypt2
+.Fl -device Ns = Ns Ar /dev/vn0
+.Fl -keyfile Ns = Ns Ar hidden.key
+.Ed
+.Sh SEE ALSO
+.Xr crypttab 5 ,
+.Xr cryptsetup 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Dx 2.11 .
+.Sh AUTHORS
+.An Alex Hornung
+